`OnUserPreferenceChanging` 在没有后台控件的情况下挂起

问题描述 投票:0回答:1

在关闭时更改桌面布局的 Windows 窗体应用程序中,Windows 窗体在处理

WM_SETTINGCHANGE
消息时似乎挂起。在应用程序关闭开始和结束之间收到的任何
WM_SETTINGSCHANGE
消息似乎都会发生这种情况。

除了添加任意延迟之外,还有办法避免这种挂起吗?

事件的明显时间表:

  1. 在主线程上,关闭唯一的表单时,会触发桌面布局的更改(特别是由于使用
    SHAppBarMessage(ABM_REMOVE, ...)
    删除了 AppBar)
  2. 主线程开始关闭(处理控件、表单等...)
  3. “.NET 系统事件”线程接收
    WM_SETTINGCHANGE
    并调用主线程
  4. 由于主线程尚未关闭,因此WindowsFormsSynchronizationContext.Send:82
    处的
    测试成功
  5. “.Net System Events”线程进入
    WaitHandle
  6. 主线程关闭其主窗体后,退出消息循环并
    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)

备注:

c# winforms winapi
1个回答
0
投票

使用自定义

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);
        });
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.