79663006

Date: 2025-06-12 06:20:40
Score: 1
Natty:
Report link

Comment to Answer by Thanatos ( https://stackoverflow.com/users/15414326/thanatos )

Just wanted to say thx because it helped me right now!

this is a version for NetFramework and Net8, they changed some of the internal names and the stacktrace functions have been deprecated, so i removed those. Still serves the purpose of finding the problematic entry.

/// <summary>
/// Based on: https://stackoverflow.com/a/70413275
/// </summary>
internal static class PreferenceChangedObserver
{
#if NETFRAMEWORK
    private const string FieldNameHandlers = "_handlers";
    private const string FieldNameDestinationThreadName = "destinationThreadRef";
#else
    private const string FieldNameHandlers = "s_handlers";
    private const string FieldNameDestinationThreadName = "_destinationThread";
#endif

    private const System.Reflection.BindingFlags FlagsInstance = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
    private const System.Reflection.BindingFlags FlagsStatic = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static;
    private const string LogFilePath = $"D:\\FreezeLog.txt";

    /// <summary>
    /// Creates a new thread and runs the check forever.
    /// </summary>
    public static void StartThread()
    {
        if (System.IO.File.Exists(LogFilePath))
        {
            System.IO.File.Delete(LogFilePath);
        }

        var tr = new System.Threading.Thread(CheckSystemEventsHandlersForFreezeLoop)
        {
            IsBackground = true,
            Name = nameof(PreferenceChangedObserver) + ".CheckThread",
        };
        tr.Start();
    }

    private static IEnumerable<EventHandlerInfo> GetPossiblyBlockingEventHandlers()
    {
        var type = typeof(Microsoft.Win32.SystemEvents);

        var handlers = type.GetField(FieldNameHandlers, FlagsStatic).GetValue(null);
        if (handlers?.GetType().GetProperty("Values").GetValue(handlers) is not System.Collections.IEnumerable handlersValues)
        {
            yield break;
        }

        foreach (var systemInvokeInfo in handlersValues.Cast<System.Collections.IEnumerable>().SelectMany(x => x.OfType<object>()).ToList())
        {
            var syncContext = systemInvokeInfo.GetType().GetField("_syncContext", FlagsInstance).GetValue(systemInvokeInfo);

            ///// Make sure its the problematic type
            if (syncContext is not WindowsFormsSynchronizationContext wfsc)
            {
                continue;
            }

            // Get the thread
            var threadRef = (WeakReference)syncContext.GetType().GetField(FieldNameDestinationThreadName, FlagsInstance).GetValue(syncContext);
            if (!threadRef.IsAlive)
            {
                continue;
            }

            var thread = (System.Threading.Thread)threadRef.Target;
            if (thread.ManagedThreadId == 1) //// UI thread
            {
                continue;
            }

            if (thread.ManagedThreadId == Environment.CurrentManagedThreadId)
            {
                continue;
            }

            // Get the event delegate
            var eventHandlerDelegate = (Delegate)systemInvokeInfo.GetType().GetField("_delegate", FlagsInstance).GetValue(systemInvokeInfo);

            yield return new EventHandlerInfo
            {
                Thread = thread,
                EventHandlerDelegate = eventHandlerDelegate,
            };
        }
    }

    private static void CheckSystemEventsHandlersForFreezeLoop()
    {
        while (true)
        {
            System.Threading.Thread.Sleep(1000);
            try
            {
                foreach (var info in GetPossiblyBlockingEventHandlers())
                {
                    var msg = $"SystemEvents handler '{info.EventHandlerDelegate.Method.DeclaringType}.{info.EventHandlerDelegate.Method.Name}' could freeze app due to wrong thread. ThreadId: {info.Thread.ManagedThreadId}, IsThreadPoolThread:{info.Thread.IsThreadPoolThread}, IsAlive:{info.Thread.IsAlive}, ThreadName:{info.Thread.Name}{Environment.NewLine}";
                    System.IO.File.AppendAllText(LogFilePath, DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") + $": {msg}{Environment.NewLine}");
                }
            }
            catch
            {
                // That's dirty.
            }
        }
    }

    private sealed class EventHandlerInfo
    {
        public Delegate EventHandlerDelegate { get; set; }

        public System.Threading.Thread Thread { get; set; }
    }
}
Reasons:
  • Blacklisted phrase (1): thx
  • Blacklisted phrase (1): stackoverflow
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (0.5):
Posted by: Otterprinz