なんだか初歩的な質問ですいませんが。。
現在、MFCでDLL内にダイアログとそのイベントハンドラなどを記述したものを作成し、
それを外部の人に呼び出して使ってもらおうとプログラムを作成しています。
CDialogを継承したクラスを作成し、そのクラスをエクスポートするまではできたのです
が、使用する側でCreateを行うと、Debug Assertionのエラーで落ちてしまいました。
以下のコードがそのときの概要です。
<DLL側>
CMyDialog : public CDialog
{
//{{AFX_DATA(CMyDialog)
enum { IDD = IDD_DIALOG_MYDIALOG };
//}}AFX_DATA
// 自作メソッドがある
// IDD_DIALOG_MYDIALOGのIDを持つダイアログが、リソースとして存在
};
// 外の人がIDDを気にせずウィンドウ作成ができるようにするため、ラッパクラスを作
成
// MYCLASS_APIは、エクスポート側では、__declspec(dllexport)が入る
#include resource.h
class MYCLASS_API CDialogWrapper
{
protected:
CMyDialog m_Dialog;
public:
void Create( CWnd *pParent )
{
m_Dialog.Create( IDD_DIALOG_MYDIALOG , pParent );
}
};
<使用側>
CTestDialog::OnInitDialog(...)
{
CDialogWrapper m_DialogWrapper;
m_DialogWrapper.Create( this ); // <-ここで落ちる
}
なお、環境はVisual C++6.0 , Windowx XPです。
あと、作成時のプロジェクトは、MFC AppWizard(dll)で作成しています。
ぱっと見簡単そうなものなのに、つまずいてしまっているので凹んでいるところです
が、なにかしらご教授いただけましたら幸いです。
よろしくお願い申し上げます。
何処のモジュールで落ちたのですか?
バックとレースと共に希望
一番簡単な話として、、DLLとEXEのビルド条件があっていますか?
マルチスレッドライブラリなのか?デバッグビルドなのか?等
投稿者のぱんぷきんです。
ご連絡いただきまいして、ありがとうございます。
Bosscatさん>何処のモジュールで落ちたのですか?
Bosscatさん>バックとレースと共に希望
自作クラスのCreateからたどっていったところ、
1:m_Dialog.Create( IDD_DIALOG_MYDIALOG , pParent );
( pParent = 0x00 )
2:(MFC--AFXWIN2.INL内)
_AFXWIN_INLINE BOOL CDialog::Create(UINT nIDTemplate, CWnd* pParentWnd)
{ return Create(MAKEINTRESOURCE(nIDTemplate), pParentWnd); }
( nIDTemplate = 1001 , これは、DLL側のダイアログのリソース番号と一致 )
3:(MFC--DLGCORE.CPP内)
CDialog::Create( LPCTSTR lpszTemplateName, CWnd* pParentWnd)関数内の
if (!_AfxCheckDialogTemplate(lpszTemplateName, FALSE))
で落ちていました。lpszTemplateNameが評価できなくなっていました。
( lpszTemplateName 0x000003e9 " --- CXX0030: Error: 式を評価できません )
あと、DLLとEXEのビルド条件ですが、これは、どちらもマルチスレッド(DLL,デバッグ)
の状態でした。
(プロジェクトの設定 -- C/C++ -- コード生成 -- 使用するランタイムライブラリの箇
所 )
>あと、DLLとEXEのビルド条件ですが、これは、どちらもマルチスレッド(DLL,デバッグ)
>の状態でした。
エクスポートするクラスの宣言を見る限り、作成する DLL は MFC 拡張 DLL で
なければなりません。
あと、エクスポートクラスの .h ファイルに
#include resource.h
などとした場合、EXE 側はどの resource.h を読もうとするか、わかりますか?
投稿者のぱんぷきんです。
ご連絡いただきまして、ありがとうございます。
お話いただいたことへの回答と、その後の状態について、申し上げます。
zxcvさん>エクスポートするクラスの宣言を見る限り、作成する DLL は MFC 拡張 DLL
で
zxcvさん>なければなりません。
MFCで作成するとき、作成するDLLの種類を「MFCの拡張DLL(MFCの共有DLL使用)」にして
いなかったかもしれません。そのあたりはもう1度確認してみます。
zxcvさん>あと、エクスポートクラスの .h ファイルに
zxcvさん>#include resource.h
zxcvさん>などとした場合、EXE 側はどの resource.h を読もうとするか、わかります
か?
勉強不足で申し訳ないのですが、、単純に考えると、EXE側は、EXEプロジェクト内の
resource.hを読みに行くと思います。となると、今回の場合、EXE側でのresource.hを読
みに行き、誤ったリソース情報を取る恐れがあるのでしょうか?
それと、先ほど、外国のホームページなども検索してみたところ、DLL内でダイアログを
呼び出している箇所の前で、 AFX_MANAGE_STATE( AfxGetStaticModuleState() );
という関数を呼び出すと良いとの情報があったので、試してみたところ、一応目的どお
り、EXE側から、DLL内のダイアログを呼び出すことができました。
(情報のあったところ)
http://www.codeguru.com/Cpp/W-P/dll/article.php/c101/
(変更したソース)
#include resource.h
class MYCLASS_API CDialogWrapper
{
protected:
CMyDialog m_Dialog;
public:
void Create( CWnd *pParent )
{
AFX_MANAGE_STATE( AfxGetStaticModuleState() ); // <-- これを入れる
m_Dialog.Create( IDD_DIALOG_MYDIALOG , pParent );
}
};
まだ、この関数の意味がよくわかっていない状態ですので、現在調べています。
AFX_MANAGE_STATE( AfxGetStaticModuleState() );
で動く、ってのなら MFC 拡張 DLL ではありません。
たぶん MFC レギュラー DLL です。
で、最初に言ったようにこのクラスは MFC 拡張 DLL にしないとだめです。
基本的に、MFC クラスの姿が .h ファイルの中で見えているような
クラスのエクスポートは MFC 拡張 DLL 以外では使えません。
#かなり制限された条件でならうまくいってしまうかもしれないですが、
#それに惑わされてはいけません
MFC レギュラー DLL の場合、DLL が用意する MFC クラスへのアクセスは、
DLL からしか行ってはいけません。
MFC 派生クラスを直接エクスポートするのはもちろんだめですが、
今回の例では、内部に CDialog 派生クラスのメンバー変数が見えてしまっています。
クラス内部に CDialog のメンバー変数が見えるってことは、
CDialog のコンストラクタ・デストラクタなどを EXE が処理し、
その他を DLL が処理するようなことになってしまうので、かなり危険です。
補足です。
MFC 拡張 DLL を作成する場合、DLL 内で使用するリソースの ID 番号は、
EXE で使用するリソースの ID 番号と重複しないよう、
EXE 作成者に周知しておかないといけません。
そのかわり、MFC クラスのエクスポート・インポートやクラスオブジェクトの
相互アクセスは自由自在です(AFX_MANAGE_STATE を使用する必要もありません)。
MFC を使用する DLL の作成方法、制限事項についての詳細は、
MSDN ライブラリヘルプに詳しく書かれているのでよく読んでください。
投稿者のぱんぷきんです。
ありがとうございます。
拡張DLL形式に変更して、作業をやってみます。
本来でしたら、できた結果をご報告するのが筋ではありますが、来週から1週間ほど出
張のため、すぐにはご報告できません。
Bosscatさま、zxcvさま、ご回答ならびに、補足をいただきましてありがとうございまし
た。
zxcvさん
便乗質問です!!
私は、レギュラーDLL及びMFCのダイナミックリンクであれば、
DLL-EXE間のダイアログは可能であり、
その場合リソースアクセスを一時的に解決する手段として
AFX_MANAGE_STATE(AfxGetStaticModuleHandle());
が必要になると考えていました。。
DLL-EXE共にMFCを使用する場合、拡張DLLを使えばリソース管理はMFC自体が勝手に解決
してくれる、、
位に考えていましたが、、
もちろん、MFCの利用するリソースを全てプログラマーが管理できるわけではないので
DLLの動的ロード&アンロードをする場合は綺麗に行かない場合が有りそうですが、
EXEとDLLの寿命が同じ場合、問題無さそうに思えますが、、どうでしょう?
私自身、ここら辺を詳しく考えたことが無かったので、誤解が有るかも知れません。
指摘を頂けると助かります!
もし、技術的なリンク等を頂けれるのならば、勝手に調べて、またココに書き込みます
が・・・
たびたびスミマセン。。
上記のイメージを図解してみます。
①EXE側からDLL側のCDialog.Create()等を呼ぶと「リソースⅠ」にアクセスしてしまう。
②EXE側からDLL側関数を叩く場合DLL関数内で
AFX_MANAGE_STATE(AfxGetStaticModuleHandle());
を行うとリソースⅡにアクセスできるので、DLL側のMFCを使える。
-----------------------------| |----------------------------|
| EXE 側 | | DLL 側 |
| | | |
| | | |
| ------------------ | | ------------------ |
| | リソースⅠ | | | | リソースⅡ | |
------------------------------ ------------------------------
↓ダイナミックリンク ↓ダイナミックリンク
-------------------------------------------------------------
| MFC |
Bosscat さん、こんにちは。
MFC DLL に絡む問題はリソース解決だけではありません。
詳しくは MSDN を参照していただきたいですが、
確か、以下のような制限事項がどこかに記載されていたと思います。
・MFC 派生クラスをエクスポートするなら MFC 拡張 DLL を使用すること
・MFC レギュラー DLL では MFC 派生クラスのポインタ/参照を渡して
モジュール間で相互に操作するようなことをしてはならない
その制約を知っているので、私は無謀な実験をやってみたことはほとんどありません。
ですから、細かいこともよくわかってません。
しかし、実際にやってみてはっきりわかっている問題はひとつだけ知っています。
EXE にて CObject 派生クラスを new 生成し、DLL にそのポインタ(または参照)を渡し、
そのポインタ(または参照)を使って Serialize() を呼び出す。
ということをやると、拡張 DLL では問題ないですが、レギュラー DLL では
おそらくクラッシュします。
他に、どのようなことがあるかはわかりません。
保証されてないことをやるのは各自の自由ですが、推奨する気にはなりません。
zxcv さん、回答有難うございます。
> ・MFC 派生クラスをエクスポートするなら MFC 拡張 DLL を使用すること
> ・MFC レギュラー DLL では MFC 派生クラスのポインタ/参照を渡して
> モジュール間で相互に操作するようなことをしてはならない
以上の件はMSDN内 レギュラーDLLの項で確認できました!
そこに関連する情報が有ったので抜粋してコピペしておきます。
-----------------
レギュラー DLL 内のメモリ割り当てはすべて、DLL の範囲内に収める必要があります。
したがって、以下のポインタを呼び出し側の実行可能ファイルとやり取りすることはでき
ません。
・MFC オブジェクトへのポインタ
・MFC によって割り当てられたメモリへのポインタ
呼び出し側の実行可能ファイルと DLL との間で上のポインタや MFC 派生オブジェクトを
やり取りする場合は、拡張 DLL を作成する必要があります。
-----------------
メモリーのやり取りは基本的に負荷ということですね・・・