-1

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.

Ciel Ruby
  • 161
  • 1
  • 13
  • 1
    [This](https://stackoverflow.com/questions/13371644/c-how-to-set-a-new-wndproc-for-a-console-application) looks like one of your issues. At that, you would want to enter an infinite loop so the app stays alive. – lakeweb Nov 21 '21 at 16:58
  • 2
    Check the return value of `SetTimer`. I suspect the function fails, as you call it on an `HWND` that is not owned by the calling thread. This has nothing to do with lambdas or function pointers, and everything to do with your mistaken assumptions about how `SetTimer` works. – Igor Tandetnik Nov 21 '21 at 17:11
  • I gave it a sleeper function, but it indeed still doesn't work because of using the wrong hwnd, I'm gonna post a different example. – Ciel Ruby Nov 21 '21 at 17:12
  • 1
    Your callback function needs to take four parameters, as documented [here](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-timerproc). (Function type mismatches can never be fixed by casting.) – molbdnilo Nov 21 '21 at 17:21
  • Oh, thank you! It seemed so weird because it almost took the same parameters as the settimer itself. I need to read better. – Ciel Ruby Nov 21 '21 at 17:28

1 Answers1

1

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
);
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
  • Wow, that's a very handy timer scheduler actually, thank you for opening my eyes, I'm still trying to understand it. Is this what you call a functor or functionoid? – Ciel Ruby Nov 21 '21 at 17:49
  • 1
    What we call callable objects n c++ are also known as functors, yes. – Michaël Roy Nov 21 '21 at 17:52
  • 3
    `You cannot cast a lamba to a TIMEPROC* or any other type of function pointers.` This is not true. This is true only for capturing lambdas. – 273K Nov 21 '21 at 17:52
  • Yeah that's what I found, this really sets me to learn. – Ciel Ruby Nov 21 '21 at 17:56
  • 1
    @S.M. From what I understand, there is no way of specifying the calling convention of lambdas, and compiler vendors are free to use any calling convention they deem practical. – Michaël Roy Nov 21 '21 at 18:00
  • 1
    @S.M. I have edited my answer to reflect that. – Michaël Roy Nov 21 '21 at 18:06
  • 2
    @MichaëlRoy VS is smart enough to implicitly apply calling convention CALLBACK to a lambda. Thus `[](HWND, UINT, UINT_PTR, DWORD){}` will work. – 273K Nov 21 '21 at 18:10
  • 1
    @S.M. This does work.. But until when? And where is that documented? Is that part of the standard? – Michaël Roy Nov 21 '21 at 18:15
  • 1
    @MichaëlRoy Which standard? CALLBACK is not standard. This is VS feature. See [C++11 Features (Modern C++)](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/hh567368(v=vs.110)) `The Visual C++ in Visual Studio 2012 is even better than that, because we've made stateless lambdas convertible to function pointers that have arbitrary calling conventions.` – 273K Nov 21 '21 at 18:18
  • It still doesn't work for me, which is because it is the wrong hWnd probably – Ciel Ruby Nov 21 '21 at 18:47
  • 3
    @KevinCrans This does not work because sleeper blocks the main thread, no chance to run the event loop of the console window. Not reading the manuals is the often mistake of beginners. `When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.` – 273K Nov 21 '21 at 18:54