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宏
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐