トップ回答者
フォームの ControlBox をクリックしたとうイベントを得ることはできませんか? または、xxx_FormClosing に必ず入るには?

質問
回答
-
Windows10 VS2013です。
プログラムはボタンを押したらフォームが表示され、無限ループの関数が動き出します。
表示されたフォーム上の終了ボタンを押すと無限ループの関数から抜け出しフォームも閉じます。
この範囲では上手く行っています。
ところが、フォームの×印をクリックしても、xxx_FormClosing に入らない場合が有ります。
この場合、終了ボタンを押しても、1回目では終われず2回目で終わります。
時には、まったく終われなくなります。
ループ内では、以下のようなC++の関数を利用し、ESCキーやマウスの右ボタンの情報を得ています。
int KbMouseBreak( void ) { unsigned int Status; int ret = 0; MSG msg; HWND hWin = GetActiveWindow(); while ( PeekMessage( &msg, hWin, WM_KEYFIRST, WM_MOUSELAST, PM_REMOVE) ) { if ( msg.message == WM_RBUTTONDOWN ) { ret = 2; return( ret ); } if ( msg.message == WM_KEYDOWN ) { Status = GetKeyState( VK_ESCAPE ) ; if ( Status >= 0x8000 ){ ret = C_BK_STATUS; return( ret ); } } TranslateMessage( &msg ); DispatchMessage( &msg ); } return( ret ) ; } void DoEvents( void ) { MSG msg; while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage(&msg); DispatchMessage(&msg); } }
ループ内で、
ret = KbMouseBreak()
だけを利用している場合に上記のような現象が出ていました。ret = KbMouseBreak();
意味は良く分かりませんが、とりあえず解決しました。
DoEvents();
のようにしたら、常に xxx_FormClosing イベントが発生し正常な動きになりました。
mnicksashimisan
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12
-
どういうコードを作っているのですか?
C# で PeekMessage/TranslateMessage/DispatchMessage を使う場面はほとんどないはずです。C/C++ のプログラムを中途半端に移植してきていませんか?
なぜ、KbMouseBreak や自作の DoEvents が必要なのですか?なお、KbMouseBreak がマウスとキー関連のメッセージしか処理しないことによって、「応答なし」とみなされ、2 回目の×クリックで強制終了されているのが大本の問題です。
解決するためには、変な作り方をやめて、あるべき姿を考えていかないといけません。
(無限ループで作り込むという考え方をやめて、モーダルダイアログを表示してバックグラウンドで処理するなど)
- 編集済み AzuleanMVP, Moderator 2017年2月27日 9:36
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12
-
キューされているメッセージを大胆に削除(PM_REMOVE)しすぎているからではないでしょうか。
提示されたKbMouseBreak()では、WM_KEYFIRST(0x0100)~WM_MOUSELAST(0x020E)の
メッセージを全て読み捨てますが、この間にはキー及びマウスだけでなく、他のメッセージも含まれます。
例えばWM_SYSKEYDOWN等はデフォルトのプロシージャの内部で他のメッセージに変換される可能性がありますので、
デフォルトの処理に回す必要があるかもしれません。
キーだけ取りたいのなら必要なキーだけ、ないしWM_KEYFIRST(0x0100)~WM_KEYLAST(0x0109)、
・・この場合WM_SYSKEYDOWN等が含まれてしまいますが・・・
マウスだけ取りたいなら必要なボタンだけ、ないしWM_MOUSEFIRST(0x0200)~WM_MOUSELAST(0x020E)
のように2つに分割して取得しないと、必要なメッセージが本来のプロシージャに届かなくなります。さて、ここからは自分の妄想になりますが、
例えば、MD-DOSの頃の(独自実装のGUI)アプリケーション等では、(処理A)ユーザーが[ESC]キーを押すか、又は、マウスの右ボタンを押すまで待つ
といった機能関数がよくありました。いわゆる「プロンプト機能」の一部です。
これをWindowsのようなメッセージドリブンなOSに「無理やり」移植すると提示されたKbMouseBreak()ようなコードになります。
この様なコードは、現在のOSの要請には合わないため、全面的な修正が必要になるのが普通です。
勘案すると、Win32SDKなど古典的で詳細な知識が必要な比較的困難な移植作業を担当しているという印象を持ちました。- 編集済み 仲澤@失業者 2017年2月28日 10:43
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12
-
C/C++ のプログラムからC#のフォームにループカウンターの
値を表示できませんでしたので、C#に持ってきました。
中途半端というよりはそのまんま持ってきました。
C# の Windows Forms アプリケーションにとって、それらの C/C++ コードを持ってきても有害のように思えます。
もちろん、すべてが有害とは言い切れませんが、今見せられている断片からは、C# アプリケーションにつなげるべきではないと思えます。少なくとも、ユーザーと対話する部分は1から作り直してください。
あるべき姿が良く分かりません。
幾つかのモードレスダイアログを連動させて作っています。
モードレスダイアログではダメなのですか?今の情報だけでその質問に答えられる人はいません。
あなたが何を作りたくて、どのようなことを試して、どのようなことを期待して、どのようになったかわかりませんので…。バックグラウンドで処理とは具体的にどのようにするのですか?
バックグラウンドで処理に興味があります。
興味があるなら調べましょう。
あるいはまとまった入門書を探してみましょう。「C# バックグラウンド」という2つだけでも立派なキーワードです。
また、DOBON.NET さんの記事 を一通り読んでいくだけでもいろいろと広げられると思います。
- 編集済み AzuleanMVP, Moderator 2017年2月28日 13:31
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12
すべての返信
-
どのようなケースで xxx_FormClosing が呼ばれないのかわからないのですが、
メッセージ(WM_SYSCOMMAND&SC_CLOSE)を捉えることで閉じるボタンを押したときの処理をかけないでしょうか?
using System.Security.Permissions; using System.Windows.Forms; namespace WindowsFormsFormClosing { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // ここに来ない!? } [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { const int WM_SYSCOMMAND = 0x112; const long SC_CLOSE = 0xF060L; if (m.Msg == WM_SYSCOMMAND && (m.WParam.ToInt64() & 0xFFF0L) == SC_CLOSE) { // 閉じるボタンが押されたときはここに来る } base.WndProc(ref m); } } }
-
Windows10 VS2013です。
プログラムはボタンを押したらフォームが表示され、無限ループの関数が動き出します。
表示されたフォーム上の終了ボタンを押すと無限ループの関数から抜け出しフォームも閉じます。
この範囲では上手く行っています。
ところが、フォームの×印をクリックしても、xxx_FormClosing に入らない場合が有ります。
この場合、終了ボタンを押しても、1回目では終われず2回目で終わります。
時には、まったく終われなくなります。
ループ内では、以下のようなC++の関数を利用し、ESCキーやマウスの右ボタンの情報を得ています。
int KbMouseBreak( void ) { unsigned int Status; int ret = 0; MSG msg; HWND hWin = GetActiveWindow(); while ( PeekMessage( &msg, hWin, WM_KEYFIRST, WM_MOUSELAST, PM_REMOVE) ) { if ( msg.message == WM_RBUTTONDOWN ) { ret = 2; return( ret ); } if ( msg.message == WM_KEYDOWN ) { Status = GetKeyState( VK_ESCAPE ) ; if ( Status >= 0x8000 ){ ret = C_BK_STATUS; return( ret ); } } TranslateMessage( &msg ); DispatchMessage( &msg ); } return( ret ) ; } void DoEvents( void ) { MSG msg; while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage(&msg); DispatchMessage(&msg); } }
ループ内で、
ret = KbMouseBreak()
だけを利用している場合に上記のような現象が出ていました。ret = KbMouseBreak();
意味は良く分かりませんが、とりあえず解決しました。
DoEvents();
のようにしたら、常に xxx_FormClosing イベントが発生し正常な動きになりました。
mnicksashimisan
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12
-
どういうコードを作っているのですか?
C# で PeekMessage/TranslateMessage/DispatchMessage を使う場面はほとんどないはずです。C/C++ のプログラムを中途半端に移植してきていませんか?
なぜ、KbMouseBreak や自作の DoEvents が必要なのですか?なお、KbMouseBreak がマウスとキー関連のメッセージしか処理しないことによって、「応答なし」とみなされ、2 回目の×クリックで強制終了されているのが大本の問題です。
解決するためには、変な作り方をやめて、あるべき姿を考えていかないといけません。
(無限ループで作り込むという考え方をやめて、モーダルダイアログを表示してバックグラウンドで処理するなど)
- 編集済み AzuleanMVP, Moderator 2017年2月27日 9:36
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12
-
> C/C++ のプログラムを中途半端に移植してきていませんか?
> なぜ、KbMouseBreak や自作の DoEvents が必要なのですか?
C/C++ のプログラムからC#のフォームにループカウンターの
値を表示できませんでしたので、C#に持ってきました。
中途半端というよりはそのまんま持ってきました。
> あるべき姿を考えていかないといけません。
あるべき姿が良く分かりません。
幾つかのモードレスダイアログを連動させて作っています。
モードレスダイアログではダメなのですか?
バックグラウンドで処理とは具体的にどのようにするのですか?
バックグラウンドで処理に興味があります。
現在、フォームをマウスで押さえると、ループが止まってしまいます。
このことを解消したいと思っているのですが、それが出来ますか?
今すぐ対応はできませんが、少しの説明やキーワード、本などを紹介して頂けませんか?
mnicksashimisan
-
キューされているメッセージを大胆に削除(PM_REMOVE)しすぎているからではないでしょうか。
提示されたKbMouseBreak()では、WM_KEYFIRST(0x0100)~WM_MOUSELAST(0x020E)の
メッセージを全て読み捨てますが、この間にはキー及びマウスだけでなく、他のメッセージも含まれます。
例えばWM_SYSKEYDOWN等はデフォルトのプロシージャの内部で他のメッセージに変換される可能性がありますので、
デフォルトの処理に回す必要があるかもしれません。
キーだけ取りたいのなら必要なキーだけ、ないしWM_KEYFIRST(0x0100)~WM_KEYLAST(0x0109)、
・・この場合WM_SYSKEYDOWN等が含まれてしまいますが・・・
マウスだけ取りたいなら必要なボタンだけ、ないしWM_MOUSEFIRST(0x0200)~WM_MOUSELAST(0x020E)
のように2つに分割して取得しないと、必要なメッセージが本来のプロシージャに届かなくなります。さて、ここからは自分の妄想になりますが、
例えば、MD-DOSの頃の(独自実装のGUI)アプリケーション等では、(処理A)ユーザーが[ESC]キーを押すか、又は、マウスの右ボタンを押すまで待つ
といった機能関数がよくありました。いわゆる「プロンプト機能」の一部です。
これをWindowsのようなメッセージドリブンなOSに「無理やり」移植すると提示されたKbMouseBreak()ようなコードになります。
この様なコードは、現在のOSの要請には合わないため、全面的な修正が必要になるのが普通です。
勘案すると、Win32SDKなど古典的で詳細な知識が必要な比較的困難な移植作業を担当しているという印象を持ちました。- 編集済み 仲澤@失業者 2017年2月28日 10:43
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12
-
C/C++ のプログラムからC#のフォームにループカウンターの
値を表示できませんでしたので、C#に持ってきました。
中途半端というよりはそのまんま持ってきました。
C# の Windows Forms アプリケーションにとって、それらの C/C++ コードを持ってきても有害のように思えます。
もちろん、すべてが有害とは言い切れませんが、今見せられている断片からは、C# アプリケーションにつなげるべきではないと思えます。少なくとも、ユーザーと対話する部分は1から作り直してください。
あるべき姿が良く分かりません。
幾つかのモードレスダイアログを連動させて作っています。
モードレスダイアログではダメなのですか?今の情報だけでその質問に答えられる人はいません。
あなたが何を作りたくて、どのようなことを試して、どのようなことを期待して、どのようになったかわかりませんので…。バックグラウンドで処理とは具体的にどのようにするのですか?
バックグラウンドで処理に興味があります。
興味があるなら調べましょう。
あるいはまとまった入門書を探してみましょう。「C# バックグラウンド」という2つだけでも立派なキーワードです。
また、DOBON.NET さんの記事 を一通り読んでいくだけでもいろいろと広げられると思います。
- 編集済み AzuleanMVP, Moderator 2017年2月28日 13:31
- 回答としてマーク mnicksashimisan 2017年3月1日 7:12