异步IO,支持拔插事件
HidCore
1 #pragma once 2 #include "stdafx.h" 3 #include "HidEvent.h" 4 5 using namespace System; 6 using namespace System::Diagnostics; 7 delegate LRESULT HidCoreWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); 8 delegate DWORD HidCoreThreadStart(LPVOID lparam); 9 10 namespace ZSHid 11 { 12 public ref class HidCore 13 { 14 public: 15 HidCore(int vid, int pid); 16 virtual ~HidCore(); 17 18 bool FindMyDevice(); 19 int Write(array<Byte> ^data); 20 int Read(array<Byte> ^data); 21 void NotifyEvents(Action ^deviceArrival, Action ^deviceRemoved); 22 23 property int InputReportLen 24 { 25 int get(); 26 } 27 28 property int OutputReportLen 29 { 30 int get(); 31 } 32 33 property bool IsConnected 34 { 35 bool get(); 36 } 37 38 Action ^_deviceArrival; 39 Action ^_deviceRemoved; 40 41 42 private: 43 int _vid; 44 int _pid; 45 int _inputReportLen; 46 int _outputReportLen; 47 bool _isConnected; 48 HANDLE _readHandle; 49 HANDLE _writeHandle; 50 DWORD _threadMessageId; 51 HANDLE _threadMessageHandle; 52 HidEvent *_hidEvent; 53 54 OVERLAPPED *_ovlpw; 55 OVERLAPPED *_ovlpr; 56 unsigned char *_readBuffer; 57 unsigned char *_writeBuffer; 58 }; 59 }
1 #include "stdafx.h" 2 #include "HidCore.h" 3 #include <vcclr.h> 4 5 using namespace ZSHid; 6 #pragma comment(lib, "hid.lib") 7 #pragma comment(lib, "setupapi.lib") 8 9 void HidCore::NotifyEvents(Action ^ deviceArrival, Action ^ deviceRemoved) 10 { 11 _deviceArrival = deviceArrival; 12 _deviceRemoved = deviceRemoved; 13 14 // 创建线程消息循环 15 pin_ptr<DWORD> pthreadMessageId = &_threadMessageId; 16 _threadMessageHandle = CreateThread(NULL, 0, 17 (LPTHREAD_START_ROUTINE)HidEvent::ThreadMessageProc, 18 NULL, THREAD_PRIORITY_NORMAL, 19 &(*pthreadMessageId)); 20 if (_threadMessageHandle == INVALID_HANDLE_VALUE) 21 { 22 int error = GetLastError(); 23 Console::ForegroundColor = ConsoleColor::Blue; 24 Console::WriteLine("[CreateThread Error]GetLastError = " + error); 25 Console::ResetColor(); 26 } 27 } 28 29 HidCore::HidCore(int vid, int pid) 30 { 31 _vid = vid; 32 _pid = pid; 33 34 OVERLAPPED ovlpw = { 0 }; 35 OVERLAPPED ovlpr = { 0 }; 36 _ovlpw = &ovlpw; 37 _ovlpr = &ovlpr; 38 39 _readBuffer = new unsigned char[256]; 40 _writeBuffer = new unsigned char[256]; 41 42 gcroot<HidCore^> *phidCore = new gcroot<HidCore^>(this); 43 _hidEvent = new HidEvent(phidCore); 44 45 FindMyDevice(); 46 } 47 48 HidCore::~HidCore() 49 { 50 _isConnected = false; 51 PostThreadMessage(_threadMessageId, WM_CLOSE, 0, 0); 52 CloseHandle(_threadMessageHandle); 53 54 delete _readBuffer; 55 delete _writeBuffer; 56 if (_hidEvent != nullptr) delete _hidEvent; 57 } 58 59 bool HidCore::FindMyDevice() 60 { 61 bool isFind = false; 62 GUID guid; 63 HidD_GetHidGuid(&guid); 64 HDEVINFO hdevinfo = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 65 SP_DEVICE_INTERFACE_DATA device_interface_data; 66 ZeroMemory(&device_interface_data, sizeof(device_interface_data)); 67 device_interface_data.cbSize = sizeof(device_interface_data); 68 69 int device_interface_index = 0; 70 while (SetupDiEnumDeviceInterfaces(hdevinfo, NULL, &guid, device_interface_index, &device_interface_data)) 71 { 72 DWORD requireSize; 73 SetupDiGetDeviceInterfaceDetail(hdevinfo, &device_interface_data, NULL, 0, &requireSize, NULL); 74 PSP_DEVICE_INTERFACE_DETAIL_DATA psp_device_interface_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requireSize); 75 psp_device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 76 if (SetupDiGetDeviceInterfaceDetail(hdevinfo, &device_interface_data, psp_device_interface_detail_data, requireSize, &requireSize, NULL)) 77 { 78 TCHAR *device_path = psp_device_interface_detail_data->DevicePath; 79 80 // 打印设备路径[将TCHAR *转换为System::String^] 81 String ^str = gcnew String(device_path); 82 Console::ForegroundColor = ConsoleColor::Blue; 83 Console::WriteLine(str); 84 Console::ResetColor(); 85 86 HANDLE handle = CreateFile(device_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); 87 if (handle != INVALID_HANDLE_VALUE) 88 { 89 HIDD_ATTRIBUTES hidd_attributes; 90 HidD_GetAttributes(handle, &hidd_attributes); 91 if (hidd_attributes.VendorID == _vid && hidd_attributes.ProductID == _pid) 92 { 93 PHIDP_PREPARSED_DATA phidp_preparsed_data; 94 HidD_GetPreparsedData(handle, &phidp_preparsed_data); 95 HIDP_CAPS hidp_caps; 96 HidP_GetCaps(phidp_preparsed_data, &hidp_caps); 97 _inputReportLen = hidp_caps.InputReportByteLength; 98 _outputReportLen = hidp_caps.OutputReportByteLength; 99 HidD_FreePreparsedData(phidp_preparsed_data); 100 101 _readHandle = CreateFile(device_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); 102 _writeHandle = CreateFile(device_path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); 103 104 isFind = true; 105 CloseHandle(handle); 106 free(psp_device_interface_detail_data); 107 break; 108 } 109 110 CloseHandle(handle); 111 } 112 } 113 else 114 { 115 int error = GetLastError(); 116 Console::ForegroundColor = ConsoleColor::Blue; 117 Console::WriteLine("[SetupDiGetDeviceInterfaceDetail Error]GetLastError = " + error); 118 Console::ResetColor(); 119 } 120 121 device_interface_index++; 122 free(psp_device_interface_detail_data); 123 } 124 125 SetupDiDestroyDeviceInfoList(hdevinfo); 126 _isConnected = isFind; 127 if (!_isConnected) 128 { 129 CloseHandle(_writeHandle); 130 CloseHandle(_readHandle); 131 _writeHandle = _readHandle = INVALID_HANDLE_VALUE; 132 } 133 134 return isFind; 135 } 136 137 int HidCore::Write(array<Byte> ^data) 138 { 139 DWORD write_size = 0; 140 if (IsConnected && _writeHandle != INVALID_HANDLE_VALUE) 141 { 142 for (int i = 0; i < data->Length; i++) _writeBuffer[i] = data[i]; 143 BOOL rt = WriteFile(_writeHandle, _writeBuffer, OutputReportLen, &write_size, _ovlpw); 144 if (!rt) 145 { 146 if (ERROR_IO_PENDING == GetLastError()) 147 { 148 // 此时是否需要对IO状态作处理看需求,处理与否不会影响IO 149 //rt = WaitForSingleObject(hUsbWriter, 50); 150 //switch (rt) 151 //{ 152 //case WAIT_OBJECT_0: 153 // cout << "指定的对象处于有信号状态" << endl; 154 // if (GetOverlappedResult(hUsbWriter, &ovr, &write_size, FALSE)) 155 // cout << "write " << write_size << " bytes" << endl; 156 // break; 157 //case WAIT_TIMEOUT: 158 // cout << "等待超时" << endl; 159 // break; 160 //case WAIT_FAILED: 161 // cout << "出现错误,CODE [" << GetLastError() << "]" << endl; 162 // break; 163 //case WAIT_ABANDONED: 164 // cout << "当Handle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值" << endl; 165 // break; 166 //} 167 } 168 else 169 { 170 CancelIo(_writeHandle); 171 172 Console::ForegroundColor = ConsoleColor::Blue; 173 Console::WriteLine("[WriteFile Error]GetLastError = " + GetLastError()); 174 Console::ResetColor(); 175 } 176 } 177 } 178 179 return write_size; 180 } 181 182 int HidCore::Read(array<Byte> ^data) 183 { 184 DWORD read_size = 0; 185 if (IsConnected && _readHandle != INVALID_HANDLE_VALUE) 186 { 187 BOOL rt = ReadFile(_readHandle, _readBuffer, InputReportLen, &read_size, _ovlpr); 188 if (!rt) 189 { 190 if (ERROR_IO_PENDING == GetLastError()) 191 { 192 // 此时是否需要对IO状态作处理看需求,处理与否不会影响IO 193 //rt = WaitForSingleObject(hUsbReader, 50); 194 //switch (rt) 195 //{ 196 //case WAIT_OBJECT_0: 197 // cout << "指定的对象处于有信号状态" << endl; 198 // if (GetOverlappedResult(hUsbReader, &ovr, &read_size, FALSE)) 199 // cout << "read " << read_size << " bytes" << endl; 200 // break; 201 //case WAIT_TIMEOUT: 202 // cout << "等待超时" << endl; 203 // break; 204 //case WAIT_FAILED: 205 // cout << "出现错误,CODE [" << GetLastError() << "]" << endl; 206 // break; 207 //case WAIT_ABANDONED: 208 // cout << "当Handle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值" << endl; 209 // break; 210 //} 211 } 212 else 213 { 214 CancelIo(_readHandle); 215 216 Console::ForegroundColor = ConsoleColor::Blue; 217 Console::WriteLine("[ReadFile Error]GetLastError = " + GetLastError()); 218 Console::ResetColor(); 219 } 220 } 221 else 222 { 223 for (int i = 0; i < read_size; i++) data[i] = _readBuffer[i]; 224 } 225 } 226 227 return read_size; 228 } 229 230 int HidCore::InputReportLen::get() 231 { 232 return _inputReportLen; 233 } 234 235 int HidCore::OutputReportLen::get() 236 { 237 return _outputReportLen; 238 } 239 240 bool HidCore::IsConnected::get() 241 { 242 return _isConnected; 243 }
HidEvent
1 #pragma once 2 3 namespace ZSHid 4 { 5 class HidEvent 6 { 7 public: 8 HidEvent(void *hidCore); 9 virtual ~HidEvent(); 10 11 static DWORD ThreadMessageProc(LPVOID lparam); 12 static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); 13 14 private: 15 static void *_hidCore; 16 static HDEVNOTIFY _devnotifyHandle; 17 }; 18 }
1 #include "stdafx.h" 2 #include "HidEvent.h" 3 #include "HidCore.h" 4 #include <Dbt.h> 5 #include <vcclr.h> 6 7 using namespace ZSHid; 8 9 void * HidEvent::_hidCore = nullptr; 10 HDEVNOTIFY HidEvent::_devnotifyHandle = INVALID_HANDLE_VALUE; 11 12 HidEvent::HidEvent(void * hidCore) 13 { 14 _hidCore = hidCore; 15 } 16 17 HidEvent::~HidEvent() 18 { 19 if (_hidCore != nullptr) 20 delete _hidCore; 21 } 22 23 DWORD HidEvent::ThreadMessageProc(LPVOID lparam) 24 { 25 HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL)); 26 WNDCLASS wndClass = { 0 }; 27 wndClass.lpfnWndProc = HidEvent::WndProc; 28 wndClass.lpszClassName = _T("ZSHid"); 29 wndClass.hInstance = hInstance; 30 if (RegisterClass(&wndClass)) 31 { 32 // HWND_MESSAGE表示创建的为线程消息循环 33 HWND hwnd = CreateWindowEx(0, wndClass.lpszClassName, NULL, 0, 0, 0, 0, 0, 34 HWND_MESSAGE, NULL, hInstance, NULL); 35 if (hwnd != INVALID_HANDLE_VALUE) 36 { 37 GUID guid; 38 HidD_GetHidGuid(&guid); 39 DEV_BROADCAST_DEVICEINTERFACE notificationFilter = { 0 }; 40 notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); 41 notificationFilter.dbcc_classguid = guid; 42 notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 43 _devnotifyHandle = RegisterDeviceNotification(hwnd, ¬ificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); 44 45 if (_devnotifyHandle != INVALID_HANDLE_VALUE) 46 { 47 Console::ForegroundColor = ConsoleColor::Blue; 48 Console::WriteLine("Notify注册成功,即将进入消息循环."); 49 Console::ResetColor(); 50 51 MSG msg; 52 if (GetMessage(&msg, NULL, 0, 0) > 0) 53 { 54 TranslateMessage(&msg); 55 DispatchMessage(&msg); 56 } 57 } 58 59 // 消息循环退出 60 Console::ForegroundColor = ConsoleColor::Blue; 61 Console::WriteLine("消息循环已退出........................"); 62 Console::ResetColor(); 63 64 UnregisterDeviceNotification(_devnotifyHandle); 65 DestroyWindow(hwnd); 66 UnregisterClass(wndClass.lpszClassName, hInstance); 67 } 68 } 69 70 return 0; 71 } 72 73 LRESULT CALLBACK HidEvent::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 74 { 75 if (msg == WM_DEVICECHANGE) 76 { 77 switch (wparam) 78 { 79 case DBT_DEVICEARRIVAL: 80 { 81 gcroot<HidCore^> *hc = (gcroot<HidCore^> *)_hidCore; 82 if (!(*hc)->IsConnected) 83 { 84 if ((*hc)->FindMyDevice()) 85 { 86 Console::ForegroundColor = ConsoleColor::Blue; 87 Console::WriteLine("MY HID DEVICE IS FOUND AND OPEN."); 88 Console::ResetColor(); 89 (*hc)->_deviceArrival(); 90 } 91 } 92 } 93 break; 94 case DBT_DEVICEREMOVECOMPLETE: 95 { 96 gcroot<HidCore^> *hc = (gcroot<HidCore^> *)_hidCore; 97 if (!(*hc)->FindMyDevice()) 98 { 99 Console::ForegroundColor = ConsoleColor::Blue; 100 Console::WriteLine("MY HID DEVICE REMOVED."); 101 Console::ResetColor(); 102 (*hc)->_deviceRemoved(); 103 } 104 } 105 break; 106 default: 107 break; 108 } 109 } 110 111 return DefWindowProc(hwnd, msg, wparam, lparam); 112 }
C++/CLI的优点就是C++或者是C#可以引入后直接使用,不必要以C接口方法来调入,dll中不需要其它工具就可以随意打印调试信息.
在C#中的使用
1 using System; 2 using ZSHid; 3 4 namespace ZSHidApp 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 HidCore hid = new HidCore(0x0b6a, 0x5346); 11 hid.NotifyEvents(() => 12 { 13 Console.WriteLine("event in c# call by clr[device matched]"); 14 }, () => 15 { 16 Console.WriteLine("event in c# call by clr[device removed]"); 17 }); 18 byte[] buffer = new byte[256]; 19 int rl = hid.Read(buffer); 20 if (hid.IsConnected) 21 { 22 Console.WriteLine("my device finded.!!!"); 23 Console.WriteLine("input report length: " + hid.InputReportLen); 24 Console.WriteLine("output report length: " + hid.OutputReportLen); 25 } 26 27 Console.WriteLine("press enter 退出dll的消息循环."); 28 Console.ReadLine(); 29 hid.Dispose(); 30 31 Console.WriteLine("press enter to exit."); 32 Console.ReadLine(); 33 } 34 } 35 }