本笔记源自windows游戏编程大师技巧第二版2.9章,记做学习随笔。
使用vs2017环境编译。
在此之前,我们写的Windows类,也就是:
WNDCLASSEX winclass;
中,有一个类的成员变量,他要求填写一个回调函数。这个函数就是我们要写的事件处理函数,他在这里:
#include <Windows.h>
#include <windowsx.h>
#define WIN32_LEAN_AND_MEAN
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline,int ncmdshow)
{
WNDCLASSEX winclass;
.
.
winclass.lpfnWndProc = WndProc; //WndProc还没有定义。
.
.
return 0;
}
当你的窗口有了新的事件,Windows就会调用这个回调函数来通知你。
这个回调函数原型如下:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
hend——这是一个窗口句柄,当你的程序有多个窗口可以用到。
msg——这是个实际处理消息的标识符。
wparam和lparam——进一步匹配或分析发送到msg参数中的信息。
这张图说明了多窗口的处理,这也是hend句柄处理多窗口的原理。
我们只需要用switch()来处理msg所表示的信息,在此之前先看一下消息标识符表:
WM_ACTIVATE | 当窗口被激活或成为一个焦点传递 |
WM_CLOSE | 当窗口关闭时关闭 |
WM_CREATE | 当窗口第一次创建时传递 |
WM_DESTROY | 当窗口可能要被销毁时传递 |
WM_MOVE | 当窗口移动时传递 |
WM_MOUSEMOVE | 当鼠标移动时传递 |
WM_KEYUP | 当松开一个键时传递 |
WM_KEYDOWN | 当发生定时程序时传递 |
WM_TIMER | 允许传递消息 |
WM_USER | 允许传递消息 |
WM_PAINT | 当一个窗口需要重回时传递 |
WM_QUIT | 当Windows应用程序最后结束传递 |
WM_SIZE | 当一个窗口改变大小时传递 |
我们现在只关心三个消息:WM_CREATE,WM_PAINT,WM_DESTROY
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (msg)
{
case WM_CREATE: //窗口第一次创建的消息
{
return 0;
}break;
case WM_PAINT: //窗口需要重绘的消息
{
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}break;
case WM_DESTROY: //窗口销毁的消息
{
PostQuitMessage(0);
return 0;
}break;
default:
break;
}
return (DefWindowProc(hwnd, msg, wparam, lparam));
}
我们还无需添加其他功能,所以WM_CREATE直接return 0即可。
WM_PAINT就非常重要了,我们调用了BeginPaint();EndPaint();重绘窗口。(这里有待补充)
WM_DESTROY中调用PostQuitMessage(0);意为终止线程,也就是终止你的应用程序。
现在来看一下我们全部的代码吧:
#include <Windows.h>
#include <windowsx.h>
#define WIN32_LEAN_AND_MEAN
//新加入的代码WndProc函数的定义。
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (msg)
{
case WM_CREATE://窗口第一次创建的消息
{
return 0;
}break;
case WM_PAINT://窗口需要重绘的消息
{
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}break;
case WM_DESTROY://窗口销毁的消息
{
PostQuitMessage(0);
return 0;
}break;
default:
break;
}
return (DefWindowProc(hwnd, msg, wparam, lparam));
}
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline,int ncmdshow)
{
WNDCLASSEX winclass;
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
winclass.lpfnWndProc = WndProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = "WINCLASS1";
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&winclass))
return 0;
//HWND hwnd;
if (!(hwnd = CreateWindowEx(NULL,
"WINCLASS1", //winclass.lpszClassName设置的字符,是类的别名。
"YouWindows", //窗口的名称
WS_OVERLAPPEDWINDOW | WS_VISIBLE, //一些窗口属性
0, 0, //窗口位置
400, 400, //窗口宽高
NULL, //父窗口句柄
NULL, //附属窗口句柄
hinstance, //WinMain第一个实参,程序句柄。
NULL //null就可以了
)))
return 0;
return 0;
}
到目前为止,我们已经很接近完成这个程序了,但是你现在执行虽然不会错报,但是仍不会有任何窗口弹出,他只会一闪而过,因为我们还没写最后的循环程序。
这三个函数并不复杂,就只在代码里注释,函数请查阅文档。
while (GetMessage(&msg,NULL,0,0)) //不断从事件队列里取到消息
{
TranslateMessage(&msg); //将虚拟消息转化为字符消息
DispatchMessage(&msg); //他可以调用回调函数,也就是刚才做的WndProc()处理消息。
}
来放出一下全部的代码:
#include <Windows.h>
#include <windowsx.h>
#define WIN32_LEAN_AND_MEAN
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (msg)
{
case WM_CREATE:
{
return 0;
}break;
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}break;
default:
break;
}
return (DefWindowProc(hwnd, msg, wparam, lparam));
}
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline,int ncmdshow)
{
HWND hwnd;
MSG msg;
WNDCLASSEX winclass; //新建窗口类
winclass.cbSize = sizeof(WNDCLASSEX); //填写WNDCLASSEX的大小,方便程序计算
winclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS; //窗口描述属性样式
winclass.lpfnWndProc = WndProc; //消息回调函数
winclass.cbClsExtra = 0; //为窗口添加附件内存,一般填0
winclass.cbWndExtra = 0; //为程序天机附件内容,也是0
winclass.hInstance = hinstance; //填写句柄
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //装置一个标准程序应用图标
winclass.hCursor = LoadCursor(NULL, IDC_ARROW); //装置鼠标样式
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //设置窗口填充颜色
winclass.lpszMenuName = NULL; //用于加载和选用窗口
winclass.lpszClassName = "WINCLASS1"; //为你的类取一个别名
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); //他是程序的小图标,用途在任务栏和标题栏实现的图标。
if (!RegisterClassEx(&winclass)) //把我们新建的窗口类注册上去
return 0; //失败就返回
//新加入的代码
//HWND hwnd;
if (!(hwnd = CreateWindowEx(NULL,
"WINCLASS1", //winclass.lpszClassName设置的字符,是类的别名。
"YouWindows", //窗口的名称
WS_OVERLAPPEDWINDOW | WS_VISIBLE, //一些窗口属性
0, 0, //窗口位置
400, 400, //窗口宽高
NULL, //父窗口句柄
NULL, //附属窗口句柄
hinstance, //WinMain第一个实参,程序句柄。
NULL //null就可以了
)))
return 0;
while (GetMessage(&msg,NULL,0,0)) //不断从事件队列里取到消息
{
TranslateMessage(&msg); //将虚拟消息转化为字符消息
DispatchMessage(&msg); //他可以调用回调函数,也就是刚才做的WndProc()处理消息。
}
return(msg.wParam);
}