[英]SystemEvents.SessionSwitch causing my Windows Forms Application to freeze
我在 .NET 4.5 上有一個 C# Windows 窗體應用程序。
此應用程序連接到 USB 設備。
我想同時支持多個會話。
為此,我需要在會話鎖定時與該設備斷開連接,以允許新會話連接到它。
我使用 SystemEvents.SessionSwitchEventArgs.Reason 來檢測此類事件: - SessionSwitchReason.ConsoleDisconnect on session switch - SessionSwitchReason.ConsoleConnect on unlock after session switch
此事件似乎是完美的解決方案,但有時在隨機時間(在多次鎖定或解鎖之后),該事件不會被觸發並且 UI 會凍結。 值得注意的是,當應用程序在調試器中運行時不會發生這種情況。
我從日志中知道其他一些后台線程仍在正常工作,但 UI 凍結並且未調用事件的訂閱函數。
我的代碼示例:
程序.cs:
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyProgram
{
static class Program
{
private static Mutex mutex = null;
[STAThread]
static void Main()
{
const string appName = "MyProgram";
bool createdNew;
mutex = new Mutex(true, appName, out createdNew);
if (!createdNew)
{
//app is already running! Exiting the application
return;
}
Application.EnableVisualStyles();
//This was one attempt to solve the UI deadlock Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { };
Application.SetCompatibleTextRenderingDefault(false);
MyProgramEngine MyProgramEngine = new MyProgram.MyProgramEngine();
Application.Run(MyProgramEngine.getForm());
}
}
}
我的程序引擎:
using log4net;
using log4net.Config;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using WindowsInput;
using System.Windows.Forms;
using Microsoft.Win32;
namespace MyProgram
{
class MyProgramEngine
{
private MainForm mainForm;
public MyProgramEngine()
{
XmlConfigurator.Configure();
Utility.logger.Info(string.Format("MyProgram Started. Version: {0}", Application.ProductVersion));
SystemEvents.SessionSwitch += new SessionSwitchEventHandler(SystemEvents_SessionSwitch);
if (!GlobalSettings.getInstance().isProperlyConfigured())
{
WarningForm warningForm = new WarningForm("MyProgram is not properly configured. Please contact support");
warningForm.ShowDialog();
Application.Exit();
Environment.Exit(0);
}
mainForm = new MainForm();
initArandomBackgroundThread();
initDeviceThread();
}
private void initDeviceThread()
{
Thread detectAndStartReader = new Thread(initDevice);
detectAndStartReader.IsBackground = true;
detectAndStartReader.Start();
}
public void initDevice()
{
//Connect to device
//Start device thread
}
public MainForm getForm()
{
return mainForm;
}
//Handles session switching events
internal void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
try
{
if (e.Reason.Equals(SessionSwitchReason.ConsoleDisconnect))
{
DisconnectFromDevice();
TerminateDeviceThread();
}
else if (e.Reason.Equals(SessionSwitchReason.ConsoleConnect))
{
initDeviceThread();
}
else
{
Utility.logger.Info("The following SesseionSwitchReason has been caught: " + e.Reason + " , No action!");
}
}
catch (Exception ex)
{
Utility.logger.Error("Something bad happened while managing session switching events", ex);
}
}
}
注意:我對 SessionSwitchReason.SessionUnlock 或 SessionSwitchReason.SessionLock 不感興趣,因為我都不希望在同一會話上對會話鎖定和解鎖進行任何操作。
感謝您的支持!
我發現了錯誤在哪里。
簡而言之,
永遠不要在后台工作線程上創建控件。
在我的代碼中,如果我刪除了 SessionSwitch 事件訂閱,掛起仍然會發生。 我能夠將主線程上的等待追溯到 SystemSettingsChanging,這也是一個 SystemEvent 但我無法控制。
在我幾乎放棄試圖找出這個掛起之后,我開始逐行閱讀代碼,這讓我發現在后台線程上創建了一個表單(彈出窗口)。
如上面給出的示例所示,這部分代碼沒有引起我的注意。
initArandomBackgroundThread();
要了解有關此凍結的更多信息,您可以前往Microsoft 支持部門進行詳細解釋。
微軟聲稱的這種凍結的低級別原因是
如果在不發送消息的線程上創建控件並且 UI 線程收到 WM_SETTINGCHANGE 消息,則會發生這種情況。
常見原因是在輔助 UI 線程上創建的閃屏或在工作線程上創建的任何控件。
修復
應用程序不應將 Control 對象留在沒有活動消息泵的線程上。 如果無法在主 UI 線程上創建控件,則應在專用的輔助 UI 線程上創建它們,並在不再需要時立即處置。
調試
在進程視圖(Spy.Processes 菜單)中使用 Spy++ 識別在哪個線程上創建哪些窗口的一種方法。 選擇掛起的進程並展開其線程以查看是否有任何意外窗口。 如果它仍然存在,這將找到本機窗口; 但是,即使本機窗口已被破壞,只要托管控件尚未被處置,該問題也會發生。
我看到您只訂閱SystemEvents.SessionSwitch
事件,但從未取消訂閱。 由於 SystemEvents.SessionSwitch 是一個 STATIC 事件,因此您必須在應用程序退出之前非常小心地取消訂閱它。 如果您不取消訂閱,那么您就會為內存泄漏敞開大門,這可能會導致奇怪故障的連鎖反應。 請參閱文檔警告:
https://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents.sessionswitch(v=vs.110).aspx
由於這是一個靜態事件,您必須在處理應用程序時分離事件處理程序,否則將導致內存泄漏。
另外,您似乎正在調用DisconnectFromDevice(); TerminateDeviceThread();
DisconnectFromDevice(); TerminateDeviceThread();
在主 UI 線程上,這可以解釋一些凍結,具體取決於它實際在做什么。 最好向我們展示該代碼的作用以進一步對其進行評論。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.