因此,我尝试创建一个类来 Package 实现单示例应用程序(SIA)所需的所有位,它应该是Application.Run<T>()
的直接替代品。
由于SIA需要覆盖WndProc
方法,以便运行的示例可以将其自身置于前台,并且该覆盖需要调用基类方法,因此我在运行时创建了一个 Package 器类,该 Package 器类继承了实现WndProc
覆盖的Form
类。
SIA部分工作正常, Package 类也是如此,但是WndProc
覆盖似乎没有被调用。
关于调用动态WndProc
覆盖的任何指示都将是可取的。
目前代码:
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace ScreenSaverControl {
public static class SingleInstanceApplication<T> where T : Form {
private const string APP_NAME = "SCREENSAVER_CONTROL";
private const string COMPANY_NAME = "DWAK";
private const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOW_RUNNING_INSTANCE = Win32Helper.RegisterWindowMessage($"WM_SHOW_RUNNING_INSTANCE_{APP_NAME}");
private static Mutex _singleInstanceMutex = new Mutex(true, $"{APP_NAME}::{COMPANY_NAME}");
private static Form? _instanceForm = null;
private static Action _showRunningInstanceAction = ActivateRunningInstance;
public static void Run() {
MessageBox.Show(WM_SHOW_RUNNING_INSTANCE.ToString());
if (_singleInstanceMutex.WaitOne(TimeSpan.Zero, true)) {
CreateSingleInstanceFormRunner();
Application.Run(_instanceForm);
_singleInstanceMutex.ReleaseMutex();
} else {
ActivateRunningInstance();
}
}
private static void ActivateRunningInstance() {
Win32Helper.PostMessage(
(IntPtr)HWND_BROADCAST,
WM_SHOW_RUNNING_INSTANCE,
IntPtr.Zero,
IntPtr.Zero);
}
private static void MessageHandler(ref Message m) {
Debug.WriteLine($"*SIA: {m.Msg}"); // This never fires.
if (null == _instanceForm) { return; }
if (SingleInstanceApplication<T>.WM_SHOW_RUNNING_INSTANCE == m.Msg) {
if (_instanceForm.WindowState == FormWindowState.Minimized) {
_instanceForm.WindowState = FormWindowState.Normal;
}
if (!_instanceForm.Visible) { _instanceForm.Show(); }
bool topMost = _instanceForm.TopMost;
_instanceForm.TopMost = true;
_instanceForm.TopMost = topMost;
}
//base.WndProc(ref m);
}
delegate void RefAction(ref Message message);
private static void CreateSingleInstanceFormRunner() {
//if (null == _instanceForm) { return; }
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);
}
Type[] methodParams = methodInfo!.GetParameters().Select(p => p.ParameterType).ToArray();
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);
// Define the new method with ref parameter
MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, MethodAttributes.Private, methodInfo.ReturnType, new Type[] { typeof(Message).MakeByRefType() });
RefAction newWndProc = MessageHandler;
ILGenerator il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Call, newWndProc.Method);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, methodInfo);
il.Emit(OpCodes.Ret);
Type dynamicType = typeBuilder.CreateType();
_instanceForm = (T)Activator.CreateInstance(dynamicType);
}
}
internal static class Win32Helper {
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
}
Program.cs:
namespace ScreenSaverControl {
internal static class Program {
[STAThread]
static void Main() {
ApplicationConfiguration.Initialize();
SingleInstanceApplication<MainDialog>.Run();
}
}
}
测试程序+结果:
1.通过VS调试运行来运行第一个示例。
1.最小化正在运行的示例。
1.通过输出文件夹中的.exe运行第二个示例。
- 观察输出窗口,注意不存在“*SIA”消息。
- 观察
MessageHandler
断点从未触发。 - 注意调试(第一个)示例没有将其自身置于前台。
出于调试的目的,我在MainDialog
上实现了一个WndProc
覆盖,它将m.Msg
值输出到输出窗口。
protected override void WndProc(ref Message m) {
Debug.WriteLine(m.Msg);
base.WndProc(ref m);
}
我已经确认运行的窗体是SingleInstanceFormRunner
的示例。
此代码是.NET 7.0上的WinForms。
如果你需要更多的信息,请告诉我。谢谢。
1条答案
按热度按时间jmp7cifd1#
1.您的
MethodAttributes
不正确,它们不代表受保护方法的重写,请更改为:1.通过IL进行的第一次调用有错误,您尝试使用一个参数调用静态方法,因此请更改为:
1.由于方法的访问级别,它仍然会失败,请将其设置为public:
1.我要说的是,实际上没有必要在这里使用
Reflection.Emit
-切换到使用source generators来生成 Package 器类(将需要更改API),这将完成您想要的工作。