2012-04-16 67 views
3

我有一个已经运行了相当长的一段时间,C#/ TCP/WinForm的应用程序,但我的用户之一已经发现,我可以很容易地复制一个问题,但我似乎无法来解决。在Windows 7中更改屏幕保护程序设置时,应用程序冻结 - System.Threading.Timer罪魁祸首?

如果应用程序是开放的Windows 7,然后用户更改他们的屏幕保护程序设置任何选项 - 无论是正在使用的屏幕保护程序或时间 - 应用程序冻结和UI变得没有响应。点击表格上的任何地方,都会让系统“叮当作响”。

当应用程序是通过Visual Studio在调试模式下运行,这个问题就不存在了,但一旦出了VS的框框,可以追溯到冻结。

一些修补和测试之后,我似乎我的问题已经缩小到运行每秒一次System.Threading.Timer。我一直在稳步地削减计时器的功能,并发现即使计时器的事件不起作用,它仍会锁定应用程序。如果我在代码中禁用计时器或在更改屏幕保护程序设置之前取消应用程序中的计时器,则应用程序将在屏幕保护程序更改后恢复正常(尽管它似乎仍冻结约2-3秒)。

这里这个代码,似乎一切必要使应用程序可冻结(WinForm的代码中):

/// <summary> 
    /// Starts the countdown timer for unit alerts 
    /// </summary> 
    private void startCountDownTimer() 
    { 
     Object timeState = new object(); 
     this._timerCall = new System.Threading.TimerCallback(this.countDown); 
     this._countdownTimer = new System.Threading.Timer(_timerCall, timeState, 0, 1000); 
    } 

    /// <summary> 
    /// Invokes the countdown logic for unit alerts 
    /// </summary> 
    /// <param name="state"></param> 
    private void countDown(Object state) 
    { 
     // REMOVED AND STILL FREEZING 
    } 

请注意,此冻结倒计时的注释掉,这样的内容即使发生了除了每秒钟发射一次外,定时器除了做什么之外什么都不做

如果我启动过程,然后连接远程调试器吧,没有什么指示什么是错的应用程序的输出。该形式实际上仍是起火像“激活”事件:

*** MAIN WINDOW ACTIVATED *** 
*** MAIN WINDOW ACTIVATED *** 

但是没有别的似乎发射和应用必须要杀死或通过EndTask关闭。如果使用EndTask而调试器还连着,我突然得到错误:

A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll 
=================================== 
ERR: Invoke or BeginInvoke cannot be called on a control until the window handle has been created. 
     at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous) 
    at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args) 
    at System.Windows.Forms.Control.BeginInvoke(Delegate method) 
    at Wcsg.UI.Windows.CadClient.client_MessageReceived(TcpMessageReceivedEventArgs mrea) in C:\Users\---------\Documents\Visual Studio 2010\Projects\Dispatch-DEVELOPMENT\CadClient\Forms\CadClientForm.cs 
    at Wcsg.Net.Tcp.WcsgTcpClient.processStream(Int32 count) 
    at Wcsg.Net.Tcp.WcsgTcpClient.performSocketRead(IAsyncResult ar) 
--------------------- 

,我也终于如愿似乎被链接到窗体的关闭,而它终于得到周围的处理消息的错误在插座上。

我正在寻找任何一种方向看这里的。

*编辑*

有人问我关于在当预期的解决方法(请参阅回答)没有工作程序的主要方法。下面是主要代码:

[STAThread] 
    static void Main(String[] args) 
    { 
     // check for other running CadClients 
     bool createdNew = _mutex.WaitOne(TimeSpan.Zero, false); 

     if (createdNew) // first-run, launch Status Monitor on load 
     { 
      List<String> newArgs = new List<string>(args); 
      newArgs.Add("-SM"); 
      args = newArgs.ToArray(); 
     } 

     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 

     AppDomain currentDomain = AppDomain.CurrentDomain; 
     Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 

     // try to catch issue 13445 -- see testing notes 
     CadClient cc = new CadClient(args); 
     CadExceptionHandler ceh = new CadExceptionHandler(ref cc); 
     currentDomain.UnhandledException += new UnhandledExceptionEventHandler(ceh.CurrentDomain_UnhandledException); 
     Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(ceh.Application_ThreadException); 

     Application.Run(cc); 
    } 

*编辑* 我也许应该补充的启动画面代码,以及:

private void showSplashScreen() 
    { 
     WaitCallback wcb = new WaitCallback(doSplashScreen); 
     ThreadPool.QueueUserWorkItem(wcb); 
    } 

    private void doSplashScreen(object state) 
    { 
     if (this._splash == null) 
      this._splash = new SplashForm(this); 

     this._splash.FormClosed += new FormClosedEventHandler(_splash_FormClosed); 
     this._splash.Show(); 

     while (this._splash != null && !this._splash.WorkDone) 
      Application.DoEvents(); 
     this._splash.Close(); 
    } 

的方法showSplashScreen()主窗体的构造函数中调用 - 原本之前和现在在InitializeComponents()调用之后。启动画面显示更新其显示,而主应用程序验证一些安全性。然后它变成一个登录屏幕,然后在主应用程序从服务器加载数据时显示更多的udpates。一旦主窗体显示(通过事件)它已完成其工作,则SplashScreen将关闭。

+4

这个问题的标准原因是一个自定义的启动画面,它将SystemEvents事件的线程关联关闭。使用套接字并用套接字回调创建第一个窗口也可以。锁定工作站是触发死锁的另一个好方法。 – 2012-04-16 21:55:44

+0

嗯,我们最近实现了一个自定义启动画面。我会看看那条小巷。 – 2012-04-16 22:22:42

+0

基于这个线程(你也帮助过,汉斯) - http://stackoverflow.com/questions/4077822/net-4-0-and-the-dreaded-onuserpreferencechanged-hang - 我看看我们的飞溅屏幕上,但启动画面实际上并没有在我们的应用程序中首先运行,它是由主窗体启动并包含登录屏幕的子窗体。 我还是试图将代理连接到UserPreferenceChanged事件,但这也没有什么不同。 到目前为止,我唯一明白的是,如果编译为.NET 2.0,则不会发生这种情况。 – 2012-04-17 21:09:26

回答

3

使用@HansPassant的注释,发现我们的splash/login屏幕的showSplash方法在Application.Run之前未被调用,但在我们的主窗体的InitializeComponent方法之前调用了WAS。将this.showSplash移动到InitializeComponent()下方的行并重新编译,问题似乎消失了。

+1

我最好把它投票。这是一个很常见的问题,很少有程序员记录他们的修复。你可以接受你自己的答案。 – 2012-04-17 23:35:32

+0

奇怪的是我的解决方法没有解决它。帮我一个忙,看看你的Main()方法。通常在Program.cs中。它是否具有[STAThread]属性?还要检查你的启动画面线程,你打电话给SetApartmentState()吗? – 2012-04-17 23:38:54

+0

另外17个小时不能接受。所以说StackExchange的神。 :-) – 2012-04-17 23:38:59

相关问题