我如何使用sendmessage发送在Win32中具有计时器proc的wm_timer

时间:2018-10-11 13:19:42

标签: winapi timer sendmessage

我有一个ID为1的计时器,其中有一个timerproc作为回调函数。

我在timerproc中创建其他计时器(ID 2、3,...),它们使用WM_TIMER事件,而不是另一个timerproc。

创建窗口时,我想立即生成ID为1的Timer事件。

所以我就这样使用了SendMessage函数

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

但是没有用。

如何在第一次出现窗口时立即激活timerproc?

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 个答案:

答案 0 :(得分:2)

  

创建窗口时,我想立即生成ID为1的Timer Event。   所以我就这样使用了SendMessage函数

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

但是没有用。

仅当计时器发信号通知拥有线程的消息队列以生成WM_TIMER消息时,计时器才会调用该回调,然后由(Peek|Get)Message()检索并由DispatchMessage()传递给DispatchMessage()线程的消息循环。如果分配了计时器,则WM_TIMER会调用计时器回调,否则它将lpmsg消息传递到窗口的WndProc:

  

如果WM_TIMER参数指向一条lParam消息,而WM_TIMER消息的NULL参数不是lParam,则SendMessage()指向而不是调用窗口过程的函数。

使用PostMessage()绕过窗口的消息队列,直接进入窗口的WndProc。这就是为什么您没有看到计时器回调被调用的原因。

因此,至少必须使用SendMessage()而不是WM_TIMER,以便您的手动DispatchMessage()消息可以通过窗口的消息队列并到达{{1} }:

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

否则,您将必须用自己的假DispatchMessage()直接呼叫MSG

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

但是,实际上并没有必要,因为...

  

如何在第一次出现窗口时立即激活timerproc?

回调是一个函数,因此就像其他任何函数一样,直接调用它即可

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