C++ PostMessage()的LPRAM是如何构造的?

C++ PostMessage()的LPRAM是如何构造的?,c++,winapi,postmessage,C++,Winapi,Postmessage,PostMessage()的参数LPARAM有问题 这是一个广泛使用的使用PostMessage()键入字母z的示例: 按下键时到达LPRAM“0x002C00001”和向上键时到达LPRAM“0xC02C0001”的公式是什么?我想为所有密钥复制它是否可以创建两个函数,例如CreateLPARAM_KeyDown()和CreateLPARAM_keydup(),在这两个函数中,您只需传递扫描代码,或者更好的是,传递与设备无关的虚拟密钥代码,然后返回LPARAM? 使用keybd_event()

PostMessage()的参数LPARAM有问题

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

按下键时到达LPRAM“0x002C00001”和向上键时到达LPRAM“0xC02C0001”的公式是什么?我想为所有密钥复制它是否可以创建两个函数,例如CreateLPARAM_KeyDown()和CreateLPARAM_keydup(),在这两个函数中,您只需传递扫描代码,或者更好的是,传递与设备无关的虚拟密钥代码,然后返回LPARAM?


使用keybd_event()更容易,只需将dwFlags参数0保留为key down,并使用KEYEVENTF_KEYUP保留为key up,但窗口必须具有焦点,并且我要发送到的窗口位于后台,因此keybd_event()和SendInput()是在我的情况下没有任何用处。

对于正在处理的特定消息,
LPARAM
WPARAM
的含义有所不同。这就是为什么针对这些参数的文档不能太具体,只说明:

其他特定于消息的信息

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

对于您询问的消息,
WM_KEYUP
WM_keyudown
,LPARAM的值表示:

重复计数、扫描代码、扩展密钥标志、上下文代码、上一个密钥状态标志和转换状态标志,如下表所示。(资料来源)

让我们看一下
WM_键下的位
LPARAM

0x002C0001

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

重复计数为1

剩余的位是
z
的扫描代码,显然是0b00101100或0x2C

WM_KEYUP
消息具有
LPARAM
值0xC02C0001,该值仅在最显著的nyble处不同,为我们提供:

0b1100000000101100000000000001
因此,这里唯一的区别是前一个状态和转换状态位都是1,这对于
WM_KEYUP
消息是有保证的

关于你的另一个问题:

是否可以创建两个函数,例如CreateLPARAM_KeyDown()和CreateLPARAM_keydup(),在其中传递扫描代码

当然。查看以确定如何从键代码中获取扫描代码,并使用位操作从中构造32位
LPARAM
,以及从上表中了解的有关必须为这些消息设置的位的所有信息。您需要使用位移位和其他位操作来完成此操作,因为扫描代码是作为32位
LPRAM的一部分存储的单个8位字节,我建议为此使用API

所以,您只需要填写相应的结构,这些结构都有很好的文档记录

有没有可能创建两个函数,比如CreateLPARAM_KeyDown()和CreateLPARAM_keydup(),您只需在其中传递扫描代码,或者更好的是,传递与设备无关的虚拟密钥代码,然后返回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);
}
std::对VirtualKeyToScanCode(UINT VirtualKey)
{
USHORT扫描代码=(USHORT)映射VirtualKey(VirtualKey,映射VK_VK_至_VSC);
bool IsExtended=false;
//因为MapVirtualKey会为某些键去掉扩展位
交换机(VirtualKey)
{
案例VK\u RMENU:案例VK\u控制:
case VK_LEFT:case VK_UP:case VK_RIGHT:case VK_DOWN://箭头键
case VK_PRIOR:case VK_NEXT://上下翻页
案例VK_结束:案例VK_主页:
案例VK_插入:案例VK_删除:
案例VK_DIVIDE://numpad slash
案例VK_NUMLOCK:
{
IsExtended=true;
打破
}
}
返回std::make_pair(扫描码,扩展);
}
LPARAM createlparamu keyuptown(UINT VirtualKey、USHORT RepeatCount、bool TransitionState、bool PreviousKeyState、bool ContextCode)
{
std::pair ScanCode=VirtualKey-toscancode(VirtualKey);
返回(

(LPARAM(TransitionState)我正在考虑使用MapVirtualKey()将虚拟键代码(与设备无关)映射到键盘的扫描代码(可能会有所不同),并使用获得的扫描代码创建LPRAM以进行向下键和向上键操作。看起来很简单,但我仍然不知道如何为给定的扫描代码创建向下键和向上键操作的LPRAM。@John这将需要位移位和其他位操作。如果您对如何操作有疑问,最好再问一个有关的问题我编辑了我的问题SendInput()无法使用,因为它需要关注。您不能通过发布此类消息来可靠地伪造输入。SendInput是您所需要的。无论这个问题在这里被问多少次,这始终是答案。David Heffernan这是一个常见的观点,但它是一个有缺陷的概括。我发布到的窗口接收消息没有问题,我测试过您甚至可以发送组合键,例如shift键向下、letter键向下/向上、shift键向上,您将获得所需的输出。这些API(PostMessage和SendMessage)如果它们没有用处,就不会存在。再说一遍,这取决于接收消息的应用程序。我没有说这些函数没有用处。我只是说它们不是用于伪造输入的。事实上,你不能将它们用于伪造输入并不意味着它们没有其他用途。你的论点很愚蠢。你似乎声称,因为se函数存在,因此它们必须放在那里解决您的问题。它工作得非常好,包括[CTRL | SHIFT | ALT]+组合键,谢谢!只是一个问题:我读到一些键盘甚至不产生扫描码,所以如果我选择一个虚拟键码,而我的键盘没有该键,Windows还会生成扫描码吗?据我所知,Windows可以做到这一点,所以理论上使用PostMessage和您提供的功能,并且唱的方式永远是w
0b1100000000101100000000000001
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);
}