共有ライブラリの中でスレッドクラスを定義。アプリからそのスレッド利用したい。

[戻る]
KJ 2010/05/06(木) 16:09:38
以下の質問について、どうかご教授願えますでしょうか。

【やりたい事】
あるアプリケーションでスレッドを利用する。スレッドクラスは、共有ライブラリの中で
定義している。

【結果】
リンク時に下記のエラーが発生します。
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

wclrp ( 'o') 2010/05/06(木) 23:56:10
直接の答えではない。
MFCのクラスなどを渡す場合
バージョンとかリリース/デバッグビルドとかコンパイルの設定とかの違いで
互換性なくなる。
EXEとDLLでともにバージョンとかリリース/デバッグビルドとかコンパイルの設定を
同一に保たなければいけない。
またEXEとDLLでともにMFCはDLL版を使うなどの制限が付くよ。


BBB.dllでThreadクラスはdllexportしてる?
AAAでThreadクラスをdllimportしてる?

KJ 2010/05/07(金) 00:41:05
wclrp ( 'o')さん、ご回答ありがとうございます。

>BBB.dllでThreadクラスはdllexportしてる?
>AAAでThreadクラスをdllimportしてる?

両方ともしてません。
ただ、これをしなくても実行時に問題が出るだけで、リンクは通ってもいいんじゃない?
と思ってます。。

なお、更に調べたところ、
CRuntimeClass *pRuntime = RUNTIME_CLASS(Thread);
の行でリンクエラーが発生してます。

プロジェクトを分けた場合に、以下のような制約はあるのでしょうか?
片方のプロジェクトで、
・「DECLARE_DYNCREATE」と「IMPLEMENT_DYNCREATE」を使用したクラスを定義
もう片方のプロジェクトから、
・「RUNTIME_CLASS」を使ってCRuntimeClassを得る

PATIO 2010/05/07(金) 10:16:47
exportしていないクラスや関数はDLL外部からは見えないと
思いましたけれど。
多分、インポート用のライブラリに情報が出てこないと思います。
exportされていないクラスは外部公開されていないクラスなので
基本的にはリンクできないと思うのですけれど、
リンクは出来て良いという話は何処から来ているのでしょう?

プロジェクトを分けない場合、プロジェクト内部でのリンクは
objファイルのリンクで行なわれますからexportする必要は有りませんが、
プロジェクトを分けてしまうと別のロードモジュールになってしまうので
exportでモジュールの外部に公開しないと外部からは参照できないと思います。

PATIO 2010/05/07(金) 10:21:10
後学の為に聞いておきたいのですが、

「RUNTIME_CLASS」を使ってCRuntimeClassを得てからCreateObjectしている意味は
何でしょう?
こうすればインスタンスを作成できるのはわかりますが、
そうする理由が良く分からないので。
普通にnewで作成するのと何が違うのかなと。

ITO 2010/05/07(金) 11:52:51
うーーん、
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();
これはいらないですね。

KJ 2010/05/09(日) 14:57:42
皆様、ご教授ありがとうございます。一応、下記方法で解決しました。
ただ、これでは不十分だ!と言う点がございましたら、ご指摘頂けると助かります。

DLLプロジェクトで「共有DLLを使用した拡張DLL」にチェックし、公開するThreadクラス
に「AFX_EXT_CLASS」キーワードをつけたら、別プロジェクトから生成し、使用すること
が出来ました。

以下、皆様の質問にお答えして参ります。

PATIOさん
>リンクは出来て良いという話は何処から来ているのでしょう?
「プロジェクトの設定でBBB.libをリンクしているから、シンボルは解決できるんじゃな
いの?」と考えての発言でした。(調べたわけではありません。実際、MFC派生クラス以外
は解決できてましたので・・・)
dllexportやexportもしくは、.defファイルで定義するなど必要だったんですね。。

>普通にnewで作成するのと何が違うのかなと。
ITOさんのおっしゃる通り、UIスレッドを作成する為です。

ITOさん
>これはいらないですね。
申し訳ありません。いらないというのがよくわかりませんでした。
別の宣言の方法を使え、ということでしょうか?

ITO 2010/05/10(月) 14:28:17
> これはいらないですね。
>> 申し訳ありません。いらないというのがよくわかりませんでした。
>> 別の宣言の方法を使え、ということでしょうか?

>>   クラスの種類は「MFCクラス」、基本クラスは「CWinThread」を継承する。
> 追加→クラスの新規作成で追加したのなら、
これを、行なえば「Thread」クラスのソースリストに
>    CRuntimeClass *pRuntime = RUNTIME_CLASS(Thread);
>    Thread *pThread = (Thread*)pRuntime->CreateObject();
が組み込まれます。
あとは、UIスレッドとして動作させれば動くはずです。 
ただ、DLLで「UIスレッド」を動作させるのはあまりお勧めできません。
どうしても必要ですか?

KJ 2010/05/11(火) 00:48:38
ITOさん

ありがとうございます。理由がわかりました。

>ただ、DLLで「UIスレッド」を動作させるのはあまりお勧めできません。
UIスレッドを選んだ理由としては以下の通りです。

これは以下の理由からです。
まず、このスレッド(以下スレッドA)は更に別のスレッド(以下スレッドB)と通信を行う必
要があります。

その通信方法としては、以下の通りです。
1. スレッドAから他社提供DLLの公開APIをコールする
2. 公開APIの中でスレッドBが生成され、公開APIはリターンする
3. スレッドBからスレッドAへのメッセージを待つ

ここで、スレッドBが実装しているメッセージ送信関数は「PostMessage」のようなので
す。(提供されたDLLの為、実装は確認できないのですが、公開APIの引数がHWNDとなって
いるので恐らくそうかと)

よって、スレッドAはウインドウを持つ必要がある為、UIスレッドを選択しました。

ITO 2010/05/13(木) 13:59:54
>ここで、スレッドBが実装しているメッセージ送信関数は「PostMessage」のようなの
>です。(提供されたDLLの為、実装は確認できないのですが、公開APIの引数がHWNDとな
>っているので恐らくそうかと)
公開している人に聞いてみるのもいいと思います。

UIスレッドを薦めない理由ですが、
  1.UIに限らずにスレッドをDLLに組込むこと自体が難しい。
   ・引継ぐ変数をどうするか?
   ・終了の方法等
 2.UIスレッドに限った問題
   ・親スレッドがAPIのためHWNDの継承が難しい。
    「HWND == NULL」になり、例外で異常終了になりやすい。
    HWNDのチェックが必要。
 3.メッセージ処理の問題

でしょうか。

探すとまだありそうに思えます。

KJ 2010/05/29(土) 10:44:05
[[解決]]
問題は解決したので閉じます。

皆様、ありがとうございました。

古いスレッドには追加発言できません。