How do I use sendmessage for sending wm_timer that

2019-07-31 17:20发布

问题:

I have a timer, ID 1, which has a timerproc as a callback function.

I am making the other timers (ID 2, 3, ...) in the timerproc and they use WM_TIMER event, not another timerproc.

When creating window, I want to immediately generate Timer Event, ID 1.

So I used SendMessage function like that

SendMessage(hWnd, WM_TIMER, 1, (LPARAM)&timerproc);

but it didn't work.

How do I activate timerproc at right that the first time of window?

void CALLBACK MakeRain(HWND hWnd, UINT iMessage, UINT_PTR wParam, DWORD lParam) 
{ /* this is timerproc for ID 1 */
    if (gRdx >= MAX_WORDS_COUNT) return;

    gRain[gRdx].f = 1;
    gRain[gRdx].x = rand() % (gRect.right - 30);
    gRain[gRdx].y = 10;

    int id = RdxToTID(gRdx);
    int vel = rand() % 2000 + 1000;
    SetTimer(hWnd, id, vel, NULL);    /* In here I am making other timers */
    gRdx++;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
        HDC hdc;
        PAINTSTRUCT ps;
        int tid = wParam;
        int rdx = TIDToRdx(tid);

        switch (iMessage)
        {
        case WM_CREATE:
            GetClientRect(hWnd, &gRect);
            srand((unsigned int)time(NULL));
            SetTimer(hWnd, 1, MAKE_RAIN_TERM, MakeRain);
            /* my trying, It is not working */
            //SendMessage(hWnd, WM_TIMER, 1, (LPARAM)&MakeRain);
            return 0;
        case WM_TIMER:
            gRain[rdx].y += 10;
            if (gRain[rdx].y >= gRect.bottom) {
                gRain[rdx].f = 0;
                KillTimer(hWnd, tid);
            }
            InvalidateRect(hWnd, NULL, TRUE);
            return 0;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            for (int i = 0; i < MAX_WORDS_COUNT; i++) {
                if (gRain[i].f == 0) continue;
                TextOut(hdc, gRain[i].x, gRain[i].y, words[i], lstrlen(words[i]));
            }
            EndPaint(hWnd, &ps);
            return 0;
        case WM_DESTROY:
            KillTimer(hWnd, 1);
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hWnd, iMessage, wParam, lParam);
    }

回答1:

When creating window, I want to immediately generate Timer Event, ID 1. So I used SendMessage function like that

SendMessage(hWnd, WM_TIMER, 1, (LPARAM)&timerproc);

but it didn't work.

The callback gets called by the timer only when the timer signals the owning thread's message queue to generate a WM_TIMER message, which is then retrieved by (Peek|Get)Message() and passed to DispatchMessage() by the thread's message loop. It is DispatchMessage() that calls the timer callback if one is assigned, otherwise it delivers the WM_TIMER message to the window's WndProc:

If the lpmsg parameter points to a WM_TIMER message and the lParam parameter of the WM_TIMER message is not NULLlParam points to a function that is called instead of the window procedure.

Using SendMessage() bypasses the window's message queue and goes directly to the window's WndProc. That is why you are not seeing the timer callback getting called.

So, at the very least, you would have to use PostMessage() instead of SendMessage() so that your manual WM_TIMER message can go through the window's message queue and reach DispatchMessage():

PostMessage(hWnd, WM_TIMER, 1, (LPARAM)&timerproc);

Otherwise, you would have to call DispatchMessage() directly with a fake MSG of your own:

MSG msg = {};
msg.hwnd = hWnd;
msg.message = WM_TIMER;
msg.wParam = 1;
msg.lParam = (LPARAM) &timerproc;
msg.time = GetTickCount();
GetCursorPos(&msg.pt);
DispatchMessage(&msg);

However, that is not actually necessary, because...

How do I activate timerproc at right that the first time of window?

The callback is a function, so just call it directly, like any other function:

//SendMessage(hWnd, WM_TIMER, 1, (LPARAM)&MakeRain);
MakeRain(hWnd, WM_TIMER, 1, GetTickCount());