0

I need to use "CreateTimerQueueTimer" to create a timer.

The parameter must be pass as pointer.

Here my code:

void sendKey(int vk, ) {

    KEYBDINPUT  kb = { 0 };
    INPUT       Input = { 0 };
    int scan = MapVirtualKey(vk, 0);

    /* Generate a "key down" */
    kb.dwFlags = KEYEVENTF_EXTENDEDKEY;
    kb.wVk = vk;
    Input.type = INPUT_KEYBOARD;
    Input.ki = kb;
    Input.ki.wScan = scan;
    SendInput(1, &Input, sizeof(Input));

    /* Generate a "key up" */
    ZeroMemory(&kb, sizeof(KEYBDINPUT));
    ZeroMemory(&Input, sizeof(INPUT));
    kb.dwFlags = KEYEVENTF_KEYUP;
    KEYEVENTF_EXTENDEDKEY;
    kb.wVk = vk;
    Input.type = INPUT_KEYBOARD;
    Input.ki = kb;
    Input.ki.wScan = scan;
    SendInput(1, &Input, sizeof(Input));

    return;
}


void CALLBACK ProcessRequests(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
    sendKey((int)lpParameter, true);

}


void myfunc()
{
    int VirtualKey=50;

    CreateTimerQueueTimer(&m_hTimer, nullptr, &ProcessRequests, &VirtualKey, 100, 0, WT_EXECUTEONLYONCE);
}

Inside "ProcessRequests" there is the function "sendKey" that accept integer as parameter but lpParameter is a pointer. How I can pass the value of "lpParameter" and convert it in integer ?

Thanks !

marcoluca987
  • 59
  • 1
  • 5
  • 1
    you probably can't store a pointer in an `int`, the former is generally 64-bits and the latter is generally 32-bits – Alan Birtles Sep 06 '22 at 18:02
  • For C-style callbacks that pass a `void*` (a.k.a. `PVOID`) for user data, you must `static_cast` the parameter back to the appropriate type (`int` in this case). Which you are already doing. So what specific problem are you having with this code? – 0x5453 Sep 06 '22 at 18:02
  • Note that you've got undefined behaviour here, since the fimer may be fired when `VirtualKey` is gone. You either nees something that survives until the timer callback is called or in this scenario you could pass the value as `intptr_t` cast to `void*`: `CreateTimerQueueTimer(..., std::bit_cast(static_cast(VirtualKey)), ...);` `sendKey(static_cast(std::bit_cast(lpParameter)), true);` and not worry about the lifetime of any value pointed to... – fabian Sep 06 '22 at 18:12
  • My app is windows In my log I see " ProcessRequests: 190831599" where 190831599 is lpParameter, but I need the value and not the pointer. I need to use "static_cast" ? Thanks ! – marcoluca987 Sep 06 '22 at 18:12
  • with "static_cast(std::bit_cast(lpParameter)" not compile but with "static_cast(std::_Bit_cast(lpParameter)" compile but the result is 190831599 and not the value. – marcoluca987 Sep 06 '22 at 18:22
  • On a side note: [Is it a bug to pass a single-element array to SendInput?](https://stackoverflow.com/questions/46744894/) – Remy Lebeau Sep 06 '22 at 19:27
  • int VirtualKey=50; declare this as a global variable or static variable (to make the variable a long term lifetime) , then in ProcessRequests, call it as: sendKey( *(LPINT) lpParameter, ...); – Exlife Sep 07 '22 at 09:58

2 Answers2

1

There are two problems with this:

  1. lpParameter points to an int, but you're treating it as if it is an int
  2. The int pointed to by lpParameter is local to myfunc and therefore no longer exists by the time your timer fires and your callback gets called

The first issue is simple to solve, just change the way you interpret lpParameter:

void CALLBACK ProcessRequests(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
    int* param = static_cast<int*>(lpParameter);
    sendKey(*param, true);
}

The latter issue is a bit tougher, and how you solve it will depend on your needs. Since it looks like these functions are all members of some class (based on the name of m_hTimer), you may want to store the key code as a class data member instead of a local variable in myfunc:

class MyClass
{
    int VirtualKey;

    // ...

    void myfunc()
    {
        VirtualKey = 50;  // set the class member instead of
                          // creating a local variable

        CreateTimerQueueTimer(
            &m_hTimer,
            nullptr,
            &ProcessRequests,
            &VirtualKey,
            100,
            0,
            WT_EXECUTEONLYONCE
        );
    }

    // ...
};

If the functions aren't part of a class or you don't want to keep any sort of global state that multiple calls to myfunc could interfere with, then another approach would be to dynamically allocate the parameter. Just remember to free it when ProcessRequests is done with it:

void CALLBACK ProcessRequests(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
    int* param = static_cast<int*>(lpParameter);
    sendKey(*param, true);
    delete param;
}

void myfunc()
{
    int* VirtualKey = new int(50);

    CreateTimerQueueTimer(
        &m_hTimer,
        nullptr,
        &ProcessRequests,
        VirtualKey,  // NOTE: no &, VirtualKey is already a pointer
        100,
        0,
        WT_EXECUTEONLYONCE
    );
}

I would recommend the former approach if possible. It would be easy to create memory leaks if you forget to free the key code in some code path (i.e. if you later add code to cancel the timer before it runs).

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
0

You are passing CreateTimerQueueTimer() an int* pointer that points to a local int variable, but then you are treating lpParameter in ProcessRequests() as-if it were the int value instead of the int* pointer that it really is.

Passing the int value into the callback is fine, but you need to pass th value type-casted to a pointer, eg:

void CALLBACK ProcessRequests(PVOID lpParameter, BOOLEAN /*TimerOrWaitFired*/)
{
    sendKey(static_cast<int>(reinterpret_cast<intptr_t>(lpParameter)), ...);
}

void myfunc()
{
    int VirtualKey = 50;

    CreateTimerQueueTimer(..., reinterpret_cast<void*>(static_cast<intptr_t>(VirtualKey)), ...);
}

Also, calling SendInput() 2 times with 1 INPUT at a time without any delay in between the calls is a logic bug in your code that will likely cause issues with the input queue. Since there is no delay in your code, you should instead call SendInput() 1 time with 2 INPUTs, eg:

void sendKey(int vk, ... ) {

    INPUT Inputs[2] = { };
    ...

    /* Generate a "key down" */
    Inputs[0].type = INPUT_KEYBOARD;
    Inputs[0].ki.dwFlags = ...;
    Inputs[0].ki.wVk = ...;
    Inputs[0].ki.wScan = ...;

    /* Generate a "key up" */
    Inputs[1] = Inputs[0];
    Inputs[1].ki.dwFlags |= KEYEVENTF_KEYUP;

    SendInput(2, Inputs, sizeof(INPUT));
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770