关于Windows应用程序的基础,基本内容就是那几项,以前学MFC的时候看孙鑫的视频讲过一遍,学习DirectX的时候学过一遍。现在回来想做MFC的程序,发现又忘了,这种学了忘忘了学的感觉真烦恼,每次都要从头学起,每次都是新手。所以我决定一定要自己写个总结,可能还是会忘记,但是查看自己的总结感觉踏实一些。
一. WinMain函数
Windows程序的入口函数:
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance // 应用程序实例的句柄。同一程序代码也有可能有多个实例,因为应用程序实例也是一种资源,所以需要对应用程序实例进行标识。 HINSTANCE hPrevInstance, // handle to previous instance // 先前实例的标识,之前没有则为空。Win32下总是为空。 LPSTR lpCmdLine, // command line //LP表示长指针,STR表示字符串,所以LPSTR表示指向字符串的指针。lpCmdLine接受命令行参数。 int nCmdShow // show state // 显示状态,指定程序运行的时候应该如何显示 );
WinMain函数由操作系统调用,因此WinMain函数的参数都是由操作系统进行赋值。
二.窗口
窗口一般需要进行以下几个步骤:
1.设计窗口
2.注册窗口
3.创建窗口
4.显示及更新窗口
窗口类是一个结构体
typedef struct WNDCLASSEX { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS;
1.设计窗口
设计窗口类。
WNDCLASSEX wc; //窗口类的类型 wc.style = CS_HREDRAW | CS_VREDRAW; //水平或垂直位置变化的时候要求窗口重画 //窗口过程函数,一个指针,指向一个窗口过程,用来接收一个函数指针,用来指定窗口对应的过程函数/回调函数 wc.lpfnWndProc = (WNDPROC) WndProc; //函数名可以代表代码的首地址 //类的附加内存 wc.cbClsExtra = 0; //通常情况下设置为0 //窗口的附加内存空间 wc.cbWndExtra = 0; wc.cbSize = sizeof(WNDCLASSEX); //应用程序实例号,来自WinMain函数参数 wc.hInstance = hInstance; //图标句柄 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); //标准图标被加载的时候第一个参数必须为NULL //如果要自定义的图标的话 //wc.hIcon = LoadIcon(hInstance, //(LPCTSTR)IDI_DIRECTX_ICON); //光标句柄 wc.hCursor = LoadCursor(NULL, IDC_ARROW); //画刷的句柄 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //菜单的名字 wc.lpszMenuName = 0; //窗口类的名字 wc.lpszClassName = "Class Name";
2.注册窗口
窗口设计完之后,需要向操作系统进行注册,之后才可以创建基于这种窗口类型的窗口。
//函数原型: //ATOM RegisterClass (CONST WNDCLASS* lpWndClass) ; //向操作系统注册窗口 RegisterClassEx(&wc) ) //传入窗口类结构体的地址 //函数的返回值为布尔值,注册成功则返回真
3.创建窗口
注册完成之后,就可以创建窗口类实例了。首先需要定义一个句柄,作为一个标识,保存下来。
HWND CreateWindow( LPCTSTR lpszClassName, //窗口类名 LPCTSTR lpszTitle, //窗口标题名 DWORD dwStyle, //创建窗口的样式 int x,y, //窗口左上角坐标 int nWidth,nHeight, //窗口宽度和高度 HWND hwndParent,//该窗口的父窗口句柄 HWENU hMenu, //窗口主菜单句柄 HINSTANCE hInstance, //创建窗口的应用程序当前句柄 LPVOID lpParam //指向一个传递给窗口的参数值的指针 ); //创建窗口 HWND hwnd = 0; //句柄,用以保存新创建的窗口的标识 hwnd = CreateWindow( " Class Name ", //注册的类名,设计窗口的时候指定的名字 "窗口标题", WS_OVERLAPPEDWINDOW,//层叠的窗口,具有标题栏和边框 0, 0, width, height, 0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); //第一个参数是注册的类名,一定要用注册之后的窗口类名 //第二个参数是窗口的名字,即窗口标题名 //第三个参数是窗口的类型 //之后的四个参数表示窗口的左上角水平坐标、垂直坐标、宽度、高度 //之后是一个窗口的句柄HWND,父窗口句柄,没有父窗口可设置为NULL //之后是菜单句柄 //之后是当前应用程序实例的句柄,WinMain形参之一 //之后是一个数据指针,多文档时用,主要作为WM_CREATE消息的附加参数
4.显示及更新窗口
//显示窗口 ShowWindow(hwnd, SW_SHOW);//窗口句柄及显示状态 //更新窗口,刷新一次窗口,可有可无 UpdateWindow(hwnd);
三.消息及消息循环
1.消息
操作系统将每个事件(输入设备上的变化)都包装成一个称为消息的结构体MSG来传递给应用程序,然后由应用程序来决定对此事件做出何种响应,应用程序通过调用函数的形式来通知操作系统执行相应的功能。应用程序对这些函数的调用就叫做系统调用,这些函数的集合就是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称Windows API。
消息结构体:
typedef struct tagMSG { HWND hwnd; //接受消息的窗口的句柄; //句柄是资源的标识,窗口句柄的类型是HWND UINT message; //消息标识符,表示具体的消息信息; WPARAM wParam; //32位的附加消息参数。根据消息的不同而不同。 LPARAM lParam; //32位的附加消息参数,其值与消息有关; DWORD time; //双16位,即32位的整数,消息放入消息队列的时间 POINT pt; //消息放入消息队列时鼠标的坐标 } MSG, *PMSG;
UINT是一个无符号整数,因为数值一般不好记忆,所以微软定义了一些宏来帮助记忆,宏以WM作为开头(Window Message),如WM_LBUTTONDOWN,WM_CHAR等。
当按下键盘之后,无论按下哪个键,收到的都是WM_CHAR消息,并不知道按下的是哪个字母,可以通过wParam获取到相对应的字母的ASCII码。
2.消息循环
消息队列是一个先进先出的缓冲区,操作系统为每一个应用程序都会建立一个消息队列,应用程序从消息队列中取出消息,进行相应的相应。
消息循环的常见写法如下:
MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage (&msg); DispatchMessage (&msg); }
GetMessage从消息队列中获取消息。检索到WM_QUIT消息时返回零值。
GetMessage( lpMSG,//指向MSGMSG结构的指针 hwnd, //窗口句柄 nMsgFilteMin, //用于消息过滤的最小消息号值 nMsgFilterMax//用于消息过滤的最大消息号值 );
TranslateMessage转换消息。比如将键盘的WM_KEYDOWN和WM_KEYUP消息转换成WM_CHAR消息,放在消息队列当中。
DispatchMessage将收到的消息传到窗口的回调函数(窗口过程函数)中。
3.窗口过程函数(回调函数)
窗口过程函数用来处理和响应消息。
LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
函数名可以更改,但是参数的类型不能变,参数名可以随便取。
接收一个消息,DispatchMessage调用的时候,操作系统调用这个窗口过程函数,并且将相关的参数传递给这个函数。直到窗口过程函数将控制返回给Windows操作系统,DispatchMessage才能返回。
函数内部一般用Switch case语句判断具体是什么消息,然后采取相应的措施。
一个回调函数的例子如下:
// // WndProc // //窗口过程函数,回调函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) //窗口句柄、消息标识、消息的附加参数 { //LRESULT是个Long型,返回的结果码 switch( msg ) { case WM_DESTROY: //在消息处理程序段中一般都有对//WM_DESTROY的处理,该消息是关闭窗口时发出的 PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) DestroyWindow(hwnd); break; default: //提供缺省处理,让系统对不感兴趣的消息进行一些缺省处理 return DefWindowProc(hwnd, msg, wParam, lParam); //按键: WM_CHAR //鼠标左键: WM_LBUTTONDOWN //窗口重绘: WM_PAINT //关闭: WM_CLOSE //DestroyWindow函数销毁窗口,并发送以下消息 //WM_DESTROY 然后PostQuitMessage(0) 退出程序,发送WM_QUIT消息,消息循环返回值,结束消息循环,退出WinMain函数 } return 0; }