转自:http://blog.csdn.net/zhmxy555/article/details/8547531
总的来说,当前版本的DirectInputAPI中,三个接口,四十七个方法,组成了这个在电脑游戏开发中不可或缺的组件。
由于IDirectInput8 API整体来说规模不大,说白了也就是三个接口,四十七个方法,不妨我们在文章中将他们一一列举出来,也让大家窥一窥DirectInput API的全貌。
我们首先需要注意的是,在使用DirectInput时,需要保证我们包含了DInput.h头文件,并且在项目中已经链接了DInput8.lib库文件。
#pragma comment(lib, "dinput8.lib") // 使用DirectInput必须包含的头文件,注意这里有8
在DirectInput中我通过们调用DirectInputCreate函数创建并初始化IDirectInput接口,我们可以在MSDN中查到该函数的声明如下:
■ 第一个参数,HINSTANCE类型的hinst,表示我们当前创建的DirectInput的Windows程序句柄,这个值填我们在WinMain函数的参数中的实例句柄就可以了。
归根揭底的话,可以通过【转到定义】大法在dinput.h中查到有如下代码:
#define DIRECTINPUT_HEADER_VERSION 0x0800
#define DIRECTINPUT_VERSION DIRECTINPUT_HEADER_VERSION
■ 第三个参数,REFIID类型的riidltf,表示接口的标志,通常取IID_IDirectInput8就可以了。
■ 第四个参数,LPVOID 类型的* ppvOut,用于返回我们新创建的IDirectInput8接口对象的指针。
■ 第五个参数,LPUNKNOWN类型的punkOuter,一个和COM对象接口相关的参数,通常我们设为NULL就可以了。
LPDIRECTINPUT8 g_pDirectInput = NULL;
if(FAILED(DirectInput8Create(hInstance,0x0800, IID_IDirectInput8,(void**)&g_pDirectInput, NULL)))
这步完成之后,咱们的定义的DIRECTINPUT8接口对象g_pDirectInput就有了权利,新官上任了。
LPDIENUMDEVICESCALLBACKlpCallback,
■ 第一个参数,DWORD类型的dwDevType,指定我们需要枚举的设备类型。
■ 第二个参数,LPDIENUMDEVICESCALLBACK类型的lpCallback,用于指定一个回调函数的地址,当系统中每找到一个匹配的设备时,就会自动调用这个回调函数。
■ 第三个参数,LPVOID类型的pvRef,返回我们当前匹配设备的GUID值。
取得我们需要使用的设备的GUID后,就可以根据这个GUID来调用IDirectInput8接口的CreateDevice方法,进而来创建设备的IDirectInputDevice8接口对象了。
我们可以在MSDN中查到IDirectInput8::CreateDevice方法的声明如下:
LPDIRECTINPUTDEVICE*lplpDirectInputDevice,
■ 第三个参数,LPUNKNOWN类型的pUnkOuter,、和COM对象的IUnknown接口相关的一个参数,一般我们不去管它,设为NULL就可以了。
讲解完了,当然得看一个调用实例。下面的代码中CreateDevice方法的第二个参数我们填的是GUID_SysMouse,所以我们在为系统鼠标创建一个DirectInput设备接口对象:
LPDIRECTINPUTDEVICE8 g_pMouseDevice = NULL;
if(FAILED (g_pDirectInput->CreateDevice(GUID_SysKeyboard,&g_pKeyboardDevice,NULL)))
SetDataFormat方法唯一的变量就是LPCDIDATAFORMAT类型的lpdf,DirectInput已经为我们准备好了一些备选的参数,下面是一个列表:
数据格式 | 精析 |
c_dfDIkeyboard | 标准键盘结构,包含256个字符,每个字符对应着每个键 |
c_dfDIMouse | 标准鼠标结构,带有3个轴和4个按钮 |
c_dfDIMouse2 | 扩展鼠标结构,带有3个轴和8个按钮 |
c_dfDIJoystick | 标准游戏杆,带有三个定位轴,3个旋转轴,两个滑块,1个POV hat和32个按钮 |
c_dfDIJoystick2 | 扩展的游戏杆 |
g_pMouseDevice->SetDataFormat(&c_dfDIMouse);
协作级别定义了进程与其他应用程序和操作系统共享设备的方式。设备一旦创建就需要设置它的协作级别,协作级别表示了应用程序对设备的控制权。
DirectInput的协作级别可以以两套方案来分类:前台、后台模式和共享、独占模式。
我们平常都是通过IDirectInputDevice8接口的SetCooperativeLevel方法来设置设备的协作级别的,我们可以在MSDN中查到SetCooperativeLevel的声明如下:
■ 第一个参数,HWND类型的hwnd,显然就是填想要与当前设备相关联的窗口句柄了,且这个窗口需要属于当前进程的顶级窗口。
■ 第二个参数,DWORD类型的dwFlags,描述了当前设备的协作级别类型,也就是填我们上面讲到的前台、后台模式和共享、独占模式等一些模式的标识符,可取一个值到多个值,浅墨把取值在下表中出来了:
协作级别类型 | 精析 |
DISCL_BACKGROUND | 后台模式,一般我们让他与DISCL_NONEXCLUSIVE(非独占模式)配合使用 |
DISCL_FOREGROUND | 前台模式,一般我们让他与DISCL_EXCLUSIVE(独占模式)配合使用 |
DISCL_EXCLUSIVE | 独占模式 |
DISCL_NONEXCLUSIVE | 非独占(共享)模式 |
DISCL_NOWINKEY | 让键盘上烦人的Windows键失效 |
注意,后台模式和独占模式不能同时选择,用脚丫子来想都知道他们两个组合起来不符合逻辑,既然都是在后台了,还谈什么独占呢?
下面依旧是一个调用实例,将鼠标设备的协作级别设为前台、独占模式:
g_pMouseDevice->SetCooperativeLevel(hwnd,DISCL_FOREGROUND |DISCL_EXCLUSIVE);
设备的特殊属性包含设备的数据模式、缓冲区大小、以及设备的最小最大范围等等。DirectInput为我们提供了SetProperty方法来设置设备的特殊属性,我们可以在MSDN中查到这个方法有如下原型:
这个方法平常用得不算多,因为篇幅原因暂且先不详细讲了,需要用的时候大家去查一下文档就可以了。
在DirectInput中,权力的敲门砖为IDirectInput8接口的Acquire方法,我们可以在MSDN中查到这个"权力权杖"有如下的原型:
我们可以发现他简简单单清清白白,没有参数,返回值为HRESULT。调用起来当然是非常简单:
为了大家看起来简明扼要,咱们这里没有用if和FAILD宏给他括起来,进行错误处理。
轮询可以准备在合适的情况下读取设备数据。因为数据可能具有临界时间的。这个轮询的原型也是非常非常的简单:
■ 第一个参数,DWORD类型的cbData,指定了我们缓冲区的大小(具体是哪个缓冲区在第二个参数中)。
■ 第二个参数,LPVOID类型的lpvData,表示一个获取当前设备状态的结构体的地址值。
他的数据格式和我们之前调用的IDirectInputDevice8::SetDataFormat方法有着前后呼应的密切联系。下面我们通过一个表格来看看是如何联系的:
SetDataFormat中指定的数据格式 | GetDeviceState中对应的缓冲区结构体 |
c_dfDIMouse | DIMOUSESTATE结构体 |
c_dfDIMouse2 | |
c_dfDIKeyboard | 大小为256个字节的数组 |
c_dfDIJoystick | DIJOYSTATE结构体 |
c_dfDIJoystick2 | DIJOYSTATE2结构体 |
比如,我们先调用了SetDataFormat设置了设备的数据格式为c_dfDIMouse:
g_pMouseDevice->SetDataFormat(&c_dfDIMouse);
那么我们在读取设备信息的时候调用GetDeviceState就需要把第二个参数填与dfDIMouse对应的DIMOUSESTATE结构体的一个实例:
g_pMouseDevice-> GetDeviceState(sizeof(dimouse),(LPVOID)&dimouse);
对此,我们可以抽象出一个函数,专门对付疑难杂症,应对各种类型的设备的数据读取,而且还考虑到了设备如果丢失掉了,在合适的时间自动重新获取该设备:
//*****************************************************************************************
//*****************************************************************************************
BOOL Device_Read(IDirectInputDevice8*pDIDevice, void* pBuffer, longlSize)
pDIDevice->Acquire(); // 获取设备的控制权
if(SUCCEEDED(hr = pDIDevice->GetDeviceState(lSize, pBuffer))) break;
if(hr !=DIERR_INPUTLOST || hr != DIERR_NOTACQUIRED) return FALSE;
if(FAILED(pDIDevice->Acquire())) return FALSE;
Device_Read(g_pKeyboardDevice,(LPVOID)g_pKeyStateBuffer,sizeof(g_pKeyStateBuffer));
if (g_pKeyStateBuffer[DIK_A] & 0x80)
当然,在最后,使用完输入设备后,必须调用IDirectInputDevice8接口的Unacquire方法释放设备的控制权,所谓的杯酒释兵权,且需要接着调用Release方法释放掉设备接口对象。