Visual C++ 2008入门经典 第十二章 Windows编程的概念
#include <iostream> using namespace std; /* //第十二章 Windows编程的概念 //窗口的基本结构 //windows API的概念和用法 //windows 消息的概念,如何处理windows消息 //windows程序中常用的符号 //windows程序中的基本结构 //如何使用windows API创建简单的程序,该程序的工作原理 //Micrsosft Foundation Classes //基于MFC的程序的基本元素 //Windows Forms //Windows Forms应用程序的基本元素 //12.1 Windows 编程基础 //12.1.1 窗口的元素 //12.1.2 windows程序与操作系统 //12.1.3 事件驱动型程序 //12.1.4 windows消息 //12.1.5 Windows API //windows数据类型的完整列表 /*BOOL和BOOLEAN boolean变量的值可以是TRUE或FALSE,注意,该类型与值为true或falsh的C++类型bool不同 BYTE 8位字节 CHAR 8位字符 DWORD 32位无符号整数,对应于C++中的unsigned long类型 HANDLE 指向某个对像的句柄,是32位的整数值,记录着该对像在内存中的位置 HBRUSH 指向某个画刷的句柄,画刷用来以颜色填充某块区域 HCURSOR 指向某个光标的句柄 HDC 指向某种设备上下文的句柄-设备上下文是允许我们在窗口上绘图的对像 HINSTANCE 指向某个实例的句柄 LPARAM 消息的形参 LPCSTR 指向某个由8位字符构成的,以空字符终止的字符串常量的指针 LPHANDLE 指向某个句柄的指针 LRESULT 处理消息产生的有符号数值 WORD 16位无符号整数,对应于C++中的unsigned short类型 //12.1.7 windows程序中的符号 b BOOL类型的逻辑变量,等价于int by unsigned char 类型,占用一个字节 c char类型 dw DWORD类型,等价于unsigned long fn 函数 h 用来引用某种对像的句柄 i int类型 l long 类型 lp long类型的指针 n int类型 p 指针 s 字符串 sz 零终止的字符串 w WORD类型,等价于unsigned short //12.2 Windows程序的结构 //12.2.1 WinMain()函数 WinMain()函数等价于控制台程序中的main()函数. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE HPrevInstance, LPSTR lpCmdLine, int nCmdShow); //WINDAPI是一个windows定义的说明符,将使系统以某种特殊的方式处理函数名和实参--该方式碰巧与Pascal和Fortran语言中处理函数的方式一致 //第一个参数hInstance属于HINSTANCE类型,是指向某个实例的句柄--这里的实例是正在运行的程序 //句柄是标识某种对像(这里是应用程序的实例)的整数值, //下一个参数是hPrevInstance是从16位版本的Windows操作系统继承下来的 //第三个参数是lpCmdLine是指向某个字符串的指针,该字符串包含启动程序的命令行字符 //LPSTR类型是另一种windows类型,用来指定32位(long)的字符串指针 //第四个参数nCmdShow决定着被创建窗口的外观,窗口可以正常显示,也可以最小化显示屏 Windows程序中的WinMain()函数需要做以下四件事情: (1) 告诉Windows该程序需要的窗口种类 (2) 创建程序窗口 (3) 初始化程序窗口 (4) 获取属于该程序的Windows消息 1 指定程序窗口 WNDCLASSEX结构的定义如下所示: struct WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrbackground; LPCTSTR lpszMenuName; LPCTSTR lpsaClassName; //成员存储着为标识该特定的窗口类而提供的名称 HICON hIconSm; } //创建类 //WNDCLASSEX WindowClass; WindowClass.cbSize = sizeof(WNDCLASSEX); WindowClass.style = CS_HREDRAW | CS_VREDRAW WindowClass.lpfnWndProc = WindowProc; WindowClass.hIcon = LoadIcon(0, IDI_APPLICATION); WindowClass.hCursor = LoadCursor(0, IDC_ARROW); WindowClass.hbrBackground = static_cast<HBRUSH>(GetStockObject)(GRAY_BRUSH); static LPCTSTR szAppName = L"OFWin"; WindowClass.lpszClassName = szAppName; 2 创建程序窗口 将WNDCLASSEX结构的所有成员都设置为所需数值之后,下一步是把相关的情况告诉Windows,可以使用Windows API函数RegisterClassEx()来做这件事 RegisterClassEx(&WindowCLass); 给RegisterClassName()函数传递该结构的地址,Windows就会析取并记录所有结构成员的值,该过程被称为注册窗口类 提醒: 这里的术语"类"是在"分类"的意义上使用的,与C++中类的概念不同,不要混淆两者 //创建函数 CreateWindow(); //在CreateWindow()函数之后,被创建的窗口现在已经存在,但还没有显示在屏幕上,需要调用另一个Windows API函数将该窗口显示出来 ShowWindow(hWnd, nCmdShow); 3 初始化程序窗口 //通过调用另一个Window API函数UpdateWindow()请求Windows给程序发送一条重画窗口客户区的消息,该函数和语句如下 UpdateWIndow(hWnd); 4 处理Windows消息 最后一项需要的WinMain()完成的任务是处理Windows为应用程序排好的消息队列,这么说似乎有点奇怪,因为前面曾经说过需要WindowProc()函数来处理消息,但是请允许我稍加解释 排队消息与非排队消息 一种是被Window放入队列的排队消息,WinMain()函数必须从队列中析取这些消息进行处理,WinMain()函数中做这件事的代码称为消息循环,排队消息包括那些因用户从键盘输入,移动鼠标以及单击鼠标按钮而产生的消息,来自定义时器的消息和请求重画窗口的Windows消息也都是排队消息 另一种是致使Windows直接调用WindowProc()函数的非排队消息,大量的非排队消息是作为处理排队消息的结果产生的 消息循环 前面曾说过,从消息队列中获取消息是使用某种标准机制完成的,该机制在Windows编程中称为消息泵或消息循环, MSG msg; while(GetMessage(&msg, 0,0,0) == TRUE) { TranslateMessae(&msg); DispatchMessae(&msg); } GetMessage(): 从队列中获取一条消息 TranslateMessage(): 对获取的消息执行任何必要的转换 DiapatchMessage(): 使Windows调用应该程序的WindowProc()函数来处理消息 struct MSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } 注意: 对于为不同于普通窗口的其他窗口类型指定的消息,也有除了WM之外的前缀,附录C中列出了各种类型的消息前缀 多任务 如果没有排队的消息,则GetMessage()函数不会返回到程序中,Windows允许执行过程传递给中一个应用程序,仅当队列中有消息时才能调用GetMessage()函数获得返回值,该机制是允许多个应用程序在旧版本windows下运行的基础,称作为协作式多任务 在当前的windows版本中,操作系统可能在一段时间之后中断某个应用程序,然后将控制权传递给另一个应用程序,该机制称为抢先式多任务 在while循环内,首先对TranslateMessage()函数的调用请求windows为与键盘有关的消息做一些转换工作,然后对DispatchMessage()函数的调用使wisnows分派该消息-换句话说,就是调用程序中的WindowProc()函数来处理该消息,在windowProc()函数结束对消息的处理之前 DispatchMessage()函数不会返回,WM_QUIT消息意味着程序应该结束,因此该消息将导致使消息循环停止的FLASH返回给应用程序 5 完整的WinMain()函数 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WindowClass; static LPCTSTR szAppName = L"OFWin"; HWND hWnd; MSG msg; WindowClass.cbSize = sizeof(WNDCLASSEX); WindowClass.style = CS_HREDRAW | CS_VREDRAW; WindowClass.lpfnWndProc = WindowProc; WindowClass.cbClsExtra = 0; WindowClass.hInstance = hInstance; WindowClass.hIcon = LoadIcon(0, IDI_APPLICATION); WindowClass.hCursor = LoadCursor(0, IDC_ARROW); WindowClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH)); WindowClass.lpszMenuName = 0; WindowClass.lpszClassName = szAppName; WindowClass.hIconSm = 0; RegisterClassEx(&WindowClass); hWnd = CreateWindow( szAppName, L"A Basic Window the Hard Way", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0 ); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg, 0, 0, 0, ) == TRUE) { TranslateMessage(&msg); DispatchMessage(&msg); } } ///12.2.2 消息处理函数 除应用程序窗口的通用外观以外,WinMain()函数不包含任何应用程序特有的代码, 1:WindowProc()函数 WindowProc()函数的原型如下: LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM , wParam, LPARAM lParam); HWND hWnd 是一个句柄,指向致使该消息发生的事件所在的窗口 UINT message 指出消息类型的32位整数值ID WPARAM wParam 包含与消息种类有关的附加信息,是32位的数值 LPARAM lParam 包含与消息种类有关的附加信息,是32位的数值 2 解码Windows消息 解码Windows发送的消息的过程通常是基于message的值,在WindowProc()函数中使用switch语句来完成的,然后,选择希望处理的消息类型就只是为switch中的每种情形放一条case语句的问题而已,这样的swtich语句的典型结构 switch(message) { case WM_PAINT: break; case WM_LBUTTONDOWN: break; case WM_LBUTTONUP: beak; case WM_DESTROY: break; default: break; } 绘制窗口客户区 Windows通过给程序发送WM_PAINT消息,告诉程序应该重画客户区,因此在我们的示例中,需要在窗口中输出文本来响应WM_APRINT消息 HDC hDC; PAINTSTRUCT PaintSt; hDC = BeginPaint(hWnd, &PaintSt); //可以在RECT结束中获得客户区的坐标 RECT aRect; GetClientRect(hWnd, &aRect); SetBkMode(hDC, TRANSPARENT); //第一个实参标识设备上下文,第二个实参设定背景模式,默认选项是OPAQUE DrawText(hDC, L"But, soft! What light through yunder window breaks?", -1, &aRect, DT_SINGLELINE|DT_CENTER|DT_VCENTER); EndPaint(hWnd, &PaintSt); 结束程序 有人可能认为,关闭窗口就会关闭应用程序,但为了获得这样的特性,我们实际上必须添加一些代码,关闭窗口时应用程序不会自动关闭的原因在于可能需要做一些清理工作,应用程序也可能有多个窗口,当用户通过双击标栏目图标或单击关闭按钮关闭窗口时,系统将生成一条WM_DESTROY消息,因此,为了关闭应用程序,需要在WindowProc()函数中处理WM_DESTROY消息,可能使用下面这条语句生成一条WM_QUIT消息进行处理 PostQuitMessage(0); //完整的WindowProc()函数所需的所有元素 LRESULT WINAPI WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hDC; PAINTSTRUCT PaintSt; RECT aRect; switch(message) { case WM_PAINT: hDC = BeginPaint(hWnd, &PaintSt); GetClientRect(hWnd, &aRect); SetBkMode(hDC, TRANSPARENT); DrawText( hDC, L"But, soft! What light through yonder window breaks?", -1, &aRect, DT_SINGLELINE | DT_CENTER | DT_VECNTER ); EndPaint(hWnd, &PaintSt); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hWnd, message, wParam, lParam); break; } } 12.2.3 简单的Windows程序 12.3 Windows程序的组织 12.4 MFC 1 应用程序类 class COurApp: public CWinApp { public: virtual BOOL InitInstance(); }; 2 窗口类 class COurWnd: public CFrameWnd { public: COurWnd() { Create(0, L"Our Dumb MFC Application"); } } 3 完成程序 BOOL COurApp::InitInstance(void) { m_pMainWnd = new COurWnd; m_pMainWnd->ShowWindo(m_nCmdShow); return TRUE; } COurApp AnApplication; 4 最终产品 12.5 使用Windows Forms 12.6 小结 Windows API 提供了标准的编程接口,应用程序通过该接口与操作系统通信 所有Widnows应用程序都包括一个由操作系统调用的WinMain()函数,其作用是启动应用程序的执行过程,WinMain()函数还包括获取操作系统消息的代码 Windows操作系统调用应用程序中的特定函数来处理具体的消息,应用程序通过调用某个Windows API函数来标识应用程序中各个窗口的消息处理函数 MFC由一级封装了WIndows API,使利用Windows API编程得以简化的类组成 Windows Forms应用程序是在CLR中执行的,windows Forms应用程序中的窗口可以以图形方式创建,而所需要的所有代码都是自动生成的 */ int main() { cout<<"hello "; system("pause"); return 0; }