友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
  
  
C语言实例教程(PDF格式)-第12部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
常量的详细参考请 自行参阅Win32 SDK中的文档。  
  l 注意:  
  l 尽管Windows 95是一个32位的操作系统,但是,其中也保留了很 
    多16位的特征,比如说,在Windows 95环境下,系统最多只可以 
   有16384个窗口句柄。而在Windows NT下则无此限。然而,事实 
   上,对于一般的桌面个人机系统来说,我们几乎不可能超过这个 
    限制。  
创建窗口完成之后,ShowWindows显示该窗口,第二个参数SW_SHOW表 
示在当前位置以当前大小激活并显示由第一个参数标识的窗口。然 
后,函数UpdateWindows向窗口发送一条WM_PAINT消息,以通知窗口 
更新其客户区。需要注意的是,由UpdateWindows发送的WM_PAINT消 
息将直接发送到窗口过程 (在上面的例子中是WndProc函数),而不是 
发送到进程的消息队列,因此,尽管这时应用程序的主消息循环尚未 
启动,但是窗口过程仍可接收到该WM_PAINT消息并更新其用户区。  
在完成上面的步骤之后,进入应用程序的主消息循环。一般情况下, 
主消息循环具有下面的格式:  
while (GetMessage(&msg;NULL;0;0))  
…………………………………………………………Page 133……………………………………………………………
{  
TranslateMessage(&msg);  
DispatchMessage(&msg);  
}  
主消息循环由对三个API函数的调用和一个while结构组成。其中 
GetMessage从调用线程的消息队列中获取消息,并将消息放到由第一 
个参数指定的消息结构中。如果指定了第二个参数,则GetMessage获 
取属于该参数指定的窗口句柄所标识的窗口的消息,如果该参数为 
NULL,则GetMessage获取属于调用线程及属于该线程的所有窗口的消 
息。最后两个参数指定了GetMessage所获取消息的范围,如果两个参 
数均为0,则GetMessage检索并获取所有可以得到的消息。  
在上面的代码中,变量msg是一个类型为MSG的结构对象,该结构体的 
定义如下:  
typedef struct tagMSG { // msg  
HWND hwnd;  
UINT message;  
WPARAM wParam;  
LPARAM lParam;  
DWORD time;  
POINT pt; } MSG;  
下面解释各成员的含义:  
hwnd: 标识获得该消息的窗口进程的窗口句柄。  
message: 指定消息值。  
wParam: 其含义特定于具体的消息类型。  
lParam: 其含义特定于具体的消息类型。  
time: 指定消息发送时的时间。  
pt: 以屏幕坐标表示的消息发送时的鼠标指针的位置。  
…………………………………………………………Page 134……………………………………………………………
在while循环体中的TranslateMessage函数将虚拟按键消息翻译为字 
符消息,然后将消息发送到调用线程的消息队列,在下一次调用 
GetMessage函数或PeekMessage函数时,该字符消息将被获取。 
TranslateMessage函数将WM_KEYDOWN和WM_KEYUP虚拟按键组合翻译为 
WM_CHAR和WM_DEADCHAR,将WM_SYSKEYDOWN和WM_SYSKEYUP虚拟按键组 
合翻译为WM_SYSCHAR和WM_SYSREADCHAR。需要注意的一点是,仅当相 
应的虚拟按键组合能够被翻译为所对应的ASCII字符时, 
TranslateMessage才发送相应的WM_CHAR消息。  
如果一个字符消息被发送到调用线程的消息队列,则 
TranlateMessage返回非零值,否则返回零值。  
  l 注意:  
  l 与在Windows 95操作系统下不同,在Windows NT下, 
   TranslateMessage对于功能键和光标箭头键也返回一个非零值。  
然后,函数DispatchMessage将属于某一窗口的消息发送该窗口的窗 
口过程。这个窗口由MSG结构中的hwnd成员所标识的。函数的返回值 
为窗口过程的返回值,但是,我们一般不使用这个返回值。这里要注 
意的是,并不一定是所有属于某一个窗口的消息都发送给窗口的窗口 
过程,比如对于WM_TIMER消息,如果其lParam参数不为NULL的话,由 
该参数所指定的函数将被调用,而不是窗口过程。  
如果GetMessage从消息队列中得到一个WM_QUIT消息,则它将返回一 
个假值,从而退出消息循环,WM_QUIT消息的wParam参数指定了由 
PostQuitMessage函数给出的退出码,一般情况下,WinMain函数返回 
同一值。  
下面我们来看一下程序主窗口的窗口过程WndProc。窗口过程名是可 
以由用户自行定义,然后在注册窗口类时在WNDCLASS结构中指定。但 
是,一般来说,程序都把窗口过程命令为WndProc来类似的名称,如 
MainWndProc等,并不是一定要这样做,但是这样明显的有利于阅 
读,因此也是我们推荐的做法。窗口过程具有如下的原型:  
LRESULT WINAPI WndProc(HWND;UINT;WPARAM;LPARAM);  
或  
LRESULT CALLBACK WndProc(HWND;UINT;WPARAM;LPARAM);  
对于编译器而言,两种书写形式都是一样的,它们都等价于  
…………………………………………………………Page 135……………………………………………………………
long __stdcall WndProc(void *;unsigned int;unsigned int;long)  
窗口过程使用了四个参数,在它被调用时(再强调一点,一般情况 
下,窗口过程是由操作系统调用,而不是由应用程序调用的,这就是 
我们为什么将它们称为回调函数的道理),这四个参数对应于所发送 
消息结构的前四个成员。下面给出了一个窗口过程的例子:  
// WndProc 主窗口过程  
LRESULT WINAPI WndProc (HWND hWnd;  
UINT msg;  
WPARAM wParam;  
LPARAM lParam)  
{  
HDC hdc;  
RECT rc;  
HPEN hPen;hPenOld;  
HBRUSH hBrush;hBrushOld;  
switch (msg)  
{  
case WM_PAINT:  
hdc=GetDC(hWnd);  
GetClientRect(hWnd;&rc);  
hPen=CreatePen(PS_SOLID;0;RGB(0;0;0));  
hBrush=CreateHatchBrush(HS_DIAGCROSS;RGB(0;0;0));  
hPenOld=SelectObject(hdc;hPen);  
hBrushOld=SelectObject(hdc;hBrush);  
Ellipse(hdc;rc。left;rc。top;rc。right;rc。bottom);  
SelectObject(hdc;hPenOld);  
SelectObject(hdc;hBrushOld);  
…………………………………………………………Page 136……………………………………………………………
ReleaseDC(hWnd;hdc);  
break;  
case WM_DESTROY:  
PostQuitMessage(0);  
break;  
default:  
break;  
}  
return DefWindowProc(hWnd;msg;wParam;lParam);  
}  
在该窗口过程中,我们处理了最基本两条消息。  
第一条消息是WM_PAINT,当窗口客户区的全部或一部分需要重绘时, 
系统向该窗口发送该消息。在前面的过程中我们已经提到过,在使用 
ShowWindow函数显示窗口之后,通常随即调用函数UpdateWindow,该 
函数直接向窗口过程发送一个WM_PAINT消息,以通知窗口绘制其客户 
区。在该消息的处理函数中,我们先使用GetDC获得窗口的设备句 
柄,关于设备句柄本书后面将要专门涉及,这里我们只需知道它是用 
来调用各种绘图方法的。然后调用GetClientRect获得当前窗口的客 
户区矩形。接着调用CreatePen创建一个黑色画笔,调用 
CreateHatchBrush创建一个45度交叉线的填充画刷,并且 
SelectObject函数将它们选入设备描述表中,原有的画笔和画刷被保 
存到hPenOld和hBrushOld中,以便以后恢复。完成以上步骤之后,调 
用Ellipse函数以当前客户区大小绘制一个椭圆。最后,再一次调用 
SelectObject函数恢复原有的画笔和画刷,并调用ReleaseDC释放设 
备描述表句柄。在这个消息的处理代码中,我们涉及到了一些新的概 
念、数据类型和API函数,然而本章并不着意于讲述这些内容,读者 
也不必深究它们,这些代码只是为了完整该示例程序才使用的。对于 
窗口来说,除了客户区以外的其它内容将由系统进行重绘,这些内容 
包括窗口标题条、边框、菜单条、工具条以及其它控件,如果包含了 
它们的话。这种重绘往往发生在覆盖于窗口上方的其它窗口被移走, 
或者是窗口被移动或改变大小时。因此,对于大多数窗口过程来说, 
WM_PAINT消息是必须处理的。  
另一个对于绝大多数窗口过程都必须处理的消息是WM_DESTROY,当窗 
…………………………………………………………Page 137……………………………………………………………
口被撤消时(比如用户从窗口的系统菜单中选择了 “关闭”,或者单 
击了右边的小叉,对于这些事件,Windows的默认处理是调用 
DestroyWindow函数撤销相应的窗口),将会接收到该消息。由于本程 
序仅在一个窗口,因此在这种情况下应该终止应用程序的执行,因此 
我们调用了PostQuitMessage函数,该函数向线程的消息队列中放入 
一个WM_QUIT消息,传递给PostQuitMessage函数的参数将成为 
WM_QUIT消息的wParam参数,在上面的例子中,该值为0。  
对于其它情况,在上面的示例程序中我们没有必要进行处理, 
Windows专门为此提供了一个默认的窗口过程,称为DefWindowProc, 
我们只需要以WndProc的参数原封不动的调用默认窗口过程 
DefWindowProc,并将其返回值作为WndProc的返回值即可。  
将上面讲述的所有内容综合起来,我们就已经使用Win32   SDK完成了 
一个功能简单,但是结构完整的Win32应用程序了。对于使用Win32  
SDK编写的实用的Win32应用程序,它们的结构与此相比要复杂得多, 
在这些情况下,应用程序也许不仅仅包括一个窗口,而对应的窗口过 
程中的switch结构一般也会是一个异常膨胀的嵌套式switch结构。如 
此庞大的消息处理过程大大增加了程序调试和维护的难度,使用MFC 
则有可能在很多程度上减轻这种负担,这便是MFC为广大程序员所乐 
于接受,以至今天成为实际上的工业标准的原因。但是,不管它如何 
复杂,归根到底,一般情况下,它仍然具有和我们的这个功能简单的 
Win32应用程序一样或类似的结构。为了读者阅读和分析方便,我们 
把这个程序的完整代码给出如下:  
#include   
// 函数原型  
int WINAPI WinMain(HINSTANCE;HINSTANCE;LPSTR;int);  
LRESULT WINAPI WndProc(HWND;UINT;WPARAM;LPARAM);  
// WinMain 函数  
int WINAPI WinMain (HINSTANCE hInstance;  
HINSTANCE hPrevInstance;  
LPSTR lpCmdLine;  
int nCmdShow)  
{  
…………………………………………………………Page 138……………………………………………………………
HWND hWnd; // 主窗口句柄  
MSG msg; // 窗口消息  
WNDCLASS wc; // 窗口类  
if (!hPrevInstance)  
{  
// 填充窗口类信息  
wc。style=CS_HREDRAW|CS_VREDRAW;  
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=GetStockObject(WHITE_BRUSH);  
wc。lpszMenuName=NULL;  
wc。lpszClassName=〃SdkDemo1〃;  
// 注册窗口类  
RegisterClass(&wc);  
}  
// 创建应用程序主窗口  
hWnd=CreateWindow (〃SdkDemo1〃; // 窗口类名  
〃第一个Win32 SDK应用程序〃; // 窗口标题  
WS_OVERLAPPEDWINDOW; // 窗口样式  
CW_USEDEFAULT; // 初始化 x 坐标  
CW_USEDEFAULT; // 初始化 y 坐标  
CW_USEDEFAULT; // 初始化窗口宽度  
…………………………………………………………Page 139……………………………………………………………
CW_USEDEFAULT; // 初始化窗口高度  
NULL; // 父窗口句柄  
NULL; // 窗口菜单句柄  
hInstance; // 程序实例句柄  
NULL); // 创建参数  
// 显示窗口  
ShowWindow(hWnd;SW_SHOW);  
// 更新主窗口客户区  
UpdateWindow(hWnd);  
// 开始消息循环  
while (GetMessage(&msg;NULL;0;0))  
{  
TranslateMessage(&msg);  
DispatchMessage(&msg);  
}  
return msg。wParam;  
}  
// WndProc 主窗口过程  
LRESULT WINAPI WndProc (HWND hWnd;  
UINT msg;  
WPARAM wParam;  
LPARAM lParam)  
{  
HDC hdc;  
RECT rc;  
HPEN hPen;hPenOld;  
…………………………………………………………Page 140……………………………………………………………
HBRUSH hBrush;hBrushOld;  
switch (msg)  
{  
case WM_PAINT:  
hdc=GetDC(hWnd);  
GetClientRect(hWnd;&rc);  
hPen=CreatePen(PS_SOLID;0;RGB(0;0;0));  
hBrush=CreateHatchBrush(HS_DIAGCROSS;RGB(0;0;0));  
hPenOld=SelectObject(hdc;hPen);  
hBrushOld=SelectObject(hdc;hBrush);  
Ellipse(hdc;rc。left;rc。top;rc。right;rc。bottom);  
SelectObject(hdc;hPenOld);  
SelectObject(hdc;hBrushOld);  
ReleaseDC(hWnd;hdc);  
break;  
case WM_DESTROY:  
PostQuitMessage(0);  
break;  
default:  
break;  
}  
return DefWindowProc(hWnd;msg;wParam;lParam);  
}  
该示例代码中的所有内容都已在前面做了完整的讲解,这里我们简单 
的说一下如何在Microsoft Developer Studio中编译该示例程序。请 
按下面的步骤进行:  
1。  选择File菜单下的New命令,新建一个Win32  Application工程, 
…………………………………………………………Page 141……………………………………………………………
这里我们假设对该工程命名为SdkDemo1,而事实上这完全取决于你的 
意愿。这个过程已经在本书的第一章中作为介绍,这里就不再重复说 
明了。  
2。  选择Project菜单下的Add  To  Project|New。。。命令,向工程中添 
加一个C++    Source  File  (C++源文件),可以将该文件命名为 
winmain。cpp,不需要键入扩展名,Microsoft  Developer  Studio在 
创建文件时会 自动加上。cpp的后缀名。这个过程也已经在第一章中作 
过介绍。阅读过该章内容的读者不应感到陌生。然后在Wordspace窗 
口的FileView 中双击文件名winmain。cpp  (在依赖于你在前面过程中 
的设定),输入下面的源代码即可。  
如果已将源代码输入为C++源文件 (以。cpp为后缀名的文件),则可以 
使用Project|Add To Project|Files。。。将其添加到工程中。  
                                         
               图3。2 示例程序SdkDemo1的运行结果  
3。  单击Build菜单下的Build  SdkDemo1。exe或Build  All或按下快捷 
键F7   (如果未对该快捷键做过 自定义操作的话)或单击Build或Build  
Minibar工具条上的   按钮,编译并创建可执行文件SdkDemo1。exe, 
运行该可执行文件 (从Developer  Studio中或资源管理器均可),将得 
到如图3。2所示的结果。  
前面已经不只一次说到过,使用这种方式编写的应用程序使用调试和 
维护的难度很大。这个问题是使用直接使用SDK编程的固有总是。但 
是,我们还是有办法可以使得该程序的结构更紧凑和更集中一些,从 
而改善代码的可读性,也使得它更接近于使用SDK编写的真正的Win32 
应用程序。  
…………………………………………………………Page 142……………………………………………………………
通过分析应用程序,我们发现,在上面的程序代码中,WinMain函数 
的代码显得有些过分臃肿,解决总是的办法就是将这些代码分离为单 
个的函数,这样,我们就可以得到更实用的基本SDK应用程序框架, 
当然,相对于MFC所提供的应用程序框架来说,我们的这个应用程序 
框架几乎不值一提,但是,它的确是要比前面的示例程序好多了。  
经过修改的代码如下:  
#include   
// 函数原型  
int WINAPI WinMain(HINSTANCE;HINSTANCE;LPSTR;int);  
LRESULT WINAPI WndProc(HWND;UINT;WPARAM;LPARAM);  
BOOL InitApplication(HINSTANCE);  
BOOL InitInstance(HINSTANCE;int);  
// WinMain 函数  
int WINAPI WinMain (HINSTANCE hInstance;  
HINSTANCE hPrevInstance;  
LPSTR lpCmdLine;  
int nCmdShow)  
{  
if (!hPrevInstance)  
if (!InitApplication(hInstance))  
return FALSE;  
if (!InitInstance(hInstance;SW_SHOW))  
return FALSE;  
MSG msg; // 窗口消息  
// 开始消息循环  
while (GetMessage(&msg;NULL;0;0))  
{  
…………………………………………………………Page 143……………………………………………………………
TranslateMessage(&msg);  
DispatchMessage(&msg);  
}  
return msg。wParam;  
}  
// WndProc 主窗口过程  
LRESULT WINAPI WndProc (HWND hWnd;  
UINT msg;  
WPARAM wParam;  
LPARAM lParam)  
{  
HDC hdc;  
RECT rc;  
HPEN hPen;hPenOld;  
HBRUSH hBrush;hBrushOld;  
switch (msg)  
{  
case WM_PAINT:  
hdc=GetDC(hWnd);  
GetClientRect(hWnd;&rc);  
hPen=CreatePen(PS_SOLID;0;RGB(0;0;0));  
hBrush=CreateHatchBrush(HS_DIAGCROSS;RGB(0;0;0));  
hPenOld=SelectObject(hdc;hPen);  
hBrushOld=SelectObject(hdc;hBrush);  
Ellipse(hdc;rc。left;rc。top;rc。right;rc。bottom);  
SelectObject(hdc;hPenOld);  
…………………………………………………………Page 144……………………………………………………………
SelectObject(hdc;hBrushOld);  
ReleaseDC(hWnd;hdc);  
break;  
case WM_DESTROY:  
PostQuitMessage(0);  
break;  
default:  
break;  
}  
return DefWindowProc(hWnd;msg;wParam;lParam);  
}  
BOOL InitApplication(HINSTANCE hInstance)  
{  
WNDCLASS wc; // 窗口类  
// 填充窗口类信息  
wc。style=CS_HREDRAW|CS_VREDRAW;  
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=GetStockObject(WHITE_BRUSH);  
wc。lpszMenuName=NULL;  
wc。lpszClassName=〃SdkDemo2〃;  
// 注册窗口类  
…………………………………………………………Pa 
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!