【问题标题】:Infinity pressed keys 'PostMessage' to hidden window无限按下“PostMessage”键到隐藏窗口
【发布时间】:2021-03-22 07:32:45
【问题描述】:

您好,我正在尝试将密钥发送到隐藏游戏,但我遇到了 1 个问题,当我将密钥发送到游戏时,它只按下第一个并且它没有结束,例如它按下 W 无限时间并且无法发送下一个钥匙。我认为关键是它没有上升。

我的代码:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Naura_2._0
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);

        public Form1()
        {
            InitializeComponent();
        }

        private const UInt32 WM_KEYDOWN = 0x0100;
        private const UInt32 WM_KEYUP = 0x0101;
        private const int VK_KEY_W = 0x57;

        private void button1_Click(object sender, EventArgs e)
        {
            IntPtr hWnd = FindWindow(null, "Game Title");

            if (!hWnd.Equals(IntPtr.Zero))

            {
                PostMessage(hWnd, WM_KEYDOWN, VK_KEY_W, 0);
                PostMessage(hWnd, WM_KEYUP, VK_KEY_W, 5);
            }
        }
    }
}

【问题讨论】:

  • 您有什么特别的原因想用 c# 和 PInvoke 来做这件事吗?您可以使用AutoHotkey,它是开源的,专为这个用例而设计。
  • 是的,但在 c# 中,制作界面和登录表单等更舒服。@Kwiksilver
  • @Kwiksilver 以及一些游戏会阻止 ahk 脚本。
  • 许多“游戏”外围设备(鼠标和键盘)都内置了宏播放器。在这种情况下,“窗口”(游戏窗口)是否有任何特殊原因隐藏。为什么不是焦点?

标签: c#


【解决方案1】:

发送按键的选项

  1. 如果窗口必须聚焦才能发送按键,请使用SendInput 而不是PostMessage,因为它是为这个过程设计的。这种情况在游戏中很常见,如果游戏窗口没有获得焦点,它们会自动暂停,因此发送任何按键都可能无济于事。
  2. 如果您希望在发送按键时窗口焦点是可选的,那么PostMessage 似乎是唯一的方法。

使用PostMessage发送按键的解决方案

要“模拟”按键,发送虚拟键代码(键“A”)和扫描代码(键盘上的位置“A”)和一些附加标志 Keystroke Message Flags

PostMessage(hWndChild, WM_KEYDOWN, virtualKey, scanCode << 16 | 0x0000001);
PostMessage(hWndChild, WM_KEYUP, virtualKey, scanCode << 16 | 0xC0000001);

您可以使用 MapVirtualKey 在 vi​​rtualKeys 和 scanCodes 之间进行映射

完整程序示例

class Program
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern uint MapVirtualKey(uint uCode, uint uMapType);

    [DllImport("user32.dll")]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);

    [DllImport("user32.dll")]
    static extern bool SetForegroundWindow(IntPtr hWnd);

    const uint WM_KEYDOWN = 0x0100;
    const uint WM_KEYUP = 0x0101;

    const uint MAPVK_VK_TO_VSC = 0x00;

    // virtual key codes
    const uint VK_ESCAPE = 0x1B;
    const uint VK_LSHIFT = 0xA0;
    const uint VK_LCONTROL = 0xA2;
    const uint VK_OEM_2 = 0xBF; // For the US standard keyboard, the '/?' key

    const uint VK_W = 'W';
    const uint VK_A = 'A';
    const uint VK_S = 'S';
    const uint VK_D = 'D';

    // Scan Code for US keyboard layout
    const uint VSC_W = 0x11; // MapVirtualKey('W', MAPVK_VK_TO_VSC);

    const uint VSC_A = 0x1E; // On QUERY MapVirtualKey('A', MAPVK_VK_TO_VSC);
                             // On AZERY MapVirtualKey('Q', MAPVK_VK_TO_VSC);

    const uint VSC_S = 0x1F;
    const uint VSC_D = 0x20;

    const uint VSC_LSHIFT = 0x2A;

    // Write 'w' (yes lower case) in Notepad.exe (*Untitled - Notepad)
    static void Example1()
    {
        uint keyPress = VK_W;
        uint scanCode = MapVirtualKey(keyPress, MAPVK_VK_TO_VSC);
        IntPtr hWnd = FindWindow(null, "*Untitled - Notepad");
        IntPtr hWndChild = FindWindowEx(hWnd, IntPtr.Zero, "Edit", string.Empty);

        PostMessage(hWndChild, WM_KEYDOWN, keyPress, scanCode << 16 | 0x0000001);
        Thread.Sleep(42);
        PostMessage(hWndChild, WM_KEYUP, keyPress, scanCode << 16 | 0xC0000001);
    }

    // Write 'help' (yes lower case) in Notepad.exe (*Untitled - Notepad)
    static void Example2()
    {
        string text = "help";

        IntPtr hWnd = FindWindow(null, "*Untitled - Notepad");
        IntPtr hWndChild = FindWindowEx(hWnd, IntPtr.Zero, "Edit", string.Empty);

        foreach (var c in text)
        {
            uint keyPress = char.ToUpper(c);
            uint scanCode = MapVirtualKey(keyPress, MAPVK_VK_TO_VSC);

            PostMessage(hWndChild, WM_KEYDOWN, keyPress, scanCode << 16 | 0x0000001);
            Thread.Sleep(42);
            PostMessage(hWndChild, WM_KEYUP, keyPress, scanCode << 16 | 0xC0000001);
            Thread.Sleep(142);
        }
    }
}

一些额外的背景阅读

https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names

到达解决方案

Visual Studio 自带一个叫做 Spy++ 的程序,可以用来查看任何窗口的消息队列。

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\spyxx.exe
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\spyxx_amd64.exe

spyxx.exe 用于检查 32 位进程,spyxx_amd64.exe 用于检查 64 位进程。使用 Spy++ 和文档 Keystroke Message Flags,我能够查看 WM_KEYDOWNWM_KEYUP 并使用 PostMessage 模仿所需的行为。

潜在的空头下跌

  • 您可以发送一个 Shift 键,大多数游戏都应该收到该 Shift 键,但文本框中的 [Shift]+[A] 等操作会产生“a”。 Windows 使用其他一些机制来生成大写字母。
  • 某些程序可能会使用GetKeyboardState 它在这种情况下发布消息不会做任何事情,因为它会查询键盘并忽略消息。请注意,有一个 SetKeyboardState 可用于设置按下哪些键,从而与这些程序进行通信。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多