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; }
}
}