USB/IP PROJECT를 이용해 C#으로 USB Keyboard 가상 장치 만들기
지난 글에서,
(User-mode 코드로 가상 USB 장치를 만들 수 있는) USB/IP PROJECT 소개
; https://www.sysnet.pe.kr/2/0/12213
USB/IP PROJECT에 버그가 있어 가상 장치를 detach 시 BSOD가 발생한다고 했는데요, 현재 해당 프로젝트는 종료되었으므로 더 이상 업데이트를 기대할 수 없지만 그 바통을 이어받은 프로젝트가 다행히 아직 살아 있습니다. ^^
cezanne / usbip-win
; https://github.com/cezanne/usbip-win
USB/IP for Windows
; https://github.com/cezanne/usbip-win/blob/master/README.md
(게다가 저 repo의 주인장인 cezanne라는 분은 한국인입니다. ^^)
따라서, 위의 repo에 있는 device driver를 빌드한 usbip_vhci.sys를 윈도우 10에 설치하면 BSOD 없는 안정적인 "USB/IP VHCI" 장치를 사용할 수 있습니다.
자, 그럼 usbip_vhci.sys에서 나열해 줄 가상 장치 역할을 할 클라이언트 코드가 필요한데 이것도 이미 전의 글에서 소개한,
lcgamboa / USBIP-Virtual-USB-Device
; https://github.com/lcgamboa/USBIP-Virtual-USB-Device
repo에 C 언어와 파이썬으로 구현한 코드를 함께 제공하고 있으니, C#에서는 그저 해당 기능들을 그대로 포팅만 하면 됩니다. 아래의 프로젝트는 그래서 만들어진 것이고,
USBIP-Virtual-USB-Device/dotnet/UsbipDevice/
; https://github.com/stjeong/USBIP-Virtual-USB-Device/tree/master/dotnet/UsbipDevice
위의 라이브러리를 바탕으로 예전에 Raspberry pi zero로 구현해 두었던 키보드 제어 방식을,
stjeong / rasp_vusb
; https://github.com/stjeong/rasp_vusb
동일하게 구현한 예제 프로젝트를 함께 실어 두었습니다.
USBIP-Virtual-USB-Device/dotnet/cs-hid-keyboard/
; https://github.com/stjeong/USBIP-Virtual-USB-Device/tree/master/dotnet/cs-hid-keyboard
사용 방법은 아래의 코드를 보면 대충 눈치채실 수 있을 것입니다. ^^
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using UsbipDevice;
namespace cs_hid_keyboard
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("cs-hid-keyboard");
bool waitLocalHost = true;
if (args.Length >= 1)
{
if (args[0] == "-w")
{
waitLocalHost = false;
}
}
byte[] reportBuffer = KeyboardDescriptors.Report;
using (Usbip device = new Usbip(UsbDescriptors.Device, KeyboardDescriptors.Hid, reportBuffer))
{
KeyboardDevice keyboard = new KeyboardDevice(device, reportBuffer);
device.Run();
if (waitLocalHost == true)
{
Thread usbipServer = new Thread(usbipServer_Run);
usbipServer.IsBackground = true;
usbipServer.Start();
}
KeyboardTest(keyboard);
}
}
private static void usbipServer_Run(object obj)
{
foreach (Process process in Process.GetProcessesByName("usbip"))
{
process.Kill();
}
// usbip attach -r 127.0.0.1 -b 1-1
Process.Start("usbip", "attach -r 127.0.0.1 -b 1-1");
}
private static void KeyboardTest(KeyboardDevice keyboard)
{
//{
// string txt = "abc+*()xptmxm";
// _usbController.SendText(txt + Environment.NewLine);
//}
//{
// string txt = "abc";
// _usbController.SendText(txt + Environment.NewLine);
//}
//{
// string txt = "";
// _usbController.SendText(txt);
//}
//{
// string txt = "test is good";
// _usbController.SendText(txt);
//}
//{
// string txt = "";
// _usbController.SendText(txt);
//}
Console.WriteLine("Wait for usbip...");
while (true)
{
Console.Write(".");
if (keyboard.Connected == true)
{
break;
}
Thread.Sleep(1000);
}
while (true)
{
Console.Write("Keyboard> ");
string text = Console.ReadLine();
Thread.Sleep(2000);
if (text == "quit")
{
keyboard.Dispose();
break;
}
keyboard.SendText(text);
}
}
}
}
빌드하고 실행하면, "장치 관리자"에 새로운 "HID Keyboard Device" 장치가 생기고 "keyboard.SendText(text);"에 넣는 글자에 따라 키 입력이 그대로 되는 것을 확인할 수 있습니다. (이제 더 이상 "Raspberry PI Zero"가 없어도 소프트웨어적으로 완벽하게 제어할 수 있는 USB 가상 키보드를 얻게 되었습니다. ^^)
참고로, "
cezanne / usbip-win" 소스 코드를 빌드하면 당연히 테스트 인증서로 서명되므로 윈도우 10의
testsigning 옵션을 켜야 합니다. 그런 후 "usbip.exe"로 다음과 같이 실행해 주면,
c:\temp> usbip install
설치가 됩니다. (물론, INF 파일을 이용해 장치 관리자에서 설치해도 됩니다.)
마지막으로, 제가 가지고 있던 책에서 관련 자료가 나오는데,
윈도우즈 드라이버 모델 : Writing Windows WDM Device Drivers
; http://www.yes24.com/Product/goods/53240347
Test 6
Kbd report 1 0 0 0 0 0 0 0 Ctrl
Kbd report 5 0 0 0 0 0 0 0 Ctrl+Alt
Kbd report 5 0 63 0 0 0 0 0 Ctrl+Alt+Del
Kbd report 4 0 0 0 0 0 0 0 Alt
Kbd report 0 0 4 0 0 0 0 0 A
Kbd report 0 0 5 0 0 0 0 0 B
Kbd report 0 0 6 0 0 0 0 0 C
Kbd report 0 0 29 0 0 0 0 0 Esc
위에서 보면, 제어 키는 0번, 문자 키는 2번 바이트에 할당됩니다. 즉, keyboard.SendText는 문자에 맞는 값으로 저 바이트 배열을 보내기만 하면 되는 것입니다. (실제로는
Report id 값도 보내기 때문에 약간 다르긴 합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]