DLL側でCFileDialog::DoModal()を呼ぶと無限ループ!

[戻る]
しん 2005/01/06(木) 12:42:36
DLL側でCFileDialog::DoModal()を呼び出したのですが、無限ループになり処理が帰って
きません。
この原因と回避方法に関する情報を少しでも教えて頂けないでしょうか。環境は下記の通
りです。

・OS
  2K Pro.(パッチ未適用) & XP Pro.(2005/1/5での全パッチを適用)
・IDE
  VC++6(SP6 & ProcessorPack5(裏技で)適用) & 7.1(GDIPLUS.DLLセキュリティパッチ適
用)
・各種コンパイラオプション
  既定のまま

具体的には、簡単なexeとdllを作り、上記環境の組み合わせを試しました。下記はexeと
dllの作成手順です。

[ exe作成手順(VC++7.1。6でもほぼ同じ) ]
1 "VisualC++プロジェクト"→"MFC"→"MFCアプリケーション"(ダイアログベース)
  →"ユーザインタフェースの機能"と"高度な機能"のチェックは全て外す
  ※ 残りの設定は既定のまま

2 下記コードを追加
  CWinApp派生クラス::InitInstance()
  {
    CWinApp::InitInstance();
    AfxLoadLibrary( "test.dll" ); // これを追加。テストの為、AfxFreeLibrary()は
やっていない
  }

[ dll作成手順 ]
1 "VisualC++プロジェクト"→"MFC"→MFC DLL"→何もいじらず完了

2 下記コードを追加
  CWinApp派生クラス::InitInstance()
  {
    CWinApp::InitInstance();
    CFileDialog  Dlg( TRUE );     // 追加コード。FALSEでも結果は同じ。又、
CColorDialogとCPrintDialogでは無限ループにならない。
    Dlg.DoModal();                // 追加コード。ここで無限ループ。内部で
GetOpenFileName()が呼ばれてる。
  }                               // 更にその内部でGetWindowTextLength()が呼ばれ
ている。ここで無限ループが発生していた。

以上、どうか宜しくお願い申し上げます。

Advance 2005/01/06(木) 18:34:35
Windowsの仕様上、DLL側の DLL_PROCESS_ATTACH のタイミングで
新スレッドを生成し、生成したスレッドからの通知待ちをやろうとすると
デッドロック(≠無限ループ)することになってます。(^^;;;

・DLL 側の InitInstance() は DLL_PROCESS_ATTACH 時に呼ばれる
・GetOpenFileName() は内部で新スレッドを起こして何らかの通知待ちをする(推測)

ということで、デッドロックするんだと思います。

Initnstance() の中ではなく、
・エクスポート用の関数を作成し、その中で CFileDialog を使うようにする
・exe からそのエクスポート関数を呼び出す
というやりかたにしないとちゃんと動かないと思います。
っていうか、それが普通のやり方だと思いますが...

Advance 2005/01/06(木) 18:58:54
>Windowsの仕様上、DLL側の DLL_PROCESS_ATTACH のタイミングで
>新スレッドを生成し、生成したスレッドからの通知待ちをやろうとすると
>デッドロック(≠無限ループ)することになってます。(^^;;;

具体例:

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, PVOID fInpLoad)
{
  HANDLE hThread;
  switch (fdwReason)
  {
    case DLL_PROCESS_ATTACH:
      hThread = CreateThread(NULL, 0, ...);
      WaitForSingleObject(hThread, INFINITE);  // こんなんでもデッドロックする
      CloseHandle(hThread);
      break;
    case DLL_THREAD_ATTACH:
       :
       :
  }
  return TRUE;
}

mxttrara 2005/01/07(金) 02:27:08
DllMain() てのは特殊で、マルチスレッドプログラムでも並行動作はしないからね。

システムが DllMain() を呼ぶときは、当該スレッドのコンテキストを使って
    EnterCriticalSection()
    DllMain()
    LeaveCriticalSection()
みたいなシーケンスで呼び出すのだと思えばいいんじゃないかと。

元のスレッドでは DLL_PROCESS_ATTACH 内の WaitForSingleObject() でイベントを待つ。 
起動したスレッドは本処理を開始する前に DLL_THREAD_ATTACH で DllMain() を呼ぼうとす
るのだけど、元のスレッドが DllMain() を実行中なのでその終了を待つ。
で、双方のスレッドが互いの処理の待ち状態になってデッドロックに至るわけ。

しん 2005/01/11(火) 11:36:31
[[解決]]
Advanceさん、mxttraraさん、どうも有難うございました。
お二方が仰った事は、MSDNのDllMain()の解説に書かれていました。
が、自力でここに辿り付く為にはとても時間を要したでしょう。
何故なら、GetOpenFileName()に目がいっていた為です。お二人に深く感謝致します。

ただ、問題は未解決のままです。どうやら別の所にも問題がありそうです。
今後はこれに取り組みます。

お世話になりました。有難うございました。

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