DX11Win32窗口实现
-
X_jun师兄的DX11初始化可能涉及到Win32 初始化的方法目前在这里不做过多描述。
-
- 对此小萌新的我微微简单说一下,如果有不对的地方,请一定留言斧正!!!
- 对于Direct3D初始化,博客已经写的很详细了,这里不多说
- 下面都是代码了,因为笔记都注释在代码上了。
- 1、Main.cpp源文件
//可以定义一个_In_的宏,这个宏什么都不做.定义在" no_sal2.h "文件中
//为了编译系统在分析代码时发现缺陷用的
//LPSTR -> char*
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE prevInstance,
_In_ LPSTR cmdLine, _In_ int showCmd)
{
//这些参数不使用
//UNREFERENCED_PARAMETER避免编译器关于未引用参数的警告
UNREFERENCED_PARAMETER(prevInstance);
UNREFERENCED_PARAMETER(cmdLine);
UNREFERENCED_PARAMETER(showCmd);
// 允许在Debug版本进行运行时内存分配和泄漏检测
#if defined(DEBUG) | defined(_DEBUG)
// 检索或修改的状态_crtDbgFlag标志来控制调试堆管理器 (仅限调试版本) 的分配行为
// 通过设置位(打开),该应用程序可指示调试堆管理器执行特殊的调试操作,包括在应用
//程序退出时检查内存泄露并报告是否找到任何内存泄露、通过指定已释放的内存块应保留
//在堆的链接列表中来模拟内存不足情况,以及通过在每次分配请求时检查每个内存块来验证该堆的完整性。
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
//传入窗口的句柄
GameApp theApp(hInstance);
if (!theApp.Init())
return 0;
return theApp.Run();
}
- 2、d3dApp.cpp的初始化数据了(d3dApp.h中所有都有对应的解释)
//多继承,调用构造函数
GameApp::GameApp(HINSTANCE hInstance): D3DApp(hInstance)
{
}
//多继承调用(函数&类)
//字符串加 L 是通知编译器采用 UNICODE编码(双字节),防止中文乱码
//用wchar_t定义,加 L
D3DApp::D3DApp(HINSTANCE hInstance)
: m_hAppInst(hInstance),
m_MainWndCaption(L"DirectX11 Initialization"),
m_ClientWidth(1280),
m_ClientHeight(720),
m_hMainWnd(nullptr),
m_AppPaused(false),
m_Minimized(false),
m_Maximized(false),
m_Resizing(false),
m_Enable4xMsaa(true),
m_4xMsaaQuality(0),
m_pd3dDevice(nullptr),
m_pd3dImmediateContext(nullptr),
m_pSwapChain(nullptr),
m_pDepthStencilBuffer(nullptr),
m_pRenderTargetView(nullptr),
m_pDepthStencilView(nullptr)
{
ZeroMemory(&m_ScreenViewport, sizeof(D3D11_VIEWPORT));
// 让一个全局指针获取这个类,这样我们就可以在Windows消息处理的回调函数
// 让这个类调用内部的回调函数了
g_pd3dApp = this;
}
- 3、注册窗口
bool GameApp::Init()
{
if (!D3DApp::Init())
return false;
return true;
}
bool D3DApp::Init()
{
if (!InitMainWindow())
return false;
if (!InitDirect3D())
return false;
return true;
}
//注册窗口
bool D3DApp::InitMainWindow()
{
//定义结构体
WNDCLASS wc;
//CS_HREDRAW | CS_VREDRAW 当窗口水平或者垂直方向变化,重新将图片画一次
// 全局窗口类 ....|CS_GLOBALCLASS
// 允许窗口接收鼠标双击 ....|CS_DBLCLKS
// 窗口没有关闭按钮 ...|CS_NOCLOSE
//窗口类风格
wc.style = CS_HREDRAW | CS_VREDRAW;
//窗口处理函数名(地址)赋值
wc.lpfnWndProc = MainWndProc;
//申请窗口类缓冲区
wc.cbClsExtra = 0;
//申请窗口缓冲区
wc.cbWndExtra = 0;
//实例句柄赋值(找一个内存,里面包含所有窗口数据)
wc.hInstance = m_hAppInst;
//窗口图标
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
//鼠标光标
wc.hCursor = LoadCursor(0, IDC_ARROW);
//窗口背景色 HBRUSH ->结构体指针
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
//菜单名字,赋值可以做菜单,0不要菜单
wc.lpszMenuName = 0;
//窗口类名字
wc.lpszClassName = L"D3DWndClassName";
if (!RegisterClass(&wc))//将以上所有赋值写入操作系统
{
//显示一个模态对话框 MessageBox(拥有的窗口,消息框的内容,消息框的标题,位标志集)
MessageBox(0, L"RegisterClass Failed.", 0, 0);
return false;
}
// Compute window rectangle dimensions based on requested client area dimensions.
//根据请求的客户区尺寸计算窗口矩形尺寸。
//RECT结构体 内有(LONG left,LONG top,LONG right,LONG bottom)
RECT R = { 0, 0, m_ClientWidth, m_ClientHeight };
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
int width = R.right - R.left;
int height = R.bottom - R.top;
//创建窗口 CreateWindow(窗口名字,标题栏信息,风格,窗口位置,窗口位置,宽,高,副窗口高,菜单,实例句柄,没用)
//CW_USEDEFAULT = (int)0x80000000,int最小值
//CreateWindow 返回窗口的句柄
m_hMainWnd = CreateWindow(L"D3DWndClassName", m_MainWndCaption.c_str(),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, m_hAppInst, 0);
if (!m_hMainWnd)
{
//显示一个模态对话框 MessageBox(拥有的窗口,消息框的内容,消息框的标题,位标志集)
MessageBox(0, L"CreateWindow Failed.", 0, 0);
return false;
}
//显示窗口(句柄,SW_SHOW:原样显示)
ShowWindow(m_hMainWnd, SW_SHOW);
//更新窗口(再画一次)(句柄)
UpdateWindow(m_hMainWnd);
return true;
}
- 4、那么基本注册完成了,现在又回到Main.cpp中了(是不是还有一个)**
return theApp.Run();
int D3DApp::Run()
{
//消息循环
//MSG结构体
MSG msg = { 0 };
m_Timer.Reset();
//捉到PostQuitMessage抛出来的WM_QUIT的值,退出循环
while (msg.message != WM_QUIT)
{
//PeekMessage 到本进程里面获取消息,然后检查是否满足后面传进去的3个条件,然后存放在msg中
// 函数PeekMessage不一样的是,GetMessage:从系统获取消息,将消息从系统中移除,
//属于阻塞函数。当系统无消息时,GetMessage会等待下一条消息。
// 函数PeekMesssge是以查看的方式从系统中获取消息 ,可以不将消息从系统中移除,
//是非阻塞函数;当系统无消息时,返回FALSE,继续执行后续代码。
//如果消息可得到,返回非零值;如果没有消息可找到,返回值是零。
//PeekMessage(MSG结构指针,检查的窗口句柄(也是消息范围),第一个消息,最后一个消息,确定消息如何被处理)
//(2、3参数)是获取消息的范围
// PM_(NO)REMOVE,可以让PeekMessage像GetMessage一样捉消息回来,NO则不让他捉,查看就行
//如果wMsgFilterMin(第一个消息)和wMsgFilterMax(最后一个消息)都为零,PeekMessage返回所有可得的消息
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
//翻译消息(如大小写aA,发送Ascll值区分大小写)
//首先检查是不是键盘消息,是就翻译,不是就退出
TranslateMessage(&msg);
//派发消息(将消息交给窗口处理函数)
DispatchMessage(&msg);
//找到msg在窗口句柄->找到那块内存->找到处理函数名字(地址)
//然后就可以调用处理函数->传参(窗口句柄,信息ID,第一附带信息,第二附带信息)
}
else//没捉到消息
{
m_Timer.Tick();
if (!m_AppPaused)
{
CalculateFrameStats();
UpdateScene(m_Timer.DeltaTime());
DrawScene();
}
else
{
Sleep(100);
}
}
}
//返回第一附带消息,建码值
return (int)msg.wParam;
}
- 5、上面都派发消息了DispatchMessage(&msg);,是不是处理消息一下
//UINT -> unsigned int
//HWND -> 结构体指针
//WPARAM -> unsigned int(消息的参数)
//LPARAM -> long指针
//窗口处理函数
//返回值,参数个数&类型,不能改变
LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Forward hwnd on because we can get messages (e.g., WM_CREATE)
// before CreateWindow returns, and thus before m_hMainWnd is valid.
return g_pd3dApp->MsgProc(hwnd, msg, wParam, lParam);
}
- 6、那么现在来看看Msgproc()函数(大致了解即可)
LRESULT D3DApp::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
// WM_ACTIVATE is sent when the window is activated or deactivated.
// We pause the game when the window is deactivated and unpause it
// when it becomes active.
//WM_ACTIVATE在窗口被激活或去激活时被发送。当窗口被禁用时,
//我们暂停游戏,当窗口被激活时,我们重新暂停游戏。
case WM_ACTIVATE:
if (LOWORD(wParam) == WA_INACTIVE)
{
m_AppPaused = true;
m_Timer.Stop();
}
else
{
m_AppPaused = false;
m_Timer.Start();
}
return 0;
// WM_SIZE is sent when the user resizes the window.
//当用户调整窗口大小时发送WI_SIZE。
case WM_SIZE:
//窗口大小变化原因(WPARAM wParam)
//窗口变化后的大小(LPARAM lParam)4字节(前2字节:宽,后2字节:高)
// Save the new client area dimensions.
//保存新的客户区域维度
m_ClientWidth = LOWORD(lParam);//前2字节:宽
m_ClientHeight = HIWORD(lParam);//后2字节:高
if (m_pd3dDevice)
{
if (wParam == SIZE_MINIMIZED)
{
m_AppPaused = true;
m_Minimized = true;
m_Maximized = false;
}
else if (wParam == SIZE_MAXIMIZED)
{
m_AppPaused = false;
m_Minimized = false;
m_Maximized = true;
OnResize();
}
else if (wParam == SIZE_RESTORED)
{
// Restoring from minimized state?
if (m_Minimized)
{
m_AppPaused = false;
m_Minimized = false;
OnResize();
}
// Restoring from maximized state?
else if (m_Maximized)
{
m_AppPaused = false;
m_Maximized = false;
OnResize();
}
else if (m_Resizing)
{
// If user is dragging the resize bars, we do not resize
// the buffers here because as the user continuously
// drags the resize bars, a stream of WM_SIZE messages are
// sent to the window, and it would be pointless (and slow)
// to resize for each WM_SIZE message received from dragging
// the resize bars. So instead, we reset after the user is
// done resizing the window and releases the resize bars, which
// sends a WM_EXITSIZEMOVE message.
//如果用户拖拽调整条,我们不会调整缓冲区的大小,因为用户是连续的拖拽调整条,
// 一串WM_SIZE消息被发送到窗口,这将是无意义的(和缓慢的)为从拖动调整条收到的每个WI_SIZE消息调整大小。
// 因此,我们将在用户调整窗口大小后进行重置,并释放调整大小条,这将发送一个II_EXITSIZEMOVE消息。
}
else // API call such as SetWindowPos or m_pSwapChain->SetFullscreenState.
{
OnResize();
}
}
}
return 0;
// WM_EXITSIZEMOVE is sent when the user grabs the resize bars.
//WI_EXITSIZEMOVE在用户抓取调整大小条时被发送。
case WM_ENTERSIZEMOVE:
m_AppPaused = true;
m_Resizing = true;
m_Timer.Stop();
return 0;
// WM_EXITSIZEMOVE is sent when the user releases the resize bars.
// Here we reset everything based on the new window dimensions.
//WM_EXITSIZEMOVE在用户释放调整条时被发送。在这里,我们根据新的窗口尺寸重置所有内容。
case WM_EXITSIZEMOVE:
m_AppPaused = false;
m_Resizing = false;
m_Timer.Start();
OnResize();
return 0;
// WM_DESTROY is sent when the window is being destroyed.
//TI DESTROY在窗口被销毁时发送。
//窗口被销毁前,做相应的处理,例如内存,资源
case WM_DESTROY:
PostQuitMessage(0);//往WM_QUIT里面扔消息,当PeekMessage拿到这个信息,可以使PeekMessage函数返回false。
return 0;
// The WM_MENUCHAR message is sent when a menu is active and the user presses
// a key that does not correspond to any mnemonic or accelerator key.
//WM_MENUCHAR消息是在菜单处于活动状态,并且用户按下一个不对应于任何助记符或加速键的键时发送的。
case WM_MENUCHAR:
// Don't beep when we alt-enter.
return MAKELRESULT(0, MNC_CLOSE);
// Catch this message so to prevent the window from becoming too small.
//捕捉此消息,以防止窗口变得太小。
case WM_GETMINMAXINFO:
((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200;
((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200;
return 0;
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
return 0;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
return 0;
case WM_MOUSEMOVE:
return 0;
}
//给各种消息默认处理(你想处理就用,不然它帮你处理)
//如帮你销毁窗口,然后走 WM_DESTROY:PostQuitMessage(0);
//return 0 不让下面的代码运行
return DefWindowProc(hwnd, msg, wParam, lParam);
}
- 还有一些其他函数作用是这样的
//assert的作用是先计算表达式(),如果其值为假(即为0),那么它先向标准错误流stderr打印一条出错信息,然后通过调用abort来终止程序运行;否则,assert()无任何作用。
assert(m_pd3dImmediateContext);
assert(m_pSwapChain);
- HR(x)用来错误原因追踪的(在DXTrace.cpp中实现)
- HR宏