闽公网安备 35020302035485号
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不是最重要的,最重要的是调用栈给弄出来了。