1.句柄(HANDLE):每个windows资源都独有的“身份证
我们对某个窗口实现操作的话,首先要得到窗口句柄HWND。
windows程序中,有各种各样的资源,比如窗口、图标、光标等,系统创建这些资源会分配内存,并返回这些资源的标识号,这标识号就是句柄
2.消息:windows应用程序与操作系统间的“信使”
消息的表现形式--MSG结构体
typedef struct tagMSG{
HWND hwnd;//定义消息所属的窗口
UINT message;//指定消息的标识符 ,消息是由一个数值来表示的,不同消息对应不同数值,数值不方便记忆,将其定义为宏的形式WM_XXX ,如鼠标左键 //WM_LBUTTONDBLCLK ,VS中鼠标选择该代码WM_XXX右键,转到定义,(F12可查看具体定义)
WPARAM wParam;//指定msg的附加信息
LPARAM lParam;//指定msg的附加信息
DWORD time;//指定投递到消息队列的时间
POINT pt;//指定投递到消息队列中时鼠标的当前位置
}MSG;
窗口创建四部曲---设计,注册,创建,显示与更新。
3.设计
在windows中控制窗口的特征的结构体有两个,我们现在用更被时代喜爱的WINDCLASSEX机构体
typedef struct tagWINDCLASSEX{
UINT cbSize; //表示该结构体字节数的大小,一般在主窗口函数这样写:windClass.cbSize=sizeof(WNDCLASSEX);
UINT style; //指定窗口的样式风格,取多个要用“|”来连接,eg:CS_HREDRAW等。
WINDPROC lpfnWndProc; //函数指针,指向窗口过程函数。
int cbClsExtra; //窗口类附加内存,一般设置为零。winClass.cbClsExtra=0;
int cbWndExtra; //窗口附加内存。
HINSTANCE hInstance; // 包含窗口过程的实例句柄。把winMain函数的第一个参数,也就是当前运行的实例句柄传给他。winClass.hInstance =hInstance;
HICON hIcon; //窗口类的图标句柄,如果为NULL,则为默认图标。
HCURSOR hCursor; //光标句柄
HBRUSH hbrBackground; //窗口类的背景画刷句柄
LPCTSTR lpszMenuName; //菜单资源的名字,没有菜单设置为NULL
LPCTSTR lpszClassName; //以一个空终止的字符串指定窗口类的名字。winClass.lpszClassName=_T("forthetream");
HICON hIconSm; //右下角小图标的句柄
} WNDCLASSEX.*PWNDCLASSEX;
在WInMain函数中定义一个WINDCLASSEX
WINDCLASSEX wndClass ={0};
窗口过程函数被调用的过程:待写。
4.窗口类的注册。
在设计完跑车后,需要经国家部门有关批准才能合法生产这种跑车。在设计完窗口类后,需要调用RegisterClassEx函数对其进行注册。
RegisterClassEx(&wndClass);
5.窗口类的正式创建
Createwindow函数来创建设计好的窗口
原型声明
HWND WINAPI CreatorWindow{
_In_opt_ LPCTSTR lpClassName, //对应窗口类的名称就是设计类的winClass.lpszClassName=_T("forthetream") _In_opt_ LPCTSTR lpWindowName, //游戏窗口左上角标题栏的名字,如 植物大战僵尸 _In_ DWORD dwStyle, //窗口样式,指定某个具体窗口的样式,WNDCLASSEX中的style成员是以后的窗口都基于这个窗口样式,
//一个代表性样式WS_OVERLAPPEDWINDOW 标题栏,系统菜单,可调边框,最小化,最大化。
_In_ int x, //窗口的水平位置,一般我们喜欢取CW_USEDEFAULT,表示默认的位置。
_In_ int y, //窗口的竖直位置,同样的喜欢取CW_USEDEFAULT,表示默认的位置。
_In_ int nWidth, //指定窗口宽度
_In_ int nHeight, //窗口高度
_In_opt_ HWND hWndParent, //指定被创建窗口的父窗口句柄,一般设为NULL
_In_opt_ HMENU hMenu, //窗口菜单资源的句柄,一般设为NULL
_In_opt_ HINSTANCE hInstance, //窗口所属的应用程序实例句柄,应用程序的ID,即WinMain函数的第一个参数一致,hInstance._In_opt_ LPVOID lpParam //附加参数传入指针,大多数设为NULL。
}
如果窗口创建成功,该函数会返回一个句柄,如果失败则为NULL
在这之前,我们先定义一个窗口句柄变量,
HWND hWnd=CreateWindow(
_T("forthetream"),"致我们永不熄灭的梦想",
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
800,600,NULL,NULL,hInstance,NULL);
6.窗口的显示与更新。
设定窗口位置的函数MoveWindow
下面代码就是改变一下窗口的位置BOOL WINAPI MoveWindow( _In_ HWND hWnd, //CreaterWindow窗口创建的窗口句柄, _In_ int X, //指定窗口左方相对于屏幕左上角的新位置 _In_ int Y, //上方相对于屏幕左上角的新位置 _In_ int nWidth, //窗口新宽度 _In_ int nHeight, //窗口新高度 _In_ BOOL bRepaint //指定是否要重新画窗口,如果为FALSE的话,不会发生任何类型的重画操作。 );Movewindow(hWnd,200,50,800,600,ture);//窗口左上角位于屏幕坐标(200,50)处。
显示窗口
BOOL WINAPI ShowWindow( _In_ HWND hWnd, //之前窗口的句柄 _In_ int nCmdShow //窗口的显示状态,可以直接填nCmdShow,因为这个函数时WinMain函数内部调用的);实例:ShowWindow(hWnd,nShowCmd);
更新窗口
显示窗口后,我们要更行刷新一下,好比买新房子要装修下, UpdateWindow(hWnd);
两套消息循环体系
四部曲的洗礼之后,我们要编写一个消息循环,不断的从消息列队中取出消息,并且进行响应。有俩个函数GetMessage 和PeekMessageGetMessage以GetMessage消息循环来写一个固定的消息循环:BOOL WINAPI GetMessage( _Out_ LPMSG lpMsg, //它指向一个消息(MSG)机构体,从线程的消息列队中取出的消息信息保存在结构体中。 _In_opt_ HWND hWnd, //指定接受属于哪一个窗口的消息,一般设为NULL,表示接收属于调用线程的所用窗口的窗口信息。 _In_ UINT wMsgFilterMin, //接受信息最小值,一般设为零就好 _In_ UINT wMsgFilterMax //接受信息最大值,设置为零的话表示接收所用消息,来者不拒 );MSG msg={0};while(GetMessage(&msg,NULL,0,0)) //不断从消息队列中取出消息{TranslateMessage(&msg); //将虚拟键消息转换为字符消息DispatchMessage(&msg); //分发一个消息给窗口程序}
PeekMessage主要用于处理发送给窗口的消息,
BOOL WINAPI PeekMessage( _Out_ LPMSG lpMsg, //和上述一样 _In_opt_ HWND hWnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _In_ UINT wRemoveMsg //指定于消息的获取方式,一般在PM NOREMOVE 和PM REMOVE中取值,如果为前者,PeekMessage函数取出某条消息后,消息将不会从消息队列中被移除(就像这条消息被偷看了一样);如果为后者,这条消息将从消息队列中移除(就像消息被拿走了一样) );GetMessage与PeekMessage的不同点:·PeekMessage 有点像乞丐,有你就施舍点,没有也不强求,使其处理消息很顺畅,符合游戏运行要求。·GetMessage 像强盗打劫,有你得给,没有我就等你有,它处理消息时程序在没有消息的时候处于休眠状态,不符合游戏运行要求。windows程序的中枢神经--窗口过程函数
LRESULT CALLBACK WindowProc( //他的名字在实际编写程序过程中可以随便取的,不一定叫WindowProc。注意:系统通过窗口过程函数的地址来调用,而不是名字。 _In_ HWND hwnd, //要处理窗口的句柄 _In_ UINT uMsg, //待处理消息的ID,即消息的类型。 _In_ WPARAM wParam, //附加信息 _In_ LPARAM lParam //附加信息 );
做好善后--窗口类的注销用到的是UnregisterClass,与之前的RegisterClassEx对应,注意前者没有Ex,UnregisterClass(类名称,实例句柄);UnregisterClass(L"gofortream",winClass.hInstance);
#include <Windows.h>
代码实例
//宏定义部分
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define WINDOW_TITLE L"为梦想而战"
//全局函数声明
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam);//窗口过程函数
//winMain 函数 我们的程序从这里开始。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//一。设计一个完整的窗口类
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = (HICON)::LoadImage(NULL, L"icon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName =L"goforgream";
//二。注册窗口类
if (!RegisterClassEx(&wndClass))
return -1;
//三。创建窗口
HWND hwnd = CreateWindow(L"goforgream", WINDOW_TITLE,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL,NULL, hInstance, NULL);
//四。窗口的移动、显示与更新
MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(hwnd, nShowCmd);
//五。消息循环
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//六。窗口类的注销。
UnregisterClass(L"goforgream", wndClass.hInstance);
return 0;
}
//窗口过程函数,消息处理。
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
ValidateRect(hwnd, NULL);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
![]()
图片放到该项目的debug文件下。