Win32编程API 基础篇 -- 3.消息处理 根据英文教程翻译
消息处理
例子:窗口点击
好的,现在我们已经得到一个窗口了,但我们什么也做不了除了DefWindowProc()允许窗口大小被调整,最大最小化等。。。这不是很激动人心啊
在接下来的一小节中我将向你展示如何修改现有的程序,让它做一些新的事情,这样我就可以告诉你,“处理消息然后这样做。。。”,我会明白我的意思是什么并且在不需要看完完整的栗子的基础上完成它。所以不管怎样,集中注意力
OK,对初学者来说拿最近的一个窗口程序的代码,保证编译通过并且正常运行,然后你就可以在这份代码的基础上进行一些小修改,或者把代码复制到新的一个项目中进行修改。
我们要添加一项功能,只要用户点击窗口,就显示我们程序的名字。这不是很激动人心,基本上是通过获取处理消息的句柄实现的,让我们看看WndProc()中有什么
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
如果我们想处理鼠标点击事件,我们需要添加一个WM_LBUTTONDOWN的处理器(或者 WM_RBUTTONDOWN, WM_MBUTTONDOWN分别对应鼠标右击和中击)
如果我或其他人提到处理一个消息,意思是在窗口类的WndProc()中添加相应的处理消息处理程序,如下所示:
1 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2 {
3 switch(msg)
4 {
5 case WM_LBUTTONDOWN: // <-
6 // <- 我们只是添加了这个东西
7 break; // <-
8 case WM_CLOSE:
9 DestroyWindow(hwnd);
10 break;
11 case WM_DESTROY:
12 PostQuitMessage(0);
13 break;
14 default:
15 return DefWindowProc(hwnd, msg, wParam, lParam);
16 }
17 return 0;
18 }
你处理消息的顺序是很重要的,确保你有在每个case结尾加上break;,你可以看到我们在switch()中添加了另一个case,现在我们希望当代码跑到这个的时候有些事情发生。
首先我会展示我们要添加的代码(向用户显示我们程序的文件名),然后我会把这些代码集合到我们的程序中。接下来我可能只会向你展示这些代码并且让你自己集合到程序中去,这对我来说是好的因为不需要说太多废话,对你来说也是好的,这样你就可以把代码嵌入到任何你想加入的程序中去而不仅仅只是我呈现给大家的这个栗子。如果你不确定怎么做,看看ZIP样例代码文件中关于这一小节的内容。
1 GetModuleFileName(hInstance, szFileName, MAX_PATH);
2 MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
现在这段代码不能代表它本身,它不能被插入我们旧代码中的任何位置。我们明确地想让它在用户点击窗口时运行,所以这就是我想整合进我们的骨骼程序中的一点代码。
1 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2 {
3 switch(msg)
4 {
5 case WM_LBUTTONDOWN:
6 // BEGIN NEW CODE
7 {
8 char szFileName[MAX_PATH];
9 HINSTANCE hInstance = GetModuleHandle(NULL);
10
11 GetModuleFileName(hInstance, szFileName, MAX_PATH);
12 MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
13 }
14 // END NEW CODE
15 break;
16 case WM_CLOSE:
17 DestroyWindow(hwnd);
18 break;
19 case WM_DESTROY:
20 PostQuitMessage(0);
21 break;
22 default:
23 return DefWindowProc(hwnd, msg, wParam, lParam);
24 }
25 return 0;
26 }
注意花括号,当我们在switch()中声明一个新的变量的时候就需要加上花括号,这应该是C语言的基础知识,但我想我应该指出你正在做的所有事情。
所以你添加那段代码,然后编译它,如果它正常运行,点击窗口,然后你会看到一个现实你可执行文件的消息盒。
你会注意到在这里添加了两个变量,hInstance和szFileName。查阅一下GetModuleFileName()函数,你会发现第一个参数是一个HINSTANCE跟可执行模块相关(我们的程序,.exe可执行文件),我们从哪里得到这个参数呢?答案是通过GetModuleHandle(),GetModuleHandle()的参考文献中指明当传入一个NULL时,会返回“创建调用进程的文件的句柄”,这恰好是我们所需要的,刚才提到的那个HINSTANCE。把这些信息都放在一起我们可以得到以下声明:
1 HINSTANCE hInstance = GetModuleHandle(NULL);
现在轮到二次个参数,再次转向我们可靠的参考手册,我们可以看到它是“一个指向接收指定的模块的路径和文件的缓冲区的指针”,数据类型是LPTSTR(或者LPSTR如果你的参考手册是老的)。因为LPSTR跟char*是相同的我们可以像下面那样声明一个字符数组:
1 char szFileName[MAX_PATH];
MAX_PATH是一个通过<windows.h>头文件中定义好的一个宏,这个宏用来定义存储一个win32下的文件名的缓冲区的最大长度,我们也把MAX_PATH传递给GetModuleFileName()这样它就会知道缓冲区的大小。
在GetModuleFileName()被调用之后,szFileName变量的缓存将会被填充,填充的内容就是我们可执行文件的文件名,我们把这个值传递给MessageBox(),通过这个简单的方式把它显示给用户。
如果你插入代码后不能正常地运行,这里是程序的完整的代码提供参考,跟它进行对比然后看看会你哪些地方犯了错误。
1 #include <windows.h>
2
3 const char g_szClassName[] = "myWindowClass";
4
5 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
6 {
7 switch(msg)
8 {
9 case WM_LBUTTONDOWN:
10 {
11 char szFileName[MAX_PATH];
12 HINSTANCE hInstance = GetModuleHandle(NULL);
13
14 GetModuleFileName(hInstance, szFileName, MAX_PATH);
15 MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
16 }
17 break;
18 case WM_CLOSE:
19 DestroyWindow(hwnd);
20 break;
21 case WM_DESTROY:
22 PostQuitMessage(0);
23 break;
24 default:
25 return DefWindowProc(hwnd, msg, wParam, lParam);
26 }
27 return 0;
28 }
29
30 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
31 LPSTR lpCmdLine, int nCmdShow)
32 {
33 WNDCLASSEX wc;
34 HWND hwnd;
35 MSG Msg;
36
37 wc.cbSize = sizeof(WNDCLASSEX);
38 wc.style = 0;
39 wc.lpfnWndProc = WndProc;
40 wc.cbClsExtra = 0;
41 wc.cbWndExtra = 0;
42 wc.hInstance = hInstance;
43 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
44 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
45 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
46 wc.lpszMenuName = NULL;
47 wc.lpszClassName = g_szClassName;
48 wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
49
50 if(!RegisterClassEx(&wc))
51 {
52 MessageBox(NULL, "Window Registration Failed!", "Error!",
53 MB_ICONEXCLAMATION | MB_OK);
54 return 0;
55 }
56
57 hwnd = CreateWindowEx(
58 WS_EX_CLIENTEDGE,
59 g_szClassName,
60 "The title of my window",
61 WS_OVERLAPPEDWINDOW,
62 CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
63 NULL, NULL, hInstance, NULL);
64
65 if(hwnd == NULL)
66 {
67 MessageBox(NULL, "Window Creation Failed!", "Error!",
68 MB_ICONEXCLAMATION | MB_OK);
69 return 0;
70 }
71
72 ShowWindow(hwnd, nCmdShow);
73 UpdateWindow(hwnd);
74
75 while(GetMessage(&Msg, NULL, 0, 0) > 0)
76 {
77 TranslateMessage(&Msg);
78 DispatchMessage(&Msg);
79 }
80 return Msg.wParam;
81 }
PS.由于本人英文水平所限,只能翻译到这个程度了,有纰漏还望多多指出,附上本篇翻译的英文原版教程地址:http://www.winprog.org/tutorial/window_click.html