連続ボタン押下にて複数の画面起動

[戻る]
SW 2007/07/03(火) 11:34:31
【開発環境】WinXP(SP2) VC++6.0(MFC)

MFCにてDLLでダイアログを作成しています。そのダイアログにDoModal()を使用して
子画面を表示するボタンを作ったのですが、そのボタンをEnterキーを押しっぱなしにして
開いたり、閉じたりを繰り返していると子画面のダイアログを閉じようとしても
一回だけでは閉じれない状態でして、開いた分だけ閉じなければ
ならないのですが、この原因がわかりません。

また同じように、ボタンにフォーカスを設定しておき
Enterキーとマウスでボタンをクリックで、ボタンを一緒に押下した場合も、
2回押した事になっていまして、2回閉じなければならない状態です。

ご教授宜しく御願いいたします。

たいちう 2007/07/03(火) 12:01:39
Enterを押しっぱなしとか、Enter & クリックとか、通常はしない動作ですよね。
出荷前のテストをしているような状況だと思いますが、もしそうでないなら
設計を見直すべきです。

閉じようとする時に、子画面は複数あるのでしょうか?
必要なのは、複数の子画面を1度に閉じる機能でしょうか?
それとも、子画面を複数開かないようにする機能でしょうか?

SW 2007/07/05(木) 00:16:43
返信遅くなり、大変もうしわけございません。

>閉じようとする時に、子画面は複数あるのでしょうか?
複数はないのですが、子画面を閉じても閉じても
何回か開いた分だけ閉じなければいけない状態です。
(タスクバーは一つです)

>必要なのは、複数の子画面を1度に閉じる機能でしょうか?
>それとも、子画面を複数開かないようにする機能でしょうか?
「子画面を複数開かないようにする」なのですが、
なぜボタンを押す時に一瞬の動作で連続で押すと、子画面が2画面
開いているのか原因が知りたいです。

たいちう 2007/07/05(木) 09:29:05
肝心な部分の情報が矛盾しています。

> 複数はないのですが、子画面を閉じても閉じても
> ...
> なぜボタンを押す時に一瞬の動作で連続で押すと、子画面が2画面
> 開いているのか原因が知りたいです。

一種類の子画面が、複数開いているということでしょうか?
複数開いていることをどうやって確認しています?
子画面を動かすと、後に同じ画面がありますか?
子画面が1つで、閉じるボタンが無視されている可能性は?


> 「子画面を複数開かないようにする」なのですが、

開く処理のソースコードを見せてもらわないと何とも。
結果として複数開いているんならば、複数開くように書いているのでしょう。

SW 2007/07/05(木) 21:56:52
>一種類の子画面が、複数開いているということでしょうか?
>複数開いていることをどうやって確認しています?
>子画面を動かすと、後に同じ画面がありますか?
動かしても、後ろに同じ画面はないです。

>子画面が1つで、閉じるボタンが無視されている可能性は?
閉じるボタンでは、ちゃんと子画面を閉じています。

>開く処理のソースコードを見せてもらわないと何とも。
>結果として複数開いているんならば、複数開くように書いているのでしょう。
処理としては、
ボタンを押した時のイベントに一回だけDoModal()をして一回だけ子画面を開く処理を
入れているだけなのですが・・・

2007/07/05(木) 22:46:36
実装を見ないでできる返答を言うと「ボタン押下のハンドラで、子ダイアログ起動
済みフラグをチェックするようにする」かな。
フラグはどの親に対して複数起動なのか、によって宣言の場所が違います。
例えばOS単位ならミューテックスとかだし、プロセス単位ならグローバル変数として
宣言するかグローバル変数として使われるクラス(MFCだとCWinAppの派生クラ
ス)のメンバとして宣言するし、ドキュメント単位ならドキュメントクラスのメンバとし
て宣言する事になる筈です。

フラグではなくてダイアログクラスそのものでも可ですが、初期化やダイアログクラス
のメンバの使い方でどちらが良いかは変わるので、適当に工夫して下さい。

調べてみたらVC6のMFC/SDIで作ったスケルトンでも再現しうるようです。
メッセージさえ飛んでくればバージョン情報ダイアログボックスを複数同時に開ける
ようです。
直ぐにモーダルダイアログが出てしまい多重に操作できなかったので、ウィンドウメッ
セージを直接送って確かめました。

2007/07/05(木) 22:55:46
追記。
>動かしても、後ろに同じ画面はないです。
の場合、子ダイアログを開くハンドラの呼び出しがネストされていないようなの
で、私が書いた内容と食い違うかも知れません。

私が書いたケースだと

親ウィンドウのメッセージループ
->ボタン押下ハンドラ
->DoModal()
->子ダイアログのメッセージループ
->ボタン押下ハンドラ
->DoModal()
->子ダイアログのメッセージループ
…

という形を想定しています。
メッセージキューはスレッドごとに共通なので、ループで処理するウィンドウを指定
しなければこうなる可能性もある、と言う話です。

たいちう 2007/07/06(金) 09:44:26
> 動かしても、後ろに同じ画面はないです。
> 
> >子画面が1つで、閉じるボタンが無視されている可能性は?
> 閉じるボタンでは、ちゃんと子画面を閉じています。

もう一度同じことを聞きますが、
複数開いていることをどうやって確認しています?


> 処理としては、
> ボタンを押した時のイベントに一回だけDoModal()をして一回だけ子画面を開く処理を
> 入れているだけなのですが・・・

それは、あなたが書いたつもりの処理。
望む結果が得られてないならば、実際にあなたが書いた処理は違うかもしれません。
あなたが提示した情報だけでは判断できません。

rin 2007/07/06(金) 20:48:46
構成は
A:メイン−B:DLLで子ダイアログ−C:B上ボタンで孫ダイアログ
でいいのかな?

BのボタンにフォーカスあててENTER押しっぱなしすると、
Cが開き、フォーカスがCにうつってCが閉じる。
この「開いては閉じる」をある程度繰り返してからENTERを放す
この時点でCが残っているので閉じるボタンを押すが、消えない(*1)
その後、何回かCを閉じる作業をすると、やっと消える

こんな感じでいいのかな?

上記の通りだとするならば
*1の段階で、
「Cは消えたが、なんらかの理由でBが押されたことになっており、すぐに次のCが出る」
といった状況になってるのではないでしょうか?
つまり、
Bに必要以上にメッセージが送られてる(溜まってる)のかと


>Enterキーとマウスでボタンをクリックで、ボタンを一緒に押下した場合も、
>2回押した事になっていまして、2回閉じなければならない状態です。
本来、同時押ししても、完全に同時にはならず、後押し分は意味ないはずだが
上の状況がおきてるならありえそうだ

SW 2007/07/08(日) 21:25:02
>麩さん
>子ダイアログを開くハンドラの呼び出しがネストされていないようなの
>で、私が書いた内容と食い違うかも知れません。
そうですね、ネストはしていないです。

>たいちうさん
>もう一度同じことを聞きますが、
>複数開いていることをどうやって確認しています?
子画面の閉じるボタンを何回か押して、やっと子画面が閉じれるという状況なので、
このようにして複数開けてるんだと確認しています。

>rinさん
>構成は
>A:メイン−B:DLLで子ダイアログ−C:B上ボタンで孫ダイアログ
>でいいのかな?
そのとおりです。

>BのボタンにフォーカスあててENTER押しっぱなしすると、
>Cが開き、フォーカスがCにうつってCが閉じる。
>この「開いては閉じる」をある程度繰り返してからENTERを放す
>この時点でCが残っているので閉じるボタンを押すが、消えない(*1)
>その後、何回かCを閉じる作業をすると、やっと消える
>こんな感じでいいのかな?
そのとおりです。

>Bに必要以上にメッセージが送られてる(溜まってる)のかと
その可能性がありそうです。
試しに、私のPCの処理が遅いのかと思い、他のPCにプロジェクトを移して
試してみたのですが、同じく再現したので、PCのスペックは関係ないみたいでした。

たいちう 2007/07/10(火) 09:57:28
> 子画面の閉じるボタンを何回か押して、やっと子画面が閉じれるという状況なので、
> このようにして複数開けてるんだと確認しています。

『閉じるボタンを3回押したら子画面が閉じる』、とします。
A.(何らかの原因で)子画面が3つ開いている
B.(何らかの原因で)最初の2回の閉じるボタンの処理が無視されている

A.のケースでは、複数開いていますが、B.のケースでは1つしか開いていません。
あなたの確認方法では両者を区別できていないと思います。

子画面のOnInitDialogなどは、複数回呼ばれていますか?
その時のインスタンスのアドレス(thisポインタ)は一緒?違う?

.s 2007/07/10(火) 10:26:35
>ボタンを押した時のイベントに一回だけDoModal()

DLLを作ってというのは検証が大変そうなので
回避方法になってしまいますが、ボタンが押されて
DoModalする前に自ボタンを無効化(EnableWindow(FALSE))とか、

DoModalの後に、メッセージクリア
MSG msg;
// キーボード
while( PeekMessage( &msg, Handle, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE ) );
// マウス
while( PeekMessage( &msg, Handle, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ) );

を入れちゃうとか

.s 2007/07/10(火) 10:28:21
ごめ
>DoModalの後に、メッセージクリア
じゃダメですね。DoModalの前に入れないと。

とおり 2007/07/10(火) 10:44:59
void CTestDlg::OnOK() 
{
    MSG    msg;
       for(int i=0;i<=100;i++) {
        if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
        Sleep(3);
    }

    AfxMessageBox("AAAA");

}

こんな状況になっているんじゃないでしょうか?

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