对VS2008生成智能win32程序简单理解

      程序中创建了主窗口,所以它必须为主窗口注册一个窗口类,创建窗口并且提供一个消息循环来为窗口处理消息。

注册窗口类

ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
 WNDCLASS wc;

 wc.style         = CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc   = WndProc;
 wc.cbClsExtra    = 0;
 wc.cbWndExtra    = 0;
 wc.hInstance     = hInstance;
 wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOCEME));
 wc.hCursor       = 0;
 wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
 wc.lpszMenuName  = 0;
 wc.lpszClassName = szAppName;

 return RegisterClass(&wc);
}

 

 

style域为窗口设置了类的风格。在Windows CE中,类风格被限制为:
CS_GLOBALCLASS  表示类是全局的。这个标志只是出于兼容性才提供的,因为Windows CE中所有窗口类都是进程级全局类。
CS_HREDRAW      告诉系统如果窗口改变了水平大小,就强制重画窗口。
CS_VREDRAW      告诉系统如果窗口改变了垂直大小,就强制重画窗口。
CS_NOCLOSE      如果[关闭]按钮出现在标题栏上,则使其失效。
CS_PARENTDC 让窗口使用父窗口的设备环境变量
CS_DBLCLKS      允许[双击]通知(Windows CE下敲击两次为双击)传递给父窗口

 

lpfnWndProc分配的是窗口的窗口过程的地址。因为该域定义为指向窗口过程的指针,所以在源代码中,必须在域被设置之前,定义该过程的声明。否则,编译器类型检查时会警告该行。


cbClsExra允许程序员为类结构增加额外的空间来存储只有应用程序才知道的类特定数据。cbWndExtra更加便于使用,这个域为Windows内部结构增加空间,该结构负责维护窗口每个实例的状态。不在窗口结构本身里存储大量的数据,应用程序应该存储一个指向应用程序特定结构的指针,该结构包含窗口每个实例的数据。在Windows CE里,cbClsExtra 和cbWndExtra域必须时4字节的倍数。


hInstance域设置为程序的实例句柄,该句柄指明拥有窗口的进程。hIcon域设置为窗口默认图标的句柄,但在Windows CE中并不支持该域,所以该域应该设置为NULL。(在Windows CE中,会在类的第一个窗口被创建后设置类的图标。对于Hello3,没有图标提供,并且与其它Windows版本不同,Windows CE中没有任何预定义图标用于装载。)

除非应用程序是为带鼠标的Windows CE系统设计的,否则hCursor域应该设置为NULL。幸运的是,如果系统不支持光标,调用LoadCursor (IDC_ARROW) 函数会返回NULL。

hbrBackground域规定Windows CE如何画窗口背景。Windows用这个域中指定的刷子brush(一个小的预定义的像素数组)来画窗口背景。Windows CE提供许多预定义的刷子,你可以用GetStockObject函数来装载。如果hbrBackground域是NULL,窗口必须处理WM_ERASEBKGND消息,重画窗口背景。

lpszMenuName域必须设置为NULL,因为Windows CE不直接支持有菜单的窗口。在Windows CE中,菜单由主窗口创建的命令工具条、命令带或菜单条控件提供。

lpszClassName设置为程序员定义的字符串,用于为Windows指明类的名字。Hello3用的是“MyClass”做为类名。

整个WNDCLASS类被填充后,RegisterClass函数被调用,并用指向WNDCLASS结构的指针作为唯一的参数。如果函数成功,一个标记窗口类的值被返,如果失败,函数返回0。


消息循环
主窗口创建后,WinMain进入消息循环,这是每个Windows 应用程序的心脏。

while (GetMessage (&msg, NULL, 0, 0)) {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
}

该循环很简单:调用GetMessage函数,从应用程序消息队列中获取下一个消息。如果没有消息可用,则调用进入等待期,阻塞应用程序线程直到消息可用。当消息可用,该函数返回包含在MSG结构的消息数据。MSG结构自身包含几个域,有的用于识别消息,有的提供特定消息参数,有的识别在消息被发送之前,被笔触摸过的最后屏幕位置点。该位置信息不同于标准Win32消息位置数据,在XP下,返回的位置是当前鼠标位置而不是最后点击(或者tapped,在Windows CE里)的位置。

TranslateMessage 把适当的键盘信息转换成字符信息。(后面会讨论其它信息过滤器,比如IsDialogMsg。)DispatchMessage 接下来告诉Windows把消息发给应用程序适当的窗口。

获取消息、转换消息、分发消息这个过程会一直循环到GetMessage 收到WM_QUIT消息,这会使GetMessage返回0,这一点不同于其它消息。从while子句可以看出,GetMessage返回0将导致循环终止。

消息循环终止后,程序除了清理和退出外几乎不做什么。在Hello3里,程序简单的从WinMain中返回。WinMain的返回值成为程序的返回码。传统上,最后一个消息WM_QUIT的wParam参数值包含有返回值。为了响应应用程序对PostQuitMessage的调用,WM_QUIT消息被发送出去,此时WM_QUIT的wParam参数值被填充。


窗口过程
发送或提交(send 或 post方式)主窗口的消息被送到MainWndProc过程中。和所有窗口过程一样,MainWndProc原型如下:
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
                              LPARAM lParam);

返回值类型LRESULT 实际上就是long型(在Windows里long是一个32位值),写成这种形式是为源代码和机器之间提供一个中间级。虽然你可以轻易的从包含文件中确定Windows编程时使用的变量的真实类型,但当你试图把代码做跨平台的转换时会产生问题。虽然了解变量类型的大小对计算内存使用是有用的,但没有什么好的理由去使用(实际上有很多不使用的理由)windows.h文件中提供的类型定义。

CALLBACK 类型指明该函数是EXE的外部入口点,这是Windows直接调用该过程所必须的。在桌面系统里,CALLBACK 指出参数是按类Pascal风格从右到左方式压进程序栈的,这和标准C语言方式相反。为外部入口点使用Pascal语言栈框架的原因可以追朔到Windows开发非常早的时期。使用固定大小、Pascal栈方式,意味着由被调用的过程来清理栈,而不是留给调用者来清理。这种方式可以有效的减少Windows及其附属程序的大小,所以早期的微软开发者认为这是一个好的方式。在Windows CE里,应用程序对所有函数都使用C栈框架,不管是否是外部调用。

传给窗口过程的第一个参数是窗口句柄,当您需要定义具体的窗口实例的时候,这个句柄是很有用的。wMsg参数表示发给窗口的消息。这不是WinMain消息循环里使用的MSG结构,而是一个包含消息值的unsigned整型。剩余两个参数,wParam 和lParam, 传递和具体消息有关的数据给窗口过程。它们的名字来自Win16时代,那时wParam是个16位值而lParam是32位值。同其它Win32操作系统一样,在Windows CE里,两个都是32位的。

和传统的窗口过程一样,Hello3的窗口过程通过一个switch语句解析wMsg消息ID。该switch语句包含2个case语句,一个用来解析WM_PAINT消息,另一个用来解析WM_DESTROY消息。这个窗口过程大概是窗口过程所能简化到及至的一个窗口过程了。

WM_PAINT
绘制窗口,处理WM_PAINT消息,这在任何Windows 程序中都是很重要的功能之一。窗口的外观是在程序处理WM_PAINT消息的过程中完成的。除了用您在注册窗口类时指定的刷子绘制默认背景外,Windows对处理该消息不提供任何帮助。Hello3中处理WM_PAINT消息如下:
case WM_PAINT:
    // Get the size of the client rectangle
    GetClientRect (hWnd, &rect);
  
    hdc = BeginPaint (hWnd, &ps);
    DrawText (hdc, TEXT ("Hello Windows CE!"), -1, &rect,
              DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  
    EndPaint (hWnd, &ps);
    return 0;
在窗口绘制之前,程序必须确定窗口大小。在Windows程序里,一个标准窗口被划分为两个区域--非客户区和客户区。窗口标题栏和可变大小的边框通常占据了窗口的非客户区,这个区域由Windows负责绘制。客户区属于窗口的内部区域,由应用程序负责绘制。应用程序通过调用GetClientRect 函数来确定客户区的大小和位置。该函数返回一个RECT结构,包含左上角、右下角坐标等描述客户区矩形边界的信息。分成客户区和非客户区的好处是,应用程序不必绘制那些窗口标准元素,例如标题栏。

其它版本的Windows提供一系列WM_NCxxx消息,允许您的应用程序绘制非客户区。在Windows CE里,窗口很少有标题栏。因为很少有非客户区,所以Windows CE不发送非客户端消息给窗口过程。

WM_PAINT消息里执行的所有绘制工作都必须由两个函数BeginPaint 和EndPaint包围。BeginPaint 函数返回设备环境句柄HDC。设备环境是物理显示设备(例如视频显示器或打印机)的逻辑代表。Windows程序从不直接修改显示硬件。相反,Windows用设备环境将程序与具体硬件隔离开。

BeginPaint 填充一个PAINTSTRUCT结构,其结构如下:
typedef struct tagPAINTSTRUCT {
    HDC  hdc;
    BOOL fErase;
    RECT rcPaint;
    BOOL fRestore;
    BOOL fIncUpdate;
    BYTE rgbReserved[32];
} PAINTSTRUCT;

hdc就是BeginPaint函数返回的句柄。fErase指出窗口过程是否需要重画窗口背景。rcPaint是RECT结构,定义了需要重画的客户区。并假设在每个WM_PAINT消息中,整个客户区窗口都需要重画。当性能是需要考虑的问题时,该域是很有用的,因为有时仅仅需要重画部分窗口即可。即使当程序尝试重画rcPaint矩形以外的区域时,Windows也会阻止这么做的。该结构的其它域,fRestore, fIncUpdate, 和rgbReserved,属于Windows内部使用,应用程序可以忽略掉它们。

程序中唯一的绘制工作是在窗口绘制一行文本。程序调用DrawText函数来完成该绘制。如果您看一下该函数,很容易会明白这个调用在窗口上绘制了一行字符串“Hello Windows CE”。在DrawText返回后,调用EndPaint来通知Windows程序已经完成了窗口更新。

EndPaint调用同时也使没有被绘制的窗口其它区域有效。Windows保持一份无效窗口区域(也就是需要重画的区域)列表和有效区域(也就是已经更新的区域)列表。不论您是否在窗口画了什么,通过成对的调用BeginPaint和EndPaint,会通知Windows由您来处理窗口的无效区域。实际上,您必须调用BeginPaint和EndPaint,或者通过其它方式使窗口无效区域变有效,否则Windows会不断发送WM_PAINT消息给窗口,直到无效区域变有效。

WM_DESTROY
程序中处理的另一个消息是WM_DESTROY。当窗口即将被销毁时,该消息被送出。因为该窗口是应用程序主窗口,当窗口被销毁时应用程序将终止。处理WM_DESTROY消息的代码调用PostQuitMessage消息来触发该动作。PostQuitMessage函数将WM_QUIT消息放到到消息队列里。该函数的参数是返回码的值,该值放在WM_QUIT消息的wParam参数里传回应用程序。

如前所述,消息循环看到WM_QUIT消息就会退出循环。WinMain 接着调用TermInstance,在Hello3里,该函数什么也不做,只是返回。WinMain 接着返回,并终止程序。

posted @ 2010-02-09 10:30  张兴业  阅读(219)  评论(0编辑  收藏  举报