【问题标题】:Reproducing UserPreferenceChanged Event to verify freezing issue fixed重现 UserPreferenceChanged 事件以验证冻结问题已修复
【发布时间】:2014-11-17 09:07:14
【问题描述】:

我最近遇到了可怕的 UserPreferenceChanged 事件 UI 冻结问题,并随后解决了可能的原因,例如:

但我遇到的最大问题是可靠地再现冻结。我发现您可以通过在 RDP 会话中运行应用程序,在不注销的情况下退出会话,然后重新连接到 RDP 会话来获得类似的复制(通常,这会引发 OnThemeChanged 事件而不是UserPreferenceChanged 事件)。使用这种方法,我设法相当一致地冻结了应用程序。然后,我遵循了上述一些建议并纠正了我发现的问题。这似乎解决了问题,我交给了 QA,使用上述方法他们也无法冻结。

但是,客户仍然看到冻结问题。当它发生时,我会从它们那里获取进程转储文件,并且可以看到 SystemEvents.OnUserPreferenceChanged 事件已被触发。

mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
System.Windows.Forms.dll!System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle waitHandle)
System.Windows.Forms.dll!System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control caller, System.Delegate method, object[] args, bool synchronous)
System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate method, object[] args)
System.dll!Microsoft.Win32.SystemEvents.SystemEventInvokeInfo.Invoke(bool checkFinalization, object[] args)
System.dll!Microsoft.Win32.SystemEvents.RaiseEvent(bool checkFinalization, object key, object[] args)
System.dll!Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(int msg, System.IntPtr wParam, System.IntPtr lParam)
System.dll!Microsoft.Win32.SystemEvents.WindowProc(System.IntPtr hWnd, int msg, System.IntPtr wParam, System.IntPtr lParam)
[Native to Managed Transition]  
[Managed to Native Transition]
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context)

所以我的下一个想法是上面的 RDP 方法不是问题的可靠再现,因此我需要重新创建 UserPreferenceChanged 事件(WM_SETTINGCHANGE)。

所以我创建了一个带有以下内容的快速控制台应用程序(基于来自pinvoke.net 的一些代码)

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
    IntPtr windowHandle,
    uint Msg,
    IntPtr wParam,
    IntPtr lParam,
    SendMessageTimeoutFlags flags,
    uint timeout,
    out IntPtr result);

const uint WM_SETTINGCHANGE = 0x1A;
IntPtr innerPinvokeResult;
var HWND_BROADCAST = new IntPtr(0xffff);

var pinvokeResult = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero,
    IntPtr.Zero, SendMessageTimeoutFlags.SMTO_NORMAL, 1000, out innerPinvokeResult);

Console.WriteLine(pinvokeResult);
Console.WriteLine(innerPinvokeResult);

Console.WriteLine(pinvokeResult == (IntPtr) 0 ? "Failed" : "Success");

这似乎有效,因为它会导致重绘(或刷新?)或在我的机器上打开的资源管理器窗口。

为了验证,我在应用程序中添加了一个按钮,当点击它时订阅事件:

SystemEvents.UserPreferenceChanged += (s, e) => MessageBox.Show("user pref");

所以我单击应用程序中的按钮进行订阅,然后运行我的控制台应用程序以触发 WM_SETTINGCHANGE,它确实会导致应用程序显示消息框。因此,我尝试使用我的控制台应用程序作为测试的一部分来尝试重现问题 - 但它不会导致 UI 冻结!

我注意到,如果我在测试事件订阅中的 MessageBox.Show 上放置一个断点,那么堆栈跟踪与我期望的不一样。我希望它与客户得到的一样。相反,它是:

TestingForm.AnonymousMethod__40(object o, Microsoft.Win32.UserPreferenceChangedEventArgs ev) Line 1041
[Native to Managed Transition]  
mscorlib.dll!System.Delegate.DynamicInvokeImpl(object[] args)
System.dll!Microsoft.Win32.SystemEvents.SystemEventInvokeInfo.InvokeCallback(object arg)
[Native to Managed Transition]  
mscorlib.dll!System.Delegate.DynamicInvokeImpl(object[] args)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackDo(System.Windows.Forms.Control.ThreadMethodEntry tme)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(object obj)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallback(System.Windows.Forms.Control.ThreadMethodEntry tme)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbacks()
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m)
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam)
[Native to Managed Transition]  
[Managed to Native Transition] 
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context)

所以我的问题是:为什么我会得到不同的堆栈跟踪;我的控制台应用程序真的正确引发了 UserPreferenceChange 事件吗?如果不是,我该如何重现它?

我正在使用 .NET 4.0。

【问题讨论】:

  • 关于Creating Controls on non-UI thread——你的程序初始化代码中有Control.CheckForIllegalCrossThreadCalls = true;吗?
  • 是的,我已经启用了。我相信我已经解决了所有已知问题 - 但我需要能够首先证明我可以在旧版本上重现,然后证明“修复”版本不再冻结。

标签: c# .net windows winforms


【解决方案1】:

UserPreferenceChanged 冻结问题通常可以通过锁定和解锁屏幕来重现。检查here如何找到根本原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 2016-09-04
    • 2020-10-26
    • 2011-01-12
    • 2020-12-26
    相关资源
    最近更新 更多