スレッドの動いてる画面でタイトルバーを右クリックすると止まる – プログラミング – Home

スレッドの動いてる画面でタイトルバーを...
 
通知
すべてクリア

[解決済] スレッドの動いてる画面でタイトルバーを右クリックすると止まる


ぽん
 ぽん
(@ぽん)
ゲスト
結合: 19年前
投稿: 9
Topic starter  

環境:MFC、VS2008

スレッドが動いている画面で、タイトルバーを右クリックしたままにすると
スレッドが終了せず止まったままになってしまいます。
画面はリソースでシステムメニューを無効にしているため、
右クリックは反応しないはずだと思っていたのですがこれは何故でしょうか?

以下のような、処理が完了したら自動でダイアログを閉じるような
ものを作成しています。
※右クリックしたままだと、画面が閉じずに止まったままになってしまいます。

void CMainDlg::BtnClk()
{
CThreadDlg dlg;
dlg.DoModal();
}
------------------

BOOL CThreadDlg::OnInitDialog()
{
m_thread = AfxBeginThread(WorkThread, this, 0, 0, CREATE_SUSPENDED, 0);
m_thread->m_bAutoDlete = FALSE;
m_thread->ResumeThread();
}

UINT CThreadDlg::WorkThread(LPVOID pParam)
{
CThreadDlg *pDlg = (CThreadDlg*)pParam;

Sleep(10000); //処理の代わり

pDlg->PostMessage(WM_COMMAND, IDOK, 0);

return 0;
}


引用未解決
トピックタグ
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 19年前
投稿: 22
 

おそらく右クリック中にメッセージループが回る仕様になっているんでしょうね。
そして、このメッセージループはマウスメッセージだけを処理していて、
クリックが解放されたら終了する、という仕様になっていると思われます。
ですからクリック中にメッセージをポストしても無視されてしまうのだと思います。


返信引用
ぽん
 ぽん
(@ぽん)
ゲスト
結合: 19年前
投稿: 9
Topic starter  

特にメッセージループが回るような仕様にはしていないのですが、
これはプロジェクトを作成した際に自動でそのような仕様になるのでしょうか?
また、この場合の回避方法はどのようなものがありますか?
自分が思いつくのでは、右クリックイベントを無効にするしか思いつかないのです
が・・・


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

結局のところ何がしたいのか書いてないのでよくわからない・・・

・右クリック中であっても終了したい
・右クリックを離したら終了したい
どっちだろう。

PostMessage でなくて EndDialog を使うべきなんぢゃないの?


返信引用
紅'
 紅'
(@紅')
ゲスト
結合: 17年前
投稿: 48
 

こんにちわ。

・PostMessage の戻り値は?
・SendMessage に変えたらどうなる?
・IDOK を送信するのではなくて、OnInitDialog の最後で、下みたくやる方が正しいような
 ::WaitForSingleObject( m_thread, ... );
 OnOK(); // or EndDialog

WaitFor... の使い方は忘れてるので正しいのかは不明ですが、
非同期処理の終了待ち合わせとして考えた方がよくないですか。

もともとの問題は、ダイアログにイベントメッセージを送信しすぎて
メッセージキューがあふれてしまっているのではないかとか思ったのだけど違うかな。


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

いや、WorkThread()はワーカースレッドですから、基本、GUIメッセージは
メインスレッドに対して飛んできます。

そして、右クリックしたままということはマウスメッセージが発生しつづ
けるということです。

メインスレッド内でメッセージが溜まっている状態ですね。

そして、メッセージの優先順位はマウスメッセージは最優先ですから、
> pDlg->PostMessage(WM_COMMAND, IDOK, 0);
は後回しと。

後回しにされたくなければ PostMessage()でなく、SendMessage()を使いましょう。


返信引用
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

解決したのでしょか。実験してみると、

1.pDlg->PostMessage(WM_COMMAND, IDOK, 0);
 は、メッセージのプライオリティが低いため、
 右ボタンを離すまで、メッセージの実行順位が回ってきませんね。

2.pDlg->SendMessage(WM_COMMAND, IDOK, 0);
 普通に動きます。これが良いと思います。

3.pDlg->EndDialog( IDOK);
 これも動作はしますが、スレッド越しのMFCの使用ということになりますので
 気持ちとしては避けたい・・・ですかね。
 やるなら、
 ::EndDialog(pDlg->m_hWnd, IDOK);
のほうが良いかも。

以上です。


返信引用
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

(捕捉)
pDlg->SendMessage(WM_COMMAND, IDOK, 0);

の場合、OnOK(){・・・}の処理中は、当該スレッドは
一時停止されてますので、スレッドのインスタンスを
削除する手段がなくなります。
従って、

m_thread->m_bAutoDelete= TRUE;//又はこの行の削除

が正しいコードだと考えられます。


返信引用
ぽん
 ぽん
(@ぽん)
ゲスト
結合: 19年前
投稿: 9
Topic starter  

みなさまありがとうございます。
理由も理解できましたし、回避策もばっちりです。

PostMessageはプライオリティが低いため、
処理が止まっていたんですね。

こちらでも、SendMessageを使用することで希望通りの動作ができました。
※タイトルバー右クリックしまままでもスレッドが止まらない

仲澤さんの補足もありがとうございます。
補足が無ければまた質問するところでした。


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

> そして、メッセージの優先順位はマウスメッセージは最優先ですから、

>> pDlg->PostMessage(WM_COMMAND, IDOK, 0);

>は後回しと。

Windowsの動作はいつから変更されたのでしょうか。

AdvancedWindows 改訂第3版だと、

PostMessageはInputEventより優先されるように書かれていますし、

TranslateMessageはこの動作を利用して、

WM_KEYxxxをWM_CHARに変換してPostしているはずです。


返信引用
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

ロマさん。

メッセージの優先度をやや簡単に説明すると、
まず、一般に発行されたメッセージは「メッセージキュー」に
エントリされるのですが、いくつかの例外とルールがあります。

1.SendMessage()等、いくつかの特定の方法で発行されたメッセージは
 「メッセージキュー」にエントリされず、直接コールバック関数に
 渡され、実行されます。全てのメッセージキューのエントリをすっ飛ばして
 最優先で処理されるわけですね。SendMessage()の発行元と受領先の
 スレッドが異なる場合は、元側のスレッドは一時停止して、
 SendMessage()の結果待ちを行います。

2.PostMessage()等で発行されたメッセージは「メッセージキュー」に
 エントリされます。「メッセージキュー」は単なるFIFOではなく、
 エントリされたメッセージを並べ替えます。このときの優先は
 「ユーザー操作」ですね。これがロマさんの発言の部分です。

3.さらに、優先順位の低い、又は最後であることが重要となる
 いくつかのメッセージは結合されて一つになったり、
 後回しになったりします。WM_PAINTなどですね。

番号の若い方が、より優先度の高いものとなります。

(参考)
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644927(v=vs.85).aspx


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 19年前
投稿: 22
 

もう解決しているようですが、私が言いたかったのは
ウィンドウズがメッセージループを回す仕様にしていると言うことです。
例えば次のようなコードでは、

#define ID_TIMER 1000

BOOL CTestDialog::OnInitDialog()
{
CDialog::OnInitDialog();

SetTimer(ID_TIMER, 1000, NULL);

return TRUE;
}

void CTestDialog::OnNcRButtonDown(UINT nHitTest, CPoint point)
{
TRACE(Test1\n);
CDialog::OnNcRButtonDown(nHitTest, point);
TRACE(Test2\n);
}

void CTestDialog::OnTimer(UINT nIDEvent)
{
if (nIDEvent == ID_TIMER)
TRACE(WM_TIMER\n);

CDialog::OnTimer(nIDEvent);
}

このダイアログのキャプションで右クリックダウンすると Test1 が出力されます。
さらに WM_TIMER の出力が停止します。
そして Test2 は右クリックアップするまで出力されません。
これは CDialog::OnNcRButtonDown() の中でメッセージループが回っていて、
そのメッセージループではマウスメッセージしか処理していないからだと推測できます。


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 19年前
投稿: 22
 

メッセージの優先度はこの件では関係ない気がします。
単にマイクロソフトが、

MSG msg;

while (::GetMessage(&msg, hWnd, WM_MOUSEFIRST, WM_MOUSELAST) < 0)
{
if (msg.message == WM_NCRBUTTONUP)
break;

// マウス処理
}

としてるからいつまで経っても WM_COMMAND が処理されないだけだと思います。


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

INFO: Window Message Priorities‎( http://support.microsoft.com/kb/96006)から引用

(MORE INFORMATIONの先頭部分です)

For example, PostMessage() puts a message in the application queue. However,
when the user moves the mouse or presses a key, these messages are placed on
another queue (the system queue in Windows 3.1; a private, per- thread input
queue in Win32).

GetMessage() and its siblings do not look at the user input queue until the
application queue is empty.

ところで、ぽんさんの最初のコードでPostMessageをSendMessageに変えると、

SendMessage発効後にm_threadはSendMessageの戻りを待っているが、

プロセスはWinMainを抜けて終了してしまうということになりませんか。


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

訂正 すみません、

> void CMainDlg::BtnClk()

> {

> CThreadDlg dlg;

> dlg.DoModal();

>}

ここを読んでいませんでした。私の前投稿の後半を取り消します。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

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