WinForms application hang due to SystemEvents.OnUserPreferenceChanged event

后端 未结 4 991
情话喂你
情话喂你 2020-11-29 10:48

I\'ve been dealing with a strange application hang in my clients installations. After trying several things i reached the conclusion that without a dump it won\'t work. So I

相关标签:
4条回答
  • 2020-11-29 11:21

    If you don't have an idea yet on how to check this out. Look at this page Kim Greenlee blog and try out the spy++ solution. Maybe it helps you, we have about the same issue and cannot simulate the problem with the application that is hanging, but still researching a lot about it!

    0 讨论(0)
  • 2020-11-29 11:32

    Well, you have thee classic problem of control 'creation' on non UI thread.

    • It may be you are creating the control.
    • Access to some property creates a control underneath without you knowing (rare).
    • Access to the Handle property before it is been created (may be indirectly).
    • Fooled by InvokeRequired (ref 1), (ref 2).

    Great read on the subject: (ref 1) (ref 2) and (ref 3).

    I don't know of a way to find the control in a dump (tried myself) but you can try to set a breakpoint in code as described in (ref 4) and (ref 5).

    Cheers.

    Reference:

    1. Mysterious Hang or The Great Deception of InvokeRequired

    2. Control.Trifecta: InvokeRequired, IsHandleCreated, and IsDisposed

    3. .NET 2.0 WinForms multithreading and a few long days

    4. Debugging UI

    5. The case of the leaking thread handles

    0 讨论(0)
  • 2020-11-29 11:32

    Following Vlad code, I have found a way to unsubscribe the system events on all objects subscribed out of the main UI thread.

    This code works for me and it is solving years of pain dealing with 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
        }
    }
    
    0 讨论(0)
  • 2020-11-29 11:36

    The freezing issue due to SystemEvents.OnUserPreferenceChanged is quite common bug and here is a Microsoft explanation and recommendation how to fix it.

    Here is the function you can invoke any time (before or even after freeze) to find out which particular controls subscribed to SystemEvents was created on wrong threads and thus could freeze your app:

        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}");
            }
        }
    
    0 讨论(0)
提交回复
热议问题