由于SystemEvents.OnUserPreferenceChanged事件,WinForms应用程序挂起

我一直在处理我的客户端安装中的一个奇怪的应用程序挂起。 在尝试了几件事之后,我得出的结论是,如果没有转储,它将无法工作。 所以我在挂机时从我的一个客户那里收了一个转储。 我必须注意,它只发生在我的安装中而不是我的开发计算机中。

在我的转储中,我看到SystemEvents.OnUserPreferenceChanged事件导致我的UI线程阻塞等待线程,这不再是消息。

在这里和谷歌搜索后,我发现有些人我们也遇到了这个问题。 我上下我的代码,看看我们是否有机会在非UI线程上创建控件或表单,但没有运气。

这是我的!clrstack

0012ee5c 7c90e514 [HelperMethodFrame_1OBJ: 0012ee5c] System.Threading.WaitHandle.WaitOneNative(Microsoft.Win32.SafeHandles.SafeWaitHandle, UInt32, Boolean, Boolean) 0012ef08 792b68af System.Threading.WaitHandle.WaitOne(Int64, Boolean) 0012ef24 792b6865 System.Threading.WaitHandle.WaitOne(Int32, Boolean) 0012ef38 7b6f1a4f System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle) 0012ef4c 7ba2d68b System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean) 0012efec 7b6f33ac System.Windows.Forms.Control.Invoke(System.Delegate, System.Object[]) 0012f020 7b920bd7 System.Windows.Forms.WindowsFormsSynchronizationContext.Send(System.Threading.SendOrPostCallback, System.Object) 0012f038 7a92ed62 Microsoft.Win32.SystemEvents+SystemEventInvokeInfo.Invoke(Boolean, System.Object[]) 0012f06c 7a92dc8f Microsoft.Win32.SystemEvents.RaiseEvent(Boolean, System.Object, System.Object[]) 0012f0b8 7a92e227 Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(Int32, IntPtr, IntPtr) 0012f0d8 7aaa06ec Microsoft.Win32.SystemEvents.WindowProc(IntPtr, Int32, IntPtr, IntPtr) 0012f0dc 003c222c [InlinedCallFrame: 0012f0dc] 0012f2a0 7b1d8d2e System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32) 0012f33c 7b1d8997 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext) 0012f390 7b1d87e1 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext) 0012f3c0 7b195931 System.Windows.Forms.Application.Run(System.Windows.Forms.Form) 0012f3d4 034608b6 InsFocusBI.Presentation.MdiMain.NewMdiMainHandler(System.Object, NewSingleInstanceEventArgs) 0012f3ec 034607ac InsFocusBI.Utilities.SingleInstanceHandler.Start(System.String[], System.String) 0012f42c 0346021a InsFocusBI.Presentation.MdiMain.Run(System.String[]) 0012f440 0346019b InsFocusBI.Presentation.MdiMain.Main(System.String[]) 0012f688 79e71b4c [GCFrame: 0012f688] 

我认为可能使用转储我可以弄清楚在另一个线程上创建的控件是什么,它会给我一个线索。

我试过了!dso来获取所有堆栈对象:

 OS Thread Id: 0x4f0 (0) ESP/REG Object Name 0012ed90 0132e8cc System.Windows.Forms.WindowsFormsSynchronizationContext 0012ee1c 06bfe2a0 System.Threading.ManualResetEvent 0012ee30 06bfe2a0 System.Threading.ManualResetEvent 0012ee9c 06bfe2a0 System.Threading.ManualResetEvent 0012eea4 0132381c System.Collections.Hashtable 0012eeb0 06bfe2a0 System.Threading.ManualResetEvent 0012eee0 06bfe2b8 Microsoft.Win32.SafeHandles.SafeWaitHandle 0012ef28 06bfe2a0 System.Threading.ManualResetEvent 0012ef78 06b4d080 System.Windows.Forms.PropertyStore 0012ef80 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012ef88 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012ef8c 06bfe1b0 System.Windows.Forms.Control+ThreadMethodEntry 0012ef90 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012ef94 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012ef9c 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012efa4 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012efd4 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012efe4 06bfe124 System.Object[] (System.Object[]) 0012efe8 06bfe104 System.Threading.SendOrPostCallback 0012efec 06bfe138 System.Windows.Forms.Control+MultithreadSafeCallScope 0012f004 064a8228 System.Threading.Thread 0012f00c 06b4d018 System.Windows.Forms.Application+MarshalingControl 0012f01c 06bfe124 System.Object[] (System.Object[]) 0012f028 06b4cf64 System.Windows.Forms.WindowsFormsSynchronizationContext 0012f034 06bfddc4 System.Object[] (System.Object[]) 0012f038 06b6096c Microsoft.Win32.SystemEvents+SystemEventInvokeInfo 0012f068 06bfddc4 System.Object[] (System.Object[]) 0012f074 0132a174 System.Object 0012f078 06bfdde8 System.Object[] (Microsoft.Win32.SystemEvents+SystemEventInvokeInfo[]) 0012f07c 0132a298 System.Object 0012f0a4 0132a3c4 Microsoft.Win32.SystemEvents 0012f0a8 0132a298 System.Object 0012f0b4 06bfddc4 System.Object[] (System.Object[]) 0012f0c0 0132a3c4 Microsoft.Win32.SystemEvents 0012f270 017dbd10 InsFocusBI.Presentation.Controls.CustomListView 0012f288 0132e8f0 System.Windows.Forms.Application+ThreadContext 0012f2cc 017860c0 System.Windows.Forms.NativeMethods+MSG[] 0012f2d0 0132e8f0 System.Windows.Forms.Application+ThreadContext 0012f2d8 01372050 System.Windows.Forms.Application+ComponentManager 0012f350 0132e8f0 System.Windows.Forms.Application+ThreadContext 0012f38c 01785a74 System.Windows.Forms.ApplicationContext 0012f428 012fd464 System.String el02 0012f6f4 012f913c System.Object[] (System.String[]) 

我唯一看到的是InsFocusBI.Presentation.Controls.CustomListView,但我没有看到它是在另一个线程上创建的。

任何建议? 有人可以提出另一个想法或尝试吗?

谢谢

好吧,你有非UI线程控制’创建’的经典问题。

  • 可能是您正在创建控件。
  • 访问某些属性会在您不知情的情况下创建一个控件(罕见)。
  • 在创建Handle属性之前访问它(可能是间接的)。
  • 被InvokeRequired(ref 1),(参考2)所欺骗。

关于这个主题的精彩阅读:(参考1)(参考2)和(参考3)。

我不知道在转储中找到控件的方法(自己尝试过),但您可以尝试在代码中设置断点,如(参考文献4)和(参考文献5)中所述。

干杯。

参考:

  1. 神秘的悬挂或InvokeRequired的大骗局

  2. Control.Trifecta:InvokeRequired,IsHandleCreated和IsDisposed

  3. .NET 2.0 WinFormsmultithreading和一些漫长的日子

  4. 调试UI

  5. 泄漏线程的情况处理

由SystemEvents.OnUserPreferenceChanged导致的冻结问题是一个非常常见的错误, 这是Microsoft的解释和建议如何解决它。

以下是您可以随时(冻结之前或之后)调用的函数,以找出在错误的线程上创建了订阅SystemEvents的特定控件,从而可以冻结您的应用:

  private static void CheckSystemEventsHandlersForFreeze() { var handlers = typeof(SystemEvents).GetField("_handlers", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null); var handlersValues = handlers.GetType().GetProperty("Values").GetValue(handlers); foreach (var invokeInfos in (handlersValues as IEnumerable).OfType().ToArray()) foreach (var invokeInfo in (invokeInfos as IEnumerable).OfType().ToArray()) { var syncContext = invokeInfo.GetType().GetField("_syncContext", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo); if (syncContext == null) throw new Exception("syncContext missing"); if (!(syncContext is WindowsFormsSynchronizationContext)) continue; var threadRef = (WeakReference) syncContext.GetType().GetField("destinationThreadRef", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(syncContext); if (!threadRef.IsAlive) continue; var thread = (Thread)threadRef.Target; if (thread.ManagedThreadId == 1) continue; // Change here if you have more valid UI threads to ignore var dlg = (Delegate) invokeInfo.GetType().GetField("_delegate", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo); MessageBox.Show($"SystemEvents handler '{dlg.Method.DeclaringType}.{dlg.Method.Name}' could freeze app due to wrong thread: " + $"{thread.ManagedThreadId},{thread.IsThreadPoolThread},{thread.IsAlive},{thread.Name}"); } } 

如果您还不知道如何检查这一点。 看看这个页面Kim Greenlee博客并试用间谍++解决方案。 也许它可以帮助你,我们有同样的问题,无法模拟挂起的应用程序的问题,但仍然研究了很多!