ホーム  ざれごと  ワシントン州  ツール  NT豆知識  Win32プログラミングノート  私的用語  ジョーク  いろいろ  ゲーム雑記  Favorites  掲示板   Mail

Win32プログラミングノート --- Raw Input ---

Last modified: Wed Feb 06 22:01:41 2008 PDT

一つ上へ

ここでの記述は、Visual C++(tm) Ver.6.0 USを元にしています

デバイスの列挙

Windows XP で加わった Raw Input API を使って、システムにつながっている入力デバイスを列挙してみる。 キーボードの場合は、タイプとサブタイプも出力。 とりあえずデバッグ出力に一覧を出してみます。
お手軽にでっちあげた AutoArray を使ってますが、本番に使うには弱いのでそのままでは使わないほうがいいかも。 蛇足ですが。

WINVER=0x500 とか _WIN32_WINNT=0x0501 をコンパイルオプションなどで定義しておく必要があります。

1: template <class T>
2: class AutoArray {
3:     public:
4:         AutoArray(size_t n)
5:         {
6: #ifdef _DEBUG
7:             // バッファオーバーランのチェックのため、 余計にひとつアロ ケート
8:             m_ptr = new T[n + 1];
9: #else
10:             m_ptr = new T[n];
11: #endif
12:             if (m_ptr) {
13:                 m_size = n;
14: #ifdef _DEBUG
15:                 // 番兵を置く
16:                 FillMemory(&m_ptr[n], sizeof m_ptr[n], SENTINEL);
17: #endif
18:             }
19:             else {
20:                 m_size = 0;
21:             }
22:  
23:         }
24:         ~AutoArray()
25:         {
26: #ifdef _DEBUG
27:             if (m_ptr) {
28:                 PBYTE pb = reinterpret_cast<PBYTE>(&m_ptr[m_size]);
29:                 ASSERT(pb);
30:                 // バッファオー バーランのチェック
31:                 for (int i = 0; i < sizeof m_ptr[m_size]; ++i) {
32:                     ASSERT(pb[i] == SENTINEL);
33:                 }
34:             }
35: #endif
36:             delete[] m_ptr;
37:         }
38:  
39:         bool OK()
40:         {
41:             return m_ptr != NULL;
42:         }
43:  
44:         operator T*()
45:         {
46:             return m_ptr;
47:         }
48:  
49:         T& operator[](size_t n)
50:         {
51:             ASSERT(n < m_size);
52:             return m_ptr[n];
53:         }
54:  
55:     protected:
56:         // to prohibit copy operations
57:         AutoArray(const AutoArray<T>&);
58:         AutoArray<T> operator=(const AutoArray<T>&);
59:  
60:         enum { SENTINEL = 0xcc };
61:  
62:     protected:
63:         T* m_ptr;
64:         size_t m_size;
65: };
66:  
67: bool KeyboardList()
68: {
69:     // 現在接続されている入力デバイスの数を取得
70:     UINT nInputDevices;
71:     if (GetRawInputDeviceList(NULL, &nInputDevices, sizeof(RAWINPUTDEVICELIST)) != 0) {
72:         return false;
73:     }
74:  
75:     // 入力デバイスの基礎的情報を取得
76:     AutoArray<RAWINPUTDEVICELIST> rawinputDeviceList(nInputDevices);
77:     if (!rawinputDeviceList.OK()) {
78:         return false;
79:     }
80:     UINT ntmp = nInputDevices;
81:     if (GetRawInputDeviceList(rawinputDeviceList, &nInputDevices, sizeof(RAWINPUTDEVICELIST)) != ntmp) {
82:         return false;
83:     }
84:  
85:     int nKbd = 0;   // つながっているキーボードの数
86:  
87:     for (UINT i = 0; i < nInputDevices; ++i) {
88:         // デバイス名の長さを取得
89:         UINT size;
90:         if (GetRawInputDeviceInfo(rawinputDeviceList[i].hDevice, RIDI_DEVICENAME, NULL, &size) != 0) {
91:             return false;
92:         }
93:         // デバイス名を取得
94:         AutoArray<TCHAR> name(size);
95:         if (!name.OK()) {
96:             return false;
97:         }
98:         if ((int)GetRawInputDeviceInfo(rawinputDeviceList[i].hDevice, RIDI_DEVICENAME, name, &size) < 0) {
99:             return false;
100:         }
101:         // デバイスの情報を表示
102:         static const TCHAR* deviceTypeNames[] = {
103:             _T("Mouse"),
104:             _T("Keybd"),
105:             _T("HID  "),
106:         };
107:         TRACE3("[%02d] %s hDevice=%p", i, deviceTypeNames[rawinputDeviceList[i]. dwType], rawinputDeviceList[i].hDevice);
108:         TRACE1(" %s\n", name);
109:  
110:         if (rawinputDeviceList[i].dwType == RIM_TYPEKEYBOARD) {
111:             // キーボードだったら、 タイプ・サブタイプを表示
112:             ++nKbd;
113:  
114:             RID_DEVICE_INFO devinfo = { sizeof devinfo, };
115:             UINT sz = sizeof devinfo;
116:             if ((int)GetRawInputDeviceInfo(rawinputDeviceList[i]. hDevice, RIDI_DEVICEINFO, &devinfo, &sz) < 0) {
117:                 return false;
118:             }
119:             ASSERT(devinfo.dwType == RIM_TYPEKEYBOARD);
120:             TRACE2(" ==> [0x%02x, 0x%02x]\n", devinfo.keyboard.dwType, devinfo.keyboard.dwSubType);
121:         }
122:     }
123:  
124:     TRACE1("Number of keyboards: %d\n", nKbd);
125:  
126:     return true;
127: }

出力の例はこんな感じ:

[00] Keybd hDevice=00010063 \??\Root#RDP_KBD#0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}
 ==> [0x51,0x00]
[01] Keybd hDevice=00010061 \??\Root#*PNP030b#1_0_22_0_32_0#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}
 ==> [0x04,0x00]
[02] Mouse hDevice=0001004B \??\Root#RDP_MOU#0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}
[03] Mouse hDevice=00010049 \??\HID#Vid_045e&Pid_0040#6&31accd24&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}
Number of keyboards: 2

タイプとサブタイプ

Type=4, Subtype=0 というのは、いわゆる 101 キーボードです。 Type=0x51 は未定義のタイプ、という意味です。 本来あってはいけませんが、USB キーボードで INF が正しく設定されない場合、タイプが不明になってしまうため、仕方なく未定義のタイプを割り当てています。
余談ですが、メーカによっては 101 でも 106 でも同じプロダクト ID を使っているケースがあるようです。 デバイスはベンダ ID・プロダクト ID によって識別されるので、101 と 106 に別々のタイプを割り当てることができないことになります。 これは最悪のケースで、INF でもどうしようもありませんね。 案外多いようですが... Microsoft をはじめとして

余談 - ダイナミックレイアウトスイッチング

Windows XP からは、ダイナミックレイアウトスイッチングという機能が加わりました。 USB などによって複数のキーボードを接続することができますが、このとき 101 / 106 などのタイプ・サブタイプを認識してダイナミックにキーボードレイアウトを切り替えるという機能です。 なお、日本と韓国向けの特殊機能ですが、英語版にも機能は入っています。
in-box でサポートされているのは、101 / 106 / NEC の三種類です。

ダイナミックレイアウトスイッチングはきちんと機能すれば便利なのですが、タイプ・サブタイプによってキーボードレイアウトを決定するため、不明なタイプ= 0x51 の場合にはうまく動作しません。 キーボードの種類が判別できないので、仕方なく PS/2 キーボードの設定にあわせるようです。 こればかりは OS 側ではどうしようもないので、USB キーボードのベンダー各社に正しい INF を用意してもらうしかありません。 そのうち無理やりタイプを設定するユーティリティを書こうと思っていますが、いかんせん時間がない…。

ダイナミックレイアウトスイッチングを有効にするためには、日本語のキーボードレイアウト(xxxx0411なもの)をアクティブにしておく必要があります。 日本語のキーボードレイアウトに埋め込まれている機能なのです。 たとえば US のキーボードレイアウトにはない機能ですので、ダイナミックレイアウトスイッチングが効かない場合にはまずアクティブなインプットロケールとそのキーボードレイアウトを要ちぇきら。

RDP デバイス

なお、↑の出力例でタイプ= 0x51 となっているのは、リダイレクト用の仮想デバイスです。 特定のハードウェアと結びついていないため不明のタイプとなっているものと思われます。

Vistaでの追加

RegisterRawInputDevicesに機能が追加されています。


Since 1996

一つ上へ

ホーム  ざれごと  ワシントン州  ツール  NT豆知識  Win32プログラミングノート  私的用語  ジョーク  いろいろ  ゲーム雑記  Favorites  掲示板   Mail