4

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've taken a dump from one of my clients at the hang time. I must note that it only happens in my installations and not in my development computers.

In my dump I see that the SystemEvents.OnUserPreferenceChanged event causes my UI thread to block waiting for a thread, that is no longer pumping messages.

After searching here and in google, I found that some people we're having that issue as well. I went up and down my code looking to see if by any chance we created a control or a form on a non UI thread, but with no luck.

This is my !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] 

I tought that maybe using the dump i can figure out what is the control that was created on the other thread and it would give me a clue.

I tried !dso to get all of the stack objects:

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[])

The only thing i see is InsFocusBI.Presentation.Controls.CustomListView but i don't see that it is created on another thread.

Any advice? can someone give another idea or something to try?

Thanks

E. Moffat
  • 3,165
  • 1
  • 21
  • 34
Daniel Taiber
  • 85
  • 1
  • 6

5 Answers5

7

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}");
        }
    }
Vlad Rudenko
  • 2,363
  • 1
  • 24
  • 24
  • You might want to note that by the time you *really* need it you can't click the button anymore :) – Hans Passant Oct 10 '18 at 13:38
  • 1
    You can invoke this function (by click) any time before freeze. Or maybe invoke it periodically by timer, for example once per minute. In case of freeze it should work from another thread. – Vlad Rudenko Oct 10 '18 at 13:57
  • The "bad" handlers are usually added to SystemEvents long before freeze :) – Vlad Rudenko Oct 10 '18 at 14:03
  • And removed before the freeze. Worst case is getting SystemEvents initialized wrong, guessing at the wrong UI thread. A splash screen that does more than show a simple bitmap can do it. Since the initialization thread is no longer around, it now fires events on a threadpool thread. I don't think your diagnostic can show that. – Hans Passant Oct 10 '18 at 14:20
  • 1
    If initialization thread is no longer around the SystemEvents will not fire such event at all because internally it keeps WeakReference to thread and also checks if thread.IsAlive. Just tested and can confirm such behavior on my app. Also tested that even if app got hanged the CheckSystemEventsHandlersForFreeze still works good from another thread. – Vlad Rudenko Oct 11 '18 at 08:19
  • 1
    You are great man, You really helped me!!, I run it and found "System.Windows.Forms.RichTextBox" was the issue. I was creating it to generate RTF at corner of application. Once it executed in none ui thread...And system get hang on next system event – Piyush Aghera Apr 29 '20 at 15:40
4

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

Arthur
  • 1,484
  • 1
  • 16
  • 18
1

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
    }
}
Luis Otero
  • 37
  • 3
0

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!

TimVK
  • 1,146
  • 6
  • 16
0

I had to dead with this bug in a big application. The faulty code might be very hard to find due to the fact that the cause and effect of the error might be minutes or even hours apart.

I changed Vlad Rudenko's answer a bit so it provides a stack trace. That helped immensely in my case. Hope it helps somebody

UI Freeze caused by WindowsFormsSynchronizationContext and System.Events.UserPreferenceChanged

Thanatos
  • 41
  • 5
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/30627483) – Shunya Dec 22 '21 at 15:17
  • What are you talking about? The problem and the solution are explained in great detail in the post i linked. – Thanatos Dec 23 '21 at 07:49
  • You did not provide an answer, you just gave a link to an answer in this same question, if it worked for you, you could have upvoted that answer or commented on it with your modifications. – Shunya Dec 23 '21 at 08:48