Get key from any process

删除回忆录丶 提交于 2019-11-30 16:26:52

If you don't particularly care which process the keys are being pressed in the easiest method would be to call GetAsyncKeyState. It's rather limited though as it does not hook the keyboard and requires you to call it continuously. The best approach in my opinion is to hook the keyboard.

Using SetWindowsHookEx you can actually explicitly specify the identifier of the thread with which the hook procedure is to be associated so you can hook keys for a specific process (see dwThreadId).

Here's a class that you can use (originally found on a Micrsoft blog but I cannot seem to find the authors name at the moment!)

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

public sealed class KeyboardHook
{
    public static event EventHandler<KeyPressedEventArgs> KeyPressed;
    private const int WH_KEYBOARD = 13;
    private const int WM_KEYDOWN = 0x0100;
    private static KeyboardProcess keyboardProc = HookCallback;
    private static IntPtr hookID = IntPtr.Zero;

    public static void CreateHook()
    {
        hookID = SetHook(keyboardProc);
    }

    public static void DisposeHook()
    {
        UnhookWindowsHookEx(hookID);
    }

    private static IntPtr SetHook(KeyboardProcess keyboardProc)
    {
        using (Process currentProcess = Process.GetCurrentProcess())
        using (ProcessModule currentProcessModule = currentProcess.MainModule)
        {
            return SetWindowsHookEx(WH_KEYBOARD, keyboardProc, GetModuleHandle(currentProcessModule.ModuleName), 0);
        }
    }

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

            if (KeyPressed != null)
                KeyPressed(null, new KeyPressedEventArgs((Keys)vkCode));
        }
        return CallNextHookEx(hookID, nCode, wParam, lParam);
    }

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

    [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);

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

}

public class KeyPressedEventArgs : EventArgs
{
    public Keys KeyCode { get; set; }
    public KeyPressedEventArgs(Keys Key)
    {
        KeyCode = Key;
    }
}

Implementation via Console Application:

class Program
{
    static void Main(string[] args)
    {
        KeyboardHook.CreateHook();
        KeyboardHook.KeyPressed += KeyboardHook_KeyPressed;
        Application.Run();
        KeyboardHook.DisposeHook();
    }

    static void KeyboardHook_KeyPressed(object sender, KeyPressedEventArgs e)
    {
        Console.WriteLine(e.KeyCode.ToString());
    }
}

What you're looking for is called a global keyboard hook. You can find more information and examples on MSDN.

Oh, so you're looking for "Autofire" in old-school gaming terms?

Instead of writing your own keyboard hook app (unless you're doing it for fun/the thrill of it/the exercise) you might want to look at AutoIt or AutoHotkey, which are both pretty good for keyboard/mouse automation.

See this thread for instance... http://www.autohotkey.com/board/topic/40598-autofire-keyboard/

I have found a way to hook only for a process. You may need it.

int ProcessId = GetProcessesByName("Your_app_here").FirstOrDefault().Id;
private IntPtr SetHook(KeyboardHookHandler proc)
    {
        return SetWindowsHookEx(13, proc, GetModuleHandle(Process.GetProcessById(ProcessId).MainModule.ModuleName), GetWindowThreadProcessId(GetModuleHandle(Process.GetProcessById(ProcessId).MainModule.ModuleName), out int MainThreadId));
    }

Remember to import these methods.

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

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

[DllImport("user32.dll", SetLastError = true)]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!