Windows应用程序的基本运行机制与HelloWin程序详细解
总的来说最基本的Windows应用程序的运行执行顺序总是以如下的基本顺序执行的。
顺序结构:
调用WinMain函数开始执行--à定义窗口类--à初始化窗口类---à窗口的实例化--à通过消息循环获取消息并将消息发送给消息处理函数做出相应的操作
由于windows应用程序运行的逻辑结构特殊所以代码的详细解释笔者就不把程序于叙述分开了了,这样有利于阅读与分析。
下载该程序:点击这里下载(82K, winzip压缩文件)
分析代码如下:
//程序作者:管宁 //站点:www.cndev-lab.com //所有稿件均有版权,如要转载,请务必注明出处和作者 #include <windows.h> #pragma comment(lib,"winmm.lib")//为了要播放声音,必须导入这个库 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) /* HINSTANCE 类型的含义为实例句柄。 hInstance 事实上就是当前应用程序自身的标识代号,代号通常都是一个32位整数。 hPrevInstance 与过去的16位应用程序有关系,表示指向前一个实例的句柄。 PSTR 类型的含义是指向以\0结尾的字符串指针。 szCmdLine 前面的sz同样是表示指向以\0结尾的字符串指针,这个对象用于保存命令行。 最后iCmdShow是一个整型数据,标记了程序最初的显示状态。 为SW_SHOWNORAML的时候为一般大小显示方式。 为SW_SHOWMAXIMIZED的时候为最大化显示方式。 为SW_SHOWMINNOACTIVE的时候程序将显示在任务栏上。 */ { static char szAppName[] = TEXT("HelloWin");//预先定义一个c风格字符串,稍后用于设置窗口类名称。 WNDCLASS wndclass;//定义窗口类对象 /* 在这里不得不说一下的是,窗口类事实上是struct结构体,内部有10个分量,他们是用来于初始化窗口类对象而用的。 这个结构体在winuser.h头文件中定义,从方式上来说,分为ASCII版的WNDCLASSA和Unicode版的WNDCLASSW两个。 typedef struct tagWNDCLASSA { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCSTR lpszMenuName; LPCSTR lpszClassName; } WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA; typedef struct tagWNDCLASSW { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCWSTR lpszMenuName; LPCWSTR lpszClassName; } WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW; */ //------------------------------- 窗口类对象初始化过程 ------------------------------------ wndclass.style = CS_HREDRAW | CS_VREDRAW; /* 设置窗口类对象的样式风格,CS_HREDRAW | CS_VREDRAW这两个值是通过位运算的与运算结合起来的。 表示了窗口在改变了水平和垂直大小的时候,窗口要强迫刷新。 这些通过define定义的标识,可以在WinUser.h头文件中找到。 #define CS_VREDRAW 0x0001 #define CS_HREDRAW 0x0002 #define CS_DBLCLKS 0x0008 #define CS_OWNDC 0x0020 #define CS_CLASSDC 0x0040 #define CS_PARENTDC 0x0080 #define CS_NOCLOSE 0x0200 #define CS_SAVEBITS 0x0800 #define CS_BYTEALIGNCLIENT 0x1000 #define CS_BYTEALIGNWINDOW 0x2000 #define CS_GLOBALCLASS 0x4000 #define CS_IME 0x00010000 */ wndclass.lpfnWndProc = WndProc ;//指定窗口的处理函数为WndProc,WndProc将处理windows消息。 wndclass.cbClsExtra = 0;//窗口类无扩展 wndclass.cbWndExtra = 0;//窗口实例无扩展 wndclass.hInstance = hInstance;//指定当前应用程序实例句柄,也就是程序当前的标识号。 wndclass.hIcon = LoadIcon (NULL,IDI_APPLICATION); /* 通过LoadIcon函数设置应用程序窗口标题的icon图标。 HICON LoadIcon(HINSTANCE hInstance,LPCTSTR lpIconName); 函数返回HICON类型的图标句柄。 第一个参数表示当前应用程序的窗口句柄,第二个参数表示图标。 默认状态下,第一个参数为NULL,第二个为IDI_APPLICATION,表示使用系统默认提供的图标,可以在WinUser.h头文件中找到。 #define IDI_APPLICATION 32512 */ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; /* 通过LoadCursor函数设置应用程序窗口光标样式。 HCURSOR LoadCursor(HINSTANCE hInstance,LPCTSTR lpCursorName); 函数返回HCURSOR类型的光标句柄。 第一个参数表示当前应用程序的窗口句柄,第二个参数表示光标。 默认状态下,第一个参数为NULL,第二个为IDC_ARROW,表示使用系统默认提供的光标,可以在WinUser.h头文件中找到。 #define IDC_ARROW MAKEINTRESOURCE(32512) */ wndclass.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH); /* 通过GetStockObject函数设置应用程序窗口的背景颜色。 HGDIOBJ GetStockObject(int fnObject); 函数返回HCURSOR类型的GDI对象句柄,为了程序能够正确执行,必须把HGDIOBJ类型强制转换成HBRUSH画刷句柄。 参数表示当前使用的画刷颜色。 这些常量的定义可以在WinGDI.h头文件中找到。 #define WHITE_BRUSH 0 #define LTGRAY_BRUSH 1 #define GRAY_BRUSH 2 #define DKGRAY_BRUSH 3 #define BLACK_BRUSH 4 #define NULL_BRUSH 5 #define HOLLOW_BRUSH NULL_BRUSH */ wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName;//窗口类对象的名称 //----------------------------------------------------------------------------------------- RegisterClass (&wndclass); /* 注册窗口类,参数为窗口类对象的指针。 函数原形为: ATOM RegisterClass(CONST WNDCLASS *lpWndClass); */ //-------------------------- 实例化过程 ------------------------------------------------- HWND hwnd ; //创建用于保存窗口句柄的对象,窗口句柄是系统识别不同窗口的依据,它只是个代号。 hwnd = CreateWindow( szAppName, // 窗口类名称 "你好世界", // 窗口标题 WS_OVERLAPPEDWINDOW, // 窗口样式 CW_USEDEFAULT, // 初始的窗口x轴位置 CW_USEDEFAULT, // 初始的窗口y轴位置 CW_USEDEFAULT, // 初始的窗口x轴大小 CW_USEDEFAULT, // 初始的窗口y轴大小 NULL, // 父窗口句柄 NULL, // 窗口功能表句柄 hInstance, // 应用程序实例句柄 NULL // 建立参数,这个参数可以存取后面程序中可能引用到的资料。 ); /* 在窗口类对象的初始化过程中,我们定义了窗口的一些简单一般特征,比如背景颜色呀,光标呀,等等。 但是在利用CreateWindow创建窗口的时候可以设置更多的细节,比如窗口标题这些。 函数原形如下: HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); 一旦窗口创建成功,那么CreateWindow将返回窗口句柄,也就是窗口代号,值保存在窗口句柄对象hwnd中。 */ ShowWindow(hwnd, iCmdShow); /* 在执行过CreateWindow函数后,在系统的内部窗口已经创建成功了。 但为了要把窗口显示在桌面上,我们还必须调用ShowWindow函数。 其函数原形如下: BOOL ShowWindow(WND hWnd,int iCmdShow); 参数1是需要显示的窗口句柄,第二个则是传递给WinMain的iCmdShow,用来确定最开始窗口的显示方式。 在这里窗口的显示方式,主要是指最大化,最小化这些。 */ UpdateWindow (hwnd); /* UpdateWindow这个函数的作用是用于重绘显示区域。 因为如果ShowWindow函数的iCmdShow从WinMain获得的参数是SW——SHOWNORMAL,那么窗口的显示区域就会被背景画刷覆盖, 调用UpdateWindow函数会通过发送给窗口消息处理函数WndProc一个WM_PAINT消息,通过这个消息完成重绘显示区域的工作。 */ //----------------------------------------------------------------------------------------- //---------------------------- 消息循环 ------------------------------------------------- /* 当调用过UpdateWindow函数后,窗口已经显示在了桌面屏幕上,接下来要做的工作是处理消息。 windows应用程序可以接受各种消息包括键盘,鼠标,等等。 windows是通过监视各种输入设备,把发生的事件转化为消息的,并将消息保存在消息队列中。 最后当前的应用程序从自己的消息队列中按顺序检索消息,并把每一个消息发送到所对应的窗口消息处理函数总去,这里是指WndProc。 */ MSG msg ;//建立消息对象。 /* MSG是个结构体类型,在WinUser.h头文件中可以找到。 typedef struct tagMSG{ HWND hwnd;//窗口句柄 UINT message;//消息识别字,在WinUser.h头文件中可以找到,以WM开头,这里就不全部举出来了。 WPARAM wParam;//32位的消息参数,其含义和值根据消息的不同而不同。 LPARAM lParam;//32位的消息参数,其值和消息无关。 DWORD time;//消息进入消息队列的时间。 POINT pt;//消息进入消息队列时候的鼠标坐标。 #ifdef _MAC DWORD lPrivate; #endif } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG; 其中POINT也是个结构体类型,在WinDef.h头文件中可以找到 typedef struct tagPOINT { LONG x; LONG y; } POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT; */ while (GetMessage (&msg, NULL, 0, 0)) { /* 我们通过这个循环代码来维护消息循环,循环的执行条件是通过GetMessage函数获得的。 函数原型如下: BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax); 参数一是一个指向msg对象的指针,剩余的参数为NULL或0表示程序接受它自己建立的所有窗口的消息。 windows从消息队列取出的下一个消息将填充MSG结构中的各成员分量。 */ TranslateMessage (&msg);//把虚拟键盘消息转换到字符消息,满足键盘输入的需要,参数为msg消息对象的指针。 DispatchMessage (&msg); /* 把当前的消息发送到窗口消息处理函数中去处理,在这里为WndProc。 当DispatchMessage调用结束后,循环再次重复,重新回到GetMessage处,接着获取消息。 如果消息循环接收到WM_QUIT消息则跳出消息循环。 */ } //---------------------------------------------------------------------------------------- return msg.wParam;//返回消息结构中的wParam成员信息。 /* MSG结构的wParam成员的值是传递给PostQuitMessage函数参数,通常是0。 因为PostQuitMessage函数是在结束消息循环必须调用的函数。 系统其实是执行了return 0;结束了WinMain函数退出了程序,很想控制台应用程序main结束的时候的return 0;,所以直接写return 0;也不会导致程序错误。 */ } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)//窗口消息处理函数 /* 函数返回类型为LRESULT,是一个长整数,修饰CALLBACK表示此函数为回调函数,函数的返回类型,和参数顺序都必须按照系统的规定设置。 参数一为窗口句柄,第二个参数是无符号整型数据,用于标识接受的消息,最后两个参数为32位的消息参数,提供了更多关于消息的信息。 WPARAM和LPARAM都表示的是长整数,该函数的四个参数与MSG结构的前四个成员相同。 消息处理函数,通常是windows自己调用的,当然程序作者也可以通过调用SendMessage函数直接呼叫自己的窗口消息处理函数,只是在这里暂时不讨论。 */ { HDC hdc;//创建设备描述句柄对象 PAINTSTRUCT ps;//创建绘制结构对象 /* PAINTSTRUCT结构包含了一些窗口消息处理程序,可以用来更新窗口显示区域中的信息。 结构如下: typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT, *PPAINTSTRUCT; */ RECT rect;//创建矩形结构对象 /* 此结构的定义如下: typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT; */ switch (message)//通过switch和case结构来确定处理什么样的消息,如果不想处理某些消息则把消息传递给DefWindowProc函数处理。 { case WM_CREATE://当窗口创建的时候获得WM_CREATE消息 PlaySound (TEXT("C:\\online.wav"),NULL,SND_FILENAME|SND_ASYNC);//播放声音 return 0;//窗口消息处理函数如果正在处理消息必须返回0 case WM_PAINT://通知窗口更新显示区域的信息 /* 当窗口刚开始建立的时候,整个显示区域都是无效的,因为程序还没有在窗口上绘制任何东西。 第一条WM_PAINT消息通常发生在调用UpdateWindows函数的时候,告诉窗口消息处理函数在显示区域绘制一些东西。 事实上当用户把wndclass.style设置成CS_HREDRAW | CS_VREDRAW后,一旦用户改变窗口大小,就会把显示区域当作无效,这时候就会收到WM_PAINT消息。 */ /* 通常在处理WM_PAINT消息的时候,总是以BeginPaint开头和EndPaint结尾的。 */ hdc = BeginPaint (hwnd, &ps); /* 调用BeginPaint函数可以传回设备句柄,这里指的是显示器的代号和显示器的驱动程序。 因为在窗口显示区域要显示文字或者图形都需要用到设备句柄。 它的函数原形为: HDC BeginPaint( HWND hwnd, // handle to window LPPAINTSTRUCT lpPaint // paint information ); 它实际的功能是:当发现窗口显示区域的背景还没有被清除的时候,则由windows来删除它。 我们前面在wndclass结构中设置了画刷为白色,这么以来系统就用白色来遮盖桌面的颜色,这样窗口显示区域就变成白色了。 */ GetClientRect (hwnd,&rect);//设置窗口显示区域的尺寸,同时它也负责获得窗口改变后的窗口显示区域的尺寸信息。 DrawText (hdc,TEXT("中国软件开发实验室,http://www.cndev-lab.com"),-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);//绘制文字在窗口显示区域中 /* DT_SINGLELINE|DT_CENTER|DT_VCENTER 表示的是文字显示的方式,这些在WinUser.h头文件中定义。 */ EndPaint (hwnd,&ps);//结束指定窗口的绘图 return 0; case WM_DESTROY://当窗口销毁的时候会返回此信息,比如ALT+F4或关闭窗口的时候,系统默认调用DestroyWindow()函数撤消窗口。 PostQuitMessage (0); /* 处理WM_DESTROY消息必须调用PostQuitMessage函数,该函数向消息队列中发送WM_QUIT消息,让程序退出消息循环。 应用程序可以在响应这个消息的同时做一些其它结束的工作。 */ return 0; } return DefWindowProc (hwnd, message, wParam, lParam);//处理不于处理的消息 }
http://pcedu.pconline.com.cn/empolder/gj/c/0506/642939_3.html