PostMessage()的LPARAM是如何构造的?

How is the LPARAM of PostMessage() constructed?

我在使用 PostMessage() 的参数 LPARAM 时遇到问题。

这是一个广泛使用的使用 PostMessage() 键入字母 z 的示例:

PostMessage(handle, WM_KEYDOWN, 0x5A, 0x002C0001) // key down for z
PostMessage(handle, WM_KEYUP, 0x5A, 0xC02C0001) // key up for z

到达 LPARAM“0x002C0001”(按下键)和“0xC02C0001”(按下键)的公式是什么?我想为所有键复制它。 是否可以创建两个函数,比如说,CreateLPARAM_KeyDown() 和 CreateLPARAM_KeyUp(),您只需在其中传递扫描码——或者更好的是,与设备无关的虚拟键码——他们 return LPARAM?

使用 keybd_event() 就容易多了,你只需将 dwFlags 参数 0 保留为按下键,并使用 KEYEVENTF_KEYUP 键,但 window 必须有焦点和我要发送到的 window 在后台,所以 keybd_event() 和 SendInput() 对我来说没有用。

LPARAMWPARAM 的含义因正在处理的特定消息而异。这就是为什么 PostMessage 的文档不能对这些参数过于具体,只说明:

Additional message-specific information.

两者都有。要准确了解它们对每条消息的含义,您需要查看该消息的文档。

对于您询问的消息 WM_KEYUPWM_KEYDOWNLPARAM 的值表示:

重复次数、扫描码、扩展键标志、上下文码、前键状态标志、过渡状态标志,如下table。 (来源 #1, #2

Bits    Meaning
0-15    The repeat count for the current message. The value is the number of times the keystroke is autorepeated as a result of the user holding down the key. 
16-23   The scan code. The value depends on the OEM.
24      Indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. The value is 1 if it is an extended key; otherwise, it is 0.
25-28   Reserved; do not use.
29      The context code. 
30      The previous key state. 
31      The transition state.

让我们看看您的 WM_KEYDOWN LPARAM 的位:

0x002C0001

0b0000000000101100000000000001

设置的位是 21、19、18 和 0。这告诉我们:

重复次数为 1

剩下的位是z的扫描码,明明是0b00101100或0x2C。

WM_KEYUP 消息具有 LPARAM 值 0xC02C0001,仅在最重要的 nybble 不同,给我们:

0b1100000000101100000000000001

所以,这里唯一的区别是前状态和过渡状态位都是 1,这对于 WM_KEYUP 消息是有保证的。

关于你的另一个问题:

Is it possible to create two functions, say, CreateLPARAM_KeyDown() and CreateLPARAM_KeyUp() where you just pass the scan code?

当然可以。查看 MapVirtualKey 以确定如何从键码中获取扫描码,并使用位运算从中构造一个 32 位 LPARAM 以及您从上面的 table 中知道的所有其他内容关于必须为这些消息设置的位。您将需要使用位移位和其他位操作来完成此操作,因为扫描码是作为 32 位 LPARAM.

的一部分存储的单个 8 位字节

我建议为此目的使用 SendInput API。

所以你只需要填写相应的 KEYBDINPUT 有据可查的结构。

Is it possible to create two functions, say, CreateLPARAM_KeyDown() and CreateLPARAM_KeyUp() where you just pass the scan code — or better yet, the device-independent virtual key code — and they return the LPARAM?

尝试这样的事情:

std::pair<USHORT, bool> VirtualKeyToScanCode(UINT VirtualKey)
{
    USHORT ScanCode = (USHORT) MapVirtualKey(VirtualKey, MAPVK_VK_TO_VSC);
    bool IsExtended = false;

    // because MapVirtualKey strips the extended bit for some keys
    switch (VirtualKey)
    {
        case VK_RMENU: case VK_RCONTROL:
        case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys
        case VK_PRIOR: case VK_NEXT: // page up and page down
        case VK_END: case VK_HOME:
        case VK_INSERT: case VK_DELETE:
        case VK_DIVIDE: // numpad slash
        case VK_NUMLOCK:
        {
            IsExtended = true;
            break;
        }
    }

    return std::make_pair(ScanCode, IsExtended);
}

LPARAM CreateLPARAM_KeyUpDown(UINT VirtualKey, USHORT RepeatCount, bool TransitionState, bool PreviousKeyState, bool ContextCode)
{
    std::pair<USHORT, bool> ScanCode = VirtualKeyToScanCode(VirtualKey);
    return (
        (LPARAM(TransitionState) << 31) |
        (LPARAM(PreviousKeyState) << 30) |
        (LPARAM(ContextCode) << 29) |
        (LPARAM(ScanCode.second) << 24) |
        (LPARAM(ScanCode.first) << 16) |
        LPARAM(RepeatCount)
    );
}

LPARAM CreateLPARAM_KeyDown(UINT VirtualKey, USHORT RepeatCount = 1)
{
    return CreateLPARAM_KeyUpDown(VirtualKey, RepeatCount, false, RepeatCount > 1, false);
}

LPARAM CreateLPARAM_KeyUp(UINT VirtualKey)
{
    return CreateLPARAM_KeyUpDown(VirtualKey, 1, true, true, false);
}