混合DotNet与Win32API来实现的Hidlibrary,c/c++可直接使用,c#可直接使用

异步IO,拔插事件订阅,数据读取事件订阅


工程目录结构

c++/cli mixed codes for standard c++ and csharp-LMLPHP

HidEvent.h
 #pragma once

 typedef void (*HidEvent)();
typedef void (__cdecl *HidReadDataEvent)(unsigned char *data, int len);
Hidimpl.h
 #pragma once
#include "HidEvent.h" class Hidimpl
{
public:
Hidimpl(int vid, int pid);
~Hidimpl(void); bool FindMyDevice();
void NotifyEvents(HidEvent arrival, HidEvent removed, HidReadDataEvent rdata); int getInputLen()
{
return mInputLen;
} int getOutputLen()
{
return mOutputLen;
} bool IsConnected()
{
return mIsConnected;
} public:
// IO Functions
//int Read(unsigned char *data, int len);
void Write(unsigned char *data, int len); public:
static Hidimpl *mTarget;
static DWORD ThreadMessageRoutine(LPVOID lparam);
static LRESULT CALLBACK ZSHidWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); public:
HidEvent mArrivalEvent;
HidEvent mRemovedEvent;
HidReadDataEvent mReadDataEvent; private:
void MyDeviceRemoved(); private:
int mInputLen;
int mOutputLen;
bool mIsConnected; int mVid;
int mPid;
HANDLE mhHidRx;
HANDLE mhHidTx; DWORD mThreadId;
HANDLE mThreadHandle; // Managed objects FileStream
void *mFsRx;
void *mFsTx;
};
Hidimpl.cpp
 #include "StdAfx.h"
#include "Hidimpl.h"
#include "HidState.h"
#include <vcclr.h> #pragma comment(lib, "hid.lib")
#pragma comment(lib, "setupapi.lib") using namespace System;
using namespace System::IO;
using namespace Microsoft::Win32::SafeHandles; Hidimpl *Hidimpl::mTarget = nullptr; Hidimpl::Hidimpl(int vid, int pid)
:mFsRx(nullptr), mFsTx(nullptr),
mArrivalEvent(nullptr), mRemovedEvent(nullptr),
mThreadId(), mThreadHandle(INVALID_HANDLE_VALUE)
{
mVid = vid;
mPid = pid; FindMyDevice();
} Hidimpl::~Hidimpl(void)
{
MyDeviceRemoved();
if (mThreadHandle != INVALID_HANDLE_VALUE)
{
PostThreadMessage(mThreadId, WM_CLOSE, , );
mThreadHandle = INVALID_HANDLE_VALUE;
}
} void Hidimpl::MyDeviceRemoved()
{
if (mFsRx)
{
gcroot<FileStream ^> *_pfs = static_cast<gcroot<FileStream ^> *>(mFsRx);
// not need only delete ok
//((FileStream ^)*_pfs)->Close();
delete _pfs;
mFsRx = nullptr;
} if (mFsTx)
{
gcroot<FileStream ^> *_pfs = static_cast<gcroot<FileStream ^> *>(mFsTx);
// not need only delete ok
//((FileStream ^)*_pfs)->Close();
delete _pfs;
mFsTx = nullptr;
} // 不需要手动关闭GC自动处理
// this is also a crazy fucking shit
// 关闭的句柄被重复关闭会引发[0xC0000008: An invalid handle was specified]
//CloseHandle(mhHidRx);
//mhHidRx = INVALID_HANDLE_VALUE;
//CloseHandle(mhHidTx);
//mhHidTx = INVALID_HANDLE_VALUE;
} bool Hidimpl::FindMyDevice()
{
bool isFind = false;
GUID guid;
HidD_GetHidGuid(&guid);
HDEVINFO hdevinfo = SetupDiGetClassDevs(&guid, , , DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
SP_DEVICE_INTERFACE_DATA device_interface_data;
ZeroMemory(&device_interface_data, sizeof(device_interface_data));
device_interface_data.cbSize = sizeof(device_interface_data); int device_interface_index = ;
while (SetupDiEnumDeviceInterfaces(hdevinfo, NULL, &guid, device_interface_index, &device_interface_data))
{
DWORD requireSize;
SetupDiGetDeviceInterfaceDetail(hdevinfo, &device_interface_data, NULL, , &requireSize, NULL);
PSP_DEVICE_INTERFACE_DETAIL_DATA psp_device_interface_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requireSize);
psp_device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(hdevinfo, &device_interface_data, psp_device_interface_detail_data, requireSize, &requireSize, NULL))
{
TCHAR *device_path = psp_device_interface_detail_data->DevicePath; // 打印设备路径[将TCHAR *转换为System::String^]
//String ^str_path = gcnew String(device_path);
//Console::ForegroundColor = ConsoleColor::Blue;
//Console::WriteLine(str_path);
//Console::ResetColor(); HANDLE handle = CreateFile(device_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
if (handle != INVALID_HANDLE_VALUE)
{
HIDD_ATTRIBUTES hidd_attributes;
HidD_GetAttributes(handle, &hidd_attributes);
if (hidd_attributes.VendorID == mVid && hidd_attributes.ProductID == mPid)
{
PHIDP_PREPARSED_DATA phidp_preparsed_data;
HidD_GetPreparsedData(handle, &phidp_preparsed_data);
HIDP_CAPS hidp_caps;
HidP_GetCaps(phidp_preparsed_data, &hidp_caps);
mInputLen = hidp_caps.InputReportByteLength;
mOutputLen = hidp_caps.OutputReportByteLength;
HidD_FreePreparsedData(phidp_preparsed_data); // 打印设备路径[将TCHAR *转换为System::String^]
String ^str_path = gcnew String(device_path);
Console::ForegroundColor = ConsoleColor::Green;
Console::WriteLine(str_path);
Console::ResetColor(); mhHidRx = CreateFile(device_path, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
mhHidTx = CreateFile(device_path, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); SafeFileHandle ^_hRx = gcnew SafeFileHandle((IntPtr)mhHidRx, true);
FileStream ^_fsRx = gcnew FileStream(_hRx, FileAccess::Read, mInputLen, true);
// managed type conversion into unmanaged pointer is not allowed.
// we can use gcroot<> wrapper
gcroot<FileStream^> *_pfs = new gcroot<FileStream ^>(_fsRx);
mFsRx = static_cast<void *>(_pfs); SafeFileHandle ^_hTx = gcnew SafeFileHandle((IntPtr)mhHidTx, true);
FileStream ^_fsTx = gcnew FileStream(_hTx, FileAccess::Write, mOutputLen, true);
// managed type conversion into unmanaged pointer is not allowed.
// we can use gcroot<> wrapper
_pfs = new gcroot<FileStream^>(_fsTx);
mFsTx = static_cast<void *>(_pfs); HidState ^state = gcnew HidState(this, _fsRx, mInputLen);
_fsRx->BeginRead(state->mBuffer, , state->mBuffer->Length, gcnew AsyncCallback(state, &HidState::EndRead), state); isFind = true;
CloseHandle(handle);
free(psp_device_interface_detail_data);
break;
} CloseHandle(handle);
}
}
else
{
int error = GetLastError();
Console::ForegroundColor = ConsoleColor::Blue;
Console::WriteLine("[SetupDiGetDeviceInterfaceDetail Error]GetLastError = " + error);
Console::ResetColor();
} device_interface_index++;
free(psp_device_interface_detail_data);
} SetupDiDestroyDeviceInfoList(hdevinfo);
mIsConnected = isFind; return isFind;
} void Hidimpl::NotifyEvents(HidEvent arrival, HidEvent removed, HidReadDataEvent rdata)
{
// Cannt reset event to device
if (mThreadHandle != INVALID_HANDLE_VALUE) return; mArrivalEvent = arrival;
mRemovedEvent = removed;
mReadDataEvent = rdata; mThreadHandle = CreateThread(NULL, ,
(LPTHREAD_START_ROUTINE)ThreadMessageRoutine, this, THREAD_PRIORITY_NORMAL, &mThreadId);
} DWORD Hidimpl::ThreadMessageRoutine(LPVOID lparam)
{
mTarget = static_cast<Hidimpl *>(lparam);
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL)); WNDCLASS wndClass = {};
wndClass.lpfnWndProc = ZSHidWndProc;
wndClass.lpszClassName = L"ZSHidlib";
wndClass.hInstance = hInstance;
if (RegisterClass(&wndClass))
{
// 为线程消息循环(HWND_MESSAGE)
HWND hwnd = CreateWindowEx(, wndClass.lpszClassName, NULL,
, , , , , HWND_MESSAGE, NULL, hInstance, NULL);
if (hwnd != INVALID_HANDLE_VALUE)
{
GUID guid;
HidD_GetHidGuid(&guid);
DEV_BROADCAST_DEVICEINTERFACE notifycationFilter = {};
notifycationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
notifycationFilter.dbcc_classguid = guid;
notifycationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
HDEVNOTIFY hdevnotify = RegisterDeviceNotification(hwnd, &notifycationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); if (hdevnotify != INVALID_HANDLE_VALUE)
{
Console::ForegroundColor = ConsoleColor::Blue;
Console::WriteLine("Notify注册成功,即将进入消息循环.");
Console::ResetColor(); MSG msg;
if (GetMessage(&msg, NULL, , ) > )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} Console::ForegroundColor = ConsoleColor::Blue;
Console::WriteLine("退出消息循环........................");
Console::ResetColor(); UnregisterDeviceNotification(hdevnotify);
DestroyWindow(hwnd);
UnregisterClass(wndClass.lpszClassName, hInstance);
}
} return ;
} LRESULT CALLBACK Hidimpl::ZSHidWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (msg == WM_DEVICECHANGE)
{
switch (wparam)
{
case DBT_DEVICEARRIVAL:
{
if (!mTarget->mIsConnected)
{
if (mTarget->FindMyDevice())
{
Console::ForegroundColor = ConsoleColor::Blue;
Console::WriteLine("MY HID DEVICE IS FOUND AND OPEN.");
Console::ResetColor(); // My device arrival event
if (mTarget->mArrivalEvent != nullptr)
mTarget->mArrivalEvent();
}
}
}
break;
case DBT_DEVICEREMOVECOMPLETE:
{
if (mTarget->mIsConnected)
{
if (!mTarget->FindMyDevice())
{
Console::ForegroundColor = ConsoleColor::Blue;
Console::WriteLine("MY HID DEVICE REMOVED.");
Console::ResetColor(); // My device remove event
mTarget->MyDeviceRemoved();
if (mTarget->mRemovedEvent != nullptr)
mTarget->mRemovedEvent();
}
}
}
break;
default:
break;
}
} return DefWindowProc(hwnd, msg, wparam, lparam);
} void Hidimpl::Write(unsigned char *data, int len)
{
if (!mIsConnected) return;
array<unsigned char> ^_data = gcnew array<unsigned char>(mOutputLen);
try
{
int length = len>mOutputLen?mOutputLen:len;
for (int i = ; i < length; i++) _data[i] = data[i]; if (mFsTx != nullptr)
{
gcroot<FileStream^> *_pfs = static_cast<gcroot<FileStream^> *>(mFsTx);
//((FileStream^)*_pfs)->Write(_data, 0, _data->Length);
((FileStream^)*_pfs)->BeginWrite(_data, , _data->Length, nullptr, nullptr);
((FileStream^)*_pfs)->Flush();
}
}
catch(IOException ^e)
{
Console::WriteLine(e->Message);
}
catch(Exception ^e)
{
Console::WriteLine(e->Message);
}
}
HidState.h
 #pragma once
#include "Hidimpl.h" using namespace System;
using namespace System::IO; public ref class HidState
{
public:
HidState(Hidimpl *hid, FileStream ^fs, int bufferLen);
~HidState();
void EndRead(IAsyncResult ^ar); public:
Hidimpl *mHidimpl;
FileStream ^mFileStream;
array<unsigned char> ^mBuffer;
};
HidState.cpp
 #include "stdafx.h"
#include "HidState.h" HidState::HidState(Hidimpl *hid, FileStream ^fs, int bufferLen)
{
mHidimpl = hid;
mFileStream = fs;
mBuffer = gcnew array<unsigned char>(bufferLen);
} HidState::~HidState()
{
mHidimpl = nullptr;
} void HidState::EndRead(IAsyncResult ^ar)
{
HidState ^state = dynamic_cast<HidState ^>(ar->AsyncState);
try
{
int count = state->mFileStream->EndRead(ar);
// [有数据读入的情况下]调用ReadData事件
if (mHidimpl->mReadDataEvent != nullptr && count > )
{
unsigned char *ptrdt = new unsigned char[count];
for (int i = ; i < count; i++) ptrdt[i] = mBuffer[i];
// 此处封送到C#的IntPtr指针(this a crazy fucking)
mHidimpl->mReadDataEvent(ptrdt, count);
delete []ptrdt;
}
if (mHidimpl->IsConnected())
state->mFileStream->BeginRead(state->mBuffer, , mBuffer->Length, gcnew AsyncCallback(this, &HidState::EndRead), state);
}
catch(IOException ^e)
{
Console::WriteLine(e->Message);
}
catch(Exception ^e)
{
Console::WriteLine(e->Message);
}
}

_______export,wrap managed clr class for csharp

Hidlib.h
 #pragma once
#include "Hidimpl.h" using namespace System; public ref class Hidlib
{
public:
delegate void HidDelegate();
delegate void HidReadDataDelegate(IntPtr data, int len); public:
Hidlib(int vid, int pid);
void NotifyEvents(HidDelegate ^arrival, HidDelegate ^removed, HidReadDataDelegate ^rdata);
void Write(array<unsigned char> ^data); private:
Hidimpl *impl;
};
Hidlib.cpp  
 #include "stdafx.h"
#include "Hidlib.h"
#include <vcclr.h> using namespace System::IO;
using namespace System::Runtime::InteropServices; Hidlib::Hidlib(int vid, int pid)
{
impl = new Hidimpl(vid, pid);
} void Hidlib::NotifyEvents(HidDelegate ^arrival, HidDelegate ^removed, HidReadDataDelegate ^rdata)
{
IntPtr _arrival = Marshal::GetFunctionPointerForDelegate(arrival);
IntPtr _removed = Marshal::GetFunctionPointerForDelegate(removed);
IntPtr _rdata = Marshal::GetFunctionPointerForDelegate(rdata);
impl->NotifyEvents((HidEvent)_arrival.ToPointer(), (HidEvent)_removed.ToPointer(), (HidReadDataEvent)_rdata.ToPointer());
} void Hidlib::Write(array<unsigned char> ^data)
{
try
{
int length = data->Length>impl->getOutputLen()?impl->getOutputLen():data->Length;
if (impl->mFsTx != nullptr)
{
gcroot<FileStream^> *_pfs = static_cast<gcroot<FileStream^> *>(impl->mFsTx);
//((FileStream^)*_pfs)->Write(_data, 0, _data->Length);
((FileStream^)*_pfs)->BeginWrite(data, , length, nullptr, nullptr);
((FileStream^)*_pfs)->Flush();
}
}
catch(IOException ^e)
{
Console::WriteLine(e->Message);
}
catch(Exception ^e)
{
Console::WriteLine(e->Message);
}
}
  • 提供PInvoke接口
PInvoke(平台调用)
 #pragma once
#include <Hidimpl.h>
#include "HidEvent.h" #ifdef ZSHIDLIB_EXPORTS
#define ZSHIDLIB_API __declspec(dllexport)
#else
#define ZSHIDLIB_API __declspec(dllimport)
#endif #ifdef __cplusplus
extern "C" {
#endif
ZSHIDLIB_API void Open(int vid, int pid);
ZSHIDLIB_API bool IsOpen();
ZSHIDLIB_API bool IsConnected();
ZSHIDLIB_API int getInputLen();
ZSHIDLIB_API int getOutputLen();
ZSHIDLIB_API void Notify(HidEvent arrival, HidEvent removed, HidReadDataEvent rdata);
ZSHIDLIB_API void Write(unsigned char *data, int len);
ZSHIDLIB_API void Close();
#ifdef __cplusplus
};
#endif
 #include "stdafx.h"
#include "Hidlib.h"
#include "Hidimpl.h" Hidimpl *pHidimpl = nullptr; ZSHIDLIB_API void Open(int vid, int pid)
{
if (pHidimpl == nullptr)
pHidimpl = new Hidimpl(vid, pid);
} ZSHIDLIB_API bool IsOpen()
{
return (pHidimpl != nullptr);
} ZSHIDLIB_API bool IsConnected()
{
if (pHidimpl != nullptr)
return pHidimpl->IsConnected(); return ;
} ZSHIDLIB_API int getInputLen()
{
if (pHidimpl != nullptr)
return pHidimpl->getInputLen(); return ;
} ZSHIDLIB_API int getOutputLen()
{
if (pHidimpl != nullptr)
return pHidimpl->getOutputLen(); return ;
} ZSHIDLIB_API void Notify(HidEvent arrival, HidEvent removed, HidReadDataEvent rdata)
{
if (pHidimpl != nullptr)
{
pHidimpl->NotifyEvents(arrival, removed, rdata);
}
} ZSHIDLIB_API void Write(unsigned char *data, int len)
{
if (pHidimpl != nullptr)
pHidimpl->Write(data, len);
} ZSHIDLIB_API void Close()
{
if (pHidimpl != nullptr)
{
delete pHidimpl;
pHidimpl = nullptr;
}
}
  • C#中的使用
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices; namespace ZSHidApp
{
delegate void HidEvent();
delegate void HidReadDataEvent(IntPtr data, int len); class Program
{
//[DllImport("ZSHidlib.dll")]
//public static extern void Open(int vid, int pid); //[DllImport("ZSHidlib.dll")]
//public static extern void Notify(HidEvent arrival, HidEvent removed, HidReadDataEvent rdata); //[DllImport("ZSHidlib.dll")]
//public static extern void Write(Byte[] data, int len); //[DllImport("ZSHidlib.dll")]
//public static extern void GetData(ref Byte[] data, int len); static void ReadData(IntPtr data, int len)
{
Byte[] _data = new Byte[len];
Marshal.Copy(data, _data, , len); // Log
Console.WriteLine("// usr subscription //_____________________________________");
for (int i = ; i < _data.Length; i++) Console.Write(string.Format("{0:x2} ", _data[i]));
Console.WriteLine();
} static void Main(string[] args)
{
//Open(0x0b6a, 0x5346);
//Notify(() => Console.WriteLine("______________device arrival."),
// () => Console.WriteLine("__________________device removed."),
// ReadData);
Hidlib hidlib = new Hidlib(0x0b6a, 0x5346);
hidlib.NotifyEvents(() => Console.WriteLine("______________device arrival."),
() => Console.WriteLine("__________________device removed."),
ReadData); while (true)
{
ConsoleKeyInfo keyInfo = Console.ReadKey();
if (keyInfo.Key == ConsoleKey.Q)
{
break;
}
else if (keyInfo.Key == ConsoleKey.W)
{
Byte[] buffer = new Byte[];
// ab 69 42 01 fe aa 00 00 aa cd
int i = ;
buffer[i++] = ;
buffer[i++] = 0xab;
buffer[i++] = 0x69;
buffer[i++] = 0x42;
buffer[i++] = 0x01;
buffer[i++] = 0xfe;
buffer[i++] = 0xaa;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0xaa;
buffer[i++] = 0xcd;
hidlib.Write(buffer);
}
}
}
}
}
  • Standard C++中的使用
 // ZSHidWin32App.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include "../ZSHidlib/Hidlib.h"
#include "../ZSHidlib/Hidimpl.h" #pragma comment(lib, "../Debug/ZSHidlib.lib") void Arrival()
{
printf("______________________device arrival.\n");
} void Removed()
{
printf("______________________device removed.\n");
} void ReadData(unsigned char *data, int len)
{
for (int i = ; i < len; i++)
{
printf("%.2x ", data[i]);
}
printf("___________________call in ReadData callback function.");
} int _tmain(int argc, _TCHAR* argv[])
{
Hidimpl *hidImpl = new Hidimpl(0x0b6a, 0x5346);
hidImpl->NotifyEvents(Arrival, Removed, ReadData); while(true)
{
unsigned char buffer[] = {0x00, 0xab, 0x69, 0x42, 0x01, 0xfe, 0xaa, 0x00, 0x00, 0xaa, 0xcd};
hidImpl->Write(buffer, ); Sleep();
} system("pause"); return ;
}

c++/cli mixed codes for standard c++ and csharp-LMLPHP

  • 参考书籍/资料

  《Visual C++/CLI从入门到精通》

《NET互操作 P_Invoke,C++Interop和COM Interop》


05-14 03:13