ウィンドウメッセージの滞留? – プログラミング – Home

ウィンドウメッセージの滞留?
 
通知
すべてクリア

[解決済] ウィンドウメッセージの滞留?

固定ページ 1 / 2

NEO
 NEO
(@NEO)
ゲスト
結合: 22年前
投稿: 18
Topic starter  

以下のようなMDIアプリケーションがあります。

MDIフレーム①
MDIフレーム②(MDIフレーム①のフォームビューのクライアント領域がダブルクリックされ
ると表示)

■MDIフレーム②の表示方法
(1) CreateNewFrame()
(2) (1)で作成したフレームでInitialUpdateFrame()する

■MDIフレーム②がすでに表示されている場合は、閉じるまで1秒間隔でSleep()しながら
待って表示。

■MDIフレーム②を閉じる仕組み
(1)MDIフレーム②に対してウィンドウメッセージをSendMessage()する。
(2)MDIフレーム②はウィンドウメッセージを受けると、Activeなフォームビュークラスに
WM_CLOSEをSendMessage()する。
(3)フォームビュークラスのOnClose()で終了処理用のスレッドを起動し、モーダルダイア
ログを表示。ダイアログにはスレッドハンドルを渡す。
(4)モーダルダイアログでは、フォームビュークラスの終了処理用スレッドの終了を150ms
間隔のTIMERイベントで監視し、スレッドが終了するとEndDialog()する。

クライアント領域のダブルクリックを連続で行うと、モーダルダイアログにTIMERイベン
トが発生しなくなってしまい、
アプリケーション自体が応答なしになってしまうことがあるので困っています。
TIMERイベントが発生しないどころか、モーダルダイアログのダイアログプロシージャが
呼ばれなくなってしまいます。
モーダルダイアログのオーナーウィンドウハンドルには、MDIフレーム②のフォームビュー
クラスのウィンドウハンドルを指定し、インスタンスハンドルにはMDIフレーム②表示元
MFC拡張DLLのDllMain()の第1引数を指定しています。

ここまでで何か原因になりそうなことがあるでしょうか?
また、調べた方が良さそうなことがあれば教えてください。


引用未解決
トピックタグ
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

MDIフレーム(1)とMDIフレーム(2)が同じスレッドに帰属してそうにみえます。

Sleepするとその間そのスレッド全体のメッセージ処理が止まるので、
MDIフレーム(2)もダイアログもメッセージが処理できなくなります。

終了処理だけは別スレッドのようなのでメインスレッドがSleepしても動きますが、
ウィンドウは何も考えずにMFCで作成するとみんなメインスレッドで動くので、
多分そのせいじゃないでしょうか。

UIスレッドを別に作ってそこでMDIフレーム(2)を動かせばとりあえず直りそうですが…

# ○付の数字は避けて()に置換えています。


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

基本的にウインドウメッセージとタイマーを使って
制御を使用としているならSleepは使わないと思います。
理由に関しては既にBANさんが書かれている通りですね。

タイマーイベントの間隔とかその辺の調整で
待ち時間を調整するとかしないとうまく行かないのでは。
UIスレッドでマルチスレッド化するよりはそっちの方が
調整は簡単そうです。

基本的にMFCを使わなかったとしても意識してマルチスレッド化
しようとしないければ、全てのウインドウはメインスレッドで
動いている状態になると思うので安易にSleepは使えませんね。
他のウインドウの動作にも影響してしまいますから。


返信引用
hirocco
 hirocco
(@hirocco)
ゲスト
結合: 14年前
投稿: 138
 

ふむふむ、SleepではなくDoEvents的な処理で待機するといいのかな?
それとも、終了用に表示させた(しばらくお待ちください?)モーダルダイアログ内で
そのスレッドでさせる処理を実行するのかな?
そんなとこではどうでしょう


返信引用
hirocco
 hirocco
(@hirocco)
ゲスト
結合: 14年前
投稿: 138
 

あ、原因って, SendMessageじゃないかな?


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

まぁSendMessage()が原因でしょうが、やや遠回りな仕組みですね。
そもそもCreateNewFrame()の戻り値のフレームの
ポインタを保管しておいてDestroyWindow()すればよいのでは。

m_MDI_Frame2 = CreateNewFrame(...); // 開く
:
:
m_MDI_Frame2->DestroyWindow(); // 閉じる
m_MDI_Frame2 = NULL;

タイマーもイベントも無用です(vv;)。


返信引用
ryo
 ryo
(@ryo)
ゲスト
結合: 23年前
投稿: 252
 

A「2のフレームを閉じるメッセージは誰がどこでどのタイミングで出すのか?」
B「1秒ごとにSleepで待つ際、たまる可能性のあるメッセージをどうしてるか?」
C「こういった流れの中で『モーダルダイアログ』で、タイマー起こして待ちをおこなっ
てる」
不安材料は多々

A
もし、
「1をダブルクリックしたとき、2がすでにあるなら、
一度閉じさせてから”、2を再表示」
とすると
SendMessageは、相手先での処理が終わるのを待つので(この先でモーダルダイアログな
のか?)
1から終了の指示を「SendMessage」で送信しつつ、Sleepで終了を待つ
という流れに疑問。

B
たとえばこんなの
ttp://www.softist.com/programming/doevents/doevents.htm

C
アクティブなウィンドウには、オリジナルの終了メッセージをなげる
ソレ受け取ったら、スレッドをたて、また、ダイアログをモードレスで作る
タイマーでスレッド終了を待つのではなく
スレッドの方から、元のウィンドウ(2番のウィンドウとかでも)に対し、
オリジナルの「スレッドが終了しました」とメッセージをPostmessageで投げる。
ソレを受け取ったら、CLOSEする


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 23年前
投稿: 1235
 

 似たようなアプリは作ったことがありますね。
 僕の場合、
  1.「CMultiDocTemplate」を使って必要分のフレームを用意する。
2. 開くときは、リソースのテンプレート文字を検索してウインドウを開く。
  3. 閉じるときは、メインフレームからメッセージ等で知らせて、子ウインドウ
   の「DestroyWindow(); 」等で閉じる。
 ですね。
 
 


返信引用
NEO
 NEO
(@NEO)
ゲスト
結合: 22年前
投稿: 18
Topic starter  

みなさん、ご意見ありがとうございます。

BANさんのおっしゃるとおりですね。
MDIフレーム(2)はMFC拡張DLLに実装してまして、MDIフレーム(1)を実装したアプリケー
ションでそのDLLを使ってもらっています。
DLLだけが私の担当で、MDIフレーム(1)を実装したアプリケーションは私の担当ではない
ので、その担当の方と調整が必要になるのですが、工数と難易度から考えると、Sleep間
隔を1秒からできる限り(10msなどに)短くしてもらうのが現実的なのでしょうか?

MDIフレーム(2)の終了処理には時間がかかることがあるので、ダイアログを表示している
のですが、DestoryWindow()を採用するにしても、MDIフレーム(2)の終了処理は必要なの
で、ダイアログを表示することになります。

> ryoさん
> A「2のフレームを閉じるメッセージは誰がどこでどのタイミングで出すのか?」
MDIフレーム(1)のフォームビューのクライアント領域ダブルクリックのイベントハンドラ
から出します。

> B「1秒ごとにSleepで待つ際、たまる可能性のあるメッセージをどうしてるか?」
特に何もしていません。

> C
>タイマーでスレッド終了を待つのではなく
>スレッドの方から、元のウィンドウ(2番のウィンドウとかでも)に対し、
>オリジナルの「スレッドが終了しました」とメッセージをPostmessageで投げる。
>ソレを受け取ったら、CLOSEする

終了処理のスレッドは、MDIフレーム(2)のフォームビューの資源解放をやっていますの
で、スレッドが終了するまではMDIフレーム(2)は破棄できません。
したがって、MDIフレーム(2)のフォームビューは終了処理用スレッドの終了を知る必要が
あるのです。
タイマーでスレッド終了を待たずに、スレッドの終了はスレッド自身で通知した方がいい
とは思っていますが、
スレッドの終了を監視して(待っている)いるのは、MDIフレーム(2)のフォームビューな
ので、そこにメッセージを投げても受けれないのではないでしょうか?


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

PATIO死ね


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> Sleep間隔を1秒からできる限り(10msなどに)短くしてもらうのが現実的なのでしょう
か?

Sleep時間を縮めても解決しない可能性もありそうに思えます。
(現象的に、応答無しになった場合でも時間がたてば復帰するのでしょうか。
SendMessageから二度と復帰しなかったりとかは大丈夫ですか?)

既にあがってますが、一番安直なのはSleepの代わりにDoEvents/メッセージループでしょ
うか。

そもそもは「UIスレッドでSleep/Waitするのがイケてない」のであって、
(UIスレッド上のウィンドウハンドラ中で外部処理を待つべきではありません)
本来はMDIフレーム(1)に「(MDIフレーム(2)の)完了待ち」という状態でも
持たせてそれに遷移させ、一度ハンドラからは抜けるべきだと思いますが。

SleepでMDIフレーム(2)を待っているのが最大の問題で、
MDIフレーム(2)のスレッド待ちはそれに比べれば些事に思えます。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> スレッドの終了を監視して(待っている)いるのは、MDIフレーム(2)の
> フォームビューなので、そこにメッセージを投げても受けれないのでは
> ないでしょうか?

ちなみに、監視方法はなんですか?
スレッドハンドルのWaitForSingleObject(orMultipleObjects)ですか?
タイムアウトの引数はどうなってますか?

もしこれでMDIフレーム(2)のハンドラ内で完了待ちをしている場合、
(ブロッキングしている/ポーリングだがループなどでハンドラを抜けない等を含む)
こちらも問題/実はこちらが問題、という可能性もありそうです。

もしこうなっているなら確かにMDIフレーム(2)はメッセージを受け取れませんが、
そもそも「ウィンドウのハンドラ内で外部処理を待つ」のが(以下略

もし完了の通知が取りたいなら、例えば、スレッドの方に完了通知を投げて欲しい
HWND(ここではダイアログ?)を渡すとか?

MDIフレーム(2)を閉じる仕組みは、(終了処理がビューに帰属するなら)
1. MDIフレーム(2)に対してウィンドウメッセージをPostMessage()する。
2. MDIフレーム(2)はウィンドウメッセージを受けると、
ActiveなフォームビュークラスにWM_CLOSEをSendMessage()する。
3. フォームビュークラスのOnClose()で終了処理用のスレッドを起動し、
モーダルダイアログを表示。スレッドにはダイアログのHWNDを渡す。
4. モーダルダイアログではスレッドからのメッセージを待つ。
5. スレッドからモーダルダイアログにメッセージが届くとEndDialogする。
(モーダルダイアログにウォッチドッグタイマが必要かは要件による)

# ダイアログを開いているのでメッセージは処理できてますが、
# 皆さんご指摘のようにSendされてきたOnClose内で待っているのですね、これ。

ちなみに、ここまでは基本的に「Windowsの作法としてハンドラ内で(以下略」前提で
メッセージポンプを止める奴が悪い、行儀の悪い奴を矯正する方向でのお話です。

で、逆に終了処理のスレッド化の目的がもしかして「そういう駄目な子がいても何とか
自分はちゃんと動きたい」だった場合は、ウィンドウのスレッドわけが必要かと。

何もしないと(DLLだろうが何だろうが)ウィンドウは全部同じスレッドで動いてしまい、
誰かにメッセージをブロックされてしまうとどうしようもないので、
こういう影響を避けたい(MDIフレームが片方止まっても他は止まらない)なら、
同じメッセージキューを使わないように自分用のUIスレッドを用意する方が多分安全。

# 処理内容にもよるでしょうが、MDIフレーム(2)自体が別スレッドで動作していれば、
# もしかしてMDIフレーム(2)の内部状態として終了中に遷移させることで
# 終了処理スレッド自体が不要に生るかもしれませんが。


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

私なら、Sleepで待つのではなくてTIMERで待つカナと。
Sleepは文字通り、スレッドを止めてしまうので得策では無いと思います。

私が以前によくやっていたのは、TIMERをかけておいて
WM_TIMERを受け取ったら、TIMERを解除。必要なチェックを行なってから
条件が揃っていなかったら、再びTIMERをセットを繰り返します。
TIMERだと一旦制御が返るのでスレッドが止まってしまう事もありません。
必要な処理がコマ切れになる事になりますから、必要なら状態管理をして
次にWM_TIMERが来た時に続きから処理できるような仕掛けは必要ですけれど。


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

TIMERだと待ち合わせ時間の精度がかなり落ちてしまうので
精度が必要な待ち合わせなら問題になるかもしれませんけれど、
今回のような状況ならTIMERの精度でも良いのではないかと思います。


返信引用
NEO
 NEO
(@NEO)
ゲスト
結合: 22年前
投稿: 18
Topic starter  

SleepをTimerに変更することで対応しようと思っているのですが、UIスレッドのマルチス
レッド化する方法について、参考までに教えてください。

CWinThreadをCreateして、そのスレッドでフレームを作成(CreateNewFrame)するだけで
良いのでしょうか?


返信引用
固定ページ 1 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

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