当前位置: 首页 > news >正文

亦庄网站设计小程序开发工具怎么用

亦庄网站设计,小程序开发工具怎么用,营销网站建设 公司排名,只做外贸的公司网站Windows消息队列和消息处理(1):基本的API和简单说明 ​ Windows的窗口是使用一种叫做消息队列的机制进行处理的。您可以想象:我们的终端是一个嗷嗷待哺等待外部指令的窗口,自身有一个处理函数(是不是感到熟…

Windows消息队列和消息处理(1):基本的API和简单说明

​ Windows的窗口是使用一种叫做消息队列的机制进行处理的。您可以想象:我们的终端是一个嗷嗷待哺等待外部指令的窗口,自身有一个处理函数(是不是感到熟悉?我们自己曾经给lpWndProc成员赋值的处理函数就是这个处理函数)。外部鼠标,键盘或者是任何交互行为都期待同窗口进行交互。我们很容易想到,直接送到窗口是不现实的,窗口自己需要处理一小段时间,这些在处理事件而无暇顾及到事件到来时刻的消息,总不能直接丢弃吧!既然这些消息都是按照先来先到的方式试着进入窗口的,我们完全可以使用一个队列做缓冲。这个时候,生产者生产消息投送到消息队列中,窗口充当消费者拿取和处理消息。

​ 这就是为什么常见的GUI框架采用事件循环和消息队列存储这些消息。

​ Windows给我们提供了大量的脚手架,在我们第一次CreateWindow或者是GetMessage的时候,我们实际上就会创建一个简单的消息队列维护消息存取。每个 GUI 线程最多有一个,所有系统输入(鼠标、键盘)、应用投递 (PostMessage) 的消息会排进这个队列。

​ 消息队列有了,如何处理呢?一般而言,我们采用的是消息循环处理,Qt用的是app.exec(),这个时候我们会进入一个反复处理消息循环中:按照“从消息队列里取消息、处理消息、分发给窗口过程”的步骤反复执行。

​ 这样事情就简单了,我们只需要关心三个部分的API

  • GetMessage / PeekMessage:从队列里取消息。
  • TranslateMessage:把按键类消息转为字符类消息。
  • DispatchMessage:把消息分发给窗口过程(WndProc)。
  • (可选)IsDialogMessageTranslateAccelerator:处理对话框与快捷键。

最经典的消息循环

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow) 
{HWND hwnd = CreateWindowEx(0,                      // 扩展风格L"MyWindowClass",       // 注册过的窗口类名L"Demo Window",         // 窗口标题WS_OVERLAPPEDWINDOW,    // 样式CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,NULL, NULL, hInstance, NULL);ShowWindow(hwnd, nCmdShow);UpdateWindow(hwnd);MSG msg;// 消息循环while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);   // 键盘消息转字符消息DispatchMessage(&msg);    // 分发给窗口过程}return (int)msg.wParam;       // WM_QUIT 的退出码
}

​ 这个消息循环是最简单的消息循环,GetMessage 阻塞等待消息,CPU 占用低。收到 WM_QUIT 时返回 0,循环退出。

带加速键和对话框支持的循环

MSG msg;
HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_MAINACCEL));
HWND hDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), hwnd, DlgProc);while (GetMessage(&msg, NULL, 0, 0)) {if (hDlg && IsDialogMessage(hDlg, &msg)) {continue; // 对话框内部消息已处理}if (hAccel && TranslateAccelerator(hwnd, hAccel, &msg)) {continue; // 快捷键消息已处理}TranslateMessage(&msg);DispatchMessage(&msg);
}
  • 常见于带菜单、快捷键、对话框的应用。

非阻塞 PeekMessage 循环

bool running = true;
MSG msg;
while (running) {while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // 这个响应就会好if (msg.message == WM_QUIT) {running = false;break;}TranslateMessage(&msg);DispatchMessage(&msg);}if (!running) break;// 无消息时,执行一帧逻辑/渲染UpdateGame(); // 这个往下都是Idel任务RenderFrame();
}

特点:

  • 主动渲染,不依赖系统空闲时机。
  • 注意加 SleepMsgWaitForMultipleObjectsEx,避免空转 100% CPU。

所以消息长啥样?

typedef struct tagMSG {HWND   hwnd;UINT   message;WPARAM wParam;LPARAM lParam;DWORD  time;     // 事件时间戳POINT  pt;       // 发生时的鼠标位置DWORD  lPrivate; // 系统内部使用
} MSG;

常见“投递(Posted)”消息:

  • 输入:WM_MOUSEMOVE / WM_LBUTTONDOWN / WM_KEYDOWN ...(输入管理器根据前台队列产生)
  • 绘制:WM_PAINT(窗口无效区域 & 队列空闲时产生,可能被合并)
  • 定时器:WM_TIMER(若创建 timer 未指定回调函数)
  • 应用投递:PostMessagePostThreadMessage 发的自定义消息

常见“派发(Sent)”消息(不入队):

  • SendMessage 发的绝大多数消息(同步调用目标窗口过程)
  • 系统同步发出的布局/激活类消息(很多 WM_* 在系统内部采用 send 语义)

合并(coalescing):例如连续的 WM_MOUSEMOVEWM_PAINTWM_TIMER 可能被合并,减少重复处理。


GetMessage —— 阻塞式取消息的主力

BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
  • 阻塞直到有符合过滤条件的消息。拿到后把它从队列移除并填入 MSG
  • 返回值:
    • >0:取到正常消息
    • 0:取到 WM_QUIT(线程应退出消息循环)
    • -1:错误(参数非法等;极少见,但要判错)
  • 过滤器
    • hWnd:仅取这个窗口的消息;常用传 NULL(取线程队列里所有窗口/线程消息)
    • wMsgFilterMin/max:按消息范围过滤(例如只取键盘消息)
  • 重要WM_QUIT 不受 hWnd 和范围过滤影响;一旦线程队列里有了 WM_QUITGetMessage 会直接返回 0。
  • 优先级:输入类(鼠标/键盘)优先于普通投递消息;WM_PAINTWM_TIMER 常在队列空闲或低优先阶段生成。

典型用法(标准 GUI 程序主循环):

MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);
}
// 退出清理
return (int)msg.wParam; // 通常 WM_QUIT 的 wParam

PeekMessage —— 非阻塞取消息与“游戏循环”

BOOL PeekMessage(LPMSG lpMsg, HWND hWnd, UINT min, UINT max, UINT remove);
  • 不阻塞:若无消息立即返回 FALSE;适合需要持续渲染/轮询的场景(游戏/实时 UI)。
  • remove
    • PM_NOREMOVE窥视,不移除
    • PM_REMOVE:取出并移除
    • (历史上有 PM_NOYIELD;现代一般不用)
  • 注意:
    • 忙等风险:错误写法里 while (PeekMessage(...)) { ... } // 紧接着渲染 但没有 WaitMessage/Sleep,会 100% 占用一个 CPU 核心。
    • 与定时器/WM_PAINT 的合并/低优先级机制叠加,可能出现饿死现象(一直有输入就很难得到 WM_PAINT)。可在空闲时调用 WaitMessage() 或使用 MsgWaitForMultipleObjectsEx 等待“任意消息或句柄事件”。

常见“游戏循环”范式:

MSG msg;
bool running = true;
while (running) {// 把队列消息处理干净while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {if (msg.message == WM_QUIT) { running = false; break; }TranslateMessage(&msg);DispatchMessage(&msg);}if (!running) break;// 无消息:做一帧渲染/更新TickFrame();// 小心空转;推荐:// WaitMessage();  // 等待下一条消息再醒(纯消息驱动)// 或用 MsgWaitForMultipleObjectsEx(..., QS_ALLINPUT, ...) 等待消息或自定义事件
}

TranslateMessage —— 把按键变成字符

BOOL TranslateMessage(const MSG *lpMsg);
  • 作用:对键盘类投递消息WM_KEYDOWN / WM_SYSKEYDOWN,以及死键相关)进行布局转换、组合键态分析,按当前键盘布局和修饰键状态,把它们“翻译”成字符类消息
    • 生成并投递:WM_CHAR / WM_SYSCHAR / WM_DEADCHAR / WM_SYSDEADCHAR / WM_UNICHAR
    • 不改变原消息;只是可能额外投递上述字符消息
  • 返回值:非 0 表示“翻译过”(投递了字符消息)。实际编程常忽略返回值,照常调用。
  • 不是快捷键/加速键:快捷键翻译用 TranslateAccelerator;对话框的 Tab/Enter/Esc 行为用 IsDialogMessage
  • 与 IME:TranslateMessage 不处理 IME 复合文本;IME 相关由 WM_IME_* 和输入法承担。

DispatchMessage —— 把消息交给窗口过程

LRESULT DispatchMessage(const MSG *lpMsg);
  • 根据 lpMsg->hwnd 找到该窗口的窗口过程(WNDPROC),调用它:

    return CallWindowProc(actualWndProc, lpMsg->hwnd, msg, wParam, lParam);
    
  • 若你的窗口过程不处理某些消息,应调用 DefWindowProc 交还系统默认行为(移动、最小化、命中测试、非客户区绘制等)。

  • 对话框:若先调用了 IsDialogMessage(hwndDlg, &msg) 且其返回 TRUE,说明对话框已经处理(焦点切换、默认按钮等),那就不要再调用 DispatchMessage


PostMessage / PostThreadMessage —— 异步投递

BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam);
  • 立即返回,不等待目标处理(异步)。消息进入目标线程的队列。
  • PostThreadMessage 目标线程必须已创建消息队列(见 §1);否则失败。
  • 失败返回 FALSE,请 GetLastError();常见原因:HWND 无效/已销毁、线程无消息队列、队列满(极端)。
  • 跨线程传指针的坑wParam/lParam 只是整数位宽。不要跨线程传原始指针指向临时/线程私有内存;需要引用计数对象、拷贝数据或使用安全的跨线程封送。
  • 顺序性:对于同一来源线程同一目标线程投递的普通 posted 消息,FIFO;但与输入/WM_PAINT/WM_TIMER 的优先级/合并会交叉

结束主循环的方式:PostQuitMessage(exitCode);
该调用会向当前线程队列投递 WM_QUIT,使 GetMessage 返回 0。


(对照)SendMessage —— 同步、可重入、容易卡死

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
  • 同步调用目标窗口过程,直到对方处理完返回不走队列
  • 同线程:本质上就是直接调用目标 WndProc;这意味着可重入——如果在此期间你的线程又进入了某种消息循环(如菜单跟踪、拖动等),会产生复杂的调用栈。
  • 跨线程:内核/USER32 通知目标线程执行该窗口过程,发送线程阻塞等待。若目标线程卡住或也在等你,容易死锁
  • 用法建议:跨线程沟通优先 PostMessage/PostThreadMessage + 自己的同步原语;确需同步时要非常小心避免等待环。

队列优先级、合并与“饿死”

  • 优先级大致从高到低:硬件输入(鼠标/键盘) > SendMessage(不在队列) > 普通 posted(应用) > WM_PAINT/WM_TIMER
  • 合并
    • WM_MOUSEMOVE:连续移动只保留最新一条
    • WM_PAINT:多个无效区域合并成一次 WM_PAINT
    • WM_TIMER:同一 ID 的定时器可能合并
  • 饿死现象:若持续有高优先级输入,WM_PAINT/WM_TIMER 会延后。
    解决:空闲时 WaitMessage()、合理调度、分帧处理、降低输入频度(鼠标采样)等。

系统内部“模态循环”

  • 菜单跟踪、窗口拖动/调整大小、滚动条跟踪、模态对话框等,系统会进入内部的临时消息循环。这时你的主循环没在跑,但消息仍会被处理。
  • 这也是重入的来源之一(你的窗口过程在一次处理里又收到了其他消息)。
  • 对话框建议:在主循环里调用 IsDialogMessage,或直接使用 DialogBox(它内部自带模态循环)。
http://www.dtcms.com/a/518473.html

相关文章:

  • 369网站建设合格的网站设计师需要会什么软件
  • asp门户网站源码国防教育网站建设方案
  • 怎么用dw做带登陆的网站邢台哪里提供网站制作
  • 如果在工商局网站上做股权质押商标怎么设计才好看
  • php和django做网站哪个好wordpress 投稿插件
  • 企业建设网站的过程手机主页网站
  • 做网站字体海口的网站建设
  • 全国八大员报名官方网站wordpress 建立相册
  • 扬中市住房和城乡建设局网站wordpress自动更新失败
  • 做网站的功能结构布局网站设计代码案例
  • 教做美食的视频网站wordpress 分享按钮
  • 网站301重定向$微信开发者工具下载安装
  • 大连 网站维护wordpress 菜单栏插件
  • 网站建设招标说明书景德镇做网站
  • 亳州市网站建设公司网上银行建设银行
  • 深圳网站建设方案优化湖北什么是网络营销
  • 10个免费的黑科技网站seo关键词排名在线查询
  • 成都网站建设单招网建设网站的计划表
  • 建设数字官方网站如何把购物网站做成非经营网站
  • 网站备案要多少钱做北京塞车网站
  • 阿里云国际wordpress安阳如何优化网站
  • 做婚纱网站的意义代做百度首页排名
  • php网站培训宣城网站建设费用
  • 在线做原型的网站wordpress do_
  • 纳森网络做网站多少钱做淘客都有什么网站
  • 邵阳做网站wordpress eshop
  • 网站平台推广方案手工做耳环银材料哪个网站可以买到
  • 十堰响应式网站建设淮南商城网站建设地址
  • 兰州产品营销网站建设担路网口碑做网站好吗
  • 手机网站关键词优化软件网站建设和维护自学