目录
2 .NET Winform 程序与传统 Win32 程序的关联
1 Win32 应用程序结构
1.1 Windows 消息循环
Windows 消息循环结构类似下图:
C++ 中 Windows 消息循环代码类似如下:
MSG msg; // 代表一条消息
BOOL bRet;
// 从UI线程消息队列中取出一条消息
while ((bRet = GetMessage(&msg,NULL,0,0)) != 0)
{
if (bRet == -1)
{
// 错误处理代码,通常是直接退出程序
}
else
{
TranslateMessage(&msg); // 转换消息格式
DispatchMessage(&msg); // 分发消息给相应的窗体
}
}
上图显示了 Windows 消息循环的结构,GetMessage 从操作系统中的消息队列(一种数据结构)获取 Windows 消息,(这些消息包括操作系统将用户输入转换而成的、操作系统本身发送给程序的以及其它程序发送给本程序的)。如果获取到的消息不为 WM_QUIT(表示退出含义),那么经过 TranslateMessage 方法进行预处理后,再由 DispatchMessage 将消息投递到指定的窗口过程(WndProc),窗口过程则负责处理消息。
注:GetMessage 方法有三种返回值,当取得 WM_QUIT 消息时,返回 0 值,While 循环退出;当有异常发生时,返回 -1 值;当获取非 WM_QUIT 的其它消息时,返回非 0 非 -1 的值。如果一个程序需要退出,那么 Windows 消息循环所在的线程必须结束,也就是说,While 循环中的 GetMessage 方法必须返回 0 值,由于 WM_QUIT 时 While 循环退出的条件,因此,消息队列中包含有一个 WM_QUIT 消息是程序退出的前提。
C++中 Windows 消息结构体的定义如下:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
如上代码所示,消息中包含有消息接收者的窗口句柄 hwnd,消息类型 message(一个数值,代码中常用 WM_PAINT、WM_QUIT 等代表),以及两个消息参数 wParam 和 IParam,还有发送消息时间 time 以及当前光标在屏幕中的坐标位置。消息投递方法 DispatchMessage 就是根据 hwnd 字段,将消息投递给指定窗口的窗口过程,最终由窗口过程处理这一消息。
1.2 窗口过程
消息处理的关键是 DispatchMessage 函数。这个函数根据取出的消息中所包含的窗体句柄,将这一消息转发给此句柄所对应的窗体对象。
而窗体负责响应消息的函数称为 “窗体过程(Window Procedure)”。
窗口过程用 C++ 代码声明类似如下:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam); // 发送消息的控件或菜单的ID
wmEvent = HIWORD(wParam); // 通知消息
// 分析菜单选择
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
// draw something here with gdi win32 api
break;
case WM_DESTROY:
PostQuitMessage(0); // 给消息队列发送一个 WM_QUIT 消息,下一次消息循环时,GetMessage 从操作系统中的消息队列中取出 WM_QUIT 后,While 消息循环就会结束。
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
注:如果窗口为一个程序的主窗体,那么当这个窗口关闭后,程序应该退出(While 循环结束),又因为当关闭窗体后,系统会有一个 WM_DESTROY 消息发送给窗口过程(注意是直接发送给窗口过程),那我们完全可以在 switch/case 中拦截 WM_DESTROY 消息,然后调用 PostQuitMessage 这个 Win32 API 处理。
在 Windows 中,消息分成了两类:
1、队列消息:需要存入消息队列,然后由消息循环取出来之后才能被窗口过程处理。例如:WM_PAINT、WM_QUIT
2、非队列消息:不需要存入消息队列,也不经过消息循环,它们直接传递给窗口过程,由窗口过程直接处理。例如:WM_DESTROY
注:可以用 PostMessage() 或 SendMessage() 发送消息。PostMessage() 把一个消息放入消息队列后立即返回,也就是当调用 PostMessage(),函数执行完成返回时,很可能消息尚未处理。SendMessage() 直接将消息发送到窗口,直到这个消息处理完成才返回。如果要关闭一个窗口,可以给它发送一个 WM_CLOSE 消息,像PostMessage(hwnd,WM_CLOSE,0,0); 效果跟点击窗口右上角的关闭按钮是一样的。注意这里的 wParam 和 IParam 的值都是0,因为 WM_CLOSE 消息会忽略上述两个参数。
1.3 创建基于 Win32 的单窗体应用程序
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) //NO.1 程序入口方法
{
WNDCLASSEX wcex;
wcex.