【问题标题】:How do I use sendmessage for sending wm_timer that has timer proc in win32如何使用 sendmessage 发送在 win32 中具有 timer proc 的 wm_timer
【发布时间】:2018-10-11 13:19:41
【问题描述】:

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

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

创建窗口时,我想立即生成Timer Event,ID 1。

所以我使用了类似的 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);
    }

【问题讨论】:

  • 据我所知,TimerProc函数的lParam就是TimerProc函数的地址。
  • 这听起来像XY problem。你实际上想做什么? SetTimer(hWnd, 1, MAKE_RAIN_TERM, MakeRain); 是否被调用 MakeRain
  • 绝对是XY Problem。除此之外,至少有 2 个错误等待发生:1 计时器将以准确的时间间隔到期的假设是没有根据的。如果您使用该假设编写动画代码,它将看起来参差不齐。 2 定时器 ID 是指针大小的。使用该功能来防止 ID 冲突,例如通过使用(全局)对象的地址作为计时器 ID。
  • 如果你想立即调用MakeRain,不要使用魔法,直接调用即可。
  • 感谢您提供适量的代码来找到问题。

标签: winapi timer sendmessage


【解决方案1】:

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

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

但是没有用。

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

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

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

因此,至少,您必须使用PostMessage() 而不是SendMessage(),这样您的手动WM_TIMER 消息才能通过窗口的消息队列并到达DispatchMessage()

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

否则,您将不得不用自己的假 MSG 直接调用 DispatchMessage()

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());

【讨论】:

  • 注意WM_TIMER 是在队列处理函数中生成的,计时器实际上不会使其进入队列。 “消息由 GetMessage 或 PeekMessage 函数发布。”所以调用PostMessage 不会复制实际触发的计时器(尽管它可能会到达同一个处理程序)
  • 是的,我知道这一点。计时器在队列中设置一个标志,然后消息检索检查该标志,如果队列中没有更高优先级的消息,则生成伪造的WM_TIMER 消息。该假消息然后像任何其他消息一样到达DispatchMessage(),DM 将调用计时器回调或将消息传递到窗口的 WndProc。手动发布WM_TIMER 消息不会“触发计时器”,但它确实会导致发布的WM_TIMER 消息从消息队列中出来并到达DispatchMessage(),就像生成WM_TIMER 消息一样。跨度>
猜你喜欢
  • 2017-05-12
  • 2019-09-22
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 2013-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多