Program hangs after leaving screen saver or locking computer Program hangs after leaving screen saver or locking computer multithreading multithreading

Program hangs after leaving screen saver or locking computer


Splash Screen Issues

The DoEvents thing is very undesirable and doesn't necessarily accomplish what you think it does. DoEvents tell the CLR to attend to the windows message loop (for the splash screen), but doesn't necessarily offer up any processing time to other threads. Thread.Sleep() will offer other threads a chance to process, but won't necessarily allow the windows message loop for your splash screen to continue pumping messages. So you really need both if you must use a loop, but in a minute I'm going to recommend getting away from this loop altogether. In addition to that loop issue, I don't see any explicit way the splash thread is being cleaned up. You need some kind of Thread.Join() or Thread.Abort() happening somewhere.

Instead of using a Application.DoEvents() loop, I like to use a ManualResetEvent to synchronize the splash forms start up with the calling thread. That way the ShowSplash() method doesn't return until the splash is shown. Anytime after that we are obviously ok to close it down as we know it was finished being shown.

Here's a thread with a few good examples:.NET Multi-threaded Splash Screens in C#

Here's how I modified my favorite example, that @AdamNosfinger posted, to include a ManualResetEvent to synchronize the ShowSplash method with the splash screen thread:

public partial class FormSplash : Form{    private static Thread _splashThread;    private static FormSplash _splashForm;    // This is used to make sure you can't call SplashScreenClose before the SplashScreenOpen has finished showing the splash initially.    static ManualResetEvent SplashScreenLoaded;    public FormSplash()    {        InitializeComponent();        // Signal out ManualResetEvent so we know the Splash form is good to go.        SplashScreenLoaded.Set();    }    /// <summary>    /// Show the Splash Screen (Loading...)    /// </summary>    public static void ShowSplash()    {        if (_splashThread == null)        {            // Setup our manual reset event to syncronize the splash screen thread and our main application thread.            SplashScreenLoaded = new ManualResetEvent(false);            // show the form in a new thread            _splashThread = new Thread(new ThreadStart(DoShowSplash));            _splashThread.IsBackground = true;            _splashThread.Start();            // Wait for the splash screen thread to let us know its ok for the app to keep going.             // This next line will not return until the SplashScreen is loaded.            SplashScreenLoaded.WaitOne();            SplashScreenLoaded.Close();            SplashScreenLoaded = null;        }    }    // called by the thread    private static void DoShowSplash()    {        if (_splashForm == null)            _splashForm = new FormSplash();        // create a new message pump on this thread (started from ShowSplash)        Application.Run(_splashForm);    }    /// <summary>    /// Close the splash (Loading...) screen    /// </summary>    public static void CloseSplash()    {        // need to call on the thread that launched this splash        if (_splashForm.InvokeRequired)            _splashForm.Invoke(new MethodInvoker(CloseSplash));        else            Application.ExitThread();    }}

Main Form Issues

It looks as though you are launching your mainform from your login window using ShowDialog and then closing the login form. Have I understood correctly? This is not good if so. ShowDialog is intended for child windows of your application and wants to have an owner window, if you don't specify an owner form in the method arguments the currently active window is assumed to be the owner. See MSDN

So your main form is assuming the login form is its parent, but you close the login form shortly after showing the main form. So I'm not sure what state the application is left in at that point. You should consider using a standard Form.Show() method instead and simply adjusting the Form properties to appear like a dialog if this is the desired outcome (ex: BorderStyle, MaximizeBox, MinimizeBox, ControlBox, TopMost).

IMPORTANT EDIT: Ok I'm human, I messed up and forgot ShowDialog was a blocking method. While that does negate the owner handle issue, I still recommend not using ShowDialog for your main application form unless you can provide a significant justification for it that is not appearance or threading related (as those should be fixed with other techniques). The advice is still sound, despite the misstep on my part.

Possible Painting Issues

You did not specify which controls you were using or if you were doing any custom painting in your application. But you need to keep in mind some windows handles will be forcibly closed when you lock the computer. For example if you have some custom painted controls and are caching fonts, brushes or other GDI resources you need to have some try { ... } catch { ... } blocks in your code that dispose of and then rebuild the cached GDI resources when an exception is raised during painting. I've run into this before where I was custom painting a list box and caching some GDI objects. If you have any custom painting code anywhere in your app, including in the splash screen, please double check all GDI objects are nicely disposed/cleaned up.


After adding a few lines of code to the code snippets above, I could compile a working program. However, I could not reproduce the problem (Windows 7 Starter). I tried locking the computer, and starting the screen saver, too. I did this while the splash screen was active, and in other situations, but the main window always remained responsive. I think there must be something else going on here, probably during the initialization of the main window.

Here is the code, maybe it helps the others figure out the problem.

using System;using System.Threading;using System.Windows.Forms; public class MainForm : Form{  //Here is an example of one of the _showLoadingForm actions used in one of the programs:  public static bool _showSplash = true;  public static void ShowSplashScreen()  {    //Ick, DoEvents!  But we were having problems with CloseSplashScreen being called    //before ShowSplashScreen - this hack was found at    //http://stackoverflow.com/questions/48916/multi-threaded-splash-screen-in-c/48946#48946    using(SplashForm splashForm = new SplashForm())    {      splashForm.Show();      while(_showSplash)        Application.DoEvents();      splashForm.Close();    }  }  //Called in MainForm_Load()  public static void CloseSplashScreen()  {    _showSplash = false;  }  public MainForm()   {     Text = "MainForm";     Load += delegate(object sender, EventArgs e)     {      Thread.Sleep(3000);      CloseSplashScreen();     };  }}//Multiple programs use this login form, all have the same issuepublic class LoginForm<TMainForm> : Form where TMainForm : Form, new(){  private readonly Action _showLoadingForm;  public LoginForm(Action showLoadingForm)  {    Text = "LoginForm";    Button btnLogin = new Button();    btnLogin.Text = "Login";    btnLogin.Click += btnLogin_Click;    Controls.Add(btnLogin);    //...    _showLoadingForm = showLoadingForm;  }  private void btnLogin_Click(object sender, EventArgs e)  {    //...    this.Hide();    ShowLoadingForm(); //Problem goes away when commenting-out this line    new TMainForm().ShowDialog();    this.Close();  }  private void ShowLoadingForm()  {    Thread loadingFormThread = new Thread(o => _showLoadingForm());    loadingFormThread.IsBackground = true;    loadingFormThread.SetApartmentState(ApartmentState.STA);    loadingFormThread.Start();  }}public class SplashForm : Form{  public SplashForm()   {     Text = "SplashForm";   }}public class Program{  public static void Main()  {    var loginForm = new LoginForm<MainForm>(MainForm.ShowSplashScreen);    loginForm.Visible = true;    Application.Run(loginForm);  }}


Several years later (with the code no longer in front of me), I'll add an answer for anyone else who experiences this problem.


The issue turned out to be exactly as Hans Passant had guessed. The problem was that, due to some incredibly obscure and innocuous bugs in the .Net framework, InvokeRequired can sometimes return false when it should return true, causing code that should run on the GUI thread to run in the background (which, due to some more obscure and innocuous bugs, causes the behavior I was seeing).

The solution is to not rely on InvokeRequired, using a hack similar to this:

void Main(){    Thread.Current.Name = "GuiThread";    ...}bool IsGuiThread(){    return Thread.Current.Name == "GuiThread";}//Later, call IsGuiThread() to determine if GUI code is being run on GUI thread

This solution, as well as an extremely in-depth look at the causes of the issue, was found here.