Windows XP (SP1 以降) で MCE 対応リモコンを使う

 Windows XP (SP1 以降)では "Windows XP Media Center Edition" 対応のリモコンを使う事ができます。"Windows XP Media Center Edition" は単体売りされていませんが、リモコンと GUI を用意すれば同等の事が可能となります。簡単に入手できるリモコンとしては

...等があります。Windows XP (SP1 以降)であればドライバは必要ありません。


・WM_KEYDOWN

 いくつかのキー入力は普通に WM_KEYDOWN メッセージで送られます。フォームの KeyPreview プロパティを True にすればフォームの OnKeyDown で捕まえる事ができます。

// キーボード入力
procedure Form1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  case Key of
    // Clear
    VK_ESCAPE:
      ;
    // クリア (サイバーリンク・マルチリモコン MultiR.C./CL-RC2)
    VK_DELETE:
      ;
    // ↑
    VK_UP:
      ;
    // ↓
    VK_DOWN:
      ;
    // ←
    VK_LEFT:
      ;
    // →
    VK_RIGHT:
      ;
    // Enter/OK(決定)
    VK_RETURN:
      ;
    // [数字キー]
    Ord('0')..Ord('9'):
      ; 
   end;
end;

・WM_APPCOMMAND
 主要なキー入力はWM_APPCOMMANDメッセージで送られます。

procedure WmAppCommand(var Mess: TMessage); message WM_APPCOMMAND;

 

const
  APPCOMMAND_BROWSER_BACKWARD                     = 1;
  APPCOMMAND_BROWSER_FORWARD                      = 2;
  APPCOMMAND_BROWSER_REFRESH                      = 3;
  APPCOMMAND_BROWSER_STOP                         = 4;
  APPCOMMAND_BROWSER_SEARCH                       = 5;
  APPCOMMAND_BROWSER_FAVORITES                    = 6;
  APPCOMMAND_BROWSER_HOME                         = 7;
  APPCOMMAND_VOLUME_MUTE                          = 8;
  APPCOMMAND_VOLUME_DOWN                          = 9;
  APPCOMMAND_VOLUME_UP                            = 10;
  APPCOMMAND_MEDIA_NEXTTRACK                      = 11;
  APPCOMMAND_MEDIA_PREVIOUSTRACK                  = 12;
  APPCOMMAND_MEDIA_STOP                           = 13;
  APPCOMMAND_MEDIA_PLAY_PAUSE                     = 14;
  APPCOMMAND_LAUNCH_MAIL                          = 15;
  APPCOMMAND_LAUNCH_MEDIA_SELECT                  = 16;
  APPCOMMAND_LAUNCH_APP1                          = 17;
  APPCOMMAND_LAUNCH_APP2                          = 18;
  APPCOMMAND_BASS_DOWN                            = 19;
  APPCOMMAND_BASS_BOOST                           = 20;
  APPCOMMAND_BASS_UP                              = 21;
  APPCOMMAND_TREBLE_DOWN                          = 22;
  APPCOMMAND_TREBLE_UP                            = 23;

  APPCOMMAND_MICROPHONE_VOLUME_MUTE               = 24;
  APPCOMMAND_MICROPHONE_VOLUME_DOWN               = 25;
  APPCOMMAND_MICROPHONE_VOLUME_UP                 = 26;
  APPCOMMAND_HELP                                 = 27;
  APPCOMMAND_FIND                                 = 28;
  APPCOMMAND_NEW                                  = 29;
  APPCOMMAND_OPEN                                 = 30;
  APPCOMMAND_CLOSE                                = 31;
  APPCOMMAND_SAVE                                 = 32;
  APPCOMMAND_PRINT                                = 33;
  APPCOMMAND_UNDO                                 = 34;
  APPCOMMAND_REDO                                 = 35;
  APPCOMMAND_COPY                                 = 36;
  APPCOMMAND_CUT                                  = 37;
  APPCOMMAND_PASTE                                = 38;
  APPCOMMAND_REPLY_TO_MAIL                        = 39;
  APPCOMMAND_FORWARD_MAIL                         = 40;
  APPCOMMAND_SEND_MAIL                            = 41;
  APPCOMMAND_SPELL_CHECK                          = 42;
  APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE    = 43;
  APPCOMMAND_MIC_ON_OFF_TOGGLE                    = 44;
  APPCOMMAND_CORRECTION_LIST                      = 45;
  APPCOMMAND_MEDIA_PLAY                           = 46;
  APPCOMMAND_MEDIA_PAUSE                          = 47;
  APPCOMMAND_MEDIA_RECORD                         = 48;
  APPCOMMAND_MEDIA_FAST_FORWARD                   = 49;
  APPCOMMAND_MEDIA_REWIND                         = 50;
  APPCOMMAND_MEDIA_CHANNEL_UP                     = 51;
  APPCOMMAND_MEDIA_CHANNEL_DOWN                   = 52;

  APPCOMMAND_DELETE                               = 53;
  APPCOMMAND_DWM_FLIP3D                           = 54;

  APPCOMMAND_MEDIA_PLAY_PAUSE2                    = 4143;
  APPCOMMAND_MEDIA_PLAY2                          = 4142;
  APPCOMMAND_MEDIA_PAUSE2                         = 4143;
  APPCOMMAND_MEDIA_RECORD2                        = 4144;
  APPCOMMAND_MEDIA_FASTFORWARD2                   = 4145;
  APPCOMMAND_MEDIA_REWIND2                        = 4146;
  APPCOMMAND_MEDIA_CHANNEL_UP2                    = 4147;
  APPCOMMAND_MEDIA_CHANNEL_DOWN2                  = 4148;

 と宣言しておき、

// リモコンキー入力
procedure Form1.WmAppCommand(var Mess: TMessage);
begin
  case Mess.LParamHi of
    APPCOMMAND_BROWSER_BACKWARD:
      // (処理)
  end;
end;

 このようにしてキーを取得します。

・WM_INPUT
 特殊なキー入力はWM_INPUTメッセージで送られます。

type
  TRAWINPUTDEVICE =
    packed record
      usUsagePage: WORD;
      usUsage: WORD;
      dwFlags: DWORD;
      hwndTarge : HWND;
    end;
  PRAWINPUTDEVICE = ^TRAWINPUTDEVICE;

const
  RI_NUM_DEVICE = 2;

var
  Rid: packed array [0..RI_NUM_DEVICE-1of TRAWInputDevice;

 

function RegisterRawInputDevices(pRawInputDevices: Pointer; uiNumDevices, cbSize: UINT): Boolean;
 stdcall; external 'user32.dll';

 

  // リモコンデバイスを登録
  Rid[0].usUsagePage := $FFBC;
  Rid[0].usUsage     := $88;
  Rid[0].dwFlags     := 0;
  Rid[0].hwndTarget  := 0;
  Rid[1].usUsagePage := $000C;
  Rid[1].usUsage     := $01;
  Rid[1].dwFlags     := 0;
  Rid[1].hwndTarget  := 0;
  RegisterRawInputDevices(@Rid, RI_NUM_DEVICE, SizeOf(TRAWInputDevice));

 このように最初にリモコンデバイスを登録しておく必要があります。認識させるデバイスを増やすには Rid の配列を増やして下さい (上記の例は MCE 対応リモコン用となっています)。dwFlags に RIDEV_NOLEGACY を指定するとレガシーなポートに繋がれているデバイスを無視します。以下に主なデバイスの usUsagePage と usUsag を列挙します。

デバイス usUsagePage usUsag
キーボード
0x0001
0x06
マウス
0x0001
0x02
ゲームパッド
0x0001
0x05
ジョイスティック
0x0001
0x04
キーボード (Generic desktop) 0x0001
0x80
キーボード (Consumer controls)
0x000C
0x01
MCE 対応リモコン (Vendor-defined)
0xFFBC
0x88

 そして...

procedure WmInput(var Mess: TMessage); message WM_INPUT;

 これでWM_INPUTメッセージを取得できるようになります。

const
  PW_CLIENTONLY               = $00000001;
  RIM_INPUT                   = $00000000;
  RIM_INPUTSINK               = $00000001;
  RIM_TYPEMOUSE               = $00000000;
  RIM_TYPEKEYBOARD            = $00000001;
  RIM_TYPEHID                 = $00000002;
  MOUSE_MOVE_RELATIVE         = $00000000;
  MOUSE_MOVE_ABSOLUTE         = $00000001;
  MOUSE_VIRTUAL_DESKTOP       = $00000002;
  MOUSE_ATTRIBUTES_CHANGED    = $00000004;
  RI_MOUSE_LEFT_BUTTON_DOWN   = $0001;
  RI_MOUSE_LEFT_BUTTON_UP     = $0002;
  RI_MOUSE_RIGHT_BUTTON_DOWN  = $0004;
  RI_MOUSE_RIGHT_BUTTON_UP    = $0008;
  RI_MOUSE_MIDDLE_BUTTON_DOWN = $0010;
  RI_MOUSE_MIDDLE_BUTTON_UP   = $0020;
  RI_MOUSE_BUTTON_1_DOWN      = RI_MOUSE_LEFT_BUTTON_DOWN;
  RI_MOUSE_BUTTON_1_UP        = RI_MOUSE_LEFT_BUTTON_UP;
  RI_MOUSE_BUTTON_2_DOWN      = RI_MOUSE_RIGHT_BUTTON_DOWN;
  RI_MOUSE_BUTTON_2_UP        = RI_MOUSE_RIGHT_BUTTON_UP;
  RI_MOUSE_BUTTON_3_DOWN      = RI_MOUSE_MIDDLE_BUTTON_DOWN;
  RI_MOUSE_BUTTON_3_UP        = RI_MOUSE_MIDDLE_BUTTON_UP;
  RI_MOUSE_BUTTON_4_DOWN      = $0040;
  RI_MOUSE_BUTTON_4_UP        = $0080;
  RI_MOUSE_BUTTON_5_DOWN      = $0100;
  RI_MOUSE_BUTTON_5_UP        = $0200;
  RI_MOUSE_WHEEL              = $0400;
  KEYBOARD_OVERRUN_MAKE_CODE  = $00ff;
  RI_KEY_MAKE                 = $0000;
  RI_KEY_BREAK                = $0001;
  RI_KEY_E0                   = $0002;
  RI_KEY_E1                   = $0004;
  RI_KEY_TERMSRV_SET_LED      = $0008;
  RI_KEY_TERMSRV_SHADOW       = $0010;
  RID_INPUT                   = $10000003;
  RID_HEADER                  = $10000005;
  RIDI_PREPARSEDDATA          = $20000005;
  RIDI_DEVICENAME             = $20000007;
  RIDI_DEVICEINFO             = $2000000b;
  RIDEV_REMOVE                = $00000001;
  RIDEV_EXCLUDE               = $00000010;
  RIDEV_PAGEONLY              = $00000020;
  RIDEV_NOLEGACY              = $00000030;
  RIDEV_INPUTSINK             = $00000100;
  RIDEV_CAPTUREMOUSE          = $00000200;
  RIDEV_NOHOTKEYS             = $00000200;
  RIDEV_APPKEYS               = $00000400;
  RIDEV_EXINPUTSINK           = $00001000;
  RIDEV_DEVNOTIFY             = $00002000;
  RIDEV_EXMODEMASK            = $000000F0;

 

type
  TRAWINPUTHEADER =
    packed record
      dwType  : DWORD;
      dwSize  : DWORD;
      hDevice : THANDLE;
      wParam  : WPARAM;
    end;
  PRAWINPUTHEADER = ^TRAWINPUTHEADER;

  TRMBUTTONS =
    packed record
      case Integer of
        0:(ulButtons: ULONG);
        1:(
            usButtonFlags : SHORT;
            usButtonData  : SHORT;
          );
      end;

  TRAWMOUSE =
    packed record
      usFlags            : SHORT;
      RMButtons          : TRMBUTTONS;
      ulRawButtons       : ULONG;
      lLastX             : LongInt;
      lLastY             : LongInt;
      ulExtraInformation : ULONG;
    end;
  PRAWMOUSE = ^TRAWMOUSE;

  TRAWKEYBOARD =
    packed record
      MakeCode         : SHORT;
      Flags            : SHORT;
      Reserved         : SHORT;
      VKey             : SHORT;
      Mess             : UINT;
      ExtraInformation : ULONG;
    end;
  PRAWKEYBOARD = ^TRAWKEYBOARD;

  TRAWHID =
    packed record
      dwSizeHid : DWORD;
      dwCount   : DWORD;
      bRawData  : BYTE;
    end;
  PTRAWHID = ^TRAWHID;

  TRAWINPUTDATA =
    packed record
      case Integer of
        0:(mouse    : TRAWMOUSE   );
        1:(keyboard : TRAWKEYBOARD);
        2:(hid      : TRAWHID     );
      end;

  TRAWINPUT =
    packed record
      header    : TRAWINPUTHEADER;
      data      : TRAWINPUTDATA;
    end;
  PRAWINPUT = ^TRAWINPUT;

 

function GetRawInputData(hRawInput: Pointer; uiCommand:UINT; pData: Pointer; pcbSize: Pointer; cbSizeHeader: UINT): UINT;
 stdcall; external 'user32.dll';

 以上を設定すればWM_INPUTメッセージでデータを取得できます。以下にサンプルコードを書いておきます。

procedure Tmain_form.WmInput(var Mess: TMessage);
var
  i, l: Integer;
  RI: PRAWINPUT;
  dwSize: UINT;
  lpb: PBYTE;
  DataSize: DWORD;
  HID_DATA: array [0..15of DWORD;
  P: PByte;
begin
  GetRawInputData(PRAWINPUT(Mess.LParam), RID_INPUT, nil, @dwSize,SizeOf(TRAWINPUTHEADER));
  if dwSize = 0 then
    Exit;
  DataSize := SizeOf(BYTE) * dwSize;
  GetMem(lpb,DataSize);
  try
    GetRawInputData(PRAWINPUT(Mess.LParam), RID_INPUT, lpb, @dwSize, SizeOf(TRAWINPUTHEADER));
    RI := PRAWINPUT(lpb);
    case RI.header.dwType of
      // マウス
      RIM_TYPEMOUSE:
        begin
          Label1.Caption := Format('Mouse : (%d:%d) %.2x', 
                                   [RI.Data.mouse.lLastX,
                                    RI.Data.mouse.lLastX,
                                    RI.Data.mouse.RMButtons.usButtonData]);
        end;
      // キーボード
      RIM_TYPEKEYBOARD:
        begin
          Label1.Caption := Format('KeyBoard : %.2x', [RI.Data.keyboard.VKey]);
        end;
      // HID準拠デバイス
      RIM_TYPEHID:
        begin
          P := @RI.Data.hid.bRawData;
          for i:=0 to RI.Data.hid.dwCount-1 do
            begin
              HID_DATA[i] := 0;
              for l:=1 to RI.Data.hid.dwSizeHid do
                begin
                  HID_DATA[i] := (HID_DATA[i] shl 8) + P^;
                  Inc(P);
                end;
            end;
          Label1.Caption := '';
          for i:=0 to RI.Data.hid.dwCount-1 do
            Label1.Caption := Label1.Caption + Format('HID[%d] : %x',[i+1, HID_DATA[i]]);
        end;
    end;
  finally
    FreeMem(lpb, DataSize);
  end;
end;

 こんな感じでデータを取得できます。

・おまけ
 "サイバーリンク・マルチリモコン(MultiR.C./CL-RC2)" でのキーに関する資料です。

ボタン メッセージ コマンドまたは UsagePage / Usag / RawData
ホーム アプリケーションで制御不可 N/A
Power WM_INPUT アプリケーションで制御すべきではない
DVD/VIDEO CD WM_INPUT 0xFFBC / 0x88 / 0x048000
音楽 WM_INPUT 0xFFBC / 0x88 / 0x040800
写真/静止画 WM_INPUT 0xFFBC / 0x88 / 0x041000
ファイル再生 WM_INPUT 0xFFBC / 0x88 / 0x042000
テレビ WM_INPUT 0xFFBC / 0x88 / 0x040200
録画 WM_APPCOMMAND APPCOMMAND_MEDIA_RECORD
ラジオ WM_INPUT 0x000C / 0x01 / 0x03040000
主/副音声
WM_INPUT 0xFFBC / 0x88 / 0x040010
文字放送/CC WM_INPUT 0xFFBC / 0x88 / 0x040020
Last CH WM_INPUT 0xFFBC / 0x88 / 0x040040
字幕切り替え WM_INPUT 0xFFBC / 0x88 / 0x040008
音声切り替え WM_INPUT 0xFFBC / 0x88 / 0x040002
アングル切り替え WM_INPUT 0xFFBC / 0x88 / 0x040001
BACK WM_APPCOMMAND APPCOMMAND_BROWSER_BACKWARD
Info/EPG WM_INPUT 0x000C / 0x01 / 0x03000002
DVDメニュー WM_INPUT 0xFFBC / 0x88 / 0x040004
音量Up(+) WM_APPCOMMAND APPCOMMAND_VOLUME_UP
音量Down(-) WM_APPCOMMAND APPCOMMAND_VOLUME_DOWN
ミュート WM_APPCOMMAND APPCOMMAND_VOLUME_MUTE
チャンネルUp WM_APPCOMMAND APPCOMMAND_MEDIA_CHANNEL_UP
チャンネルDown WM_APPCOMMAND APPCOMMAND_MEDIA_CHANNEL_DOWN
WM_KEYDOWN VK_UP
WM_KEYDOWN VK_DOWN
WM_KEYDOWN VK_LEFT
WM_KEYDOWN VK_RIGHT
決定
WM_KEYDOWN VK_RETURN
再生 WM_APPCOMMAND APPCOMMAND_MEDIA_PLAY
巻き戻し WM_APPCOMMAND APPCOMMAND_MEDIA_REWIND
一時停止 WM_APPCOMMAND APPCOMMAND_MEDIA_PAUSE
早送り WM_APPCOMMAND APPCOMMAND_MEDIA_FASTFORWARD
前のチャプタ
WM_APPCOMMAND APPCOMMAND_MEDIA_PREVIOUSTRACK
停止 WM_APPCOMMAND APPCOMMAND_MEDIA_STOP
次のチャプタ WM_APPCOMMAND APPCOMMAND_MEDIA_NEXTTRACK
数字キー WM_KEYDOWN VK_0...VK_9
クリア WM_KEYDOWN VK_DELETE (リファレンスでは VK_ESCAPE)

See Also:
 MSDN: "Using the Remote Control Device with Windows"


 BACK