2012-04-16 70 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

相關問題