启动画面问题
DoEvents 的事情是非常不可取的,并且不一定能实现您认为它所做的事情。 DoEvents 告诉 CLR 关注 Windows 消息循环(用于启动屏幕),但不一定为其他线程提供任何处理时间。Thread.Sleep()
将为其他线程提供处理的机会,但不一定允许启动屏幕的 Windows 消息循环继续发送消息。因此,如果您必须使用循环,那么您确实需要两者,但稍后我将建议完全摆脱此循环。除了循环问题之外,我没有看到任何明确的方式来清理启动线程。你需要某种Thread.Join()
or Thread.Abort()
发生在某处。
而不是使用Application.DoEvents()
循环中,我喜欢使用 ManualResetEvent 来同步启动窗体与调用线程的启动。这样,ShowSplash() 方法在显示启动画面之前不会返回。此后的任何时候,我们显然都可以关闭它,因为我们知道它已经完成了展示。
这是一个包含一些很好示例的线程:C# 中的 .NET 多线程启动屏幕 https://stackoverflow.com/questions/48916/multi-threaded-splash-screen-in-c
下面是我如何修改我最喜欢的示例(@AdamNosfinger 发布的),以包含 ManualResetEvent,以将 ShowSplash 方法与闪屏线程同步:
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();
}
}
主要表格问题
看起来好像您正在使用 ShowDialog 从登录窗口启动主窗体,然后关闭登录窗体。我理解正确吗?如果这样的话就不好了。 ShowDialog 适用于应用程序的子窗口,并且希望有一个所有者窗口,如果您没有在方法参数中指定所有者表单,则当前活动窗口被假定为所有者。看MSDN http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx
因此,您的主表单假设登录表单是其父表单,但您在显示主表单后不久就关闭了登录表单。所以我不确定应用程序此时处于什么状态。您应该考虑使用标准Form.Show()
方法,如果这是所需的结果,只需调整 Form 属性以显示为对话框(例如:BorderStyle、MaximizeBox、MinimizeBox、ControlBox、TopMost)。
重要编辑:好吧,我是人,我搞砸了,忘记了 ShowDialog 是一个阻塞方法。虽然这确实否定了所有者句柄问题,但我仍然建议不要在主应用程序表单中使用 ShowDialog,除非您可以提供与外观或线程无关的重要理由(因为这些应该通过其他技术来修复)。尽管我犯了错误,但这个建议仍然是合理的。
可能的绘画问题
您没有指定您正在使用哪些控件,也没有指定您是否在应用程序中进行任何自定义绘制。但您需要记住,当您锁定计算机时,某些窗口句柄将被强制关闭。例如,如果您有一些自定义绘制的控件并且正在缓存字体、画笔或其他 GDI 资源,您需要有一些try { ... } catch { ... }
当绘制期间引发异常时,代码中的块会处理然后重建缓存的 GDI 资源。我之前在自定义绘制列表框并缓存一些 GDI 对象时遇到过这种情况。如果您的应用程序中的任何位置(包括启动屏幕中)有任何自定义绘制代码,请仔细检查所有 GDI 对象是否已妥善处理/清理。