在关闭时更改桌面布局的 Windows 窗体应用程序中,Windows 窗体在处理
WM_SETTINGCHANGE
消息时似乎挂起。在应用程序关闭开始和结束之间收到的任何 WM_SETTINGSCHANGE
消息似乎都会发生这种情况。
除了添加任意延迟之外,还有办法避免这种挂起吗?
事件的明显时间表:
SHAppBarMessage(ABM_REMOVE, ...)
删除了 AppBar)WM_SETTINGCHANGE
并调用主线程WindowsFormsSynchronizationContext.Send:82
处的测试成功WaitHandle
Main
返回挂起时的调用堆栈:
主线程(没有.Net堆栈,只有本机)
win32u.dll!NtUserMsgWaitForMultipleObjectsEx()
combase.dll!CCliModalLoop::BlockFn(void * * ahEvent, unsigned long cEvents, unsigned long * lpdwSignaled) Line 2108
combase.dll!ClassicSTAThreadWaitForHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * pdwIndex) Line 54
combase.dll!CoWaitForMultipleHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * lpdwindex) Line 126
[Inline Frame] hostpolicy.dll!coreclr_t::shutdown(int *) Line 152
hostpolicy.dll!run_app_for_context(const hostpolicy_context_t & context, int argc, const wchar_t * * argv) Line 264
...snip...
.NET 系统事件线程
System.Private.CoreLib.dll!System.Threading.WaitHandle.WaitOneNoCheck(int millisecondsTimeout) Line 139
at /_/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs(139)
System.Windows.Forms.dll!System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle waitHandle) Line 3967
at /_/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs(3967)
System.Windows.Forms.dll!System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control caller, System.Delegate method, object[] args, bool synchronous) Line 7141
at /_/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs(7141)
System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate method, object[] args) Line 6587
at /_/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs(6587)
System.Windows.Forms.dll!System.Windows.Forms.WindowsFormsSynchronizationContext.Send(System.Threading.SendOrPostCallback d, object state) Line 88
at /_/src/System.Windows.Forms/src/System/Windows/Forms/WindowsFormsSynchronizationContext.cs(88)
Microsoft.Win32.SystemEvents.dll!Microsoft.Win32.SystemEvents.SystemEventInvokeInfo.Invoke(bool checkFinalization, object[] args) Line 35
at Microsoft.Win32\SystemEvents.cs(35)
Microsoft.Win32.SystemEvents.dll!Microsoft.Win32.SystemEvents.RaiseEvent(bool checkFinalization, object key, object[] args) Line 850
at Microsoft.Win32\SystemEvents.cs(850)
locals:
array[0]
._delegate = System.Windows.Forms.VisualStyles.VisualStyleRenderer.OnUserPreferenceChanging
._syncCtx = System.Windows.Forms.WindowsFormsSynchronizationContext
Microsoft.Win32.SystemEvents.dll!Microsoft.Win32.SystemEvents.WindowProc(nint hWnd, int msg, nint wParam, nint lParam) Line 961
at Microsoft.Win32\SystemEvents.cs(961)
locals:
msg = 8218
wParam = 0x2f
lParam = 0
[Native to Managed Transition]
[Managed to Native Transition]
Microsoft.Win32.SystemEvents.dll!Interop.User32.DispatchMessageW.____PInvoke|210_0(Interop.User32.MSG* msg)
Microsoft.Win32.SystemEvents.dll!Microsoft.Win32.SystemEvents.WindowThreadProc() Line 1038
at Microsoft.Win32\SystemEvents.cs(1038)
备注:
SystemEvents.UserPreferenceChanging
创建 VisualStyleRenderer..cctor
)SHAppBarMessage
和 内存转储SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, ...)
使用自定义
ApplicationContext
来确保 SystemEvents
线程当前在退出之前不会挂起:
Application.Run(new SystemEventsSafeAppContext(new Form1()));
internal class SystemEventsSafeAppContext : ApplicationContext
{
public SystemEventsSafeAppContext(Form mainForm)
: base(mainForm)
{
}
[DllImport("user32.dll")]
static extern void PostQuitMessage(int nExitCode);
protected override void OnMainFormClosed(object sender, EventArgs e)
{
var syncCtx = SynchronizationContext.Current;
SystemEvents.InvokeOnEventsThread(() =>
{
PostQuitMessage(0);
syncCtx.Post((_) =>
{
ExitThread();
}, null);
});
}
}