You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32平台Event系统实时响应异常问题求助

问题:Win32自定义事件队列无法实时响应窗口调整事件

基于Win32 API实现的抽象事件系统,使用自定义队列分发系统消息,但在拖拽窗口边框调整尺寸时,Quest_Log不会实时打印窗口尺寸,仅在停止拖拽后批量输出所有期间的尺寸信息。

示例代码

#include "Quest.h"

int main(void) {
    if(Quest_Init(QUEST_SUBSYSTEM_VIDEO) > 0) {
        return 1;
    }
    Quest_Window *window = Quest_CreateWindow("Example", 100, 100, 640, 480, QUEST_WINDOW_DEFAULT);
    if(window == NULL) {
        return 2;
    }
    bool running = true;
    while(running) {
        Quest_Event event;
        while(Quest_PollEvent(&event)) {
            switch(event.type) {
                case QUEST_QUIT:
                    running = false;
                    break;
                case QUEST_WINDOW_RESIZING:
                    Quest_Log("Window resizing: %d;%d\n", event.window.width, event.window.height);
                    break;
                default:
                    break;
            }
        }
    }
    Quest_DestroyWindow(window);
    Quest_Shutdown();

    return 0;
}

事件轮询与推送实现

#define QUEST_MAX_EVENTS 0xffffui16

static Quest_Event  sQueue[QUEST_MAX_EVENTS] = {0};
static unsigned int Qsize = 0;

bool Quest_PollEvent(Quest_Event *event) {
    MSG  msg;
    bool result = false;
    if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) {
        if(msg.message == WM_QUIT) {
            event->type = QUEST_QUIT;
            result = true;
            return result;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    if(Qsize > 0) {
        *event = sQueue[0];
        {
            for(unsigned int i = 0; i < Qsize - 1; i++) {
                sQueue[i] = sQueue[i + 1];
            }
            --Qsize;
        }
        result = true;
        return result;
    }
    return result;
}

void Quest_PushEvent(const Quest_Event *event) {
    if(Qsize < sizeof(sQueue) / sizeof(sQueue[0])) {
        sQueue[Qsize++] = *event;
    }
}

WindowProc核心实现

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam) {
    Quest_Event event;
    memset(&event, 0x0000, sizeof(Quest_Event));
    switch(uMsg) {
    // other messages
        case WM_SIZING: {
            switch(wParam) {
                case SIZE_MAXIMIZED:
                    event.type = QUEST_WINDOW_MAXIMIZE;
                    break;
                case SIZE_MINIMIZED:
                    event.type = QUEST_WINDOW_MINIMIZE;
                    break;
                case SIZE_RESTORED:
                    event.type = QUEST_WINDOW_RESIZE;
                    break;
                default:
                    break;
            }
            event.window.width = LOWORD(lParam);
            event.window.height = HIWORD(lParam);
        } break;
    default:
        break;
    }
    Quest_PushEvent(&e);
    return DefWindowProc(hWnd, uMsg, wParam, lParam);

}

问题原因分析

  1. 系统模态循环阻塞主循环:拖拽窗口边框时,Win32系统会进入内部模态循环,持续发送WM_SIZING消息,但此时主循环while(running)会被阻塞,直到拖拽操作结束、模态循环退出后,所有积压的WM_SIZING消息才会被一次性派发给WindowProc,导致事件队列批量积压。
  2. 事件处理时机滞后:当前Quest_PollEvent的逻辑是先处理一个Win32消息,再处理自定义队列中的事件,但在模态循环期间,主循环根本无法执行,直到拖拽结束才会一次性处理所有队列中的事件。
  3. 事件类型不匹配:示例代码中监听的是QUEST_WINDOW_RESIZING事件,但WindowProc中处理WM_SIZING的default分支未设置该事件类型,同时存在变量名错误(Quest_PushEvent(&e)应为Quest_PushEvent(&event))。

解决方案

方案一:调整事件轮询逻辑,实时处理队列

拆分事件队列处理逻辑,确保每次处理Win32消息后立即处理自定义事件,避免积压。

修改后的事件轮询代码

// 新增仅处理队列事件的函数
bool Quest_PollPendingEvent(Quest_Event *event) {
    if(Qsize > 0) {
        *event = sQueue[0];
        for(unsigned int i = 0; i < Qsize - 1; i++) {
            sQueue[i] = sQueue[i + 1];
        }
        --Qsize;
        return true;
    }
    return false;
}

bool Quest_PollEvent(Quest_Event *event) {
    MSG  msg;
    // 先处理所有待处理的Win32消息,每处理一个就检查队列
    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) {
        if(msg.message == WM_QUIT) {
            event->type = QUEST_QUIT;
            return true;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            // 处理完Win32消息后立即处理一个自定义事件
            if(Quest_PollPendingEvent(event)) {
                return true;
            }
        }
    }
    // 没有Win32消息时,处理队列剩余事件
    return Quest_PollPendingEvent(event);
}

修正WindowProc的事件类型与变量错误

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam) {
    Quest_Event event;
    memset(&event, 0x0000, sizeof(Quest_Event));
    switch(uMsg) {
    // other messages
        case WM_SIZING: {
            switch(wParam) {
                case SIZE_MAXIMIZED:
                    event.type = QUEST_WINDOW_MAXIMIZE;
                    break;
                case SIZE_MINIMIZED:
                    event.type = QUEST_WINDOW_MINIMIZE;
                    break;
                case SIZE_RESTORED:
                    event.type = QUEST_WINDOW_RESIZE;
                    break;
                default:
                    // 匹配示例中的监听事件类型
                    event.type = QUEST_WINDOW_RESIZING;
                    break;
            }
            event.window.width = LOWORD(lParam);
            event.window.height = HIWORD(lParam);
            Quest_PushEvent(&event); // 修正变量名错误
        } break;
    default:
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

方案二:直接在WindowProc中触发事件回调(推荐)

跳过自定义队列,在WindowProc中直接调用事件回调,完全避免队列积压问题,实现实时响应。

新增事件回调机制

// 定义事件回调类型
typedef void (*Quest_EventCallback)(Quest_Event*);
static Quest_EventCallback sEventCallback = NULL;

// 设置全局事件回调
void Quest_SetEventCallback(Quest_EventCallback callback) {
    sEventCallback = callback;
}

修改WindowProc直接触发回调

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam) {
    Quest_Event event;
    memset(&event, 0x0000, sizeof(Quest_Event));
    bool hasValidEvent = false;

    switch(uMsg) {
        case WM_SIZING: {
            switch(wParam) {
                case SIZE_MAXIMIZED:
                    event.type = QUEST_WINDOW_MAXIMIZE;
                    break;
                case SIZE_MINIMIZED:
                    event.type = QUEST_WINDOW_MINIMIZE;
                    break;
                case SIZE_RESTORED:
                    event.type = QUEST_WINDOW_RESIZE;
                    break;
                default:
                    event.type = QUEST_WINDOW_RESIZING;
                    break;
            }
            event.window.width = LOWORD(lParam);
            event.window.height = HIWORD(lParam);
            hasValidEvent = true;
        } break;
        case WM_QUIT:
            event.type = QUEST_QUIT;
            hasValidEvent = true;
            break;
        // 处理其他消息...
        default:
            break;
    }

    // 触发回调
    if(hasValidEvent && sEventCallback != NULL) {
        sEventCallback(&event);
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

简化主循环

// 事件处理回调函数
void OnQuestEvent(Quest_Event* event) {
    switch(event->type) {
        case QUEST_QUIT:
            running = false;
            break;
        case QUEST_WINDOW_RESIZING:
            Quest_Log("Window resizing: %d;%d\n", event->window.width, event->window.height);
            break;
        default:
            break;
    }
}

int main(void) {
    if(Quest_Init(QUEST_SUBSYSTEM_VIDEO) > 0) {
        return 1;
    }
    // 设置事件回调
    Quest_SetEventCallback(OnQuestEvent);
    
    Quest_Window *window = Quest_CreateWindow("Example", 100, 100, 640, 480, QUEST_WINDOW_DEFAULT);
    if(window == NULL) {
        return 2;
    }

    bool running = true;
    MSG msg;
    // 使用GetMessage保证消息被及时处理
    while(running && GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    Quest_DestroyWindow(window);
    Quest_Shutdown();
    return 0;
}

内容的提问来源于stack exchange,提问作者ガリレオだけ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:54:58