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

Time:11-22

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 and it wasn't so similar to the next example after all.

This is the other app, a maybe ugly windowed application that has the same issue, no timerproc execution:

else if (message == WM_PAINT)
{
    PAINTSTRUCT ps;
    static HDC hdc = BeginPaint(hWnd, &ps);// TODO: Add any drawing code that uses hdc here...
    static std::vector<std::vector<std::vector<int>>> color[4] =
    {
        std::vector<std::vector<std::vector<int>>>(0x7F),
        std::vector<std::vector<std::vector<int>>>(0x7F),
        std::vector<std::vector<std::vector<int>>>(0x7F),
        std::vector<std::vector<std::vector<int>>>(0xFF)
    };
    SetTimer
    (hWnd, 1, 5000, [](HWND hWnd, UINT nMsdg, UINT_PTR unnamedParam, DWORD dwTime)->void
        {
            int x = 0;
            int y = 0;
            SetPixelV(hdc, x, y, COLORREF(color[x][y][0] * 0x1   color[x][y][1] * 0x100   color[x][y][2] * 0x10000));
        }
    );
    EndPaint(hWnd, &ps);
}

Now in the second sample it has the message handling I think, but it doesn't draw a pixel. I have to say, separating RGBA was not so smart, I'm just too used to scripting.

CodePudding user response:

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://docs.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://docs.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&MFC/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
);
  • Related