2011-02-10 12 views
1

非常に複雑なアプリケーションを簡略化してこのサンプルプログラムを作成しました。同じバイナリ[.exe]は、起動時に一部のコンピュータでNULLポインタ例外をスローします。だから、Windows Formsフォームを適切に構築する方法を知りたい。Windowsフォームアプリケーションを正しく初期化する方法

Form1_SizeChangedメソッドは、InitializeComponents()の最後の文であるthis.ResumeLayout(false)メソッドの結果です。私はそれをシミュレートすることを知らないので、私はちょうどこのテストプログラムのためにサイズを変更しました。

public partial class Form1 : Form 
{ 
    public class Logger { 
     public Logger() { } 
     public void log(string str) { 
      Console.WriteLine("logging - " + str); 
     } 
    } 

    Logger logger = null; 

    public Form1() 
    { 
     InitializeComponent(); 
     this.Size = new Size(200, 300); 
     MyInitialize(); 
    } 

    private void MyInitialize() { 

     // Just that it takes some time. 
     Console.WriteLine("MyInitialize -- Enter"); 
     for (int count = 0; count <5 ; count++){ 
      Console.WriteLine("Sleeping -- " + count); 
      Thread.Sleep(1000); 
     } 
     logger = new Logger(); 
    } 

    private void sleepingPill(int millisec) { 
     Thread.Sleep(millisec); 
    } 

    private void Form1_SizeChanged(object sender, EventArgs e) 
    { 
     logger.log("Form1_SizeChanged -- Enter"); 
    } 
} 

Form1が適切に構築されていない限り、私の理解によると、Form1_SizeChangedが呼ばれるべきではありません。このシナリオでは、Windowsフォームのイベントアーキテクチャはどのように機能するのか、


Original Stack Trace: from our complex application 
System.NullReferenceException was unhandled 
    Message=Object reference not set to an instance of an object. 
    Source=ABCD 
    StackTrace: 
     at ABCD.Form1.AppendToLog(String s) 
     at ABCD.Form1.Form1_SizeChanged(Object sender, EventArgs e) 
     at System.Windows.Forms.Control.OnSizeChanged(EventArgs e) 
     at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight) 
     at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height) 
     at System.Windows.Forms.Control.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified) 
     at System.Windows.Forms.Form.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified) 
     at System.Windows.Forms.Control.ScaleControl(SizeF factor, BoundsSpecified specified) 
     at System.Windows.Forms.ScrollableControl.ScaleControl(SizeF factor, BoundsSpecified specified) 
     at System.Windows.Forms.Form.ScaleControl(SizeF factor, BoundsSpecified specified) 
     at System.Windows.Forms.Control.ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl) 
     at System.Windows.Forms.ContainerControl.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl) 
     at System.Windows.Forms.ContainerControl.PerformAutoScale(Boolean includedBounds, Boolean excludedBounds) 
     at System.Windows.Forms.ContainerControl.PerformNeededAutoScaleOnLayout() 
     at System.Windows.Forms.ContainerControl.OnLayoutResuming(Boolean performLayout) 
     at System.Windows.Forms.Control.ResumeLayout(Boolean performLayout) 
     at ABCD.Form1.InitializeComponent() 
     at ABCD.Form1..ctor() 
     at ABCD.Program.Main() 
    InnerException: 

スタックトレースからの通知:Form1_sizeChangedはInitializeComponents()から呼び出されます。これは起こるべきではないと思います。 Form1_sizeChangedはForm1クラスのインスタンスメソッドであり、Form1が構築される前に呼び出されるべきではありません。 .NET環境がこのイベントを処理したい場合、Form1が適切に構築されるまで待機する必要があります。

+0

この例外はどこにスローされますか?どのマシン?スタックトレースはありますか? – Oded

+0

@Oded:スタックトレースが質問に追加されました。 –

答えて

2

コンストラクタの2行目で、Sizeを設定します。これにより、ロガーが作成される前にForm1_SizeChanged()が呼び出されます。 MyInitializeの呼び出し後にSizeの設定を移動します。

+0

問題がどこにあるかはわかりますが、なぜ問題なのかを知りたいのですが? Windowsフォームの "events"は[@nagative()suggested]のようにキューに入れられ、オブジェクト "Form1"が正しく構築された後に実行されます。また、Windowsフォームアプリケーションとしてメインスレッドが1つしかないことを覚えておいてください。 @ALL適切な変更を行いました。ヌルをチェックし、できるだけロガーの初期化を "上"にして、問題は表示されません。しかし、私はまだ、知ってほしい、一体何が!なぜそれが問題なのですか? – karephul

+1

いいえ、ユーザー入力(キーボード、マウスなど)からのイベントはウィンドウによってキューに入れられます。ただし、コードでSizeプロパティを設定すると、すぐにSizeChangedイベントが発生します。 –

1

私は

あなたのInitializeComponent()ルーチンのセットアップフォームのSizeChangedイベントにマッピングするForm1_SizeChangedイベント/あなたがSizeChangedイベントにごヌルエラーを取得していることを推測します。あなたのthis.Sizeコードは上記のイベントを発生させます。 logger.logはMyInitializeルーチンまで初期化されないため、散発的な結果が表示されることがあります。

基本的に、WindowsはForm1_SizeChangedイベントをキューに入れ、アプリケーションは非同期で応答するため、Logger()が初期化された後に応答することがあり、ロガーが初期化される前に応答することがあります。

@ Karephul:この動作が表示される理由は、メッセージループが個々のクラスレベルではなくアプリケーションレベルにあることです。 GUIウィンドウは他のものと同様のクラスであり、メッセージループがそれらをクラスに送ることができるとすぐにメッセージループからイベントを受け取り、必ずしもコンストラクタが終了するのを待つ必要はありません。

+0

()、あなたが正しいと思う行動に基づいて、あなたが言っていることへの参照を投稿できますか? – Karephul

+0

私が言っていることを正確に記述する具体的なリンクはありませんが、Windows Messagingの詳細については、次のリンクを参照してください。 http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspxおそらくあなたのフォームで起こっている出来事を見てSpy ++を使うことができますが、私が推測するならば、サイズ変更を行うと、WM_SIZINGイベントがフォーム用に送信されました。彼らがPostMessageを行う場合、それは非同期で、SendMessageは同期的です。 PostMessageとSendMessageの詳細については、http://msdn.microsoft.com/en-us/magazine/cc301431.aspxを参照してください。 –

1

間違いなく、Form1_SizeChangedがMyInitializeより前に呼び出されているため、ロガーが初期化される前に呼び出されます。あなたは、あなたの質問に質問を自分で答え

if (logger != null) 
     logger.log("Form1_SizeChanged -- Enter"); 
0

にその方法を変更し 。それはすべてResumeLayout()呼び出しと関係しています。

自動生成された "InitializeComponent()"コードは、レイアウトをオフにし、コンポーネントをアセンブルしてレイアウトを元に戻します。これは、すべてのサブコンポーネントの追加でレイアウトを再計算することが遅くなる可能性があるため、最適化されていると思います。レイアウトが再び有効になると、あなたを含め、多くのコンポーネントのサイズ変更イベントが呼び出される可能性が高くなります。

フォームコードは、コンストラクタから呼び出すことができるもので高速かつ緩やかに動作します。コンストラクタが終了するまで絶対に呼び出されるべきではないすべての種類の仮想メソッドを呼び出すことに注意してください。

修正プログラムは、Loadイベントで完全に構築されたオブジェクトを持つ必要があるものをすべて初期化します。 Loadイベントは一度だけ呼び出され、インスタンスが完全に構築された後に呼び出されます。あなたのためにこれは開発環境が管理して

private void Form1_Load(object sender, EventArgs e) 
{ 
    this.Load += Form1_SizeChanged; 
} 

はい、それは苦痛であるが含まれてForm1_Loadイベントを追加したものとForm1_SizeChangedイベントを除去することを意味します。はい、あなたは正しく、フォームは間違っています。少なくとも周囲の仕事はあまりに面倒ではありません。

ああ、私は行っていません。私は、MoveイベントはLoadイベントの前に発生する可能性があると聞いたことがありますが、コンストラクタの終了後に何らかの理由ですべてのMoveイベントが必要な場合は、sgmooreのようにMoveイベントハンドラで防御コーディングを行う必要があります。

関連する問題