该程序包含了WinMain函数、WNDCLASS结构、CreateWindow函数、tagMSG结构、MessageBox函数等的解析,参考:Introduction to 3D Game Programming with DirectX 11附录1,MSDN。
程序流程:
1. 初始化窗口类并注册;
2. 创建窗口;
3. 执行消息循环,如果有消息,调用WndProc函数,否则转4;
4. 图形渲染,游戏逻辑等。
#include <windows.h>
/*
* 创建主窗口句柄:在windows编程中,经常用句柄(handle)来引用由windows内部维护的对象。
* 很多API的调用需要传递该参数。
*/
HWND mainWnd = 0;
bool initWndApp(HINSTANCE hInstance, int show);
int run();
LRESULT CALLBACK
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
/*
* hInstance: 当前应用程序实例的句柄,可能有多个应用程序同时运行
* hPreInstance: 该参数不在Win32编程中使用
* pCmdLine: 命令行参数字符串
* nShowCmd: 指定应用程序的显示方式,
* SW_SHOW: 按当前大小和位置,
* SW_SHOWMAXIMIZED:最大化,
* SW_SHOWMINIMIZED:最小化
*/
int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR pCmdLine, int nShowCmd)
{
if ( !initWndApp(hInstance, nShowCmd) ){
return 0;
}
return run();
}
WINAPI声明符强制参数从左边向右边传递,而默认的CDECL声明符使参数从右到左转移。
lpcmdline相当于C语言main函数的参数char **argv。比如在命令行带参数的运行test.exe:
test.exe hellow
lpcmdline = "hellow"
typedef struct _WNDCLASSEX {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPSTR lpszMenuName;
LPSTR lpszClassName;
HICON hIconSm;
}WNDCLASS;
- style: 指定类的风格,CS_HREDRAW和CS_VREDRAW的组合表示,窗口水平和垂直方向上大小改变时要重绘
- cbSize: 结构体的大小
- lpfnWndProc: 指向windows过程函数的指针,该函数与指定的WNDCLASS对象相关联
- cbClsExtra, cbWndExtra: 可以额外使用的内存空间,
- hIcon: 图标句柄
- hCursor: 光标句柄
- hbrBackground: 背景刷句柄
- lpszMenuName: 指定窗口菜单
- lpszClassName: 指定窗口类结构的名称,方便后面引用
bool initWndApp(HINSTANCE hInstance, int show)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
//使用默认的应用程序图标和光标
wc.hIcon = LoadIcon(0,IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_CROSS);
//调用win32函数GetStockObject()返回白色画刷句柄
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = L"BasicWndClass";
//注册填好的WNDCLASS实例,以此创建窗口
if ( !RegisterClass(&wc)) {
MessageBox(0, L"RegisterClass Failed", 0, 0);
return false;
}
mainWnd = CreateWindow(L"BasicWndClass", L"Win32Basic",WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, hInstance, 0);
if (mainWnd == 0) {
MessageBox(0, L"CreateWindow FAILED", 0, 0);
return false;
}
ShowWindow(mainWnd, show);
UpdateWindow(mainWnd);
return true;
}
//HWND CreateWindow(
// LPCTSTR lpClassName,
// LPCTSTR lpWindowName,
// DWORD dwStyle,
// int x,
// int y,
// int nWidth,
// int nHeight,
// HWND hWndParent,
// HMENU hMenu,
// HANDLE hInstance,
// LPVOID lpParam
// );
/*
* lpClassName: 注册的WNDCLASS类的名称
* lpWindowName: 窗口名称,将显示在标题栏
* dwStyle: 定义窗口风格
* x, y: 窗口左上角坐标
* nWidth, nHeight:窗口的宽和高,可指定为默认的CW_USEDEFAULT
* hWndParent: 父窗口句柄,没有为0
* hMenu: 菜单句柄,没有为0
* hInstance: 应用程序句柄
* lpParam: 指向用户定义类型的指针,如果需要用到WM_CREATE消息句柄。该消息在窗口创建时发出,
* 一般用于在创建窗口时进行一些初始化。
*/
//typedef struct tagMSG{
// HWND hwnd;
// UNIT message;
// WPARAM wParam;
// LPARAM lParam;
// DWORD time;
// POINT pt;
//}MSG;
/*
* hwnd: 用于接收消息的窗口句柄
* message: 消息标示,如:WM_QUIT
* wParam, lParam:消息的额外信息
* time: 消息发出的时间
* pt: 消息发出时,鼠标光标在屏幕坐标系中的坐标(x,y)
*/
int run()
{
MSG msg = {0};
BOOL bRet = 1;
//while ( (bRet = GetMessage(&msg,0,0,0)) != 0 ) {
// //返回-1表示GetMessage出现错误
// //返回0 表示收到WM_QUIT消息,需要终止消息循环,
// //返回其他值时,先TranslateMessage,Windows会做一些键盘转换,
// //特别是将按键转换成字符消息;然后DispatchMessage将消息发送给
// //合适的窗口过程
// if (bRet == -1) {
// MessageBox(0, L"GetMessage FAILED", L"Error", MB_OK);
// }else {
// TranslateMessage(&msg);
// DispatchMessage(&msg);
// }
//}
// 当消息队列中没有消息时,GetMessage会将进程挂起,直到收到消息。
// 对于游戏程序,我们并不想这样。在没有消息的时候我们需要做游戏相关的事。
// PeekMessage在没有消息时会立即返回
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}else {
//do animation/game stuff
}
}
return (int)msg.wParam;
}
int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
- hWnd: 该消息父窗口的句柄,NULL表示该消息盒子父窗口为桌面
- lpText: 在盒子中显示的信息
- lpCaption:盒子的标题,如果是NULL,则默认为Error
- uType: 指定盒子的内容和行为,
MB_OK 表示会有一个OK键,类似的还有MB_OKCANCLE,MB_YESNO等
MB_ICONEXCLAMATION 表示显示一个惊叹号图标,类似的有MB_ICONINFORMATION,MB_ICONQUESTION,MB_ICONSTOP,分别表示I图标,问号图标,终止符图标。MB_DEFBUTTONn 控制默认高亮的按钮,n=1-4 - 返回值,例如,IDYES表示按下了YES按钮,该返回值可以用来测试用户按下的是哪个键。
//WndProc是一个回调函数,Windows将在程序的代码空间外调用该函数
//用于处理收到的消息,如按键,鼠标点击等。
/*
* hWnd: 接受消息的窗口
* msg: 接受的消息,如:WM_QUIT
* wParam, lParam:消息的额外信息
*/
LRESULT CALLBACK
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg){
case WM_CREATE:
MessageBox(0, L"窗口创建成功!", L"Create", 0);
return 0;
case WM_LBUTTONDOWN:
MessageBox(0, L"你好!", L"Hello", MB_OK);
return 0;
case WM_KEYDOWN:
//如果按下Escape键,DestroyWindow销毁主窗口
//wParam参数保存着虚拟键码(Virtual key code)
if (wParam == VK_ESCAPE){
DestroyWindow(mainWnd);
}
return 0;
case WM_CLOSE:
//关闭窗口时提示是否要退出
if (MessageBox(0, L"确定要退出吗?", L"Exit",
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) == IDYES){
PostQuitMessage(0);
}
return 0;
case WM_DESTROY:
//当窗口销毁时,发出WM_QUIT消息,退出消息循环
PostQuitMessage(0);
return 0;
}
//最后调用默认Windows过程函数,处理其他消息
return DefWindowProc(hWnd, msg, wParam, lParam);
}
- WM_CREATE,当窗口第一次创建的时候传递该消息,以便进行一些初始化或资源分配。
- WM_DESTROY,在关闭窗口时被发送。当然,这仅仅是关闭窗口,而不是终止应用程序。必须通过发送WM_QUIT消息,来终止消息循环,从而终止应用程序。