-1

I'm having trouble with the parameter LPARAM of PostMessage().

This is a widely available example of the letter z being typed with PostMessage():

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

What is the formula for arriving at LPARAM "0x002C0001" for key down and "0xC02C0001" for key up? I want to replicate it for all keys. 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?

With keybd_event() it's much easier, you just leave the dwFlags parameter 0 for key down, and use KEYEVENTF_KEYUP for key up, but the window must have focus and the window I'm sending to is in the background, so keybd_event() and SendInput() are of no use in my case.

John
  • 17
  • 4
  • 4
    You can't reliably fake input by posting such messages. SendInput is what you need. No matter how many times this question gets asked here, this is always the answer. – David Heffernan Feb 11 '19 at 21:13
  • David Heffernan This is a common view but it's a flawed generalization. The window I'm posting to has no problem receiving messages, I tested and you can even send key combinations, e.g. shift key down, letter down/up, shift key up and you'll have the desired output. These APIs (PostMessage and SendMessage) wouldn't be there if they were useless. Again, it depends on the application that is receiving your messages. – John Feb 12 '19 at 00:48

3 Answers3

4

The meaning of LPARAM and WPARAM vary for the specific message being processed. This is why the documentation for PostMessage cannot get too specific for these parameters, only stating:

Additional message-specific information.

On both. To know exactly what they mean for each message, you need to look at the documentation for that message.

In the case of the messages you're asking about, WM_KEYUP and WM_KEYDOWN, the value of LPARAM indicates:

The repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag, as shown in the following table. (Source #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.

Let's look at the bits for your WM_KEYDOWN LPARAM there:

0x002C0001

0b0000000000101100000000000001

The bits that are set are 21, 19, 18, and 0. That tells us that:

Repeat count is 1

The remaining bits are the scan code for z, which is clearly 0b00101100 or 0x2C.

The WM_KEYUP message has the LPARAM value 0xC02C0001, which only differs at the most significant nybble, giving us:

0b1100000000101100000000000001

So, the only difference here is that the previous state and transition state bits are both 1, which is guaranteed for a WM_KEYUP message anyway.

With regard to your other question:

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

Sure. Look at MapVirtualKey to determine how to get the scan code from a key code, and use bit operations to construct a 32-bit LPARAM from that and everything else you know from the table above about the bits that must be set for these messages. You will need to use bit shifting and other bit operations to accomplish this, since the scan code is a single 8-bit byte stored as part of a 32-bit LPARAM.

Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
  • 1
    I'm thinking about using MapVirtualKey() to map a virtual key code (device-independent) to the scan code of the keyboard (which may vary), and use the obtained scan code to create the LPARAM for key down and key up. Seems straightforward, but I still don't know how to create the LPARAM for key down and key up for a given scan code. – John Feb 11 '19 at 21:00
  • @John It will require bit shifting and other bit operations. If you have questions about how to do that, it would be best to ask another question about it. – Govind Parmar Feb 11 '19 at 21:06
2

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?

Try something like this:

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);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • It's working wonderfully, including [ CTRL | SHIFT | ALT ] + key combinations, thank you! Just a question: I've read that some keyboards don't even produce scan codes, so if I choose a virtual key code and my keyboard doesn't have the key, will Windows nevertheless generate the scan code? It's my understanding that Windows can do this, so in theory using PostMessage with the functions you provided, and using the [official list of virtual key codes](https://learn.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes) means it will always work, or am I wrong? – John Feb 12 '19 at 01:48
  • @John `MapVirtualKey()` returns 0 if it can't return a scan code. – Remy Lebeau Feb 12 '19 at 05:51
0

I'd suggest to use SendInput API for that purpose.

So you just need to fill corresponding KEYBDINPUT structure that is well documented.

c-smile
  • 26,734
  • 7
  • 59
  • 86