最近项目又用到windows消息,多线程环境并遇到了问题:
1、消息处理函数没有执行。
2、GetMessage API一直等待。
详见 :多线程与WINDOWS消息。
因时间久了没接触windows消息,有点忘记了,需要熟悉一下创建窗口都消息循环的步骤流程,
下面给出两种实现,都需要windows环境:
1、控制台程序:通用方式,任意的程序都可以直接用。
------多线程注意事项参考另一篇:多线程与WINDOWS消息。
2、WIN32窗体程序:一般方式。
控制台程序流程
程序入口:int main();
#include <windows.h> //包含 Windows 相关的 API 函数
// 消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 注册窗口
bool RegisterWindow();
// 窗口创建
bool Create();
// 消息循环
void MsgLoop();
/*************************
* 入口函数,测试窗口消息
***************************/
int main()
{
RegisterWindow(); // 窗口注册
Create(); // 窗口创建
MsgLoop(); // 消息循环
return 0;
}
// 消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
bool handle = true;
switch (uMsg) //消息选择
{
case WM_LBUTTONDOWN:
break;
default:
handle = false;
break;
}
if (handle) {
return S_OK;
}
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}
// 注册窗口
bool RegisterWindow() {
HINSTANCE hInstance = GetModuleHandleA(NULL);
//当然, 如果不创建窗口, 不使用窗口 也可以使用 main.
WNDCLASSEXA wc = { 0 }; //窗口类结构, 为注册窗口类作准备
wc.cbClsExtra = 0; //附加的类信息, 没有, 设为0
wc.cbSize = sizeof(wc); //WNDCLASSEX结构的大小
wc.cbWndExtra = 0; //窗口额外内存, 没有, 设为0
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); //窗口背景, 这里使用灰色背景
wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_ARROW)); //应用程序使用的鼠标类型
wc.hIcon = LoadIconA(NULL, MAKEINTRESOURCEA(IDI_APPLICATION)); //光标类型
wc.hIconSm = NULL; //应用程序程序的小光标, 不管, 设为 NULL
wc.hInstance = hInstance; //应用程序程序实例句柄, 由 WinMain 函数传递过来
//当前窗口的消息处理函数, 传递 WndProc 的地址
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "vbgk_class"; //创建类时使用的类名, 可以自定义
wc.lpszMenuName = NULL; //菜单, 没有, 就使用 NULL
wc.style = CS_HREDRAW | CS_VREDRAW; //类的风格, 垂直重绘, 水平重绘
if (!RegisterClassExA(&wc)) { //注册窗口类
MessageBoxA(NULL, "Register Class Failed!", NULL, MB_OK);
return false;
}
return true;
}
// 窗口创建
bool Create() {
HINSTANCE hInstance = GetModuleHandleA(NULL);
//创建窗口
HWND hWnd = CreateWindowExA(
0, //扩展窗口风格
"vbgk_class", //这里就是我们刚才创建的窗口类名
"test title",
WS_OVERLAPPEDWINDOW,//窗口风格
CW_USEDEFAULT, //初始化时的 X 坐标
CW_USEDEFAULT, //Y坐标
320, //窗口宽度, 我们这里设为 320
240, //窗口高度
NULL, //父窗口句柄, 没有
NULL, //菜单, 没有
hInstance, //实例句柄, 来自WinMain
NULL //发送 WM_CREATE 消息时的附加参数, 一般为零
);
if (!hWnd || INVALID_HANDLE_VALUE == hWnd) {
return false;
}
UpdateWindow(hWnd); //更新窗口
ShowWindow(hWnd, SW_HIDE); //隐藏窗口
return true;
}
// 消息循环
void MsgLoop() {
MSG msg = {0};
BOOL bRet = TRUE;
while ((bRet = GetMessageA(&msg, NULL, 0, 0)) != -1) {
if (bRet == 0) {
break;
}
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
}
WIN32窗体程序流程
程序入口:int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
网上查了一下发现一篇简单易懂的例子文章,转载过来了,做个记录,
内容来自:木木ing 的博客: Win32 SDK 编程开始, 创建窗口, 消息的处理, 消息循环
以下是正文:
Windows SDK 编程的一般步骤为:
1. 注册窗口类, 使用到的结构 WNDCLASSEX, 函数 RegisterClassEx.
2. 创建窗口, 函数 CreateWindowEx.
3. 消息循环, 结构 MSG, 函数 GetMessage, TranslateMessage, DispatchMessage.
4. 进入无限循环, 消息处理 :-)
下面是代码, 当然, 还有分析:
#include <windows.h> //包含 Windows 相关的 API 函数
//这里就是所谓的消息处理函数了
//CALLBACK 说明当前函数是回调函数, 就是说明调用约定
//hwnd:窗口句柄, uMsg:收到的消息, wParam, lParam:与该消息有关的参数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) //消息选择
{
//这里我添加一个左键按下的消息
case WM_LBUTTONDOWN:
MessageBoxA(hwnd, "左键按下", "提示", MB_OK);
return 0;
case WM_DESTROY: //当前窗口被销毁时
PostQuitMessage(0); //发出退出程序的消息
return 0; //如果处理了该消息, 就返回 0 来告知 Windows
case WM_CLOSE: //当用户点击窗口右上角的关闭按钮时
DestroyWindow(hwnd); //销毁窗口
return 0;
case WM_PAINT: //当窗口需要重绘时
{
PAINTSTRUCT ps = { 0 };
BeginPaint(hwnd, &ps); //开始重绘
EndPaint(hwnd, &ps); //结束重绘
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
//对于我们不想处理的消息, 比如鼠标在窗口上移动时发出的消息
//我们就原样传给默认的窗口消息处理函数处理, 不然应用程序会失去响应
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//注意 Windows 程序应该使用 WinMain 入口函数了, 不再使用 main 函数
//当然, 如果不创建窗口, 不使用窗口 也可以使用 main.
WNDCLASSEXA wc = { 0 }; //窗口类结构, 为注册窗口类作准备
wc.cbClsExtra = 0; //附加的类信息, 没有, 设为0
wc.cbSize = sizeof(wc); //WNDCLASSEX结构的大小
wc.cbWndExtra = 0; //窗口额外内存, 没有, 设为0
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); //窗口背景, 这里使用灰色背景
wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCE(IDC_ARROW)); //应用程序使用的鼠标类型
wc.hIcon = LoadIconA(NULL, MAKEINTRESOURCE(IDI_APPLICATION)); //光标类型
wc.hIconSm = NULL; //应用程序程序的小光标, 不管, 设为 NULL
wc.hInstance = hInstance; //应用程序程序实例句柄, 由 WinMain 函数传递过来
wc.lpfnWndProc = WndProc; //这个很重要, 就是当前窗口的消息处理函数, 传递 WndProc 的地址
wc.lpszClassName = "vbgk_class"; //创建类时使用的类名, 可以自定义
wc.lpszMenuName = NULL; //菜单, 没有, 就使用 NULL
wc.style = CS_HREDRAW | CS_VREDRAW; //类的风格, 垂直重绘, 水平重绘
if (!RegisterClassExA(&wc)) //注册窗口类
{
MessageBoxA(NULL, "Register Class Failed!", NULL, MB_OK);
return 1;
}
//创建窗口咯, 介绍一下 CreateWindowEx 函数的参数
HWND hWnd = CreateWindowExA(
0, //扩展窗口风格
"vbgk_class", //这里就是我们刚才创建的窗口类名
"木木ing",
WS_OVERLAPPEDWINDOW,//窗口风格
CW_USEDEFAULT, //初始化时的 X 坐标
CW_USEDEFAULT, //Y坐标
320, //窗口宽度, 我们这里设为 320
240, //窗口高度
NULL, //父窗口句柄, 没有
NULL, //菜单, 没有
hInstance, //实例句柄, 来自WinMain
NULL //发送 WM_CREATE 消息时的附加参数, 一般为零
);
if (!hWnd)
{
MessageBoxA(NULL, "CreateWindowEx Failed!", NULL, MB_OK);
return 2;
}
UpdateWindow(hWnd); //更新窗口
ShowWindow(hWnd, nCmdShow); //显示窗口
MSG msg;
BOOL bRet;
//进入消息循环,
//注意,消息循环一定要和上面的窗口创建在同一个线程
// 否则GetMessage函数不会返回,会一直等到。
while ((bRet = GetMessageA(&msg, NULL, 0, 0)) != -1)
{
if (bRet == 0)
{
break;
}
TranslateMessage(&msg); //翻译消息
//分发消息- 即会调用wc.lpfnWndProc = WndProc;
DispatchMessageA(&msg);
}
return msg.wParam; //WinMain函数结束, 整个程序退出
}