Windows程序一般都等待用户进行一些操作,然后响应并采取行动。
一般来说,对win32的程序的操作都会转换为系统事件队列中的消息,如按键消息WM_KEYDOWN,WM_MOUSECLICK等传递键盘以及鼠标的操作消息。系统消息传递给程序的本地事件队列,然后在传递给WinProc()函数进行主窗口的消息处理,处理完消息后,程序转到WinMain()主函数中,而此时一般主函数依然在进行消息循环,于是又等待新的消息并执行。
win32的程序都是有winmain开始,最简单的一个win32程序,从空项目开始:
#define WIN32_LEAN_AND_MEAN #include <Windows.h> int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { MessageBoxA(NULL,"TRY A TRY","MY TRY", MB_OK | MB_ICONEXCLAMATION ); return (0); }
winMain 函数
SDK中一个简单的提示声音的函数 MessageBeep(UINT utype),参数值utype常用的有 MB_OK 系统默认声音,当然如果你将计算机系统中的系统声音设置为无声,就听不到声音的。
从一个空项目开始创建一个完整的Windows程序的步骤:
创建一个Windows类。
创建一个事件处理程序WinProc
向Windows注册创建的Windows类:定义了Windows类后,还要通过注册,让Windows操作系统知道这个类,注册通过函数 RegisterClassEx()来完成,接收一个指向Windows类的指针作为参数。调用政策函数之前,Windows系统还不知道有这个类,因此不能使用新的类名来引用它,而是用但钱储存的类的实际数据结构来进行注册。
使用Windows类创建一个窗口
创建一个主事件循环,用于接收Windows消息并将其发送给事件处理程序。
最后,一个简单的空白win32项目代码如下:
//不加载MFC #define WIN32_LEAN_AND_MEAN #include <Windows.h> #include <windowsx.h> //Windows类类名常量 const char* MYCLASSNAME = "WINCLASS"; //消息处理函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; switch(msg){ case WM_CREATE: { //初始化代码 }break; case WM_PAINT: { hdc = BeginPaint(hwnd,&ps); //重绘 EndPaint(hwnd,&ps); }break; case WM_DESTROY: { //释放资源,关闭应用程序 PostQuitMessage(0); }break; default: return (DefWindowProc(hwnd,msg,wParam,lParam)); } return (0); } //主函数 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprevInstance, LPSTR lpcmdline, int ncmdshow) { WNDCLASSEX wcex;//创建的窗口类 HWND hwnd;//窗口句柄 MSG msg;//消息 //设置窗口类 wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(NULL,IDI_APPLICATION); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = MYCLASSNAME; wcex.hIconSm = LoadIcon(NULL,IDI_APPLICATION); //注册窗口类 if(!RegisterClassEx(&wcex)){ return (0); } //创建窗口 if( !( hwnd = CreateWindowEx(NULL,MYCLASSNAME, "MY", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 400, 500, NULL, NULL, hInstance, NULL) ) ) { return(0); } //进入主循环 while( GetMessage(&msg,NULL,0,0) ){ TranslateMessage(&msg); DispatchMessage(&msg); } //返回到操作系统 return msg.wParam; }
但这为一般程序的基本结构,对于游戏,要构建一个实时事件循环,使其既能进行游戏逻辑处理,例如一个Game_main()函数,又能实时检测消息队列中的消息并处理。
这里就要构建一个实时事件循环,使用函数PeekMessage () 来检测消息队列中是否有消息。如果有,对其进行处理,否则继续处理其他游戏逻辑并重复循环。PeekMessage 函数原型如下:
BOOL PeekMessage( LPMSG IpMsg, //消息 的指针 HWND hWnd,//窗口句柄 UINT wMSGfilterMin,//第一条消息 UINT wMsgFilterMax,//最后一条消息 UINT wRemoveMsg//删除标记 );一般第一条消息和最后一条消息都设为0,而删除标记有三种:
PM_NOREMOVE
PeekMessage处理后,消息不从队列里除掉。
PM_REMOVE
PeekMessage处理后,消息从队列里除掉。
PM_NOYIELD
此标志使系统不释放等待调用程序空闲的线程。可将PM_NOYIELD随意组合到PM_NOREMOVE或PM_REMOVE。
PM_NOREMOVE或PM_REMOVE是主要的标记,若使用不删除消息,就要配合GetMessage()来获得消息进行处理。若使用PM_REMOVE,则直接使用PeekMessage获得消息,对应的实时事件循环代码如下:
while(true){ //使用peekMessage获得消息,若没有直接进游戏逻辑 if(PeekMessage(&msg,hwnd,0,0,PM_REMOVE)){ if(msg.message == WM_QUIT)//如果消息为WM_QUIT,则结束主循环 break; TranslateMessage(&msg); DispatchMessage(&msg); } //主游戏处理逻辑 Game_Main(); }这里主游戏处理逻辑必须有返回,即生成一个动画帧或执行了一段游戏逻辑后必须返回。