<br /> //以下是調用windows的API的函數<br /> //獲得GUID<br /> [DllImport("hid.dll")]<br /> public static extern void HidD_GetHidGuid(ref Guid HidGuid);<br /> Guid guidHID = Guid.Empty;<br /> //過濾裝置,擷取需要的裝置<br /> [DllImport("setupapi.dll", SetLastError = true)]<br /> public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr HwndParent, DIGCF Flags);<br /> IntPtr hDevInfo;<br /> //擷取裝置,true擷取到<br /> [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]<br /> public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);<br /> public struct SP_DEVICE_INTERFACE_DATA<br /> {<br /> public int cbSize ;<br /> public Guid interfaceClassGuid;<br /> public int flags;<br /> public int reserved;<br /> }</p><p> // 擷取介面的詳細資料 必須調用兩次 第1次返回長度 第2次擷取資料<br /> [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]<br /> private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData,<br /> int deviceInterfaceDetailDataSize, ref int requiredSize, SP_DEVINFO_DATA deviceInfoData);<br /> [StructLayout(LayoutKind.Sequential)]<br /> public class SP_DEVINFO_DATA<br /> {<br /> public int cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));<br /> public Guid classGuid = Guid.Empty; // temp<br /> public int devInst = 0; // dumy<br /> public int reserved = 0;<br /> }</p><p> [StructLayout(LayoutKind.Sequential, Pack = 2)]<br /> internal struct SP_DEVICE_INTERFACE_DETAIL_DATA<br /> {<br /> internal int cbSize;<br /> internal short devicePath;<br /> }</p><p> public enum DIGCF<br /> {<br /> DIGCF_DEFAULT = 0x1,<br /> DIGCF_PRESENT = 0x2,<br /> DIGCF_ALLCLASSES = 0x4,<br /> DIGCF_PROFILE = 0x8,<br /> DIGCF_DEVICEINTERFACE = 0x10<br /> }</p><p> //擷取裝置檔案<br /> [DllImport("kernel32.dll", SetLastError = true)]<br /> private static extern int CreateFile(<br /> string lpFileName, // file name<br /> uint dwDesiredAccess, // access mode<br /> uint dwShareMode, // share mode<br /> uint lpSecurityAttributes, // SD<br /> uint dwCreationDisposition, // how to create<br /> uint dwFlagsAndAttributes, // file attributes<br /> uint hTemplateFile // handle to template file<br /> );<br /> //讀取裝置檔案<br /> [DllImport("Kernel32.dll",SetLastError = true)]<br /> private static extern bool ReadFile<br /> (<br /> IntPtr hFile,<br /> byte[] lpBuffer,<br /> uint nNumberOfBytesToRead,<br /> ref uint lpNumberOfBytesRead,<br /> IntPtr lpOverlapped<br /> );</p><p> //釋放裝置<br /> [DllImport("hid.dll")]<br /> static public extern bool HidD_FreePreparsedData(ref IntPtr PreparsedData);<br /> //關閉訪問裝置控制代碼,結束進程的時候把這個加上保險點<br /> [DllImport("kernel32.dll")]<br /> static public extern int CloseHandle(int hObject);</p><p>//接下來是訪問裝置的代碼</p><p>//代碼暫時沒有整理,傳入參數是裝置序號,<br />//有些USB裝置其實有很多HID裝置,就是一個介面上有幾個裝置,這個時候需要<br />//用index++來逐個迴圈,直到擷取裝置返回false後,跳出去,把擷取的裝置<br />//路徑全記錄下來就好了,我這裡知道具體裝置號,所以沒有迴圈,浪費我時間</p><p> //定於控制代碼序號和一些參數,具體可以去網上找這些API的參數說明,後文我看能不能把資料也寫上去<br /> int HidHandle = -1;<br /> public const uint GENERIC_READ = 0x80000000;<br /> public const uint GENERIC_WRITE = 0x40000000;<br /> public const uint FILE_SHARE_READ= 0x00000001;<br /> public const uint FILE_SHARE_WRITE = 0x00000002;<br /> public const int OPEN_EXISTING = 3;</p><p>private void UsBMethod(int index)<br /> {<br /> HidD_GetHidGuid(ref guidHID);<br /> hDevInfo = SetupDiGetClassDevs(ref guidHID, 0, IntPtr.Zero, DIGCF.DIGCF_PRESENT | DIGCF.DIGCF_DEVICEINTERFACE);<br /> int bufferSize = 0;<br /> ArrayList HIDUSBAddress = new ArrayList();</p><p> //while (true)<br /> //{<br /> //擷取裝置,true擷取到<br /> SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();<br /> DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData);<br /> //for (int i = 0; i < 3; i++)<br /> //{<br /> bool result = SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guidHID, (UInt32)index, ref DeviceInterfaceData);<br /> //}<br /> //第一次調用出錯,但可以返回正確的Size<br /> SP_DEVINFO_DATA strtInterfaceData = new SP_DEVINFO_DATA();<br /> result = SetupDiGetDeviceInterfaceDetail(hDevInfo, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, strtInterfaceData);<br /> //第二次調用傳遞傳回值,調用即可成功<br /> IntPtr detailDataBuffer = Marshal.AllocHGlobal(bufferSize);<br /> SP_DEVICE_INTERFACE_DETAIL_DATA detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();<br /> detailData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA));<br /> Marshal.StructureToPtr(detailData, detailDataBuffer, false);<br /> result = SetupDiGetDeviceInterfaceDetail(hDevInfo, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, strtInterfaceData);<br /> if (result == false)<br /> {<br /> //break;<br /> }<br /> //擷取裝置路徑訪<br /> IntPtr pdevicePathName = (IntPtr)((int)detailDataBuffer + 4);<br /> string devicePathName = Marshal.PtrToStringAuto(pdevicePathName);<br /> HIDUSBAddress.Add(devicePathName);<br /> //index++;<br /> //break;<br /> //}</p><p> //串連裝置檔案<br /> int aa = CT_CreateFile(devicePathName);<br /> bool bb = USBDataRead(HidHandle);<br /> }</p><p> //建立和裝置的串連<br /> public unsafe int CT_CreateFile(string DeviceName)<br /> {<br /> HidHandle = CreateFile(<br /> DeviceName,<br /> GENERIC_READ,// | GENERIC_WRITE,//讀寫,或者一起<br /> FILE_SHARE_READ,// | FILE_SHARE_WRITE,//共用讀寫,或者一起<br /> 0,<br /> OPEN_EXISTING,<br /> 0,<br /> 0);<br /> if (HidHandle == -1)<br /> {<br /> return 0;<br /> }<br /> else<br /> {<br /> return 1;<br /> }<br /> }</p><p> //根據CreateFile拿到的裝置handle訪問檔案,並返回資料<br /> public unsafe bool USBDataRead(int handle)<br /> {<br /> while (true)<br /> {<br /> uint read = 0;<br /> //注意位元組的長度,我這裡寫的是8位,其實可以通過API擷取具體的長度,這樣安全點,<br /> //具體方法我知道,但是沒有寫,過幾天整理完代碼,一起給出來<br /> Byte[] m_rd_data = new Byte[8];<br /> bool isread = ReadFile((IntPtr)handle, m_rd_data, (uint)8, ref read, IntPtr.Zero);<br /> //這裡已經是拿到的資料了<br /> Byte[] m_rd_dataout = new Byte[read];<br /> Array.Copy(m_rd_data, m_rd_dataout, read);<br /> }<br /> }</p><p>
OK,如果只是擷取USB傳過來的資料,這裡已經足夠了,但是有點要注意,2000和XP如果要擷取HID鍵盤和滑鼠的資料,readfile是不行的,;
在Win2000和WinXP下不能用CreateFile+ReadFile/WriteFile的方式來讀寫標準滑鼠和標準鍵盤的資料,因為它們是系統獨佔的(Exlusive)。
如果你是其他HID類裝置,比如遊戲手柄或者自訂HID裝置,都可以用上面的方式來收發資料,
怎麼訪問我暫時也不知道,估計要用它方法,看到有些軟體是用截取的手段,估計是用鉤子了吧。。
最後忘記引用空間了,其實大家都知道的
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
using System.Collections;
using System.IO;
還有擷取裝置資訊的代碼,主要是擷取報文的長度,要不然每次報文長度換了,又要改代碼,其實硬體換了,改下代碼還算是正常的
-
C# code
-
//擷取裝置具體資訊<br /> [DllImport("hid.dll", SetLastError = true)]<br /> private unsafe static extern int HidP_GetCaps(<br /> int pPHIDP_PREPARSED_DATA, // IN PHIDP_PREPARSED_DATA PreparsedData,<br /> ref HIDP_CAPS myPHIDP_CAPS); // OUT PHIDP_CAPS Capabilities</p><p> [DllImport("hid.dll", SetLastError = true)]<br /> private unsafe static extern int HidD_GetPreparsedData(<br /> int hObject, // IN HANDLE HidDeviceObject,<br /> ref int pPHIDP_PREPARSED_DATA); </p><p> // HIDP_CAPS<br /> [StructLayout(LayoutKind.Sequential)]<br /> public unsafe struct HIDP_CAPS<br /> {<br /> public System.UInt16 Usage; // USHORT<br /> public System.UInt16 UsagePage; // USHORT<br /> public System.UInt16 InputReportByteLength;<br /> public System.UInt16 OutputReportByteLength;<br /> public System.UInt16 FeatureReportByteLength;<br /> [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]<br /> public System.UInt16[] Reserved; // USHORT Reserved[17];<br /> public System.UInt16 NumberLinkCollectionNodes;<br /> public System.UInt16 NumberInputButtonCaps;<br /> public System.UInt16 NumberInputValueCaps;<br /> public System.UInt16 NumberInputDataIndices;<br /> public System.UInt16 NumberOutputButtonCaps;<br /> public System.UInt16 NumberOutputValueCaps;<br /> public System.UInt16 NumberOutputDataIndices;<br /> public System.UInt16 NumberFeatureButtonCaps;<br /> public System.UInt16 NumberFeatureValueCaps;<br /> public System.UInt16 NumberFeatureDataIndices;<br /> }</p><p> int reportLength = 8;</p><p> //擷取裝置發送的位元組的長度(也有其他資訊)<br /> int myPtrToPreparsedData = -1;<br /> int result1 = HidD_GetPreparsedData(handle, ref myPtrToPreparsedData);<br /> HIDP_CAPS myHIDP_CAPS = new HIDP_CAPS();<br /> int result2 = HidP_GetCaps(myPtrToPreparsedData, ref myHIDP_CAPS);<br /> reportLength = myHIDP_CAPS.InputReportByteLength;</p><p>
必選先拿到handle,我這裡放到CreatFile之後使用就好了