
ãããããCtrl + CãšCtrl + Vãäœã§ããããç¥ããªã人ã¯ã»ãšãã©ããªãã§ãããã ããçµéšè±å¯ãªãŠãŒã¶ãŒã¯ãäžè¬çã«äœ¿çšãããã¢ããªã±ãŒã·ã§ã³ã®ãããããŒãç¥ã£ãŠããŸãã ããè€éãªçµã¿åããã䜿çšãã人ãããŸããããšãã°ããã¬ãŒã€ãŒãããã¯ã°ã©ãŠã³ãã§å¶åŸ¡ããããã§ãã éçºè ã«ãšã£ãŠããã®ãããªæ©èœã®å®è£ ã¯éåžžããã»ã©å€§ããªåé¡ãåŒãèµ·ãããŸããã ãã®ã¿ã¹ã¯ã¯åºãè¡ãããŠããããã®ãœãªã¥ãŒã·ã§ã³ã«ã€ããŠã¯ãã§ã«å€ãã®ããšãæžãããŠããŸãã ãããããžã§ã€ã¹ãã£ãã¯ãŸãã¯ãã¬ãŒã³ã¿ãŒããã®ãŠãŒã¶ãŒå ¥åãæå°éã«æããã€ãã³ãã®çºçå ã®ããã€ã¹ãææ¡ããå¿ èŠãããå Žåã¯ã©ãã§ããããïŒ æ£çŽã«èšããšãç§ãã¡ã«ãšã£ãŠãã®ã¿ã¹ã¯ã¯æ°ãããã®ã§ããããšãå€æããŸããã ç«ã®äžã§ãã Raw Input API ãã䜿çšããŠWPFã¢ããªã±ãŒã·ã§ã³ã®CïŒã§ã©ã®ããã«è§£æ±ºãããã説æããŸã ã
èæ¯
Jalinga Studioã¢ããªã±ãŒã·ã§ã³ã¯ããããªããŠã§ãããŒãããã³ãªã³ã©ã€ã³ãããŒããã£ã¹ãã®æ®åœ±ã«äœ¿çšãããäž»ã«ãã¬ãŒã³ã¿ãŒãŸãã¯ãžã§ã€ã¹ãã£ãã¯ã䜿çšããŠå¶åŸ¡ãããŸãïŒãããå¿ èŠãªçç±ã«ã€ããŠã¯ãåã®èšäºããã¬ãŒã³ããŒã·ã§ã³ãã¢ãã¡ãŒã·ã§ã³åããæ¹æ³ããåç §ããŠãã ããïŒã ã»ãšãã©ã®ãã¬ãŒã³ã¿ãŒã¯Power Pointã§åäœããããã«èšèšãããŠãããããF5ãPage UpãPage Downãªã©ã®éåžžã®ããŒããŒãå ¥åãçæããŸãã WPFã«ã¯ãããŒããŒãå ¥åãæäœããããã®æšæºã¡ã«ããºã ããããŸãã é倧ãªæ¬ ç¹ã«ééãããŸã§ãåããŠäœ¿çšããŸããã å®éããã®ã¡ã«ããºã ã¯ã¢ããªã±ãŒã·ã§ã³ãã¢ã¯ãã£ãïŒãã©ã¢ã°ã©ãŠã³ãïŒã®å Žåã«ã®ã¿æ©èœããŸãããäžéšã®ã¯ã©ã€ã¢ã³ãã¯ãããšãã°ãã©ãŠã¶ãŒå ¥åãããŒããŒãå ¥åã確å®ã«å¥ªãå¥ã®ããã°ã©ã ãžã®ã¢ã¯ã»ã¹ãæãã§ããŸãã æåã«ãSkypeã§è¡ãããæ¹æ³ãšåæ§ã«ããã©ã¢ã°ã©ãŠã³ãã«è¿œå ã®å°ããªãŠã£ã³ããŠãäœæããŠããã®åé¡ãåé¿ããããšããŸããã ãŠãŒã¶ãŒãããŠã¹ãå¶åŸ¡ããæ¹ã䟿å©ãªå Žåããã®ãŠã£ã³ããŠã«ã¯ããã°ã©ã ã®ã¹ããŒã¿ã¹ãšå¶åŸ¡çšã®ããã€ãã®ãã¿ã³ã衚瀺ãããŸãã ãã®ã¢ãããŒãã¯æã䟿å©ã§ã¯ãããŸããã§ãã-ã³ã³ãããŒã«ãŠã£ã³ããŠãã¢ã¯ãã£ãã«ããå¿ èŠããããŸãã ãŠãŒã¶ãŒããã©ãŒã«ã¹ãåãæ¿ããã®ãå¿ããå Žåããã¬ãŒã³ã¿ãŒããã®ããŒããŒãå ¥åã¯çŸåšã¢ã¯ãã£ããªã¢ããªã±ãŒã·ã§ã³ã«è¡ããŸããã ããšãã°ããã©ãŠã¶ã®F5ãŸãã¯Page Downã§ãã ããã«å ããŠãããæç¹ã§ãã¬ãŒã³ã¿ãŒã®ãã¿ã³ãèŠéãå§ããæšæºã®WPFã¡ã«ããºã ã§ã¯ãµããŒããããŠããªããžã§ã€ã¹ãã£ãã¯ã䜿çšããããšã«ããŸããã
ãœãªã¥ãŒã·ã§ã³ãæ€çŽ¢ãã
ãŸããæ°ããã¡ã«ããºã ã®èŠä»¶ãçå®ããŸããã
- ã¢ããªã±ãŒã·ã§ã³ãããã¯ã°ã©ãŠã³ãã§å®è¡ãããŠããå ŽåããŠãŒã¶ãŒå
¥åã«é¢ããæ
å ±ãåãåããŸãã
- ãã¬ãŒã³ã¿ãŒããžã§ã€ã¹ãã£ãã¯ãã²ãŒã ããããšé£æºããæ©èœã
- å
¥åããã€ã¹ãåºå¥ããæ¹æ³ã®ååšã
æåã«æãã€ããã®ã¯ã SetWindowsHookExé¢æ°ã䜿çšããŠèšå®ã§ããããã¯ã§ãã ããããããã§ããžã§ã€ã¹ãã£ãã¯ãšã²ãŒã ãããããµããŒããããšããåé¡ã¯æªè§£æ±ºã®ãŸãŸã§ãã ããŒãã¬ãŒãä»ã®äººã®ããã°ã©ã ã®åäœã«å¯Ÿããããã¯ã®åœ±é¿ã32ãããããã³64ãããdllã®äœæãå¥ã®ããã»ã¹ããã®ã¢ããªã±ãŒã·ã§ã³ãšã®çžäºäœçšãããã³ãµããŒãã®äžè¬çãªè€éãã«ã€ããŠãç§ãã¡ãé£ããŠè¡ã£ãŠãããã¢ã³ããŠã€ã«ã¹ã«ã€ããŠã¯æ²é»ããŠããŸãã
DirectInputãŸãã¯XInputã®äœ¿çšãæ€èšã DirectInputã¯éæšå¥šã§ã; 代ããã«XInputã䜿çšããããšããå§ãããŸã ã SetCooperativeLevelã¡ãœããã䜿çšããŠBackgroundãã©ã°ãèšå®ãããšããããã䜿çšããŠãããã¯ã°ã©ãŠã³ãã§ããžã§ã€ã¹ãã£ãã¯ãããŠãŒã¶ãŒå ¥åãååŸã§ããŸãã ãã ãã XInputã¯ããŒããŒããšããŠã¹ããµããŒãããŠããŸããã ç§ã¯ãŸã ãã«ã¢ãã«ã®äœ¿çšã奜ãã§ã¯ãããŸããã§ããããã®ãããèå³ã®ããããã€ã¹ã«ããçšåºŠã®é »åºŠã§åãåãããå¿ èŠããããŸãã
ããã«æãç¶ããŸããã Parallelsã®ããå人ã¯ã Raw Input APIã«ç®ãåããããææ¡ããŸããã ãã®APIã®æ©èœãåæããçµæãå¿ èŠãªã®ã¯HIDã¯ã©ã¹ã®ããŸããŸãªããã€ã¹ã®æäœãã¢ã¯ãã£ããŠã£ã³ããŠãªãã§å ¥åãåä¿¡ããæ©èœãããã³ã€ãã³ãã®çºçå ã®ããã€ã¹ã®äœ¿çšå¯èœãªIDã ãã§ããããšãããããŸããã å¶éããããŸã-å ¥åã管çããã»ã¹ã§å®è¡ãããããã»ã¹ã管çè ã§ãªãå Žåãå ¥åã€ãã³ãã¯çºçããŸããã ããããç§ãã¡ã¯ãããå¿ èŠãšããŸããã 極端ãªå Žåã管çè æš©éã§ãã€ã§ãã¢ããªã±ãŒã·ã§ã³ãå®è¡ã§ããŸãã
å®è£
äžè¬ã«ããRaw Input APIãã䜿çšããŠãŠãŒã¶ãŒå ¥åãååŸããããã»ã¹ã¯ã次ã®æé ã§æ§æãããŸãã
- RegisterRawInputDevicesã䜿çšããŠãå
¥åã€ãã³ããåãåãããã€ã¹ã®ã¿ã€ããç»é²ããŸãã
- ãŠã£ã³ããŠããã·ãŒãžã£ã§WM_INPUTã€ãã³ãããªãã¹ã³ããŸã ã
- GetRawInputDataã䜿çšããŠãåä¿¡ããã€ãã³ãã解æããŸã ã
- ã€ãã³ãã®ã¿ã€ãïŒRAWMOUSEãRAWKEYBOARDãRAWHIDïŒãå€å¥ãããã®ã¿ã€ãã«åŸã£ãŠè§£æããŸãã
ãã®ãã¹ãŠã®ã·ãŒã±ã³ã¹ã¯ãWPFã¢ããªã±ãŒã·ã§ã³ã®CïŒã§å®è£ ããå¿ èŠããããŸããã Win APIé¢æ°ã®å€æ°ã®ã©ãããŒãç¬èªã«èšè¿°ããªãããã«ã SharpDX.RawInputã䜿çšããããšã決å®ãããŸãã ã
ããã¯ãWindows.Formsã䜿çšããå Žåã«ãç°¡ç¥åãããCïŒã³ãŒããã©ã®ããã«èŠãããã§ãã
public class RawInputListener { public void Init(IntPtr hWnd) { Device.RegisterDevice(UsagePage.Generic, UsageId.GenericGamepad, DeviceFlags.InputSink, hWnd); Device.RegisterDevice(UsagePage.Generic, UsageId.GenericKeyboard, DeviceFlags.InputSink, hWnd); Device.RawInput += OnRawInput; Device.KeyboardInput += OnKeyboardInput; } public void Clear() { Device.RawInput -= OnRawInput; Device.KeyboardInput -= OnKeyboardInput; } private void OnKeyboardInput(object sender, KeyboardInputEventArgs e) { } private void OnRawInput(object sender, RawInputEventArgs e) { } }
DeviceFlags.InputSinkãã©ã°ã¯ ãã¢ããªã±ãŒã·ã§ã³ãã¡ãã»ãŒãžããã©ã¢ã°ã©ãŠã³ãã«ãªãå Žåã§ãåä¿¡ããããã«å¿ èŠã§ãã ãã®ãã©ã°ã䜿çšããå ŽåãhWndãæå®ããå¿ èŠããããŸãã
WPFã䜿çšããå ŽåãOnRawInputããã³OnKeyboardInputã¡ãœããã¯ãã®æ¹æ³ã§åŒã³åºãããŸããã Deviceã¯ã©ã¹å ã«ãWindows.Formsã®IMessageFilterã€ã³ã¿ãŒãã§ã€ã¹ãå®è£ ãããŠããŸãã Deviceã®ãœãŒã¹ã³ãŒããèŠããšãPreFilterMessageã¡ãœããã§HandleMessageãåŒã³åºãããŠããããšãããããŸãã
WPFã䜿çšããå Žåã®ç°¡ç¥åãããCïŒã³ãŒãïŒ
public class RawInputListener { private const int WM_INPUT = 0x00FF; private HwndSource _hwndSource; public void Init(IntPtr hWnd) { if (_hwndSource != null) { return; } _hwndSource = HwndSource.FromHwnd(hWnd); if (_hwndSource != null) _hwndSource.AddHook(WndProc); Device.RegisterDevice(UsagePage.Generic, UsageId.GenericGamepad, DeviceFlags.InputSink, hWnd); Device.RegisterDevice(UsagePage.Generic, UsageId.GenericKeyboard, DeviceFlags.InputSink, hWnd); Device.RawInput += OnRawInput; Device.KeyboardInput += OnKeyboardInput; } public void Clear() { Device.RawInput -= OnRawInput; Device.KeyboardInput -= OnKeyboardInput; } private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_INPUT) { Device.HandleMessage(lParam, hWnd); } return IntPtr.Zero; } private void OnKeyboardInput(object sender, KeyboardInputEventArgs e) { } private void OnRawInput(object sender, RawInputEventArgs e) { } }
ã€ãã³ãã®çºçå ã®ããã€ã¹ãç¹å®ããã«ã¯ãRawInputEventArgsã¯ã©ã¹ã®Deviceããããã£ã䜿çšããŸããããã«ããã次ã®åœ¢åŒã®DeviceNameãååŸã§ããŸãã
"\\?\HID#{00001124-0000-1000-8000-00805f9b34fb}_VID&000205ac_PID&3232&Col02#8&26f2f425&7&0001#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}"
ãã®ååã§ãã³ããŒIDãšè£œåIDãèŠã€ãããã GetRawInputDeviceInfoãŸãã¯HidD_GetAttributesé¢æ°ã䜿çšã§ããŸã ã VIDãšPIDã®è©³çŽ°ã«ã€ããŠã¯ã ãã¡ããšãã¡ããã芧ãã ãã ã
次ã«ãã€ãã³ãã®åæã«ç®ãåããŸãã ããŒããŒãã䜿çšãããšããã¹ãŠãã·ã³ãã«ã§ããããšãå€æããŸãããæ å ±ã¯ãKeyboardInputEventArgsã¯ã©ã¹ã§èª¬æãããŠããéã¢ã»ã³ãã«ããã圢åŒã§æ¢ã«æäŸãããŠããŸãã ããããã²ãŒã ãããã§ã¯ãã¹ãŠãããè€éã«ãªããŸããã åºæ¬åRawInputEventArgsã®åŒæ°ãOnRawInputã«æž¡ãããŸãã ãã®åŒæ°ã¯ãHidInputEventArgsåã«ãã£ã¹ãããå¿ èŠããããæ©èœããå Žåã¯åŒãç¶ãæ©èœããŸãã HidInputEventArgsã«ã¯ãããã€ã¹ããéä¿¡ããããã€ãã®é åã®ã¿ãããããã®é åã®ãã€ãæ°ã¯ããžã§ã€ã¹ãã£ãã¯ãã²ãŒã ãããã«ãã£ãŠç°ãªããŸãã
æ®å¿µãªããããã®ããŒã¿ã解æããæ¹æ³ã説æããå°ããªããã¥ã¡ã³ããèŠã€ããããšã¯ã§ããŸããã§ãããããã¯éåžžãçã圢åŒã§ããèŠã€ãããŸããïŒMSDNã§ããããã®åé¡ã«ã€ããŠãŸã£ããæ§ãããªè¡šçŸããããŸããïŒã Cã®ãã®ãããžã§ã¯ã㯠ãæãæçšã§ããããšãå€æããŸãã ã æåã¯äœæ¥ç°å¢ã«æ»ããªããã°ãªããŸããã§ãããããã§ã«çŽ æŽãããã¹ã¿ãŒãã§ããã ãã®åŸãå¿ èŠãªéšåãCïŒã«è»¢éããå¿ èŠããããŸããã
æåã®ã¹ãããã¯ããã€ãã£ãé¢æ°ãã©ããããŠCïŒã³ãŒãããåŒã³åºãããšã§ããã ããã§ã¯ããã€ãã®ããã«ã pinvoke.netã圹ã«ç«ã¡ãŸããã CïŒã®MarshalãPInvokeãããã³å®å šã§ãªãã³ãŒãã«ã€ããŠã¯ã ãã¡ããã芧ãã ãã ã
次ã®ã¹ãããã§ã¯ãã¡ãã»ãŒãžè§£æã¢ã«ãŽãªãºã ã転éããŸããããã¯ã次ã®ããã«èŠçŽãããŸãã
- PreparsedDataããã€ã¹ã®ååŸïŒ GetRawInputDeviceInfoãŸãã¯HidD_GetPreparsedData ïŒ;
- ããã€ã¹æ©èœã«ã€ããŠåŠç¿ããŸãïŒ HidP_GetCaps ïŒã
- ããã€ã¹ãã¿ã³ã®è©³çŽ°ïŒ HidP_GetButtonCaps ïŒ
- æŒããããã¿ã³ã®ãªã¹ããååŸããŸãïŒ HidP_GetUsages ïŒã
以äžã¯ãã²ãŒã ãããããã®ããŒã¿è§£æã³ãŒãã®äžéšã§ãããæŒããããã¿ã³ã®ãªã¹ããåºåããŸãã
public static class RawInputParser { public static bool Parse(HidInputEventArgs hidInput, out List<ushort> pressedButtons) { var preparsedData = IntPtr.Zero; pressedButtons = new List<ushort>(); try { preparsedData = GetPreparsedData(hidInput.Device); if (preparsedData == IntPtr.Zero) return false; HIDP_CAPS hidCaps; CheckError(HidP_GetCaps(preparsedData, out hidCaps)); pressedButtons = GetPressedButtons(hidCaps, preparsedData, hidInput.RawData); } catch (Win32Exception e) { return false; } finally { if (preparsedData != IntPtr.Zero) { Marshal.FreeHGlobal(preparsedData); } } return true; } }
PreparsedDataã¯GetRawInputDeviceInfoã䜿çšãããšç°¡åã«ååŸã§ããŸãã å¿ èŠãªããã€ã¹ãã³ãã«ã¯æ¢ã«RawInputEventArgsã«ãããŸãã HidD_GetPreparsedDataé¢æ°ã¯ãã®ãã³ãã«ãåãå ¥ããŸãã; CreateFileã䜿çšããŠååŸã§ãããã³ãã«ãå¿ èŠã§ãã
åå¥ã®ãã¿ã³ã®å€ãååŸããæé ã¯åãã§ããHidP_GetButtonCapsã®ä»£ããã«ãæåã«HidP_GetValueCapsãåŒã³åºããŠããã HidP_GetUsageValueãåŒã³åºããŠãã¿ã³ã®åå¥ã®å€ãååŸããå¿ èŠããããŸãã
HidInputEventArgsããã®ãã€ãã»ãããã©ã®ãã¿ã³ãæŒããããã«é¢ããããŒã¿ã«å€æãããã®ã§ãããŒããŒããšããŠã¹ãæäœããããã®WPFã«ãããã®ãšåæ§ã®ã¡ã«ããºã ãäœæã§ããŸãã
ã²ãŒã ããããšããŒããŒãããã®ãŠãŒã¶ãŒå ¥åã解æããã¢ããªã±ãŒã·ã§ã³ã®å®å šãªã³ãŒãã¯ãGitHubã®RawInputWPFãããžã§ã¯ãã§è¡šç€ºã§ããŸãã
ãŸãšã
ãã®ããã«ããRaw Input APIãã䜿çšãããšãã¢ããªã±ãŒã·ã§ã³ãããã¯ã°ã©ãŠã³ãã«ããå Žåã§ããããŒããŒããããŠã¹ããžã§ã€ã¹ãã£ãã¯ãã²ãŒã ãããããŸãã¯ãã®ä»ã®ãŠãŒã¶ãŒå ¥åããã€ã¹ãããŠãŒã¶ãŒå ¥åãååŸã§ããŸãã
ãããŠãæŒããããã¿ã³ã®ããŒã¿ãã©ããããã¯ããªã次第ã§ãã