学习DirectX之前需要了解一下Windows编程基础,每个Windows程序需要创建一个窗体。创建窗体大致分为三步:(1)注册一个窗体类(2)创建窗体对象(3)创建消息处理函数,用来接收并处理Windows发来的消息。使用如下三个函数来完成以上的三个步骤:
RegisterClassEx(); // 注册窗口
CreateWindowEx(); // 创建窗口
ShowWindow(); // 显示窗口
关于Windows的接口可在msdn上查询。类似于win32控制台程序的main函数,Windows程序的入口点是WinMain函数。WinMain函数结构如下:
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
);
其中,WINAPI是一个调用约定(#define WINAPI __stdcall),定义了参数出现在堆栈上的顺序:
压栈顺序:从右至左
清栈:被调用函数负责清空,称为callee cleanup
名字修饰:在函数名前加上下划线做前缀,名字后用@加上函数参数大小做后缀
【注:WINAPI就是__stdcall的#define】
hInstance | 应用程序当前实例的句柄。当它被加载到内存中时,操作系统使用这个值来识别可执行文件 (EXE)。某些 Windows 功能需要实例句柄,例如加载图标或位图。 |
hPrevInstance | 应用程序前一个实例的句柄。此参数始终为NULL。 |
pCmdLine | 应用程序的命令行,不包括程序名称。 |
nCmdShow | 是一个控制窗口显示方式的标志,表示主应用程序窗口是最小化、最大化还是正常显示。 |
一、注册一个窗体类
首先需要创建一个窗口类WNDCLASSEX,设置窗体的各种属性;窗口类与C++中类的概念不同,它是窗口属性的一种模板。
WNDCLASSEX结构如下:
typedef struct tagWNDCLASSEXA {
UINT cbSize; // 此结构体大小
UINT style; // 窗口类风格,该成员可以是Class Style的任意组合
WNDPROC lpfnWndProc; // 指向窗口的指针
int cbClsExtra; // 按照窗口类结构分配的额外字节数,系统将字节初始化为零
int cbWndExtra; // 在窗口实例之后分配的额外字节数
HINSTANCE hInstance; // 包含窗口类实例句柄
HICON hIcon; // 类图标的句柄
HCURSOR hCursor; // 类光标的句柄
HBRUSH hbrBackground; // 类背景画笔的句柄
LPCSTR lpszMenuName; // 指向以空字符结尾的字符串的指针,该字符串指定类菜单的资源名称
LPCSTR lpszClassName; // 指向以空字符结尾的字符串的指针,窗口类名称
HICON hIconSm; // 与窗口类关联的小图标的句柄
};
相关代码如下:
// 这个结构体用于保存窗口类相关的信息
WNDCLASSEX wc;
// 清空窗口类以供使用
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// 在结构体中填写所需的窗口类信息
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"MyFirstWindow";
wc.hIconSm = LoadIcon(wc.hInstance, IDI_APPLICATION);
// 注册这个窗口类
if (!RegisterClassEx(&wc))
return E_FAIL;
二、创建窗体对象
根据创建的窗口类来创建窗口。要创建窗口,需要函数:
HWND CreateWindowEx(
DWORD dwExStyle, // 指定窗口的扩展样式
LPCTSTR lpClassName, // 窗口类名称
LPCTSTR lpWindowName, // 窗口名称
DWORD dwStyle, // 指定正在创建的窗口的样式
int x, // 指定窗口的初始水平位置
int y, // 指定窗口的初始垂直位置
int nWidth, // 以设备为单位指定窗口宽度
int nHeight, // 以设备为单位指定窗口高度
HWND hWndParent, // 正在创建的窗口的父窗口或所有者窗口的句柄
HMENU hMenu, // 菜单句柄
HINSTANCE hInstance, // 要与窗口关联的模块实例的句柄
LPVOID lpParam // 长指针,指向要通过WM_CREATE消息的lParam参数中
// 传递的CREATESTRUCT结构传递给窗口的值
);
相关代码如下:
// 创建窗口,并将返回的结果作为句柄
hWnd = CreateWindowEx(NULL,
L"MyFirstWindow", // 窗口类的名字
L"Initial Window", // 窗口的标题
WS_OVERLAPPEDWINDOW, // 窗口的样式
300, // 窗口的x坐标
300, // 窗口的y坐标
500, // 窗口的宽度
400, // 窗口的高度
NULL, // 我们没有父窗口,设置为NULL
NULL, // 我们不使用菜单,设置为NULL
hInstance, // 应用程序句柄
NULL); // 与多个窗口一起使用,设置为NULL
显示窗口
显示窗口函数:
BOOL ShowWindow(HWND hWnd, // 创建的窗口句柄
int nCmdShow // WinMain()函数的最后一个参数,控制窗口的显示方式
);
三、创建消息处理函数
Windows消息处理机制:Windows通过GetMessage()函数从消息队列中取得消息,使用TranslateMessage()来处理某些消息格式,然后由DispatchMessage()函数将消息派送到指定窗口,再由窗口函数WindowProc()对消息处理。流程如下:
其中:
TranslateMessage()函数将虚拟键消息转换成字符消息;
DipatchMessage()函数将消息派送给窗口,通常情况下被用来发送GetMessage()函数获得的消息。
WindowProc()是应用程序定义,用来处理发送到窗口的消息的函数,定义如下:
LRESULT CALLBACK WindowProc (
HWND hWnd, // 指向窗口的句柄
UINT message, // 执行消息类型
WPARAM wParam, // 指定其余的、消息特定的信息
LParam lParam // 指定其余的、消息特定的信息
);
Windows窗口初始化
完整代码:
#include <Windows.h>
// 全局变量
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
// 函数声明
HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
if (FAILED(InitWindow(hInstance, nCmdShow)))
return 0;
// 消息循环
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
// 注册窗口类并创建窗口
HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow)
{
// 注册窗口类
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"MyFirstWindow";
wc.hIconSm = LoadIcon(wc.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
return E_FAIL;
// 创建窗口
g_hInst = hInstance;
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
g_hWnd = CreateWindow(
L"MyFirstWindow",
L"Initial Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
rc.right - rc.left,
rc.bottom - rc.top,
NULL,
NULL,
hInstance,
NULL);
if (!g_hWnd)
return E_FAIL;
ShowWindow(g_hWnd, nCmdShow);
return S_OK;
}
// 每次当应用程序接收消息时调用这个方法
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
参考博客:
https://blog.csdn.net/weixin_34194087/article/details/92631247