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

  • 質問

  • xxx_FormClosingで処理しいたのですが、プログラムにより、
    xxx_FormClosing に入らない場合が有り、その解決策として考えています。
    本来は xxx_FormClosingに必ず入れば良いのですが。

    mnicksashimisan

    2017年2月23日 2:58

回答

  • 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

    2017年2月27日 9:10
  • どういうコードを作っているのですか?
    C# で PeekMessage/TranslateMessage/DispatchMessage を使う場面はほとんどないはずです。

    C/C++ のプログラムを中途半端に移植してきていませんか?
    なぜ、KbMouseBreak や自作の DoEvents が必要なのですか?

    なお、KbMouseBreak がマウスとキー関連のメッセージしか処理しないことによって、「応答なし」とみなされ、2 回目の×クリックで強制終了されているのが大本の問題です。
    解決するためには、変な作り方をやめて、あるべき姿を考えていかないといけません。
    (無限ループで作り込むという考え方をやめて、モーダルダイアログを表示してバックグラウンドで処理するなど)

    2017年2月27日 9:36
    モデレータ
  • キューされているメッセージを大胆に削除(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:19
  • C/C++ のプログラムからC#のフォームにループカウンターの
    値を表示できませんでしたので、C#に持ってきました。
    中途半端というよりはそのまんま持ってきました。

    C# の Windows Forms アプリケーションにとって、それらの C/C++ コードを持ってきても有害のように思えます。
    もちろん、すべてが有害とは言い切れませんが、今見せられている断片からは、C# アプリケーションにつなげるべきではないと思えます。

    少なくとも、ユーザーと対話する部分は1から作り直してください。

    あるべき姿が良く分かりません。

    幾つかのモードレスダイアログを連動させて作っています。
    モードレスダイアログではダメなのですか?

    今の情報だけでその質問に答えられる人はいません。
    あなたが何を作りたくて、どのようなことを試して、どのようなことを期待して、どのようになったかわかりませんので…。

    バックグラウンドで処理とは具体的にどのようにするのですか?
    バックグラウンドで処理に興味があります。

    興味があるなら調べましょう。
    あるいはまとまった入門書を探してみましょう。

    「C# バックグラウンド」という2つだけでも立派なキーワードです。
    また、DOBON.NET さんの記事 を一通り読んでいくだけでもいろいろと広げられると思います。

    2017年2月28日 13:29
    モデレータ

すべての返信

  • 直接知る方法は無いと思います。
    むしろ、なぜFormClosingイベントが発生しないかをまず探るべきだと思いますので、そちらで新しく質問のスレッドを立てられても良いと思います。その際には、作成されている環境、コード等、質問内容ができるだけよくわかるように書かれると、的確な回答を早く得られる可能性が高まります。

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2017年2月23日 3:09
    モデレータ
  • どのようなケースで 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);
            }
        }
    }
    

    2017年2月23日 3:11
  • 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

    2017年2月27日 9:10
  • どういうコードを作っているのですか?
    C# で PeekMessage/TranslateMessage/DispatchMessage を使う場面はほとんどないはずです。

    C/C++ のプログラムを中途半端に移植してきていませんか?
    なぜ、KbMouseBreak や自作の DoEvents が必要なのですか?

    なお、KbMouseBreak がマウスとキー関連のメッセージしか処理しないことによって、「応答なし」とみなされ、2 回目の×クリックで強制終了されているのが大本の問題です。
    解決するためには、変な作り方をやめて、あるべき姿を考えていかないといけません。
    (無限ループで作り込むという考え方をやめて、モーダルダイアログを表示してバックグラウンドで処理するなど)

    2017年2月27日 9:36
    モデレータ
  • > C/C++ のプログラムを中途半端に移植してきていませんか?
    > なぜ、KbMouseBreak や自作の DoEvents が必要なのですか?
    C/C++ のプログラムからC#のフォームにループカウンターの
    値を表示できませんでしたので、C#に持ってきました。
    中途半端というよりはそのまんま持ってきました。

    > あるべき姿を考えていかないといけません。
    あるべき姿が良く分かりません。

    幾つかのモードレスダイアログを連動させて作っています。
    モードレスダイアログではダメなのですか?

    バックグラウンドで処理とは具体的にどのようにするのですか?
    バックグラウンドで処理に興味があります。
    現在、フォームをマウスで押さえると、ループが止まってしまいます。
    このことを解消したいと思っているのですが、それが出来ますか?
    今すぐ対応はできませんが、少しの説明やキーワード、本などを紹介して頂けませんか?






    mnicksashimisan

    2017年2月28日 6:18
  • キューされているメッセージを大胆に削除(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:19
  • C/C++ のプログラムからC#のフォームにループカウンターの
    値を表示できませんでしたので、C#に持ってきました。
    中途半端というよりはそのまんま持ってきました。

    C# の Windows Forms アプリケーションにとって、それらの C/C++ コードを持ってきても有害のように思えます。
    もちろん、すべてが有害とは言い切れませんが、今見せられている断片からは、C# アプリケーションにつなげるべきではないと思えます。

    少なくとも、ユーザーと対話する部分は1から作り直してください。

    あるべき姿が良く分かりません。

    幾つかのモードレスダイアログを連動させて作っています。
    モードレスダイアログではダメなのですか?

    今の情報だけでその質問に答えられる人はいません。
    あなたが何を作りたくて、どのようなことを試して、どのようなことを期待して、どのようになったかわかりませんので…。

    バックグラウンドで処理とは具体的にどのようにするのですか?
    バックグラウンドで処理に興味があります。

    興味があるなら調べましょう。
    あるいはまとまった入門書を探してみましょう。

    「C# バックグラウンド」という2つだけでも立派なキーワードです。
    また、DOBON.NET さんの記事 を一通り読んでいくだけでもいろいろと広げられると思います。

    2017年2月28日 13:29
    モデレータ
  • とりあえず、今回の自分の変更で問題ない動きをしていますので、当面これで行こうと思います。

    KbMouseBreak()ようなコードは現在のOSの要請には合わないとのことのようです。
    メッセージドリブンなOSにあうような方法を勉強していきたいと思います。
    バックグラウンドで処理で、フォームをマウスで押さえると、ループが止まう
    現象も解決できそうな感じもあります。近い将来にチャレンジしてみたいと思います。

    色々勉強させて頂きありがとうございました。



    mnicksashimisan

    2017年3月1日 7:11