WinForm二三事(一)メッセージ循環

12125 ワード

本論文は、http://www.cnblogs.com/yuyijq/archive/2009/11/04/1595775.htmlから回転する。
 
 
本文に入る前に、下の二つのコードをご覧ください。
   1: //         ,    System.Windows.Form.dll   
   2: using System.Windows.Form;
   3:  
   4: public class ConsoleApplicationShowDialog
   5: {
   6:     static void Main()
   7:     {
   8:         Form frm = new Form();
   9:         frm.ShowDialog();
  10:     }
  11: }
   1: //         ,    System.Windows.Form.dll   
   2: using System.Windows.Form;
   3:  
   4: public class ConsoleApplicationShow
   5: {
   6:     static void Main()
   7:     {
   8:         Form frm = new Form();
   9:         frm.Show();
  10:     }
  11: }
二つのコードセグメントはいずれもコンソールプログラムです。この2つのプログラムの唯一の違いはフォームを表示する時に最初にShow Dialogを使って、2番目にShowを使っています。
テストの結果、Showを使って表示されたフォームが表示されると死んでしまいました。ユーザーの入力に応答しないでフォームにボタンを押すと、ボタンが表示されなくなり、クリックしても応答がありません。ShowDialogで表示されたフォームが違っていて、ユーザーの入力に応答することができます。これはどういう理由ですか?
問題の根源を見つけるために、Show方法とShow Dialog方法の実現の違いを見てみよう。Show方法はControlで定義されています。Form間接的な派生はControl類から来ています。(ここはコンビネーションモードのようです。)Show方法コード:
   1: public void Show()
   2: {
   3:     this.Visible = true;
   4: }
Show方法のコードはかなり簡単です。やる仕事はフォームを表示するだけです。前の第二段コードは下のコードと同じです。
   1: //         ,    System.Windows.Form.dll   
   2: using System.Windows.Form;
   3:  
   4: public class ConsoleApplicationShow
   5: {
   6:     static void Main()
   7:     {
   8:         Form frm = new Form();
   9:         frm.Visible = true;
  10:     }
  11: }
今からShow Dialogの方法を見にきます。Show Dialogの方法はちょっと複雑ですが、この百行のコードの中で、あなたがよく知っているはずです。
   1: public DialogResult ShowDialog(IWin32Window owner)
   2: {
   3:     //...  
   4:     Application.RunDialog(this);
   5:     //...  
   6: }
この行のコードは私達のWinFormプログラムの起動部分と似ています。
   1: public class Program
   2: {
   3:     static void Main()
   4:     {
   5:         Form frm = new Form();
   6:         Application.Run(frm);
   7:     }
   8: }
MSDNのApplication.Runに対する説明は:
Begins running a standard application message loop on the current thread, and makes the specified form visible.
                  “    ”,         
以下はApplication.Runのコードです。
public static void Run(Form mainForm)
{
    ThreadContext.FromCurrent().RunMessageLoop(-1, new ApplicationContext(mainForm));
}
えっと?何がニュースサイクルですか?Netが開発された場合、Win 32時代の洗礼を経ていないと、このニュースのサイクルがよく分かりません。登録されたイベントだけが、イベントを処理します。Netはパッケージを通して、ユーザークリックなどのイベントを処理するためのプログラムモデルを簡略化しましたが、Netの下はまだWin 32です。時々私たちは理解しなければなりません。一部の問題を理解するのに役立つかもしれません。
メッセージループ(Message Loop)
Application.Runが一つのメッセージサイクルを起動すると言っていますが、何がニュースサイクルですか?下のコードを見てください。
MSG msg;
 
while(GetMessage(&msg,NULL,0,0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
Win 32 APIを使ってWindowsアプリを作成するプログラムのほとんどにあるコードです。これはニュースの循環です。上記のコードを徹底的に理解する必要はありません。このような意味を理解する必要があります。
Windows   Windows            ,           ,Windows              “  ”   (         MSG  ),
             ,         ,        。
    while    GetMessage                    ,    ,              。
上の討論を通じて、私達は大体分かりました。なぜShowとShow Dialogの違いがこんなに大きいのかというと、Show Dialogはメッセージループを起動しました。このようにShow Dialogで表示されたフォームでユーザーの入力イベントに応答できます。ShowはフォームのVisible属性を設定するだけで、メッセージループを起動しませんでした。ショーで表示されたフォームを使っても、ユーザーの入力イベントに応答できなくなります。つまりそこで死んでしまいます。
上記のGetMessageはメッセージを取り出して処理すると言いましたが、どこで処理しますか?Win 32プログラムでは、このようなフラグメントも見られます。
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    switch(message)
    {
        case WM_CREATE:
            //        
               return 0;
        case WM_PAINT:
            //        
              return 0;
        
        //    ,       
    }
}
あ、醜い対処法です。もともとはメッセンジャーの種類によって処理が異なりますが、WindowsはWMを山のように定義しています。最初のもの。しかし、私たちのかわいい.Net,WinFormの中の美しいイベントハンドリングモデルはこれに基づいています。上のコードを通じて、あなたと.Netで事件を使う感触は想像できますか?Netはどのようにこのプロセスをカプセル化しますか?
WinFormにおけるメッセージ処理
ネットのWinFormには、メッセージ処理の影がまだ存在しています。跡形もなく消えていません。Formにはもう一つのプロテctedの方法があります。
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case 0x10://......
        case 0x11:
        //....
    }
    base.WndProc(ref m);
}
えっと、Win 32の中のそれと同じです。実際にこの方法を書き換えることによって、正常なやり方では実現しにくいものができます。
なぜ時間がかかりますか?
こんなにたくさん話しました。私たちの身の回りのことについて話しましょう。あなたはこのような場面に出くわすべきです。プログラムを作成して、ボタンをクリックして、比較的時間がかかる操作をします。たとえば、大量のデータをデータベースに更新します。この時、プログラムは本文の冒頭のプログラムのように死んでしまいました。ユーザーはどのようにクリックしても、プログラムが灰色になり、タイトル欄には「応答がない」と表示されています。あるプログラムはヒントさえ与えられません。ユーザーは本当に死んだと思っています。これはなぜですか?
前の議論を通して、ユーザーの入力に応答することはメッセージによって循環することであり、メッセージの循環は現在のスレッド上にある、いわゆるUIスレッドであり、もし1つの時間が費やされてもUIスレッド上にあるならば、メッセージの循環は「カード」であり、後のメッセージを処理することができなくなり、プログラムは仮死してしまうことが分かりました。
このような時間のかかる操作はどうやって処理しますか?もちろん、この時間つぶし操作を他のスレッドに置いて、UIスレッドを占有しないで、メッセージのループを継続させている。
このシリーズの他の文章
WinForm二三事(一)メッセージ循環
WinForm二三事(一)補遺
WinForm二三事(二)非同期操作
WinForm二三事(三)Control.Invoke&Control.BeginnInvoke
WinForm二三事(四)インターフェースレイアウト(上)
WinForm二三事(四)インターフェースレイアウト(下)
WinForm二三事(五)実作
WinForm二三事(六)データバインディング
WinForm二三事(七)GDI+
WinForm二三事(八)開源項目
WinForm二三事(九)第三者コントロールライブラリを常用する
WinForm二三事(十)雑談