C# WinForms应用程序因SystemEvents.OnUserPreferenceChanged事件而挂起

C# WinForms应用程序因SystemEvents.OnUserPreferenceChanged事件而挂起,c#,.net,winforms,C#,.net,Winforms,我一直在处理一个奇怪的应用程序挂在我的客户安装。 在尝试了几件事情之后,我得出结论,没有垃圾桶是行不通的。 所以我在挂断的时候从我的一个客户那里得到了一个垃圾。 我必须注意,它只发生在我的安装中,而不是在我的开发计算机中 在我的转储文件中,我看到SystemEvents.OnUserPreferenceChanged事件导致UI线程阻塞等待线程,该线程不再发送消息 在这里和谷歌搜索之后,我发现有些人也有这个问题。 我在我的代码中走来走去,想看看我们是否有机会在非UI线程上创建了一个控件或表单,但

我一直在处理一个奇怪的应用程序挂在我的客户安装。 在尝试了几件事情之后,我得出结论,没有垃圾桶是行不通的。 所以我在挂断的时候从我的一个客户那里得到了一个垃圾。 我必须注意,它只发生在我的安装中,而不是在我的开发计算机中

在我的转储文件中,我看到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] 
我想,也许通过使用dump,我可以找出在另一个线程上创建的控件是什么,这会给我一个线索

我试过了!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,但我没有看到它是在另一个线程上创建的

有什么建议吗?有人能提出另一个想法或尝试什么吗


如果您还不知道如何查看此信息,请向表示感谢。查看此页面并尝试spy++解决方案。也许这对你有帮助,我们也有同样的问题,不能用挂起的应用程序来模拟问题,但仍在对此进行大量研究

好吧,在非UI线程上有一个典型的控件“创建”问题

  • 可能是您正在创建控件
  • 对某些属性的访问会在您不知道的情况下在下面创建控件(很少)
  • 在创建句柄属性之前访问它(可能是间接的)
  • 被invokerrequired(参考文献1)、(参考文献2)愚弄
关于这个主题的伟大阅读:(参考文献1)(参考文献2)和(参考文献3)

我不知道如何在垃圾堆中找到控件(我自己尝试过) 但您可以尝试在代码中设置断点,如(参考文献4)和(参考文献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<object>().ToArray())
            foreach (var invokeInfo in (invokeInfos as IEnumerable).OfType<object>().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}");
            }
        }
    
    private static void CheckSystemEventsHandlersForFreeze()
    {
    var handlers=typeof(SystemEvents).GetField(“_handlers”,BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
    var handlersValues=handlers.GetType().GetProperty(“值”).GetValue(handlers);
    foreach(var invokeInfo in(handlersValues as IEnumerable).OfType().ToArray())
    foreach(var invokeInfo in(invokeInfo作为IEnumerable.OfType().ToArray())
    {
    var syncContext=invokeInfo.GetType().GetField(“_syncContext”,BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo);
    如果(syncContext==null)抛出新异常(“syncContext丢失”);
    如果(!(syncContext是WindowsFormsSynchronizationContext))继续;
    var threadRef=(WeakReference)syncContext.GetType().GetField(“destinationThreadRef”,BindingFlags.NonPublic | BindingFlags.Instance).GetValue(syncContext);
    如果(!threadRef.IsAlive)继续;
    var thread=(thread)threadRef.Target;
    如果(thread.ManagedThreadId==1)继续;//如果要忽略更多有效的UI线程,请在此处更改
    var dlg=(委托)invokeInfo.GetType().GetField(“_Delegate”,BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo);
    MessageBox.Show($“SystemEvents处理程序”{dlg.Method.DeclaringType}.{dlg.Method.Name}”可能由于线程错误而冻结应用程序:
    +$“{thread.ManagedThreadId},{thread.IsThreadPoolThread},{thread.IsAlive},{thread.Name}”);
    }
    }
    
    遵循Vlad代码,我找到了一种方法,可以取消订阅主UI线程之外订阅的所有对象上的系统事件

    这段代码适合我,它解决了多年来处理systemevents的痛苦:

    public static void UnsubscribeSystemEvents()
    {
        try
        {
            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<object>().ToArray())
                foreach (var invokeInfo in (invokeInfos as IEnumerable).OfType<object>().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 = (System.Threading.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);
                    var handler = (UserPreferenceChangedEventHandler)Delegate.CreateDelegate(typeof(UserPreferenceChangedEventHandler), dlg.Target, dlg.Method.Name);
                    SystemEvents.UserPreferenceChanged -= handler;
                }
        }
        catch ()
        {                
            //trace here your errors
        }
    }
    
    public static void UnsubscribeSystemEvents()
    {
    尝试
    {
    var handlers=typeof(SystemEvents).GetField(“_handlers”,BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
    var handlersValues=handlers.GetType().GetProperty(“值”).GetValue(handlers);
    foreach(var invokeInfo in(handlersValues as IEnumerable).OfType().ToArray())
    foreach(var invokeInfo in(invokeInfo作为IEnumerable.OfType().ToArray())
    {
    var syncContext=invokeInfo.GetType().GetField(“_syncContext”,BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo);
    if(syncContext==null)
    抛出新异常(“syncContext缺失”);
    如果(!(syncContext是WindowsFormsSynchronizationContext))
    继续;
    var threadRef=(WeakReference)syncContext.GetType().GetField(“destinationThreadRef”,BindingFlags.NonPublic | BindingFlags.Instance).GetValue(syncContext);
    如果(!threadRef.IsAlive)
    继续;
    var thread=(System.Threading.thread)threadRef.Target;
    if(thread.ManagedThreadId==1)
    继续;//如果要忽略更多有效的UI线程,请在此处进行更改
    var dlg=(委托)invokeInfo.GetType().GetField(“_Delegate”,BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo);
    var handler=(UserPreferenceChangedEventHandler)Delegate.CreateDelegate(typeof(UserPreferenceChangedEventHandler),dlg.Target,dlg.methodo