2010-12-13 2 views
0

私はスプラッシュ画面を追加して以来、私のメインフォームは時には(約20回ごとに約1回)最小化されたように消えます(それは見えなくなりますが、タスクバー上に残っています。ここに私のコードは次のとおりです。なぜこのコードはフォームを消滅させるのですか?

static class Program 
{ 
    private static SplashScreen splashScreen = null; 
    private static ManualResetEvent splashScreenWaiter = null; 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     ShowSplashAsync(); 
     BuilderForm2 builderForm2 = new BuilderForm2(); 
     builderForm2.Shown += new EventHandler(builderForm2_Shown); 
     Application.Run(builderForm2); 
    } 

    private static void HideSplash() 
    { 
     if (splashScreenWaiter != null) 
     { 
      splashScreenWaiter.WaitOne(); 
      splashScreen.Invoke(new Action(splashScreen.Close)); 
      splashScreenWaiter = null; 
      splashScreen = null; 
     } 
    } 

    private static void builderForm2_Shown(object sender, EventArgs e) 
    { 
     HideSplash(); 
    } 

    private static void ShowSplashAsync() 
    { 
     splashScreenWaiter = new ManualResetEvent(false); 
     Thread splashThread = new Thread(ShowSplash); 
     splashThread.IsBackground = true; 
     splashThread.SetApartmentState(ApartmentState.STA); 
     splashThread.Start(splashScreenWaiter); 
    } 

    private static void ShowSplash(object resetEvent) 
    { 
     splashScreen = new SplashScreen((ManualResetEvent)resetEvent); 
     Application.Run(splashScreen); 
    } 
} 

そして、これはスプラッシュコードです:

public partial class SplashScreen : Form 
{ 
    private ManualResetEvent ResetEvent; 
    bool handleCreated = false; 
    bool formShown = false; 

    public SplashScreen(ManualResetEvent resetEvent) 
    { 
     ResetEvent = resetEvent; 
     HandleCreated += new EventHandler(SplashScreen_HandleCreated); 
     InitializeComponent(); 
    } 

    private void SetResetEventIfReady() 
    { 
     if(handleCreated && formShown) ResetEvent.Set(); 
    } 

    private void SplashScreen_Shown(object sender, EventArgs e) 
    { 
     formShown = true; 
     SetResetEventIfReady(); 
    } 

    void SplashScreen_HandleCreated(object sender, EventArgs e) 
    { 
     handleCreated = true; 
     SetResetEventIfReady(); 
    } 
} 

答えて

4

何が飛び出しません。しかし、あなたのコードには非常に深刻な競合状態があります。これは、SystemEventsクラスに関連しています。このクラスは、コントロールに重要な通知を提供し、ユーザーがWindowsのテーマを変更するのに応答できるようにします。そのクラスには、ユーザーが行った変更に関するメッセージを受け取るための隠し通知ウィンドウが必要です。

これは非常にです。プログラムの最初のウィンドウがUIスレッドではなくワーカースレッドで作成されている場合は間違っています。これにより、SystemEventsクラスは間違ったスレッド(ワーカースレッドbtwではなく)上にその通知ウィンドウを作成します。そして、それが発生するイベントはそのスレッドから呼び出されます。その間違ったスレッドでイベントを取得すると混乱が生じ、コントロールはスレッドセーフではありません。最も典型的な結果は、ワークステーションをロックすると、奇妙な塗りつぶしの問題やフォームのデッドロックが発生することです。私はあなたが間違っていることが、このことによっても説明できると想像することができます。

.NETフレームワークには、既に、優れたテスト済みのスプラッシュ画面があります。私はあなた自身のスピンの代わりにそれを使用することをお勧めします。コードについてはthis answerを確認してください。

あなたは、あなたがShowSplashAsync呼び出しの前に、自分のメインメソッドにこのコード行を貼り付けて、レースの問題を回避することができ、あなた自身を維持したい場合:

Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { }; 
関連する問題