📄 di.cpp
字号:
#include "stdafx.h"
#include "define.h"
#include "DI.h"
///////////////////////////////////////////////////////////////////////////
CDI::CDI()
{
for(int i=0;i<256;i++)
{
m_bKeyDown[i]=false;
}
}
///////////////////////////////////////////////////////////////////////////
CDI::CDI(HWND hWnd)
{
Initialize(hWnd);
}
///////////////////////////////////////////////////////////////////////////
CDI::~CDI()
{
Destroy();
}
////////////////////////////////////////////////////////////////////////////
bool CDI::Initialize(HWND hWnd)
{
HRESULT hr;
//创建一个 DIRECTINPUT 对象
hr=DirectInputCreate(NULL,
0x0300, //版本号;这个号低也不行,高也不行,很麻烦;
//DIRECTINPUT_VERSION,//这个宏定义为0x0500,即DIRECTX5.0,没想到竟然初始化不了,现在只好改成3.0版了;
&m_lpDI,
NULL);
if(FAILED(hr))goto Destroy;
//创建 DIRECTINPUTDEVICE mouse & keyboard界面
hr=m_lpDI->CreateDevice(GUID_SysMouse, &m_lpDID_Mouse, NULL);
if(FAILED(hr))goto Destroy;
hr=m_lpDI->CreateDevice(GUID_SysKeyboard, &m_lpDID_Keyboard, NULL);
if(FAILED(hr))goto Destroy;
//设定查询鼠标状态的返回数据格式
hr=m_lpDID_Mouse->SetDataFormat(&c_dfDIMouse);
if(FAILED(hr))goto Destroy;
//设定为通过一个256字节的数组返回查询状态值
hr=m_lpDID_Keyboard->SetDataFormat(&c_dfDIKeyboard);
if(FAILED(hr))goto Destroy;
//设定协作模式
hr=m_lpDID_Mouse->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE|DISCL_FOREGROUND);
if(FAILED(hr))goto Destroy;
hr=m_lpDID_Keyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
if(FAILED(hr))goto Destroy;
// 设定缓冲区大小
// 如果不设定,缓冲区大小默认值为0,程序就只能按立即模式工作
// 如果要用缓冲模式工作,必须使缓冲区大小超过0
DIPROPDWORD property;
property.diph.dwSize=sizeof(DIPROPDWORD);
property.diph.dwHeaderSize=sizeof(DIPROPHEADER);
property.diph.dwObj=0;
property.diph.dwHow=DIPH_DEVICE;
property.dwData=DINPUT_BUFFERSIZE;
hr=m_lpDID_Mouse->SetProperty(DIPROP_BUFFERSIZE, &property.diph);
if(FAILED(hr))goto Destroy;
hr=m_lpDID_Keyboard->SetProperty(DIPROP_BUFFERSIZE, &property.diph);
if(FAILED(hr))goto Destroy;
hr=m_lpDID_Mouse->Acquire();
if(FAILED(hr))goto Destroy;
hr=m_lpDID_Keyboard->Acquire();
if(FAILED(hr))goto Destroy;
return true;
Destroy:;
Destroy();
MessageBox(NULL,"DirectInput Initialize Error","Sorry.",MB_OK);
return false;
}
////////////////////////////////////////////////////////////////////////////////////
HRESULT CDI::UpDateInputState(void)
{
DWORD i;//暂时不用,缓冲区被设为1;
///*
//鼠标状态更新:
if(m_lpDID_Mouse!=NULL)
{
DIDEVICEOBJECTDATA didod; // Receives buffered data
DWORD dwElements;
HRESULT hr;
while(true)
{
dwElements=1; //每次从缓冲区中读一个数据
hr=m_lpDID_Mouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
&didod,
&dwElements,
0);
if(FAILED(hr))// 发生了一个错误
{
if(hr==DIERR_INPUTLOST)
{
hr=m_lpDID_Mouse->Acquire(); // 试图重新取回设备
if(FAILED(hr))return S_FALSE; // 失败
}
}
else
if(dwElements==1)
{
switch(didod.dwOfs)
{
case DIMOFS_X:m_dwMouse=DI_MOUSE_MOVE_X;
m_MousePoint.x=didod.dwData;// X 轴偏移量,didod.dwData 里是具体偏移相对值,单位为像素
break;
case DIMOFS_Y:m_dwMouse=DI_MOUSE_MOVE_Y;
m_MousePoint.y=didod.dwData;// Y 轴偏移量,didod.dwData 里是具体偏移相对值,单位为像素
break;
case DIMOFS_BUTTON0:if((LOWORD(didod.dwData)&0x80)!=0)m_dwMouse=DI_MOUSE_LEFT_DOWN; //0号键(左键)状态,didod.dwData 里是具体状态值,低字节最高位为1则表示按下,低字节最高位为 0 表示未按下
else m_dwMouse=DI_MOUSE_LEFT_UP;
break;
case DIMOFS_BUTTON1:if((LOWORD(didod.dwData)&0x80)!=0)m_dwMouse=DI_MOUSE_RIGHT_DOWN; // 1 号键(右键)状态
else m_dwMouse=DI_MOUSE_RIGHT_UP;
break;
case DIMOFS_BUTTON2:if((LOWORD(didod.dwData)&0x80)!=0)m_dwMouse=DI_MOUSE_MIDDLE_DOWN; // 2 号键(中键)状态
else m_dwMouse=DI_MOUSE_MIDDLE_UP;
break;
case DIMOFS_BUTTON3://3号键状态,保留,一般鼠标上没有这个键;
break;
}
}
else if(dwElements==0)break; // 缓冲区读空
}
}
//*/
//键盘状态更新:
//立即模式
BYTE diks[256];
if(m_lpDID_Keyboard!=NULL) // 如果 lpKeyboard 对象界面存在
{
HRESULT hr;
hr=DIERR_INPUTLOST; // 为循环检测做准备
// if input is lost then acquire and keep trying
while(hr==DIERR_INPUTLOST)
{
// 读取输入设备状态值到状态数据缓冲区
hr=m_lpDID_Keyboard->GetDeviceState(sizeof(diks),&diks);
if(hr==DIERR_INPUTLOST)
{
// DirectInput 报告输入流被中断// 必须先重新调用 Acquire 方法,然后再试一次
hr=m_lpDID_Keyboard->Acquire();
if(FAILED(hr))return hr;
}
else
{
for(i=0;i<256;i++)
{
if((diks[i]&&0x80)!=0)m_bKeyDown[i]=true;
else m_bKeyDown[i]=false;
}
}
}
if(FAILED(hr))return hr;
}
/*缓冲模式:
DIDEVICEOBJECTDATA didod[DINPUT_BUFFERSIZE]; //Receives buffered data
DWORD dwElements;
HRESULT hr;
if(m_lpDID_Keyboard!=NULL)
{
hr=DIERR_INPUTLOST;
while(hr!=DI_OK)
{
dwElements=1;//DINPUT_BUFFERSIZE;
hr=m_lpDID_Keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
didod,
&dwElements,
0);
if (hr!=DI_OK)
{
// 发生了一个错误
// 这个错误有可能是 DI_BUFFEROVERFLOW 缓冲区溢出错误
// 但不管是哪种错误,都意味着同输入设备的联系被丢失了
// 这种错误引起的最严重的后果就是如果你按下一个键后还未松开时
// 发生了错误,就会丢失后面松开该键的消息。这样一来,你的程序
// 就可能以为该键尚未被松开,从而发生一些意想不到的情况
// 现在这段代码并未处理该错误
// 解决该问题的一个办法是,在出现这种错误时,就去调用一次
// GetDeviceState(),然后把结果同程序最后所记录的状态进行
// 比较,从而修正可能发生的错误
hr=m_lpDID_Keyboard->Acquire();
if(FAILED(hr))return hr;
}
}
if(FAILED(hr))return hr;
}
// GetDeviceData() 同 GetDeviceState() 不一样,调用它之后,
// dwElements 将指明此次调用共读取到了几条缓冲区记录
*/
// 我们再用一个循环来处理每条记录
/*
for(i=0;i<dwElements;i++)
{
// 此处放入处理代码
// didod[i].dwOfs 表示那个键被按下或松开
// didod[i].dwData 记录此键的状态,低字节最高位是 1 表示按下,0 表示松开
// 一般用 didod[i].dwData&0x80 来测试
}
*/
/*取键盘数据参考:
if(SUCCEEDED(hr)&&cod>0)
{
DWORD iod;
for(iod=0; iod<cod; iod++)
{
if((rgod[iod].dwData & 0x80))
{
KEYBUFFER[KEYENDCOUNTER] = (BYTE)(rgod[iod].dwOfs);
KEYENDCOUNTER ++;
if(KEYENDCOUNTER >= DINPUT_BUFFERSIZE) KEYENDCOUNTER = 0;
}
KEYSTATUSFLAG[rgod[iod].dwOfs] = (BYTE)(rgod[iod].dwData & 0x80);
}
}*/
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////////
void CDI::Destroy()
{
if(m_lpDI)
{
if(m_lpDID_Mouse)
{
// Always unacquire the device before calling Release().
m_lpDID_Mouse->Unacquire();
m_lpDID_Mouse->Release();
m_lpDID_Mouse=NULL;
}
if(m_lpDID_Keyboard)
{
// Always unacquire the device before calling Release().
m_lpDID_Keyboard->Unacquire();
m_lpDID_Keyboard->Release();
m_lpDID_Keyboard=NULL;
}
m_lpDI->Release();
m_lpDI=NULL;
}
return;
}
////////////////////////////////////////////////////////////////////////////////////
//class CDI end;
/*
long FAR PASCAL WindowProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_ACTIVATEAPP:
if(bActive)
{
if(lpKeyboard) lpKeyboard->Acquire();
}
else
{
if(lpKeyboard) lpKeyboard->Unacquire();
}
break;
...
}
哦,对了,前一段例程中还提到了立即模式和缓冲模式。在 DirectINPUT 中,这两种工作模式是有区别的。
如果使用立即模式的话,在查询数据时,只能返回查询时的设备状态。而缓冲模式则将记录所有设备状态变化过程。就个人喜好而言,笔者偏好后者,因为这样一般不会丢失任何按键信息。对应的,如果在使用前者时的查询频度太低,则很难保证采集数据的完整性。
DIRECTINPUT 的数据查询
立即模式的数据查询比较简单,请看下面的示例:
BYTE diks[256]; // DirectInput keyboard state buffer 键盘状态数据缓冲区
HRESULT UpdateInputState(void)
{
if(lpKeyboard != NULL) // 如果 lpKeyboard 对象界面存在
{
HRESULT hr;
hr = DIERR_INPUTLOST; // 为循环检测做准备
// if input is lost then acquire and keep trying
while(hr == DIERR_INPUTLOST)
{
// 读取输入设备状态值到状态数据缓冲区
hr = lpKeyboard->GetDeviceState(sizeof(diks), &diks);
if(hr == DIERR_INPUTLOST)
{
// DirectInput 报告输入流被中断
// 必须先重新调用 Acquire 方法,然后再试一次
hr = lpKeyboard->Acquire();
if(FAILED(hr))
return hr;
}
}
if(FAILED(hr))
return hr;
}
return S_OK;
}
在上面的示例中,关键处就是使用 GetDeviceState 方法来读取输入设备状态值以及对异常情况的处理。通过使用 GetDeviceState 方法,我们把输入设备的状态值放在了一个 256 字节的数组里。如果该数组中某个数组元素的最高位为 1,则表示相应编码的那个键此时正被按下。例如,如果 diks[1]&0x80>0,那么就表示 ESC 键正被按下。
学会了立即模式的数据查询以后,下面我们开始研究缓冲模式的情况:
HRESULT UpdateInputState(void)
{
DWORD i;
if(lpKeyboard != NULL)
{
DIDEVICEOBJECTDATA didod[DINPUT_BUFFERSIZE]; // Receives buffered data
DWORD dwElements;
HRESULT hr;
hr = DIERR_INPUTLOST;
while(hr != DI_OK)
{
dwElements = DINPUT_BUFFERSIZE;
hr = lpKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
didod,
&dwElements,
0);
if (hr != DI_OK)
{
// 发生了一个错误
// 这个错误有可能是 DI_BUFFEROVERFLOW 缓冲区溢出错误
// 但不管是哪种错误,都意味着同输入设备的联系被丢失了
// 这种错误引起的最严重的后果就是如果你按下一个键后还未松开时
// 发生了错误,就会丢失后面松开该键的消息。这样一来,你的程序
// 就可能以为该键尚未被松开,从而发生一些意想不到的情况
// 现在这段代码并未处理该错误
// 解决该问题的一个办法是,在出现这种错误时,就去调用一次
// GetDeviceState(),然后把结果同程序最后所记录的状态进行
// 比较,从而修正可能发生的错误
hr = lpKeyboard->Acquire();
if(FAILED(hr))
return hr;
}
}
if(FAILED(hr))
return hr;
}
// GetDeviceData() 同 GetDeviceState() 不一样,调用它之后,
// dwElements 将指明此次调用共读取到了几条缓冲区记录
// 我们再用一个循环来处理每条记录
for(int i=0; i<dwElements; i++)
{
// 此处放入处理代码
// didod[i].dwOfs 表示那个键被按下或松开
// didod[i].dwData 记录此键的状态,低字节最高位是 1 表示按下,0 表示松开
// 一般用 didod[i].dwData&0x80 来测试
}
return S_OK;
}
其实,每条记录还有 dwTimeStamp 和 dwSequence 两个字段来记录消息发生的时间和序列编号,以便作更复杂的处理。本文是针对初学者写的,就不打算去谈论这些内容了。
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -