八宝书库 > 文学其他电子书 > C语言实例教程(PDF格式) >

第17部分

C语言实例教程(PDF格式)-第17部分

小说: C语言实例教程(PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




APIENTRY等。它们对编译器而言都是一回事,最终将被解释为 

__stdcall。在Windows环境下编程,会遇到很多这样的情况,注意不 

要混淆它们。  



一般情况下,我们应该在WinMain函数中完成下面的操作:  



1。 注册窗口类;  



2。 创建应用程序主窗口;  


…………………………………………………………Page 129……………………………………………………………

3。 进入应用程序消息循环。  



接下来我们将依次讨论这些内容。  



在Windows应用程序中,每一个窗口都必须从属于一个窗口类,窗口 

类定义了窗口所具有的属性,如它的样式、图标、鼠标指针、菜单名 

称及窗口过程名等。在注册窗口类前,我们先创建一个类型为 

WNDCLASS的结构,然后在该结构对象中填入窗口类的信息,最后将它 

传递给函数RegisterClass,整个过程如下面的代码所示:  



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=〃SdkDemo1〃;  



// 注册窗口类  



RegisterClass(&wc);  



下面解释一下结构WNDCLASS 中各成员的含义:  



                    style:  指定窗口样式。该样式可以为一系列屏 

                    蔽位的按位或,在前面的例子中,CS_HREDRAW表 

                    示当窗口用户区宽度改变时重绘整个窗口,而 

                    CS_VREDRAW则表则表示当窗口用户区高度改变时 

                    重绘整个窗口。对于其它的窗口样式,请参阅 

                    SDK中关于WNDCLASS的联机文档。(顺便说一句, 

                    请注意该成员的大小写,它是小写的style,而 


…………………………………………………………Page 130……………………………………………………………

不是Style。)  



lpfnWndProc:  指向窗口过程的指针。关于窗口 

过程我们将以后面的内容中讲述。在前面的例子 

中,我们使用名为WndProc的窗口过程。  



cbClsExtra: 指定在窗口类结构之后分配的附加 

字节数。操作系统将这些字节初始化为0。  



cbWndExtra: 指定在窗口实例之后分配的附加字 

节数。操作系统将这些字节初始化为0。如果应 

用程序使用WNDCLASS结构注册一个使用资源文件 

中的CLASS指令创建的对话框,那么cbWndExtra 

必须被设置为DLGWINDOWEXTRA。  



hInstance : 标识该类的窗口过程所属的实例。  



hIcon : 标识类图标。该成员必须为一个图标资 

源的句柄。如果该成员为NULL,则应用程序必须 

在用户最小化应用程序窗口时绘制图标。  



hCursor : 标识类鼠标指针。该成员必须为一个 

光标资源的句柄,如果该成员为NULL,当鼠标移 

进应用程序窗口时应用程序必须显式指定指针形 

状。  



hbrBackground:  标识类背景刷子。该成员可以 

为一个用来绘制背景的画刷句柄,或者为标准系 

统颜色值之一。   



lpszMenuName: 指向一個以NULL结尾的字符串的 

指针,该字符串指定了类菜单的资源名称。如果 

在资源名称为的菜单为一个整数所标识,则可以 

使用MAKEINTRESOURCE宏将其转换为一个字符 

串;如果该成员为NULL,则属于该类的窗口无默 

认菜单。  



lpszClassName:  指向一个以NULL结尾的字符串 

或为一个原子。如果该参数为一个原子,那么它 

必须是一个使用GlobalAddAtom函数创建的全局 

原子;如果为一个字符串,该字符器将成员窗口 

类名。  


…………………………………………………………Page 131……………………………………………………………

   l 注意:  



   l 这里多次提到窗口类这一名词,但是它和前面常说的C++类没有任 

    何联 系。窗口类只表示了窗口的类型,它完全不是面向对象意义 

    上的类,因为它不支持面向对象技术中的继承及多态等。  



在使用RegisterClass注册窗口类成功之后,即可以使用该窗口类创 

建并显示应用程序的窗口。这个过程如下面的代码所示:  



// 创建应用程序主窗口  



hWnd=CreateWindow (〃SdkDemo1〃; // 窗口类名  



〃第一个Win32 SDK应用程序〃; // 窗口标题  



WS_OVERLAPPEDWINDOW; // 窗口样式  



CW_USEDEFAULT; // 初始化 x 坐标  



CW_USEDEFAULT; // 初始化 y 坐标  



CW_USEDEFAULT; // 初始化窗口宽度  



CW_USEDEFAULT; // 初始化窗口高度  



NULL; // 父窗口句柄  



NULL; // 窗口菜单句柄  



hInstance; // 程序实例句柄  



NULL); // 创建参数  



// 显示窗口  



ShowWindow(hWnd;SW_SHOW);  



// 更新主窗口客户区  



UpdateWindow(hWnd);  



由于上述代码均加上了详尽的注释,这里仅作一些简单的说明和强 

调。CreateWindow函数的原型是这样的:  



HWND CreateWindow(LPCTSTR lpClassName; // 指向已注册的类名  



LPCTSTR lpWindowName; // 指向窗口名称  



DWORD dwStyle; // 窗口样式  


…………………………………………………………Page 132……………………………………………………………

int x; // 窗口的水平位置  



int y; // 窗口的垂直位置  



int nWidth; // 窗口宽度  



int nHeight; // 窗口高度  



HWND hWndParent; // 父窗口或所有者窗口句柄  



HMENU hMenu; // 菜单句柄或子窗口标识符  



HANDLE hInstance; // 应用程序实例句柄  



LPVOID lpParam; // 指向窗口创建数据的指针  



);  



在前面的示例中,我们对x、y、nWidth和nHeight参数都传递了同一 

个值CW_USEDEFAULT,表示使用系统默认的窗口位置和大小,该常量 

仅对于重叠式窗口 (即在dwStype样式中指定了 

WS_OVERLAPPEDWINDOW,另一个常量WS_TILEDWINDOW有着相同的值)有 

效。对于CreateWindows函数的其它内容,比如关于dwStyle参数所用 

常量的详细参考请 自行参阅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:  

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的