首页 > 解决方案 > 方法.Invoke(o, null); 只能从主体调用一次,而不能从键盘处理程序中调用

问题描述

我正在尝试创建一个低级键盘处理程序,以在检测到特定键盘组合时使用反射启动外部应用程序。

我可以直接启动应用程序一次而没有错误,但是从按键处理程序内部,应用程序在正确显示MessageBox对话框后崩溃。

我正在将外部 dll 读入一个字节数组,对于这个测试,您可以使用任何 .Net 应用程序,一个简单的 Hello World win 表单应用程序就可以工作。

string filePath = @".\x.exe";
FileStream fs = new FileStream(filePath, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
fs.Close();
br.Close();

然后加载外部Assembly并通过反射执行它:

public Assembly a;
public MethodInfo method;
public object o;

a = Assembly.Load(bin);
method = a.EntryPoint;
o = a.CreateInstance(method.Name);
method.Invoke(o, null); 

这工作一次,但如果我重复上面的代码再次执行它会失败。

下面的代码演示了第二次调用的失败,如果你注释掉你可以看到键盘检测的失败Left++DownA

即使您在程序根目录中注释掉这两个调用,应用程序在第一次检测到键时也会意外关闭。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Windows.Input;

public class Program
{
    public static bool lctrlKeyPressed;
    public static bool f1KeyPressed;
    public static bool AKeyPressed;
    public static bool LeftKeyPressed;
    public static bool DownKeyPressed;

    public static Assembly a;
    // search for the Entry Point
    public static MethodInfo method;
    // create an istance of the Startup form Main method
    public static object o;

    [STAThread]
    public static void Main()
    {
        //Application.EnableVisualStyles();
        //Application.SetCompatibleTextRenderingDefault(false);

        string filePath = @".\x.exe";
        FileStream fs = new FileStream(filePath, FileMode.Open);
        BinaryReader br = new BinaryReader(fs);
        byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
        fs.Close();
        br.Close();

        //public Assembly a;
        //public MethodInfo method;
        //public object o;

        a = Assembly.Load(bin);
        method = a.EntryPoint;
        o = a.CreateInstance(method.Name);
        method.Invoke(o, null);

        a = Assembly.Load(bin);
        method = a.EntryPoint;
        o = a.CreateInstance(method.Name);
        method.Invoke(o, null);

        LowLevelKeyboardHook kbh = new LowLevelKeyboardHook();
        kbh.OnKeyPressed += kbh_OnKeyPressed;
        kbh.OnKeyUnpressed += kbh_OnKeyUnPressed;

        kbh.HookKeyboard();

        Application.Run();

        kbh.UnHookKeyboard();
    }

    public static void kbh_OnKeyPressed(object sender, Keys e)
    {
        if (e == Keys.A)
        {
            AKeyPressed = true;
        }
        else if (e == Keys.Left)
        {
            LeftKeyPressed = true;
        }
        else if (e == Keys.Down)
        {
            DownKeyPressed = true;
        }
        CheckKeyCombo();
    }

    public static void kbh_OnKeyUnPressed(object sender, Keys e)
    {
        if (e == Keys.A)
        {
            AKeyPressed = false;
        }
        else if (e == Keys.Left)
        {
            LeftKeyPressed = false;
        }
        else if (e == Keys.Down)
        {
            DownKeyPressed = false;
        }
    }

    public static void CheckKeyCombo()
    {
        if (AKeyPressed && LeftKeyPressed && DownKeyPressed)
        {
            MessageBox.Show("Left Key + Down Key + A Key + Pressed");
            
            //a = Assembly.Load(bin);
            method = a.EntryPoint;
            o = a.CreateInstance(method.Name);
            method.Invoke(o, null);
        }
    }
}

public class LowLevelKeyboardHook
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_SYSKEYDOWN = 0x0104;
    private const int WM_KEYUP = 0x101;
    private const int WM_SYSKEYUP = 0x105;

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    public event EventHandler<Keys> OnKeyPressed;
    public event EventHandler<Keys> OnKeyUnpressed;

    private LowLevelKeyboardProc _proc;
    private IntPtr _hookID = IntPtr.Zero;

    public LowLevelKeyboardHook()
    {
        _proc = HookCallback;
    }

    public void HookKeyboard()
    {
        _hookID = SetHook(_proc);
    }

    public void UnHookKeyboard()
    {
        UnhookWindowsHookEx(_hookID);
    }

    private IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
        }
    }

    private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);

            OnKeyPressed.Invoke(this, ((Keys)vkCode));
        }
        else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
        {
            int vkCode = Marshal.ReadInt32(lParam);

            OnKeyUnpressed.Invoke(this, ((Keys)vkCode));
        }

        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}

标签: c#

解决方案


非常感谢大家的参与

基本上我打算让 C# 代码做的是这个

  1. X.exe 是由 JitBit Macro Recorder 创建的宏
  2. C# 读取键盘按钮按下
  3. 当按下一系列键盘按钮时,它会运行 X.exe 宏

这是一种可怕的做事方式

但这是我现在要做的最好的事情

因此,在 C# 代码中,我需要能够尽快运行 X.exe

这就是为什么我将它加载到内存中

X.exe应该也可以被调用很多很多次

这是我正在玩的游戏

我玩游戏时 C# 程序在后台运行

如果我按下某个键盘按钮序列,C# 将运行 X.exe

这本质上只是 JitBit Macro Recorder 的一个宏

为什么我使用 Jitbit 宏录制器?

因为它是一个速度极快的宏引擎,而且效果很好

以我刚才所说的

非常感谢对 C# 程序的任何进一步贡献

真心感谢大家!!!


推荐阅读