DirectX——Windows编程基础框架
前言
DirectX是由微软公司创建的一系列专为多媒体以及游戏开发的应用程序接口,内部组件按照性质可分为:显示部分、声音部分、输入部分和网络部分。其中Direct3D便属于显示部分中的3D图形显示子集,Direct3D按照前后版本的发布可分为Direct3D9、Direct3D10、Direct3D11、Direct3D12;本系列文章介绍D3D的版本会在标题的括号中说明,首先介绍的会是Direct3D9系列的文章。关于DirectX的详细介绍可点击这里,由于DirectX是一套调用接口,所以我们必须编写应用程序,在程序中进行调用。这里对windows编程进行简单(详细)介绍,只介绍使用DirectX需要的那一部分。
Windows应用程序介绍
了解Windows编程的人都知道,windows程序遵循事件驱动模型,通常Windows程序启动后会等到事件的发生,只有等事件发生后Windows程序才进行处理,比如:鼠标点击,键盘点击,窗口大小变化等等;这里对一些概念进行一下梳理:
Windows————Windows操作系统
Windows应用程序————我们编写的应用程序
应用窗口————一个Windows应用程序所创建的窗口(可能多个)
当我们点击屏幕时,接受到该指令的是Windows,然后Windows会向Windows应用程序进行发送该消息。具体的实现是每个Windows应用程序都有一个消息队列,Windows向Windows应用程序发送消息其实就是将消息添加到Windows应用程序的消息队列里,Windows应用程序会在一个消息循环中不断检查消息队列,当检查到消息时便将消息派送给特定的窗口(一个应用程序可能有多个窗口)。具体流程下面这张图挺清楚的
Windows应用程序简单实现
知道了Windows应用程序的运行原理,我们就可以来进行程序的编写了,这里实现的是一个空白窗口,按esc键可退出,鼠标左键点击会弹出“hello world”窗口;
具体代码如下(注释中有各模块的解释):
/*windows 开发所需头文件
包含Windows开发所需要的宏、类、函数、结构体等结构的定义*/
#include <windows.h>
HWND MainWindowHandle = ; //主窗口句柄,相当于窗口的ID
bool InitWindowsApp(HINSTANCE instanceHandle, int show); //初始化窗口函数
int Run(); //消息循环函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, //消息处理函数
WPARAM wParam, LPARAM lParam);
/*WinMain函数,相当于C++中的main函数,是程序运行的入口*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd)
{
//初始化失败则弹出消息窗口提示
if (!InitWindowsApp(hInstance, nShowCmd))
{
::MessageBox(, "Init - Failed", "Error", MB_OK);
return ;
}
return Run();
}
//初始化窗口函数实现
bool InitWindowsApp(HINSTANCE instanceHandle, int show)
{
//windows窗口类型的自定义
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = ;
wc.cbWndExtra = ;
wc.hInstance = instanceHandle;
wc.hIcon = ::LoadIcon(, IDI_APPLICATION);
wc.hCursor = ::LoadCursor(, IDC_ARROW);
wc.hbrBackground = static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
wc.lpszMenuName = ;
wc.lpszClassName = "Hello";
//向Windows系统注册窗口
if (!::RegisterClass(&wc))
{
::MessageBox(, "RegisterClass - Failed", , );
return false;
}
//根据自定义的窗口进行创建
MainWindowHandle = ::CreateWindow(
"Hello",
"Hello",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
,
,
instanceHandle,
);
//创建失败则弹出消息窗口提示
if (MainWindowHandle == )
{
::MessageBox(, "CreateWindow - Failed", , );
return false;
}
::ShowWindow(MainWindowHandle, show); //显示窗口
::UpdateWindow(MainWindowHandle); //进行窗口的刷新
return true;
}
//消息循环函数定义
int Run()
{
MSG msg;
::ZeroMemory(&msg, sizeof(MSG));
while (::GetMessage(&msg, , , ))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
//消息处理函数定义
LRESULT CALLBACK WndProc(HWND windowHandle, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
::MessageBox(, "Hello, Word", "Hello", MB_OK);
return ;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
::DestroyWindow(MainWindowHandle);
}
return ;
case WM_DESTROY:
::PostQuitMessage();
return ;
}
return ::DefWindowProc(windowHandle, msg, wParam, lParam);
}
Windows应用程序详细介绍
- WinMain函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd)
WinMain函数是Windows程序运行的入口,就跟c++中的main函数一样,各参数定义如下:
WINAPI:
#define WINAPI __stdcall;
该语句指定了一个呼叫约定,包括如何生产机械码以在堆栈中放置函数呼叫的参数。许多Windows函数呼叫声明为WINAPI;
HINSTANCE:
typedef HINSTANCE_ *HINSTANCE;
可以看出HINSTANCE实际是一个指针类型;
PSTR:
typedef CHAR *PSTR;
可以看到PSTR代表一个字符串指针;
hInstance:当前windows应用程序实例(程序自身)的句柄,由系统分配。当同时运行同一程序多次时,便创建了该程序的多个执行实例;
hPresInstance:通过检查hPrevInstance参数能够确定自身的其它执行实例是否正在运行。在32位Windows版本中,该概念已被抛弃,传给WinMain的第二个参数总是NULL(定义为0);
pCmdLine:用于运行程序的命令行字符串;
nShowCmd:指定应用程序窗口的显示方式;SW_SHOWNORMAL:窗口按一般大小显示;SW_SHOWMAXIMIZED窗口最大化显示;SW_SHOWMINNOACTIVE窗口最小化显示。
msdn有WinMain的详细介绍
- InitWindowsApp函数
bool InitWindowsApp(HINSTANCE instanceHandle, int show)
该函数首先进行窗口类型的自定义,然后注册窗口、创建窗口、显示窗口。这里大部分都是调用系统函数,所以很多东西只贴一个查找连接,以便查阅;
WNDCLASS:
typedef struct tagWNDCLASSA { ... } WNDCLASSA typedef WNDCLASSA WNDCLASS;
可以看出WNDCLASS是一个结构体类型,里面各成员的取值都有参考范围,具体范围比较长,这里贴一个MSDN连接以供查阅
::LoadIcon:这里的作用域符号”::”需要注意,这里代表的是全局作用域,即调用的是全局LoadIcon函数,关于作用域符号”::”的三种用法,这里再贴一个链接。另外关于LoadIcon函数的用法,这里贴一个查找连接;
::LoadCursor:与::LoadIcon类似,不过设置的是鼠标指针的样式,查找连接
::GetStockObject(WHITE_BRUSH):返回一个笔刷或者字体或者调色板等的句柄,具体参数参考查找连接
static_cast<>():static_cast用于编译时进行类型转换,与之相关的还有reinterpret_cast、dynamic_cast。dynamic_cast用于运行时类型转换、reinterpret_cast是完全的按照二进制存储重新解释。
::RegisterClass向系统注册窗口,参考文档
::MessageBox:消息窗口函数,参考文档
::CreateWindow:创建窗口函数,参考文档
::ShowWindow:显示窗口,控制窗口的显示/隐藏,参考文档
::UpdateWindow:更新窗口,控制窗口绘制内容的更新,参考文档
-
Run函数
消息循环,用来处理Windows系统向程序发动的消息。
MSG:
typedef struct tagMSG { ... } MSG, *PMSG, *LPMSG;
可以看出,MSG为一个结构体,里面包含一些跟消息相关的成员信息。具体各成员意义参考这里
::ZeroMemory:初始化内存为0,参考文档
::GetMessage:从消息队列中获取一个消息,该函数会阻塞进程,直到获取到消息。与之相对应的是PeekMessage函数,该函数不会阻塞程序。参考文档
::TranslateMessage:负责将消息中的虚拟键转换为字符信息,参考文档
::DispatchMessage:将消息派送到窗口消息处理函数,参考文档
- WndProc函数
LRESULT CALLBACK WndProc(HWND windowHandle, UINT msg, WPARAM wParam, LPARAM lParam)
负责接受并处理消息,具体可参考
LRESULT:实际是一个long类型;
CALLBACK:
#define CALLBACK __stdcall
;同WINAPI一样,表明该函数是一个回调函数,表明Windows可以对该函数进行外部调用;
msg:表示消息的数值。
WPARAM、LPARAM:实际上是usigned int,表示与消息相关的参数
WM_KEYDOWN、WM_LBUTTONDOWN、WM_DESTROY:表示消息的类型,Windows定义额很多消息类型,具体可参考
::DestroyWindow:手动销毁窗口,并向Windows发送WM_DESTROY消息,详细可参考这里
::PostQuitMessage:向程序的消息队列中插入一个WM_QUIT消息。前面提到过,GetMessage对于除了WM_QUIT之外的从消息队列中取出的所有消息都传回非0值。而当GetMessage得到一个WM_QUIT消息时,它传回0。这将导致WinMain退出消息循环,并终止程序。参考文档
::DefWindowProc:默认的消息处理函数,该函数针对不同的消息实现了不同的默认处理方法。对于不需要我们处理的消息,只需调用该函数即可。参考文档
到这里整个程序差不多就能理解了,以后在可以在此程序基础上学习DirectX接口的使用。