0:000:x86> !clrstack OS Thread Id: 0x4eb688 (0) Child SP IP Call Site 002fed38 0000002b [HelperMethodFrame_1OBJ: 002fed38] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean) 002fee1c 5cddad21 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean) 002fee34 5cddace8 System.Threading.WaitHandle.WaitOne(Int32, Boolean) 002fee48 538d876c System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle) 002fee88 53c5214a System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean) 002fee8c 538dab4b [InlinedCallFrame: 002fee8c] 002fef14 538dab4b System.Windows.Forms.Control.Invoke(System.Delegate, System.Object[]) 002fef48 53b03bc6 System.Windows.Forms.WindowsFormsSynchronizationContext.Send(System.Threading.SendOrPostCallback, System.Object) 002fef60 5c774708 Microsoft.Win32.SystemEvents+SystemEventInvokeInfo.Invoke(Boolean, System.Object[]) 002fef94 5c6616ec Microsoft.Win32.SystemEvents.RaiseEvent(Boolean, System.Object, System.Object[]) 002fefe8 5c660cd4 Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(Int32, IntPtr, IntPtr) 002ff008 5c882c98 Microsoft.Win32.SystemEvents.WindowProc(IntPtr, Int32, IntPtr, IntPtr) ...这个程序之所以被卡死,底层原因到底大概是这样的。
3.那些非主线程控件由于没有 MessageLoop 机制,导致主线程给这些UI发消息时得不到响应,最终引发悲剧。
(一个超经典 WinForm 卡死问题的最后一次反思)[https://www.cnblogs.com/huangxincheng/p/17654394.html]
public partial class Form1 : Form { public Form1() { InitializeComponent(); var harmony = new Harmony("一线码农聊技术"); // 堆代码 duidaima.com Type applicationType = typeof(Application); Type marshalingControlType = applicationType.GetNestedType("MarshalingControl", BindingFlags.NonPublic); ConstructorInfo constructor = marshalingControlType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); var prefix = typeof(HookMarshalingControl).GetMethod("OnActionExecuting"); harmony.Patch(constructor, new HarmonyMethod(prefix)); } private void Form1_Load(object sender, EventArgs e) { } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { Button btn = new Button(); var query = btn.Handle; } private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); } } /// <summary> /// Hook MarshalingControl 的描述类 /// </summary> public class HookMarshalingControl { /// <summary> /// 原生方法之前执行的 action /// </summary> public static void OnActionExecuting() { Console.WriteLine("----------------------------"); Console.WriteLine($"控件创建线程:{Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine(Environment.StackTrace); Console.WriteLine("----------------------------"); } }卦中的代码逻辑我就不详述了,核心就是将 OnActionExecuting 方法注入到 MarshalingControl..ctor 构造函数里,把程序运行起来后观察 output 窗口,截图如下:
public partial class Form1 : Form { public Form1() { MessageBox.Show("开启你的注入吧..."); InitializeComponent(); } }弹框之后,使用 dnspy 的进程附加。
11:20:01.548 控件创建线程:<<<当线程位于不安全状态时无法对表达式进行求值。按步调试或运行直到触发断点。>>> 11:20:01.550 System.Windows.Forms.Application.MarshalingControl.MarshalingControl 11:20:01.551 System.Windows.Forms.Application.ThreadContext.MarshalingControl.get 11:20:01.552 System.Windows.Forms.WindowsFormsSynchronizationContext.WindowsFormsSynchronizationContext 11:20:01.553 System.Windows.Forms.WindowsFormsSynchronizationContext.InstallIfNeeded 11:20:01.553 System.Windows.Forms.Control.Control 11:20:01.554 System.Windows.Forms.ButtonBase.ButtonBase 11:20:01.554 System.Windows.Forms.Button.Button 11:20:01.554 WindowsFormsApp1.Form1.backgroundWorker1_DoWork 11:20:01.555 System.ComponentModel.BackgroundWorker.OnDoWork 11:20:01.555 System.ComponentModel.BackgroundWorker.WorkerThreadStart 11:20:01.556 System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage 11:20:01.556 System.Threading.ExecutionContext.RunInternal 11:20:01.557 System.Threading.ExecutionContext.Run 11:20:01.557 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem 11:20:01.557 System.Threading.ThreadPoolWorkQueue.Dispatch 11:20:01.558 [本机到托管的转换] 11:20:01.558这里稍微提醒一下,tid 在这里没有显示出来,大家可以换成问号面板上的关键词 $TID 即可,不过TID不是最重要的,最重要的是调用栈给弄出来了。