《Win32 Api编程指南.pdf》由会员分享,可在线阅读,更多相关《Win32 Api编程指南.pdf(37页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Win32 APIWin32 API 编程指南编程指南(1 1 )起步起步 本书的内容本书的内容 本书将会展示给您使用 Win32 API 来开发 windows 程序的基本方法。开发语言采用 C 语言,大多数 C+编译器也都能够正常编译 C 语言的程序。事实上,书中的绝大部分内容对于任何调用 API 的语言都是有效的,像JAVA,汇编语言,以及 Visual Basic。但我不会用这些语言来编写范例代码,先前也有一些人尝试用其它的语言来实现本书中的例子,很可惜,没有几个人成功。本书不会教您什么是 C 语言,也不会教您如何使用特定的编译器(Borland C+,Visual C+,LCC-Wi
2、n32,等等)。最后,我会在附录中提供一些我个人的使用编译器的经验。如果您不知道宏或 typedef 是什么,不知道 switch 语句是如何工作的,请现在就放下本书,先找一本介绍 C 语言的书读一读。重要的说明重要的说明 在全书中,通常我会指出一些非常重要的东西,这些都是必读的。因为它们常常把人弄得糊里糊涂,如果您不读这些东西,很有可能也会身陷其中。首先:范例压缩包里的源代码是必读的!范例压缩包里的源代码是必读的!我没有在书中包含全部的代码,而只包含了那些与我们当前讨论的内容有关的代码片断。如果您想详细了解代码在程序中的体现,就必须去参看压缩包内提供的源代码。其次:通读所有的内容!通读所有的
3、内容!如果您在书中的某个部分有什么疑问,请耐心的读下去,也许不久,在后面的部分中就会找到答案。如果您非想要立刻就弄清楚这个问题,至少也要保证,在通过 IRC 或者 Email 来寻求答案之前,你已经浏览或者搜索了其余的部分了。另外,请记住,题目 A 中涉及的问题,有可能在 B 或者 C 甚至 L 中才会做出解答,所以还是先泛读一遍比较好。好了,以上就是我想说的全部注意事项了,接下来让我们看一些实际的例子吧。最简单的最简单的 Win32Win32 程序程序 即使您完全是个初学者,至少也要保证能够正确编译一个最基本的 windows 程序。将下面的代码敲入编辑器,如果一切正常,您就能创建一个最基本
4、的程序了。切记,要将它作为一个 C 源文件来编译,而不是 C+源文件。虽然这可能没什么关系,但是,既然我们的代码完全是用 C 写的,就保证它从一开始的时候就朝着一个有益的方向前进吧。本书中,绝大多数情况下,都要求将代码保存为 a.c 而不是 a.cpp。如果您觉得这个名字很不爽,那就将它保存为 test.c 吧。#include int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)MessageBox(NULL,Goodbye,cruel world!,Note,
5、MB_OK);return 0;如果程序报错,首先请看一看您是否理解错误的提示信息,在您的编译器的帮助或者联机文档中搜索一下这个错误的详细描述。有一点需特别注意,确保已将您的程序指明为确保已将您的程序指明为 Win32 GUIWin32 GUI 程序,而不是控制台程序。程序,而不是控制台程序。很抱歉,对于排错的问题,我也没什么好办法。因为根据编译器的不同,错误提示的信息也不同(而且人与人也不同)。程序编译时也可能会有一些警告,例如没有使用 WinMain()函数中的参数,这是正常现象,不用理会。至此,我们已经建立了一个能正常运行的程序,接下来让我们来分析一下这段代码.int WINAPI Wi
6、nMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)WinMain()函数等价于 DOS 或者 UNIX 编程中的 main()函数。这是我们程序执行的入口点。参数的含义如下:HINSTANCE hInstance 程序执行模块的句柄。(内存中的.exe 文件)。HINSTANCE hPrevInstance 在 Win32 程序中总为 NULL。LPSTR lpCmdLine 一个字符串的命令行参数。不包括程序名。int nCmdShow 一个可能传递给 ShowWindow()函数的整
7、数。稍后我会解释。hInstance 主要用来装载资源和执行一些基于其它模块的功能。模块是指装载到程序中的 exe 或者 dll 文件。在本书的大部分(不是全部)内容中,它们都是指 exe 文件。hPrevInstance 在过去的 Win16 程序中,用来指向一个已经运行了的程序的实例。现在已经不再使用了,在 Win32 中,您完全可以忽略这个参数。调用方式调用方式 WINAPI 指明了调用的方式,它被定义为_stdcall。即使您不明白,也不用担心。这并不影响我们的学习。记住在这里它是必需的就行了。Win32Win32 数据类型数据类型 您可能发现了,许多关键字在 windows 下面都被
8、重新定义了,例如 UINT 是 unsigned int,LPSTR 是 char*等等。至于用哪种写法完全取决于您自己。如果您觉得用 char*比用 LPSTR 更爽,您尽可以自由的使用。只要确定您所使用的数据类型能够正确的匹配重新定义的数据类型就行了。其实只要记住几个要点也就很容易理解了。LP 前缀代表指向长整形的指针(long pointer)。在 Win32中,long 是一种古老的类型了,这里不用细说。如果您不知道指针是什么,您有两种选择:1)去找本 C 的书读一读。2)继续读下去,可能会弄得一团糟。我强烈建议您选择第一种,但还是有很多人坚持选择第二种(我已经给了您建议哦:)别怪我没
9、提醒您!)接下来是跟在 LP 后面的一个表示常量的字符 C,LPCSTR 表示一个指向字符串常量的长指针,它指向的内容是不可修改的。LPSTR 是指向字符串的长指针,没有 const 关键字,它指向的内容是可以修改的。Win32 APIWin32 API 编程指南编程指南(2 2 )一个简单的窗口一个简单的窗口 范例:simple_window 许多人到 IRC 里询问,“如何创建一个窗口?”.其实,这并不是一件非常容易的小事。但一旦您明白了我们正在学的东西,也就不那么困难了。为了创建一个能够正常显示的窗口,确实需要做很多事情;这不是在聊天室里三言两语能说得清楚的。我总是倾向于先实际的做点什么
10、,然后再进行分析讨论。.先给出一段创建简单窗口的代码,稍后再对其进行说明。#include const char g_szClassName=myWindowClass;/Step 4:the Window Procedure/步骤 4:窗口过程函数 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:re
11、turn DefWindowProc(hwnd,msg,wParam,lParam);return 0;int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)WNDCLASSEX wc;HWND hwnd;MSG Msg;/Step 1:Registering the Window Class /步骤 1:注册窗口类 wc.cbSize =sizeof(WNDCLASSEX);wc.style =0;wc.lpfnWndProc =WndProc;wc.cbClsE
12、xtra =0;wc.cbWndExtra =0;wc.hInstance =hInstance;wc.hIcon =LoadIcon(NULL,IDI_APPLICATION);wc.hCursor =LoadCursor(NULL,IDC_ARROW);wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);wc.lpszMenuName =NULL;wc.lpszClassName=g_szClassName;wc.hIconSm =LoadIcon(NULL,IDI_APPLICATION);if(!RegisterClassEx(&wc)MessageBo
13、x(NULL,Window Registration Failed!,Error!,MB_ICONEXCLAMATION|MB_OK);return 0;/Step 2:Creating the Window /步骤 2:创建窗口 hwnd=CreateWindowEx(WS_EX_CLIENTEDGE,g_szClassName,The title of my window,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,240,120,NULL,NULL,hInstance,NULL);if(hwnd=NULL)MessageBox(NULL
14、,Window Creation Failed!,Error!,MB_ICONEXCLAMATION|MB_OK);return 0;ShowWindow(hwnd,nCmdShow);UpdateWindow(hwnd);/Step 3:The Message Loop /步骤 3:消息循环 while(GetMessage(&Msg,NULL,0,0)0)TranslateMessage(&Msg);DispatchMessage(&Msg);return Msg.wParam;这段代码能够创建一个真正具有窗口功能的最简单的 windows 程序,仅仅 70 左右行代码。如果第一个范例能够
15、正常编译通过,这个也应该没有什么问题。步骤步骤 1:1:注册窗口类注册窗口类 窗口类中保存着窗口类型的相关信息,包括控制窗口行为的窗口过程函数、窗口最大化和最小化时的图标、窗口的背景颜色等等。利用这种方法,您就能够基于创建的这个窗口类,创建多个窗口,而不需要在创建每一个窗口的时候都重新设置这些属性。我们设置的大多数的属性的值都可以在窗口创建之前进行更改。窗口类和 C+中的类没有任何关系。const char g_szClassName=myWindowClass;上面的数组中存储着我们要创建的窗口类的名字,我们将用它向系统注册我们的窗口类。WNDCLASSEX wc;wc.cbSize =si
16、zeof(WNDCLASSEX);wc.style =0;wc.lpfnWndProc =WndProc;wc.cbClsExtra =0;wc.cbWndExtra =0;wc.hInstance =hInstance;wc.hIcon =LoadIcon(NULL,IDI_APPLICATION);wc.hCursor =LoadCursor(NULL,IDC_ARROW);wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);wc.lpszMenuName =NULL;wc.lpszClassName=g_szClassName;wc.hIconSm =Lo
17、adIcon(NULL,IDI_APPLICATION);if(!RegisterClassEx(&wc)MessageBox(NULL,Window Registration Failed!,Error!,MB_ICONEXCLAMATION|MB_OK);return 0;这是在 WinMain()函数中注册窗口类的代码。我们为 WNDCLASSEX 结构的各个成员赋值然后调用RegisterClassEx()函数来注册这个窗口类。结构的各个成员的含义如下:cbSize 结构的大小。style 窗口类的风格(CS_*),不要同窗口风格(WS_*)弄混了,通常置 0。lpfnWndProc
18、指向这个窗口类对应的窗口过程函数的指针。cbClsExtra 为这个类在内存中保留的扩展空间,通常置 0。cbWndExtra 为这个类创建的窗口在内存中保留的扩展空间,通常置 0。hInstance 应用程序实例化句柄(WinMain()函数中的第一个参数)。hIcon 当用户按下 Alt+Tab 键时显示的程序的大图标(通常为 32*32)。HCursor 在窗口上显示的指针。hbrBackground 设置窗口的背景色。lpszMenuName 窗口类使用的菜单资源的名字。lpszClassName 窗口类的名字。hIconSm 程序最小化到任务栏和程序窗口左上角显示的小图标(通常为 1
19、6*16)。如果您对前面所讲的这些不是很理解,也不用担心,后面不久还会详细解释的。另外还有一点要提醒您,不用刻意的把每个成员都背下来,我很少见有人能完全记下这个结构中的每一个成员、或者函数的每一个参数。不要在这上面浪费精力,时间很宝贵。如果您知道要调用哪个函数,只需要花几秒钟去帮助文档里面搜一下那个函数的详细说明就行了。如果您还没有帮助文档,赶快弄一份。没有这个您就落伍了。通过查阅帮助文档,久而久之,那些常用函数的参数基本上也就都记住了。接着,我们调用了 RegisterClassEx()函数并且检验调用是否成功,如说调用失败了,会弹出一个对话框提示出错,然后结束程序。步骤步骤 2:2:创建窗
20、口创建窗口 一旦窗口类注册成功,我们就能够通过它来创建窗口了。您可能迫不及待的想知道 CreateWindowEx()函数的参数了(当接触到一个新的 API 函数的时候,您可能总是这样),接下来让我简要地介绍一下。HWND hwnd;hwnd=CreateWindowEx(WS_EX_CLIENTEDGE,g_szClassName,The title of my window,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,240,120,NULL,NULL,hInstance,NULL);第一个参数(WS_EX_CLIENTEDGE)是扩展的
21、 windows 窗口风格。在这个例子中,我创建了一个内部完全是客户区的窗口。如果您想比较一下不同的值,效果有什么不同,可以将它设置为 0,或者将它设置为其它的值。接下来是窗口类名(g_szClassName),这个参数通知系统创建一个哪种类型的窗口。既然我们想要创建一个基于刚才注册的窗口类的窗口,我们就要将那个窗口类名作为参数。然后,指定窗口标题上显示的内容(就是在窗口左上角显示的内容,也称为窗口标题)。WS_OVERLAPPEDWINDOW 这个参数是要创建的窗口的风格。关于这个参数有很多不同的值,您可以自己去查阅。后面我们也会涉及到。接下来的四个参数(CW_USEDEFAULT,CW_U
22、SEDEFAULT,320,240)分别是窗口左上角的 x 坐标、y 坐标、窗口宽度和高度。我将 x 和 y 设置为 CW_USEDEFAULT,让 windows 来设定窗口在屏幕上显示的位置。需要注意的是,屏幕的 x 坐标是自左向右由 0 开始递增的,y 坐标是从上到下递增的。递增的单位是像素,是屏幕所能显示的最基本单位。接下来的参数(NULL,NULL,g_hInst,NULL)分别是,窗口的父窗口句柄,菜单句柄,程序实例化句柄和指向窗口数据的指针。在 Windows 中,窗口是具有继承性的。您可能看到有的窗口上有一个按钮,这个按钮就是窗口的子窗口,而窗口就是按钮的父窗口。在这个例子中,
23、父窗口的句柄为 NULL,因为我们这个窗口并没有父窗口,这个窗口就是我们的主窗口或称为最顶级窗口。到目前为止,我们还没有创建菜单,所以菜单窗口句柄也为空。程序实例化句柄是由 WinMain()函数的第一个参数传递过来的。窗口数据的指针(我几乎从来没有用过)传递一些窗口创建时候需要的附加数据。如果您想知道神秘的 NULL 是什么,呵呵,它其实只是被简单的定义为 0。在 C 中,因为经常被用于指针,它的定义为(void*)0)。所以,当你把它用在整形的位置的时候,很可能编译器会有警告信息,当然,这和您的编译器以及警告的设置有关。您可以忽略这些警告或者用 0 来代替 NULL。有时候,我们实在不知道
24、该死的 bug 是怎么出现的,其中很大的可能性是没有测试函数的返回值。即使是一个有经验的老手,调用 CreateWindow()函数也经常会失败。调用的时候,确实有许多错误很容易就犯了。这种情况直到我们学会如何快速的找出错误,或者至少能够推断哪里可能有问题,并且总是检验函数的返回值的时候,才能有所改善。if(hwnd=NULL)MessageBox(NULL,Window Creation Failed!,Error!,MB_ICONEXCLAMATION|MB_OK);return 0;当我们创建了窗口并且确定返回的指针有效的时候,就可以显示这个窗口了。传递 WinMain()函数的最后一个
25、参数,然后更新窗口,使窗口重画到显示器上。ShowWindow(hwnd,nCmdShow);UpdateWindow(hwnd);nCmdShow 参数是任意的。您也可以一直简单的传递一个 SW_SHOWNORMAL。通过这个参数,可以指定程序启动的方式,任何人运行的时候都一样。启动的时候是否可见、最大化、最小化等等。您可以在 Windows 的快捷方式里面找到这一项,这个参数就是指明程序如何启动的。步骤步骤 3:3:消息循环消息循环 这是整个程序的核心部分,通过这里的控制合理的安排程序的执行。while(GetMessage(&Msg,NULL,0,0)0)TranslateMessage
26、(&Msg)DispatchMessage(&Msg);return Msg.wParam;GetMessage()函数从系统消息队列中取得消息。无论何时,用户移动鼠标,敲击键盘,点击程序的菜单或者其余的一些操作,操作系统都会产生消息并且将它们发送到系统消息队列中。调用 GetMessage()函数,能够将属于本窗口的消息取回到本程序消息队列处理,然后删除这条消息。如果当前程序没有消息,GetMessage()函数就会被阻塞。如果您对这一点不了解,可以这样认为,被阻塞的时候,这个函数会一直等待,直到有一个消息了,才会返回一个值。TranslateMessage()函数是当接收到键盘消息时,例如
27、 WM_CHAR和 WM_KEYDOWN 消息一起传送过来的时候,做一些附加的处理。最后 DispatchMessage()函数将消息发送给窗口处理。(译者注:实际上是发送给窗口的窗口过程函数处理)这个窗口也许是我们的主窗口,也许是别的窗口,或者仅仅是一个操作,很多情况下,窗口的创建是不显示在屏幕上的,可能由系统隐式的创建或者由另一个程序创建,而这些都不需要我们操心,因为我们通信的途径就是通过发送和接受消息,其余的事情操作系统会为我们打理。步骤步骤 4:4:窗口过程窗口过程 如果说消息循环是程序的心脏,那么窗口过程就是程序的大脑了。所有发送给程序的消息都是在这里被处理的。LRESULT CAL
28、LBACK 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;处理每一个消息都会调用窗口过程函数。HWND 参数是消息所属的窗口的句柄。当我们的程序有基于同一个窗口类的两个或多个窗口的时候,这个参数就特别重要了。这些窗口共用窗口过程
29、函数(WndProc()。区别这些窗口的方法就是靠 hwnd 参数的不同。举个例子,当我们接收到一个 WM_CLOSE 消息的时候,通过不同的hwnd 参数,销毁相对应的那个窗口,而其它的窗口不会受到任何影响。当用户点击关闭按钮或者 Alt+F4 键的时候,都会触发 WM_CLOSE 消息,Windows 会默认的关闭这个窗口。但我还是习惯自己来处理这个消息,因为有些事情可能需要在关闭窗口之前解决,像清除某些选项,或者询问用户是否要保存文件等等。当我们调用了 DestroyWindow()函数,系统就会发送一个 WM_DESTROY 消息给窗口,然后销毁窗口的子窗口,释放掉窗口占用的资源。既然
30、我们的程序只有一个窗口,就可以让程序立即结束,所以我们继续调用PostQuitMessage()函数。这个函数会将 WM_QUIT 消息发送到消息循环中。我们永远也不会处理这个消息,因为它使 GetMessage()函数的返回值为 FALSE,您将会在我们的消息循环代码中了解到这一点。当接收到这条消息以后,窗口过程就停止处理任何消息了。WM_QUIT 消息的 wParam 成员包含着传递给 PostQuitMessage()函数的值。只有当您的程序是要启动别的程序的时候,这个函数的返回值才有用。(译者注:实际上这个函数的返回值表示程序是否是正常退出)步骤步骤 5:5:没有步骤没有步骤 5 5
31、嘿!就这样吧!如果我还有什么没解释清楚的,就先放着吧。希望随着我们不断地深入学习更多的程序,疑问会逐渐的解开。Win32 APIWin32 API 编程指南编程指南(3 3 )处理消息处理消息 范例:window_click 我们已经能够创建一个窗口了,可是这个窗口除了默认处理函数 DefWindowProc()能执行的一些诸如改变窗口大小、最大化等操作之外,再没什么别的操作了,仔细想想,好像没那么振奋人心哦。在下面的这一部分,我会讲解,如何修改我们前面的程序,使它能够执行一些新的功能。鉴于我只是阐述如何处理消息,所以并不需要看全部的代码,您就能明白我的意思。这也是我们的目标,好了,集中精力:
32、P 对于初学者,确保前面的程序能够正常的编译、运行就足够了。然后,在前面程序的原有代码的基础上直接修改或者将程序的代码拷贝到一个新的工程中再修改就可以了。我们想要添加到程序中的功能是,当用户在窗口内单击(译者注:这里的单击是指鼠标左键单击,以下均如此)鼠标以后,程序会以对话框的形式显示出程序的名字。听上去好像并不那么令人激动啊,但这是处理消息的最基本的内容。让我们先来看一下窗口过程中的代码。LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)switch(msg)case WM_CLOSE:Destro
33、yWindow(hwnd);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,msg,wParam,lParam);return 0;若要处理鼠标单击产生的消息,我们需要在窗口过程中添加 WM_LBUTTONDOWN 消息(或者WM_RBUTTONDOWN,WM_MBUTTONDOWN,它们分别对应右键和中键)。如果我或者其他人提到处理消息,就是指将消息处理代码添加到 WndProc()函数中。LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,W
34、PARAM wParam,LPARAM lParam)switch(msg)case WM_LBUTTONDOWN:/-/-we just added this stuff break;/-这里就是我们添加的 case WM_CLOSE:DestroyWindow(hwnd);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,msg,wParam,lParam);return 0;在窗口过程中,消息处理的顺序是不分先后的,只要确保在每条消息处理结束的时候都添加一个 break语句就可
35、以了。很明显,我们在 switch()中添加了一个 case 语句。现在,我们想要程序接收到这个消息的时候执行我们想要的操作。我先把要添加到原程序中的代码写出来(这段代码会显示出程序的名字),然后把它添加到我们先前的程序中。稍后,我会告诉您如何将它添加到您自己的程序中。这对我、对您都是一件好事。因为我不用再敲全部的代码了,而您也可以将它应用于任何程序,不必局限于我举例的这个。如果您还不清楚怎么做,请仔细阅读压缩包内的这部分内容的源代码。GetModuleFileName(hInstance,szFileName,MAX_PATH);MessageBox(hwnd,szFileName,This
36、 program is:,MB_OK|MB_ICONINFORMATION);这段代码并不是独立存在的,不能够随意的添加到程序的任何部分。我们想要的是只有当用户单击鼠标左键的时候它才被执行,所以要这样将它添加到基本程序中。LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)switch(msg)case WM_LBUTTONDOWN:/BEGIN NEW CODE /新代码开始 char szFileNameMAX_PATH;HINSTANCE hInstance=GetModuleHandle(NUL
37、L);GetModuleFileName(hInstance,szFileName,MAX_PATH);MessageBox(hwnd,szFileName,This program is:,MB_OK|MB_ICONINFORMATION);/END NEW CODE /新代码结束 break;case WM_CLOSE:DestroyWindow(hwnd);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,msg,wParam,lParam);return 0;请注意内的部分。
38、当在 switch()语句中声明变量的时候,必须要加上。虽然这是 C 语言的基础知识,但我认为还是有必要提一下。代码添加结束,重新编译。如果一切正常,在窗口内单击一下鼠标,就会弹出一个对话框显示程序的名字。您可能注意到我们添加了两个变量,hInstance 和 szFileName。查看 GetModuleFileName()函数会发现,它的第一个参数是个 HINSTANCE 型变量,一个可执行模块的指针。(指向我们的程序,.exe 文件)。为什么我们要得到它呢?GetModuleHandle()函数就是答案。GetModuleHandle()函数的说明中指出,传递一个 NULL参数给它,它就
39、会返回一个指向调用它的过程的句柄(实际上就是返回这个程序的句柄),这正是我们所期望的,将这些语句结合起来,我们可以这样声明:HINSTANCE hInstance=GetModuleHandle(NULL);再看第二个参数,这是我们都熟悉的,这是一个指向 保存程序的路径和名字的字符串的指针。数据类型是LPTSTR(或者 LPSTR)。既然 LPSTR 和 char*是等价的,我们也可以这样声明一个数组:char szFileNameMAX_PATH;MAX_PATH 是由定义的一个宏,很方便。它定义了在 Win32 系统下存储一个文件名所需要的最大的缓冲区。我们将 MAX_PATH 传递给 G
40、etModuleFileName()函数,这样它就能知道缓冲区的大小了。调用 GetModuleFileName()函数以后,szFileName 中就存储了包含.exe 文件的名字的字符串,字符串以NULL 结束,我们简单的将它传递给 MessageBox()函数然后显示给用户。好了,添加完了这段代码,现在就可以编译了。如果一切正常,在窗口内单击鼠标,就会弹出一个对话框,包含.exe 程序的名字。如果不能正常运行,下面给出这个程序的全部的源代码,与您的代码对比一下,看看有什么不同。#include const char g_szClassName=myWindowClass;LRESULT
41、CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)switch(msg)case WM_LBUTTONDOWN:char szFileNameMAX_PATH;HINSTANCE hInstance=GetModuleHandle(NULL);GetModuleFileName(hInstance,szFileName,MAX_PATH);MessageBox(hwnd,szFileName,This program is:,MB_OK|MB_ICONINFORMATION);break;case WM_CLOSE:
42、DestroyWindow(hwnd);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,msg,wParam,lParam);return 0;int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)WNDCLASSEX wc;HWND hwnd;MSG Msg;wc.cbSize =sizeof(WNDCLASSEX);wc.style =0;wc.lp
43、fnWndProc=WndProc;wc.cbClsExtra =0;wc.cbWndExtra =0;wc.hInstance =hInstance;wc.hIcon =LoadIcon(NULL,IDI_APPLICATION);wc.hCursor =LoadCursor(NULL,IDC_ARROW);wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);wc.lpszMenuName=NULL;wc.lpszClassName=g_szClassName;wc.hIconSm =LoadIcon(NULL,IDI_APPLICATION);if(!Reg
44、isterClassEx(&wc)MessageBox(NULL,Window Registration Failed!,Error!,MB_ICONEXCLAMATION|MB_OK);return 0;hwnd=CreateWindowEx(WS_EX_CLIENTEDGE,g_szClassName,The title of my window,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,240,120,NULL,NULL,hInstance,NULL);if(hwnd=NULL)MessageBox(NULL,Window Creat
45、ion Failed!,Error!,MB_ICONEXCLAMATION|MB_OK);return 0;ShowWindow(hwnd,nCmdShow);UpdateWindow(hwnd);while(GetMessage(&Msg,NULL,0,0)0)TranslateMessage(&Msg);DispatchMessage(&Msg);return Msg.wParam;Win32 APIWin32 API 编程指南编程指南(4 4 )理解消息循环理解消息循环 理解消息循环和 windows 程序的消息收发体系不仅是编写小程序,也是其它应用程序开发的基础。前面我们已经学习了一些
46、消息处理的方法,接下来要深入到整个消息处理过程中去,如果不弄清楚这些,以后就可能会越学越糊涂。消息是什么?消息是什么?消息的本质就是一个整数。如果您查看过头文件(这是一种很通用、很好的研究 API 运行原理的方法),可能会发现类似这样的定义:#define WM_INITDIALOG 0 x0110#define WM_COMMAND 0 x0111#define WM_LBUTTONDOWN 0 x0201 等等。在 Windows 系统中,一切都是靠消息紧密地联系在一起的,至少在基础层是这样。如果您想让一个窗口或者一个控件(实际上它也是一个特殊的窗口)执行一定的功能,就要给它发送一个消息。
47、如果另一个窗口想要您的程序执行一定的操作,它也会给您发送一个消息。如果某一事件被触发,例如用户的键盘输入,鼠标移动,点击按钮等,系统就会将这个事件对应的消息发送给相应的窗口。如果这些消息中有属于您的窗口的,您就得接收并处理这些消息。每个 Windows 消息结构都有两个成员,wParam 和 lParam。最初的 wParam 是 16bit,lParam 是 32bit,但是在 Win32 中,它们都是 32bit。并不是所有的消息都会用到这两个成员,每个消息应用它们的方法也不完全相同。例如 WM_CLOSE 消息,这两个成员都不用,您完全可以忽略它们。而对 WM_COMMAND 消息,这两
48、个成员都使用,wParam 包含两个值,HIWORD(wParam)是通知码(如果可用),LOWORD(wParam)是控制码或者是发送消息的菜单的 ID 值。lParam 是发送消息的控件的 HWND(窗口句柄),如果为空,说明消息不是来自于控件。HIWORD()和 LOWORD()是 windows 定义的两个宏,用来分离出 32bit 数据的高位字(0 xFFFFFFFF0000)和低位字(0 xFFFF00000000)。在 Win32 系统中,字(WORD)是 16bit 的数据,双字(DWORD)是 32bit。您可以调用 PostMessag()函数或者 SendMessage(
49、)函数发送一个消息。PostMessage()函数将消息放入消息队列后立即返回,也就是说,调用 PostMessage()函数返回后,发送的消息有可能已经被处理了,也有可能尚未被处理。而 SendMessage()函数直接将消息发送给窗口,并且直到窗口处理完这个消息以后,函数才会返回。若想要关闭一个窗口,我们可以调用 PostMessage()函数,发送一个 WM_CLOSE 消息,PostMessage(hwnd,WM_CLOSE,0,0);这样做同点击右上角的关闭按钮效果相同。注意此时 wParam 和 lParam都是 0。前面已经提到了,WM_CLOSE 不用这两个成员。对话框对话框
50、如果您要使用对话框,就要通过向它发送消息,同它通信。您可以先利用对话框的 ID,调用 GetDlgItem()函数得到这个对话框的句柄,然后调用 SendMessage()函数发送消息;或者直接调用 SendDlgItemMessage()函数,它将这些操作合并起来执行。您传递给它一个窗口的句柄和一个子窗口的 ID,它就会返回子窗口的句柄,然后发送消息。SendDlgItemMessage()函数以及和它相似的 GetDlgItemText()函数不仅仅可以应用于对话框,还可以应用于任何的 windows 程序。什么是消息循环?什么是消息循环?while(GetMessage(&Msg,NUL