设为首页 加入收藏

TOP

3.2.3 Windows程序分析
2013-10-07 00:05:21 来源: 作者: 【 】 浏览:60
Tags:3.2.3 Windows 程序 分析

3.2.3  Windows程序分析

为了抓住Windows程序代码的主干,先避开代码中许多烦琐的细节,从前面介绍过的WinMain函数开始分析。WinMain函数的开始两行是: 

MSG msg;
HACCEL hAccelTable;

这两行定义了两个变量,一个是msg,它是一个消息结构体,用来存放消息循环获得的消息;另一个是hAccelTable,它用来存放键盘加速键表的句柄。键盘加速键是应用程序运行时的快捷键,比如在使用Word编辑时按下快捷键Ctrl+F就可以打开查找对话框。这里的快捷键Ctrl+F就是Word的加速键。加速键表就是加速键与菜单命令的一个映射表。这个表将在消息转换的时候被用到。

接下来的两句是装入字符串资源。其中szTitle装入的是窗口的标题栏字符串,szWindowClass装入的是类名字符串。

LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_HELLOWORLD, szWindowClass, MAX_LOADSTRING);
LoadString的函数原型是:

int LoadString(
HINSTANCE hInstance, // 应用程序实例句柄
UINT uID,   // 资源ID
LPTSTR lpBuffer,  // 存放字符串的缓冲区
int nBufferMax   // 缓冲区大小
);

hInstance:字符串资源所在的模块的句柄,这个句柄就是WinMain函数传进来的应用程序的实例句柄。通过这个句柄,就可以访问到相关的资源。

uID:资源标志符。这里传递的是两个字符串资源的标志符。

lpBuffer:目的字符串。LoadString把字符串资源装入到lpBuffer指定的目的字符串。

WinMain函数接下来执行MyRegisterClass函数进行窗口类的注册。MyRegisterClass是自定义的一个函数。它的主要工作是填充一个WNDCLASSEX结构体再调用WindowsAPI函数RegisterClassEx向系统进行注册。WNDCLASSEX是一个窗口类的结构体,它定义了一种类型的窗口行为特性。WNDCLASSEX的定义如下。

WNDCLASSEX wcex;
wcex.cbSize   = sizeof(WNDCLASSEX);
wcex.style   = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra  = 0;
wcex.cbWndExtra  = 0;
wcex.hInstance  = hInstance;
wcex.hIcon   = LoadIcon(hInstance, (LPCTSTR)IDI_HELLOWORLD);
wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = (LPCSTR)IDC_HELLOWORLD;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm  = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);


它的各个成员字段的意义如下:

(1)cbSize:它决定了WNDCLASSEX的大小。一般情况下给它赋值为sizeof(WNDCLASSEX)就可以了。
(2)style:该字段决定了希望创建的窗口类的风格属性,常用的一些属性标志值如表3-3所示。

表3-3 常用的窗口类属性标志值

(3)lpfnWndProc:指向窗口过程函数的指针。窗口过程函数就是消息处理的地方,程序员在窗口过程中拦截感兴趣的消息进行处理。窗口过程函数是一个回调函数,回调函数不需要程序主动去调用,而是由系统去调用。当系统分发一个消息的时候,会自动寻找消息对应的目标窗口,并根据窗口类定义这个指针把消息投递到到对应的窗口过程中去。本例中这个字段被赋值为WndProc,WndProc就是本程序的窗口处理过程。

(4)cbClsExtra:类结构体的附加字节大小。一般为0。

(5)cbWndExtra:窗口对象的附加字节大小。一般为0。

(6)hInstance:窗口所属的应用程序的实例句柄。这里就简单地赋值为WinMain函数传入的应用程序实例句柄。

(7)hIcon:窗口的图标句柄。该句柄通过调用LoadIcon函数得到。LoadIcon的函数原型是:

HICON LoadIcon(
HINSTANCE hInstance, // 应用程序句柄
LPCTSTR lpIconName  // 图标资源名称
);

表3-4  LoadIcon使用的系统默认图标资源


(8)hCursor:窗口的光标句柄,这个参数决定了鼠标光标移到窗口中时的样式。这个句柄通过调用LoadCursor函数得到。LoadCursor的用法与LoadIcon相类似,详细内容可以参考MSDN。

(9)hbrBackground:窗口的背景画刷的颜色。当窗口移动或改变大小而需要刷新时候,系统就以这个参数定义的画刷的颜色对窗口背景进行刷新。

(10)lpszMenuName:菜单资源的名称,它是以一个空字符为结束符的字符串指针。

(11)lpszClassName:窗口类名,它是以一个空字符为结束符的字符串指针。它的作用是给这个窗口类起一个名字,以后在CreateWindow的时候指定这个名字时系统就可以根据类名进行窗口的创建。

(12)hIconSm:应用程序窗口的小图标的句柄。当应用程序窗口被最小化到工具栏时,在工具栏上显示的图标就由这个参数指定。

在填充完WNDCLASSEX结构体之后,下一步就是向系统注册这个结构体。注册结构体的API函数是RegisterClassEx,它只接收一个参数,就是WNDCLASSEX结构体的地址指针,执行完这一句之后,窗口类就向系统注册完成了。

再回到WinMain函数,接下来调用的是InitInstance函数。InitInstance函数也是Hello World应用程序自定义的函数。这个函数主要完成的工作是调用系统函数CreateWindow创建窗口。它的函数原型是:

HWND CreateWindow(
LPCTSTR lpClassName,  // 窗口类名
LPCTSTR lpWindowName, // 窗口名称
DWORD dwStyle,   // 窗口风格
int x,     // 窗口位置的x坐标
int y,     // 窗口位置的y坐标
int nWidth,    // 窗口的宽度
int nHeight,    // 窗口的高度
HWND hWndParent,   // 父窗口句柄
HMENU hMenu,    // 菜单句柄
HANDLE hInstance,  // 应用程序句柄
LPVOID lpParam    //窗口创建参数
); 


如果这个函数执行成功的话,那么它就返回一个新生成的窗口的句柄,否则就返回NULL。它的参数定义如下:

(1)lpClassName:窗口的类名,这里的类名就是使用前面刚刚注册过的类名。
(2)lpWindowName:窗口的标题栏的文本。
(3)dwStyle:这是一个设定窗口外观和行为的参数。如表3-5所示列出了常用的参数。
表3-5  dwStyle常用的参数

(4)x:窗口的左上角位置横坐标。如果设定为CW_USEDEFAULT,那么将使用系统默认的位置坐标。

(5)y:窗口的左上角位置纵坐标。如果x设定为CW_USEDEFAULT,y将被忽略。

(6)nWidth:窗口的宽度。如果设定为CW_USEDEFAULT,那么将使用系统默认宽度值。

(7)nHeight:窗口的高度。如果nWidth设定为CW_USEDEFAULT,该值将被忽略。

(8)hWndParent:指向父窗口的句柄,如果设定为NULL,则桌面就是新生成窗口的父窗口。

(9)hMenu:指向附属于该窗口菜单的句柄。

(10)hInstance:应用程序实例句柄。前面多次提到它的作用,这里不再赘述。
(11)lpParam:附加参数。如果希望在创建窗口的时候,窗口过程在收到WM_CREATE消息时带有附加的参数,那么就可以通过这个字段来进行设定。一般情况下设定为NULL。

(12)创建窗口完成之后,窗口并没有显示出来,而必须调用ShowWindow函数使窗口显示在屏幕上。ShowWindow的函数原型如下:

BOOL ShowWindow(
HWND hWnd,  // 窗口的句柄
int nCmdShow  // 窗口的显示方式
);

hWnd:要显示的窗口的句柄。

nCmdShow:窗口的显示方式。该程序直接使用了WinMain函数传入的默认显示方式,但也可以选择其它的显示方式。最常用的两种方式就是SW_SHOW,和SW_HIDE。顾名思义,它们分别是让窗口显示和隐藏。

接下来在WinMain函数中调用LoadAccelerators函数装入了加速键表句柄,代码如下:

hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_HELLOWORLD);

给LoadAccelerators函数传递应用程序实例句柄和加速键表的资源ID作为参数传入,然后返回加速键的句柄,并保存在hAccelTable中。这个句柄将在下面的消息循环中处理加速键消息时用到。下面的代码是整个消息循环的代码:

while (GetMessage(&msg, NULL, 0, 0)) 
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
消息循环首先执行GetMessage函数,如果该函数返回值为假,那么消息循环就结束,如果返回值为真,那么就继续进行消息循环。GetMessage函数的作用是从消息队列中读取消息到msg结构体中进行保存,如果读取的消息是WM_QUIT,那么GetMessage返回的值就为假,读取到其他消息的时候GetMessage返回真值。

接下来一句是调用TranslateAccelerator函数把加速键消息转化为菜单命令消息WM_COMMAND。它需要三个参数:窗口句柄、加速键表句柄和消息结构体。如果当前msg中读取的消息是加速键消息,那么TranslateAccelerator直接向窗口过程发送转换后的命令消息并返回真值,如果msg中不是加速键消息,那么就返回假值。

TranslateMessage的作用是将键盘消息转换为字符消息,如果程序是要对字符消息进行处理的话,则必须加上对该函数的调用,否则这一句可以省略。

DispatchMessage的作用是将消息分发到窗口过程中去。它首先根据msg的窗口句柄判断窗口属于哪一类,然后把消息分发到对应窗口类的窗口过程。

这个时候,应用程序就进入了消息循环和窗口过程相交互的阶段。消息循环一旦接收到一个消息,就向窗口过程分发。在窗口过程中,程序员处理自己感兴趣的消息。对于不感兴趣的消息则交给系统处理。一个典型的窗口过程的代码框架如下:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:

break;
case WM_PAINT:

break;
case WM_DESTROY:

break;
…..   
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

窗口过程的主要框架是一个switch-case结构,它对传入的消息类型进行判断和筛选。对于感兴趣的消息就直接在case中进行处理,对不感兴趣的消息则交给系统的默认消息处理函数DefWindowProc进行处理。可以想象,如果一个应用程序处理大量消息的时候,它将会有一个非常大的switch-case结构,这是一件很可怕的事情。在后面的章节将会看到,MFC在消息处理这一方面时做得很好,它把消息的处理分散到各个类成员函数中进行,但这也是MFC难于学习和理解的地方。

至此已经将整个Hello World的程序流程介绍完毕了。为了加深理解,再来回顾一下创建一个Windows程序所必须要经历的几个步骤:

(1)注册窗口类。
(2)创建窗口并显示。
(3)进入消息循环,在消息循环中用GetMessage和DispatchMessage接收和分发消息。
(4)在窗口过程中对消息进行各种的处理。

【责任编辑:雪花 TEL:(010)68476606】

回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇3.3.3 处理重要的事件 下一篇3.3.1 使用位图资源

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: