动态IL生成的WndProc重载未触发的WinForms单实例应用问题
解决动态生成Form子类的WndProc未被调用问题
核心问题分析
- 方法属性错误:基类
Form.WndProc是protected virtual,但你定义的动态方法用了private修饰符,导致无法正确覆盖基类方法,WinForms消息循环只会调用类型的虚方法实现。 - 未明确标记方法覆盖:动态生成方法后,未调用
DefineMethodOverride告诉CLR该方法是对基类WndProc的覆盖,CLR无法识别这是一个重写方法。 - IL指令错误:调用静态方法
MessageHandler时错误传递了实例参数(Ldarg_0),且多传了一个不存在的Ldarg_2参数(WndProc仅包含实例和ref Message两个参数)。
修正后的关键代码
修正CreateSingleInstanceFormRunner方法
private static void CreateSingleInstanceFormRunner() { string methodName = "WndProc"; Type type = typeof(T); MethodInfo? methodInfo = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); if (null == methodInfo) { methodInfo = typeof(Form).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); } if (methodInfo == null) throw new InvalidOperationException("无法获取WndProc方法"); AssemblyName assemblyName = new AssemblyName("SingleInstanceAssembly"); AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("SingleInstanceModule"); TypeBuilder typeBuilder = moduleBuilder.DefineType("SingleInstanceFormRunner", TypeAttributes.Public, type); // 定义匹配基类的protected virtual方法 MethodBuilder methodBuilder = typeBuilder.DefineMethod( methodName, MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig, methodInfo.ReturnType, new Type[] { typeof(Message).MakeByRefType() }); RefAction newWndProc = MessageHandler; ILGenerator il = methodBuilder.GetILGenerator(); // 调用静态MessageHandler,仅传递ref Message参数 il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Call, newWndProc.Method); // 调用基类WndProc方法 il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Call, methodInfo); il.Emit(OpCodes.Ret); // 关键:标记该方法覆盖基类的WndProc typeBuilder.DefineMethodOverride(methodBuilder, methodInfo); Type dynamicType = typeBuilder.CreateType(); _instanceForm = (T)Activator.CreateInstance(dynamicType); }
额外注意事项
- 确保
MessageHandler中的逻辑正确:因为已经在IL中调用了基类WndProc,不需要在MessageHandler中再调用base.WndProc。 WM_SHOW_RUNNING_INSTANCE的注册是全局的,RegisterWindowMessage会为相同的字符串返回唯一值,多实例调用不会有冲突。- Mutex的使用正确:第一个实例获取所有权,退出时释放Mutex,确保单实例逻辑正常。
内容的提问来源于stack exchange,提问作者Sam Axe
相关产品推荐
相关产品推荐

