Search code examples
c++c++11lambdatimerfunction-pointers

Wy can't I cast a statically cast Lambda as a TIMEPROC Function pointer in a settimer?


I coded this multiple times. But it doesn't even seem to work in a simple console hello word application. Is hWND the one to blame, lambda, or the casting of the lambda?

void sleeper()
{
    Sleep(10000);
}
int main()
{
    SetTimer
    (GetConsoleWindow(), 1, 1000, [](HWND, UINT, UINT_PTR, DWORD)
        {
        printf("Hello World!");
        }
    );
    sleeper();
    return 0;
}

It doesn't give me warnings.


Solution

  • You cannot cast a lamba to a TIMEPROC* or any other type of function pointers that use a different calling convention than the default (one can not specify the calling convention of a lambda). Lambdas are callable objects. This type is similar to a class, with a member function.

    Aside from that, you MUST use the correct declaration for yout TIMERPROC hook. It is:

    // definition from the MS website.  It is incomplete (typical from MS)
    // ref: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-timerproc
    void Timerproc(
      HWND unnamedParam1,
      UINT unnamedParam2,
      UINT_PTR unnamedParam3,
      DWORD unnamedParam4
    )
    
    // the actual definition from winuser.h.
    typedef VOID (CALLBACK* TIMERPROC)(HWND, UINT, UINT_PTR, DWORD):
    
    // note the use of CALLBACK, which expands to __stdcall.  That's the very important 
    // detail missing from the actual documentation.
    

    You can declare your timeproc as a free-standing function, or as a static member function of a class, Unfortunately the onluy parameter you can pass to the callback is a HWND, this means that if you want to pass any extra parameter to your callback, you have to use static (aka global) variables.

    Example 1.

    void CALLBACK myTimerProc(HWND, UINT, UINT_PTR, DWORD)
    {
       printf("Hello World!");
    }
    
    int main()
    {
        // NOTE nIDEvent, the timer ID has to be unique for the window and NON-ZERO,
        // See MS documentation here: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-settimer
        SetTimer(GetConsoleWindow(), 1, 1000, myTimerProc);
        sleeper();
        return 0;
    }
    

    Example2, if you want to define locally:

    int main()
    {
        struct local // or any other 
        {
            static void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD)
            {
                printf("Hello World!");
            }
        }
    
        SetTimer(GetConsoleWindow(), 1, 1000, local::timerProc);
        sleeper();
        return 0;
    }
    

    EDIT: For reference, the actual parameters for the TIMERPROC callback.

    Source: http://www.icodeguru.com/VC%26MFC/MFCReference/html/_mfc_cwnd.3a3a.settimer.htm

    void CALLBACK EXPORT TimerProc(
       HWND hWnd,      // handle of CWnd that called SetTimer
       UINT nMsg,      // WM_TIMER
       UINT nIDEvent   // timer identification
       DWORD dwTime    // system time
    );