2010-12-13 20 views
0

Depuis que j'ai ajouté un écran de démarrage, mon formulaire principal va parfois disparaître (environ 1 fois toutes les 20 fois) (il sera invisible mais sera toujours dans la barre des tâches et réapparaît). Voici mon code:Pourquoi ce code fait-il disparaître mon formulaire?

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); 
    } 
} 

Et ce code est 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(); 
    } 
} 

Répondre

4

Rien ne saute. Il y a cependant une condition de course très sérieuse dans votre code. Il est lié à la classe SystemEvents. Cette classe fournit des notifications importantes aux contrôles afin qu'ils puissent répondre à l'utilisateur en changeant le thème Windows. Cette classe a besoin d'une fenêtre de notification masquée pour recevoir des messages sur les modifications apportées par l'utilisateur.

Cela va très incorrect si la première fenêtre de votre programme est créée sur un thread de travail au lieu du thread de l'interface utilisateur. Cela fait que la classe SystemEvents crée cette fenêtre de notification sur le mauvais thread (pas votre thread de travail btw). Et les événements qu'il soulève seront appelés à partir de ce fil. Obtenir l'événement sur ce mauvais thread crée des ravages, les contrôles ne sont pas thread-safe. Le résultat le plus typique est que vous aurez des problèmes de peinture étranges ou les blocages de formulaire lorsque vous verrouillez le poste de travail. J'imagine que ce que vous voyez mal tourner pourrait s'expliquer aussi.

Le framework .NET dispose déjà d'une excellente prise en charge des écrans de démarrage. Je vous recommande de l'utiliser au lieu de tourner le vôtre. Vérifiez this answer pour le code.

Si vous voulez garder votre propre alors vous pouvez contourner le problème de la course en collant cette ligne de code dans votre méthode principale, avant l'appel ShowSplashAsync:

Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { };