以下の質問について、どうかご教授願えますでしょうか。
【やりたい事】
あるアプリケーションでスレッドを利用する。スレッドクラスは、共有ライブラリの中で
定義している。
【結果】
リンク時に下記のエラーが発生します。
AAADlg.obj : error LNK2001: 外部シンボル "public: static struct CRuntimeClass
const Thread::classThread (?classThread@Thread@@2UCRuntimeClass@@B) は未解決です
Debug/AAA.exe : fatal error LNK1120: 外部参照 1 が未解決です。
link.exe の実行エラー
【開発環境】
・WindowsXP
・Visual C++ 6.0(サービスパック6)
当方で行った事は以下の通りです。
「MFCプロジェクト作成」
1. AAAという名前のプロジェクト作成する。
MFC AppWizard(exe)を選択。ウィザードは全てデフォルト。
2. DLLのThreadクラスヘッダをインクルードする。
BBB/Thread.hを記述する。
3. DLLをリンクする。
プロジェクトの設定→リンク→オブジェクト/ライブラリモジュールに、「BBB/Debug
/BBB.lib」を記述する。
4. ボタンを追加し、クリック時にスレッドが作成されるようにする。
OnButton1()に以下のコードを追加。
CRuntimeClass *pRuntime = RUNTIME_CLASS(Thread);
Thread *pThread = (Thread*)pRuntime->CreateObject();
pThread->CreateThread(0, 0, NULL);
「DLLプロジェクト作成」
1. BBBという名前のプロジェクト作成する。
MFC AppWizard(dll)を選択。MFCのスタティックライブラリを使用した標準DLLに
チェック。
2. Threadという名前のスレッドクラスを追加する。
クラスの種類は「MFCクラス」、基本クラスは「CWinThread」を継承する。
なお、AAAとBBBのフォルダ構成は以下の通り。
AAA - BBB - Debug
・Thread.h ・BBB.dll
・BBB.lib
直接の答えではない。
MFCのクラスなどを渡す場合
バージョンとかリリース/デバッグビルドとかコンパイルの設定とかの違いで
互換性なくなる。
EXEとDLLでともにバージョンとかリリース/デバッグビルドとかコンパイルの設定を
同一に保たなければいけない。
またEXEとDLLでともにMFCはDLL版を使うなどの制限が付くよ。
BBB.dllでThreadクラスはdllexportしてる?
AAAでThreadクラスをdllimportしてる?
wclrp ( 'o')さん、ご回答ありがとうございます。
>BBB.dllでThreadクラスはdllexportしてる?
>AAAでThreadクラスをdllimportしてる?
両方ともしてません。
ただ、これをしなくても実行時に問題が出るだけで、リンクは通ってもいいんじゃない?
と思ってます。。
なお、更に調べたところ、
CRuntimeClass *pRuntime = RUNTIME_CLASS(Thread);
の行でリンクエラーが発生してます。
プロジェクトを分けた場合に、以下のような制約はあるのでしょうか?
片方のプロジェクトで、
・「DECLARE_DYNCREATE」と「IMPLEMENT_DYNCREATE」を使用したクラスを定義
もう片方のプロジェクトから、
・「RUNTIME_CLASS」を使ってCRuntimeClassを得る
exportしていないクラスや関数はDLL外部からは見えないと
思いましたけれど。
多分、インポート用のライブラリに情報が出てこないと思います。
exportされていないクラスは外部公開されていないクラスなので
基本的にはリンクできないと思うのですけれど、
リンクは出来て良いという話は何処から来ているのでしょう?
プロジェクトを分けない場合、プロジェクト内部でのリンクは
objファイルのリンクで行なわれますからexportする必要は有りませんが、
プロジェクトを分けてしまうと別のロードモジュールになってしまうので
exportでモジュールの外部に公開しないと外部からは参照できないと思います。
後学の為に聞いておきたいのですが、
「RUNTIME_CLASS」を使ってCRuntimeClassを得てからCreateObjectしている意味は
何でしょう?
こうすればインスタンスを作成できるのはわかりますが、
そうする理由が良く分からないので。
普通にnewで作成するのと何が違うのかなと。
うーーん、
UIスレッドを作りたいのかな?
MFCの場合、拡張DLLになるのでスタティックリンクは出来ないですね。
> 「DLLプロジェクト作成」
> 1. BBBという名前のプロジェクト作成する。
> MFC AppWizard(dll)を選択。MFCのスタティックライブラリを使用した標準DLLに
> チェック。
共有DLLを使用した拡張DLLですね。
なので、wclrpさんの意見どおり、互換性の問題が発生します。
> 2. Threadという名前のスレッドクラスを追加する。
> クラスの種類は「MFCクラス」、基本クラスは「CWinThread」を継承する。
追加→クラスの新規作成で追加したのなら、
> CRuntimeClass *pRuntime = RUNTIME_CLASS(Thread);
> Thread *pThread = (Thread*)pRuntime->CreateObject();
これはいらないですね。
皆様、ご教授ありがとうございます。一応、下記方法で解決しました。
ただ、これでは不十分だ!と言う点がございましたら、ご指摘頂けると助かります。
DLLプロジェクトで「共有DLLを使用した拡張DLL」にチェックし、公開するThreadクラス
に「AFX_EXT_CLASS」キーワードをつけたら、別プロジェクトから生成し、使用すること
が出来ました。
以下、皆様の質問にお答えして参ります。
PATIOさん
>リンクは出来て良いという話は何処から来ているのでしょう?
「プロジェクトの設定でBBB.libをリンクしているから、シンボルは解決できるんじゃな
いの?」と考えての発言でした。(調べたわけではありません。実際、MFC派生クラス以外
は解決できてましたので・・・)
dllexportやexportもしくは、.defファイルで定義するなど必要だったんですね。。
>普通にnewで作成するのと何が違うのかなと。
ITOさんのおっしゃる通り、UIスレッドを作成する為です。
ITOさん
>これはいらないですね。
申し訳ありません。いらないというのがよくわかりませんでした。
別の宣言の方法を使え、ということでしょうか?
> これはいらないですね。
>> 申し訳ありません。いらないというのがよくわかりませんでした。
>> 別の宣言の方法を使え、ということでしょうか?
>> クラスの種類は「MFCクラス」、基本クラスは「CWinThread」を継承する。
> 追加→クラスの新規作成で追加したのなら、
これを、行なえば「Thread」クラスのソースリストに
> CRuntimeClass *pRuntime = RUNTIME_CLASS(Thread);
> Thread *pThread = (Thread*)pRuntime->CreateObject();
が組み込まれます。
あとは、UIスレッドとして動作させれば動くはずです。
ただ、DLLで「UIスレッド」を動作させるのはあまりお勧めできません。
どうしても必要ですか?
ITOさん
ありがとうございます。理由がわかりました。
>ただ、DLLで「UIスレッド」を動作させるのはあまりお勧めできません。
UIスレッドを選んだ理由としては以下の通りです。
これは以下の理由からです。
まず、このスレッド(以下スレッドA)は更に別のスレッド(以下スレッドB)と通信を行う必
要があります。
その通信方法としては、以下の通りです。
1. スレッドAから他社提供DLLの公開APIをコールする
2. 公開APIの中でスレッドBが生成され、公開APIはリターンする
3. スレッドBからスレッドAへのメッセージを待つ
ここで、スレッドBが実装しているメッセージ送信関数は「PostMessage」のようなので
す。(提供されたDLLの為、実装は確認できないのですが、公開APIの引数がHWNDとなって
いるので恐らくそうかと)
よって、スレッドAはウインドウを持つ必要がある為、UIスレッドを選択しました。
>ここで、スレッドBが実装しているメッセージ送信関数は「PostMessage」のようなの
>です。(提供されたDLLの為、実装は確認できないのですが、公開APIの引数がHWNDとな
>っているので恐らくそうかと)
公開している人に聞いてみるのもいいと思います。
UIスレッドを薦めない理由ですが、
1.UIに限らずにスレッドをDLLに組込むこと自体が難しい。
・引継ぐ変数をどうするか?
・終了の方法等
2.UIスレッドに限った問題
・親スレッドがAPIのためHWNDの継承が難しい。
「HWND == NULL」になり、例外で異常終了になりやすい。
HWNDのチェックが必要。
3.メッセージ処理の問題
でしょうか。
探すとまだありそうに思えます。
問題は解決したので閉じます。
皆様、ありがとうございました。