DLL内のダイアログからメッセージボックス – プログラミング – Home

DLL内のダイアログからメッセージボッ...
 
通知
すべてクリア

[解決済] DLL内のダイアログからメッセージボックス


NOR
 NOR
(@NOR)
ゲスト
結合: 23年前
投稿: 128
Topic starter  

Visual C++ 2008 MFCです。

MFCアプリケーションのプロジェクトに作成していたダイアログクラスを
MFC標準DLLに移し、そのダイアログをモーダル表示する関数をエクスポートし、
EXEからそれをコールしてモーダル表示するようにしました。

それ自体は正しく動いているのですが、
このダイアログからメッセージボックスを出そうとすると、
モニタ中央に表示されてるようになってしまいました。

void CDllDialog::OnBnClickedButton1()
{
AfxMessageBox(_T(1));
CWnd::MessageBox(_T(2));
::MessageBox(m_hWnd, _T(3), _T(3), MB_OK);
}

上記のどの方法でも結果は同じでした。

http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200304/03040023.txt
こちらのログを読ませていただいた限り、MFC自身が勝手に
親ウィンドウの中央に合わせてくれるものなんだと思っていましたが、
MFC標準DLLではそのようにはならないものなのでしょうか?
それとも、なにか忘れているものがありますでしょうか?


引用未解決
トピックタグ
bun
 bun
(@bun)
ゲスト
結合: 24年前
投稿: 761
 

私の思っているとおりなら気になることが。
・メッセージボックスを表示していても親ウィンドウが非ロック状態では?

当たりならDLLのメインウィンドウ登録が必要です。
AfxGetApp()->m_pMainWnd にEXE側のフレームウィンドウを登録してやる必要
があります。
その際、m_pMainWndはCWnd*型ですが、ウィンドウハンドルのアタッチをDLL
側でしてやらないとうまくいかないと思います。
(CWndのウィンドウハンドル管理がスレッドセーフでないため)


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 23年前
投稿: 128
Topic starter  

情報ありがとうございます。

> メッセージボックスを表示していても親ウィンドウが非ロック状態では?

いえ、親ウィンドウ(今回の例ではDLL内のCDllDialog)はロックされていて、
メッセージボックス表示中にクリックすると、メッセージボックスが点滅します。

また、CDllDialog自身はちゃんとEXE側のウィンドウの中央に表示され、
その親ウィンドウもロックされています。


返信引用
bun
 bun
(@bun)
ゲスト
結合: 24年前
投稿: 761
 

思っていたものと違うようです。
そこで、ちょっと検証してみました。

AfxMessageBox()、CWnd::MessageBox()とも、
最終的には、APIのMessageBox()を呼んでいます。
なので、検証は::MessageBox()だけで十分と踏んでいます。

普通にEXEからMessageBox()を呼んだ場合は、は第一引数に渡したウィンドウの
中央に表示されます。
第一引数をNULLにすると、デスクトップ中央(モニタ中央)に表示されます。

元々のコードにおいて、
::MessageBox(m_hWnd, _T(3), _T(3), MB_OK);

のm_hWndがNULLになっていることはありませんか?


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 23年前
投稿: 128
Topic starter  

> 元々のコードにおいて、
> ::MessageBox(m_hWnd, _T(3), _T(3), MB_OK);
>
> のm_hWndがNULLになっていることはありませんか?

この部分にブレークポイントを置いて調べてみましたが、
NULLでない値がちゃんと入っていました。

なお、このDLL内のダイアログをモーダル表示するエクスポート関数は

void Test(HWND hWnd)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CDllDialog dlg(CWnd::FromHandle(hWnd));
dlg.DoModal();
}

と書いています。

EXE側のウィンドウがこのエクスポート関数を呼ぶときに
自身のm_hWndを引数に渡していて、
CDllDialog自体はEXE側のウィンドウの中央に表示されています。


返信引用
bun
 bun
(@bun)
ゲスト
結合: 24年前
投稿: 761
 

うーん、問題は無さそうですねぇ。

> AFX_MANAGE_STATE(AfxGetStaticModuleState());
が出てくるなら、最後にもう一つだけアドバイスできることがあるかな。

AFX_MANAGE_STATE(AfxGetStaticModuleState()); の直後に以下の2行を
入れてみてください。

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
pState->m_pCurrentWinThread = AfxGetApp();

これで駄目なら、私的にはお手上げです。


返信引用
ロマ
 ロマ
(@ロマ)
ゲスト
結合: 18年前
投稿: 170
 

VS2003で追ってみました。

wincore.cppのAfxCbtFilerHook(int, WPARAM, LPARAM)で
1)exe中の::MessageBoxは、bContextIsDLL==FALSE
2)mfcdll中の::MessageBoxは、bContextIsDLL==TRUE
ついでに作った
3)非mfcDLL中の::MessageBoxは、bContextIsDLL==FALSE
と、なります(bContextIsDLLはAfxGetModuleState()->m_bDLLの値です)。

1),3)の場合はこの関数の下の方の_AfxActivationWndProc()呼び出しから
CheckAutoCener(),CenterWindow()が呼ばれます。

とりあえず、mfcdll側で
BOOL bDLL = AfxGetModuleState()->m_bDLL;
AfxGetModuleState()->m_bDLL=FALSE;
::MessageBox(....);
AfxGetModuleState()->m_bDLL=bDLL
これでメインウィンドウのセンターになりました。

これがどんな悪影響をもたらすのかはわかりません。

# ソースを追うのが楽しかったので実験したのです。
# 私はMFCの経験がないので、正当性は分かりません。


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 23年前
投稿: 128
Topic starter  

情報ありがとうございます。

> AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
> pState->m_pCurrentWinThread = AfxGetApp();

こちらの方法ではやはりモニタ中央になってしまいましたが、

> とりあえず、mfcdll側で
> BOOL bDLL = AfxGetModuleState()->m_bDLL;
> AfxGetModuleState()->m_bDLL=FALSE;
> ::MessageBox(....);
> AfxGetModuleState()->m_bDLL=bDLL
> これでメインウィンドウのセンターになりました。

Visual C++ 2008でもこれで親ウィンドウの中央に表示されました。
詳しい解析ありがとうございます。

リンク元に書かれていたログに書いてある内容は、
MFC標準DLLには当てはまらないことだったということですね。

> これがどんな悪影響をもたらすのかはわかりません。

このafxContextIsDLLという判定、デバッグ中にMFCのソースの中でたまに出会い、
「なぜここで処理を分けているんだ?」と疑問に思うことがありますが、
今回の件もこの判定が関わっていたのですね。

「DLLだよ」という情報を偽ってしまってよいのか、たしかに不安ですが、
なぜDLLでは意図的に中央に移動する処理を通らないようにしているかも疑問です。

教えていただいた処理を入れて、
いろいろなケースでメッセージボックスを出して試してみたいと思いますが、
このへんの実情をご存じのかたがいらっしゃれば、
引き続き情報をいただけると嬉しいです。


返信引用
ロマ
 ロマ
(@ロマ)
ゲスト
結合: 18年前
投稿: 170
 

変な方法を書いてしまい、気掛りですので、合法な対案を示します。

本格的にやるにはフックですが、簡単に済ませるには、
非MFCのDLLをつくり、
int MyMessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpCpation, UINT uType)
{
return MessageBoxA(hwnd, lpText, lpCaption, uType);
}
int MyMessageBoxW(HWND hwnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
return MessageBoxW(hwnd, lpText, lpCaption, uType);
}
をExportします。このDLLはRelease版で良いです。

# 屋上屋な感じではあります。


返信引用
ロマ
 ロマ
(@ロマ)
ゲスト
結合: 18年前
投稿: 170
 

すみません、頭おかしかったです。前記取り消します。

MFC標準DLLの中で
AFX_MANAGE_STATE(AfxGetStaticModuleState());
::MessageBox(hwnd, text, caption, MB_OK);
のようになっているのが原因と思います。

APIのMessageBox()呼び出しの前後でステートを戻すことが出来れば
なんとかなりそうな気がします。


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 23年前
投稿: 128
Topic starter  

> 前記取り消します。

情報ありがとうございます。

この「前記」というのは、
新たに提案していただいたMyMessageBox()のほうでしょうか?

> MFC標準DLLの中で
> AFX_MANAGE_STATE(AfxGetStaticModuleState());
> ::MessageBox(hwnd, text, caption, MB_OK);
> のようになっているのが原因と思います。

前述のエクスポート関数を以下のようにしてみたところ、

void Test(HWND hWnd)
{
AfxMessageBox(_T(1));
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxMessageBox(_T(2));

CDllDialog dlg(CWnd::FromHandle(hWnd));
dlg.DoModal();
}

「1」のメッセージボックスは親ウィンドウの中央に出ましたが、
「2」のほうは画面中央になりました。
この部分が、挙動が変わるきっかけになっているのですね。

ただ、これを入れないとCDllDialog自体が表示されませんし、
そのダイアログ中から状態を元に戻さないといけないということになります。

ちなみに、MFC拡張DLLでCDllDialogクラス自体をエクスポートしたときは
メッセージボックスはウィンドウ中央に表示されました。

同じDLLでも非MFCのDLLだったりMFC拡張DLLであればウィンドウ中央に出るのなら
MFC標準DLLでもウィンドウ中央に出てくれてもよいと思うのですが、
なんでMFC標準DLLのときだけこんな仕様になっているのでしょうかね。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました