什么是消息循环,一个简单的win32程序如何运行?
预备知识
1.什么是句柄? (HANDLE)
在win32编程中有各种句柄,那么什么是句柄呢?
#define DECLARE_HANDLE(name)
struct name##_
{
int unused;
};
typedef struct name_* name;
例如HDC的定义
#define DECLARE_HANDLE(HDC)
struct HDC_
{
int unused;
};
typedef struct HDC_ * HDC
当一个函数需要HWND类型参数的时候,你就不能传递HDC,因为类型不匹配。
总结:1.一个窗口句柄本质上是一个void * 2.win32编程中有特别多不同类型的窗口句柄,所以我们就把他们定义成不同的类型。例如HDC就是 HDC_*类型,HWND就是HWND_*类型。这样能避免参数类型错误。
2.calling convention 函数调用约定
这些现象通常是出现在C和C++的代码混合使用的情况下或在C++程序中使用第三方的库的情况下(不是用C++语言开发的),其实这都是函数调用约定(Calling Convention)和函数名修饰(Decorated Name)规则惹的祸。函数调用方式决定了函数参数入栈的顺序,是由调用者函数还是被调用函数负责清除栈中的参数等问题,而函数名修饰规则决定了编译器使用何种名字修饰方式来区分不同的函数,如果函数之间的调用约定不匹配或者名字修饰不匹配就会产生以上的问题。
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_ASTERISK);
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.lpszClassName = szClassName;
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
3.窗口函数WNDPROC。
窗口类初始化中有一个(WNDPROC)WndProc窗口函数。窗口函数就是窗口对各种消息的处理函数(鼠标点击消息,键盘消息)。
我们使用switch case 结构来处理消息。
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
//The PAINTSTRUCT structure contains information for an application.
//This information can be used to paint the client area of a window owned by that application.
PAINTSTRUCT ps;
//The RECT structure defines the coordinates of the upper-left and lower-right corners of a rectangle.
RECT rect;
switch(msg)
{
case WM_CREATE:
PlaySound (L"hello.wav", NULL, SND_FILENAME | SND_ASYNC) ;
break;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps);
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
4.创建窗口
HWND hWnd; //创建窗口返回的句柄。如果不是NULL则创建成功。
hWnd = CreateWindow(szClassName, L"HelloWorld", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 650, 400, NULL, NULL, hInstance, NULL);
5.显示窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
6.消息循环
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
什么是消息循环,它如何工作?
1.消息循环调用while循环中的GetMessage()函数,GetMessage()函数在消息队列中寻找消息。如果没有消息,程序就一直“停”在while循环中。
2.当一个消息进入消息队列时,比如你点击鼠标触发了一个消息。GetMessage()函数返回一个大于0的值,表示这个消息正在被处理,并给msg结构体赋值。
(WM_QUIT消息 GetMessage()函数返回0,如果产生错误 GetMessage()函数负值)。
3.我们获得msg这个消息结构体,传递给TranslateMessage()函数,TranslateMessage()函数将虚拟的鼠标,键盘消息转化成WM_开头的字符串消息。
4.我们将字符串消息传递给DispatchMessage()函数。DispatchMessage()函数将会查找是那个窗口产生的消息,并且调用该窗口的窗口函数来处理。
我们将传递窗口的句柄,msg,wParam,lParam给窗口函数。
5.在窗口处理函数中,将检查消息(该消息是WM_那种消息?),并在特定的case中处理。如果没有这个消息的分类,则在DefWindowProc()函数中,默认处理。
6.一旦处理完消息,窗口处理函数返回,DispatchMessage()函数返回。程序又去消息队列中寻找下一个消息(返回最初始的状态)。
更多win32学习: http://www.winprog.org/tutorial/
完整代码: https://github.com/Superxy/Win32/blob/master/SimpleWindow/SimpleWindow/SimpleWindow.cpp