MessageBoxが出ているのにビューに触れてしまうのをなくすには?

解決


Gign  2010-01-13 07:32:24  No: 71247

現在、Visual Studio 2005とVisual C++ 2005を用いて勉強していて、
http://support.microsoft.com/kb/99562/ja
などを参考にし、AView,BViewの2つのビューを作成し、SendMessageを使ってAViewからBViewにメッセージを送信し、
BViewでメッセージを受信したらMessageBoxを使って、受け取ったデータを表示するプログラムを作成しています。
これが、BViewでMessageBoxやモーダルダイアログを出している状態で受信すると、
SendMessageによるMessageBoxにもあらかじめ出しておいたダイアログのどちらにも触れる事が出来、
BViewで出していたダイアログを先に閉じるとMessageBoxがモーダルでなくなり、ビューにも触る事が出来てしまいます。
MessageBoxを消すまで他には触れないようにしたいのですが、何故このような問題が起きているのかもよく分かりません。
解決方法をご教授いただけないでしょうか。


rin  2010-01-13 09:31:13  No: 71248

BViewにてメッセージボックスや、
モーダルダイアログをつくってる部分のソースは出せますでしょうか?


Gigin  2010-01-14 05:52:44  No: 71249

応答が遅れて申し訳ありません。
BViewでメッセージボックスやダイアログを作る部分は
void BView::TestDlg(){
    MessageBox("Test","",MB_OK);
}
void BView::TestDlg(){
    CTestDlg Test;
    Test.DoModal();
}
などのように、それを表示する関数以外は作っておりません。
また、メッセージを受信する部分は

LRESULT BView::RecvMSG(WPARAM wParam,LPARAM lParam){
    CheckParam(wParam,lParam);  // wParam,lParamの値をViewに表示する
    DispMsg(); // メッセージボックスを表示する
}

void BView::DispMsg(){
    MessageBox("You've got new message.","check",MB_OK);
}
と、メッセージを受け取ってから、メッセージボックスを出す関数を呼ぶようにしています。


gak  2010-01-15 02:18:58  No: 71250

> 何故このような問題が起きているのかもよく分かりません。
MessageBox って特殊用途のモーダルダイアログに過ぎない。
となると、恐らく内部では DialogBox() を使っている(少なくとも似た実装になっている)筈。

http://msdn.microsoft.com/ja-jp/library/cc410759.aspx
の解説にモーダルダイアログ構築/消滅時の動作が載っている。
これを元にMessageBox()の動作を推測/コード化すると以下の感じ?

int MessageBox(HWND owner, ...) {
    DialogBox(...) {
        MessageBox構築();
        モーダルループ(); // [1]
    }
}

DialogBoxプロシージャBase(UINT msg, ...) {
    static BOOL ownerEnabled;
    switch (msg) {
        case WM_CREATE相当:
            ownerEnabled = IsWindowEnabled(owner, wnd);
            EnableWindow(owner, FALSE);
            break;
        case WM_DESTROY相当:
            EnableWindow(owner, ownerEnabled); // [2]
            Postモーダルループ終了通知(); // [3]
            break;
    }
}

Gignさんの操作を追いかけてみる。

> BViewでMessageBoxやモーダルダイアログを出す
MessageBox(α)を構築。
上記コードの [1] まで進んでモーダルループ(a)に入る。
ownerEnabled変数 は(特殊なケースを除き)TRUE が設定されるハズ。

> SendMessageによるMessageBox
MessageBox(β)を構築。
モーダルループ(a)内でメッセージが処理されて MessageBox() 関数が再度呼ばれる。
 [1] まで進んでモーダルループ(b)に入る。

モーダルループ(a)と(b)はこんな関係。
主メッセージループ() {
    モーダルループ(a)() {
        モーダルループ(b)() {
        }
    }
}

> BViewで出していたダイアログを先に閉じると
(α)の [2] でオーナーウィンドウが有効化される。

加えて、(α)の [3] でモーダルループ(a)を抜ける事を意図する(プライベート?)メッセージが投げられる。
が、現在の進行位置はモーダルループ(b)な為、結果モーダルループ(b)を抜けてモーダルループ(a)に戻る。
  ↓
(β)の [3] が処理された時点でモーダルループ(a)も抜け、主メッセージループに戻る。

という感じに動いていると俺は予想する。

> MessageBoxを消すまで他には触れないようにしたい
最初のモーダルダイアログを表示する際に

const BOOL enabled = IsWindowEnabled();
EnableWindow(FALSE);
モーダルダイアログ表示();
EnableWindow(enabled);

とでもしてやればいけるのか。
主観だが、モーダルダイアログを表示中に其れ(若しくはその子孫)以外のウィンドウをオーナとして
新たなモーダルダイアログが表示される事は仕様上想定されていない感じがする。


gak  2010-01-15 02:26:09  No: 71251

>  ownerEnabled = IsWindowEnabled(owner, wnd);
  ↓
ownerEnabled = IsWindowEnabled(owner);


ロマ  2010-01-15 23:19:24  No: 71252

AfxMessageBoxはいかがですか。


fuku  2010-01-16 02:58:44  No: 71253

記憶だけで確認してないのですが、gakさんのサンプルコードを元にすると

> BViewで出していたダイアログを先に閉じると
(α)の [2] でオーナーウィンドウが有効化され、表示しているダイアログが非表示になる。
以降、モーダルループ(a)は終了するようになりますが、
モーダルループ(b)が呼ばれているのでまだ抜けません。

この時点でメインウィンドウは有効化されてしまったため、操作できるようになります。
この場合のウィンドウメッセージはモーダルループ(b)が汲み上げて
メインウィンドウのメッセージハンドラを呼ぶような形になっていたと思います。
そのため、この状況でメインウィンドウを操作すると

メインメッセージループ
   (a)のモーダルループ
      (b)のモーダルループ
          メインウィンドウのメッセージハンドラ

のような形で呼ばれていると思います。
この状況でダイアログを出すとどんどん積もってしまったと思うので、状況はあまりよくありません。

ここでモーダルループ(b)のダイアログを閉じるとモーダルループ(a)も直ちに終了して
メインメッセージループに戻ると思われます。

私もgakさんと同意見で本来こういう使い方はするものではないのだと思います。
それでもこの形が必要なら、メインウィンドウのメッセージハンドラに操作中は他のことをしないようにコードを埋めるのが良いかと。


Gign  2010-01-17 03:30:41  No: 71254

色々ありがとうございます。
自分でも色々と試してみた結果、EnableWindow()を用いて他のウィンドウに触れないようにする事で、
何とか求めていた動きをするようになりました。
これで解決とさせていただきます。
皆さんありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加