📄 hidcontrollerclass.pas
字号:
{ a Class interface for HID access }
unit HidControllerClass;
interface
uses
Windows, Messages, Classes, DsgnIntf,
Dialogs, Forms, Registry, SysUtils,
DBT, SetupAPI, Hid;
const
// strings from the registry for CheckOutByClass
cHidKeyboardClass = 'Keyboard';
cHidMouseClass = 'Mouse';
type
// forward declarations
TJvHidDeviceController = class;
TJvHidDevice = class;
// the Event function declarations
TJvHidEnumerateEvent = function (HidDev: TJvHidDevice; Index: Integer): Boolean of object;
TJvHidUnplugEvent = procedure(HidDev: TJvHidDevice) of object;
// the representation of a HID device
TJvHidDevice = class(TObject)
private
// internal control variables
FMyController: TJvHidDeviceController;
FIsPluggedIn: Boolean;
FIsCheckedOut: Boolean;
FIsEnumerated: Boolean;
FHidFileHandle: THandle;
// internal properties part
FDevRegKey: DWORD;
FDevicePath: string;
FHardWareKey: string;
FRegDescr: string;
FRegClass: string;
FVendorName: AnsiString;
FProductName: AnsiString;
FSerialNumber: AnsiString;
FDeviceStrings: TStringList;
FLanguageStrings: TStringList;
FPreparsedData: PHIDPPreparsedData;
FAttributes: THIDDAttributes;
FCaps: THIDPCaps;
FConfiguration: THIDDConfiguration;
FNumInputBuffers: Integer;
FLinkCollection: PHIDPLinkCollectionNode;
FLinkCollectionNodes: TList;
FMaxDataListLength: ULong;
FMaxUsageListLength: ULong;
FMaxButtonListLength: ULong;
FReportTypeParam: THIDPReportType;
FUsagePageParam: TUsage;
FLinkCollectionParam: WORD;
FUsageParam: TUsage;
FUnplug: TJvHidUnplugEvent;
// tells if access to device is allowed
function IsAccessible: Boolean;
// internal property implementors
procedure SetConfiguration (Config: THIDDConfiguration);
procedure SetNumInputBuffers(const Num: Integer);
procedure SetReportTypeParam(const ReportType: THIDPReportType);
procedure SetUsagePageParam (const UsagePage: TUsage);
// Constructor is hidden! Only a TJvHidDeviceController can create a TJvHidDevice object.
constructor Create(DevicePath: string; DevID: DWORD; Unplug: TJvHidUnplugEvent);
protected
// internal property implementors
procedure DoUnplug;
procedure SetUnplug(const value: TJvHidUnplugEvent);
public
published
destructor Destroy; override;
// methods
procedure CloseFile;
function FlushQueue: Boolean;
function GetButtonCaps (ButtonCaps: PHIDPButtonCaps; var Count: WORD): NTSTATUS;
function GetButtons (UsageList: PUsage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function GetButtonsEx (UsageList: PUsageAndPage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function GetData (DataList: PHIDPData; var DataLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function GetFeature (var Report; const Size: Integer): Boolean;
function GetPhysicalDescriptor(var Buffer; const Size: Integer): Boolean;
function GetScaledUsageValue (var UsageValue: Integer;
var Report; ReportLength: ULong): NTSTATUS;
function GetSpecificButtonCaps(ButtonCaps: PHIDPButtonCaps; var Count: WORD): NTSTATUS;
function GetSpecificValueCaps (ValueCaps: PHIDPValueCaps; var Count: WORD): NTSTATUS;
function GetUsages (UsageList: PUsage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function GetUsagesEx (UsageList: PUsageAndPage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function GetUsageValue (var UsageValue: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function GetUsageValueArray (UsageValue: PChar; UsageValueByteLength: WORD;
var Report; ReportLength: ULong): NTSTATUS;
function GetValueCaps (ValueCaps: PHIDPValueCaps; var Count: WORD): NTSTATUS;
function OpenFile: Boolean;
function SetButtons (UsageList: PUsage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function SetData (DataList: PHIDPData; var DataLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function SetFeature (var Report; const Size: Integer): Boolean;
function SetScaledUsageValue (UsageValue: Integer;
var Report; ReportLength: ULong): NTSTATUS;
function SetUsages (UsageList: PUsage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function SetUsageValue (UsageValue: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function SetUsageValueArray (UsageValue: PChar; UsageValueByteLength: WORD;
var Report; ReportLength: ULong): NTSTATUS;
function UnsetButtons (UsageList: PUsage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function UnsetUsages (UsageList: PUsage; var UsageLength: ULong;
var Report; ReportLength: ULong): NTSTATUS;
function ReadFile (var Report; ToRead: DWORD; var BytesRead: DWORD): Boolean;
function WriteFile (var Report; ToWrite: DWORD; var BytesWritten: DWORD): Boolean;
// management properties
property Attributes: THIDDAttributes read FAttributes;
property Caps: THIDPCaps read FCaps;
property Configuration: THIDDConfiguration read FConfiguration write SetConfiguration;
property DevicePath: string read FDevicePath;
property DeviceStrings: TStringList read FDeviceStrings;
property DevRegKey: DWORD read FDevRegKey;
property HardWareKey: string read FHardWareKey;
property HidFileHandle: THandle read FHidFileHandle;
property IsCheckedOut: Boolean read FIsCheckedOut;
property IsPluggedIn: Boolean read FIsPluggedIn;
property LanguageStrings: TStringList read FLanguageStrings;
property LinkCollectionNodes: TList read FLinkCollectionNodes;
property LinkCollectionParam: WORD read FLinkCollectionParam write FLinkCollectionParam;
property MaxButtonListLength: ULong read FMaxButtonListLength;
property MaxDataListLength: ULong read FMaxDataListLength;
property MaxUsageListLength: ULong read FMaxUsageListLength;
property NumInputBuffers: Integer read FNumInputBuffers write SetNumInputBuffers;
property PreparsedData: PHIDPPreparsedData read FPreparsedData;
property ProductName: string read FProductName;
property RegClass: string read FRegClass;
property RegDescr: string read FRegDescr;
property ReportTypeParam: THIDPReportType read FReportTypeParam write SetReportTypeParam;
property SerialNumber: string read FSerialNumber;
property VendorName: string read FVendorName;
property UsageParam: TUsage read FUsageParam write FUsageParam;
property UsagePageParam: TUsage read FUsagePageParam write SetUsagePageParam;
// the only event property
property OnUnplug: TJvHidUnplugEvent read FUnplug write SetUnplug;
end;
// controller class to manage all HID devices
TJvHidDeviceController = class(TComponent)
private
// internal property part
FHidGuid: TGUID;
FDeviceChangeEvent: TNotifyEvent;
FDeviceChangeFired: Boolean;
FEnumerateEvent: TJvHidEnumerateEvent;
FDevUnplugEvent: TJvHidUnplugEvent;
// internal list of all HID device objects
FList: TList;
// internal worker functions
function CheckThisOut(var HidDev: TJvHidDevice; Index: Integer; Check: Boolean): Boolean;
procedure FillInList (var List: TList);
function EventPipe (var Msg: TMessage): Boolean;
protected
procedure Loaded; override;
// internal property implementors
procedure DoDeviceChange;
function DoEnumerate (HidDev: TJvHidDevice; Index: Integer): Boolean;
procedure SetDeviceChangeEvent(const value: TNotifyEvent);
procedure SetEnumerate (const value: TJvHidEnumerateEvent);
procedure SetDevUnplug (const value: TJvHidUnplugEvent);
published
// normal constructor/destructor
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// methods to hand out HID device objects
procedure CheckIn (var HidDev: TJvHidDevice);
function CheckOut (var HidDev: TJvHidDevice): Boolean;
function CheckOutByClass (var HidDev: TJvHidDevice; ClassName: string): Boolean;
function CheckOutByID (var HidDev: TJvHidDevice; Vid, Pid: Integer): Boolean;
function CheckOutByIndex (var HidDev: TJvHidDevice; const Index: Integer): Boolean;
function CheckOutByProductName(var HidDev: TJvHidDevice; ProductName: string): Boolean;
function CheckOutByVendorName (var HidDev: TJvHidDevice; VendorName: string): Boolean;
// iterate over the HID devices
function Enumerate: Integer;
// just to be complete the GUID
property HidGuid: TGUID read FHidGuid;
// the central event for HID device changes
property OnDeviceChange: TNotifyEvent read FDeviceChangeEvent write SetDeviceChangeEvent;
// this event is copied to TJvHidDeviceOnUnplug on creation
property OnDeviceUnplug: TJvHidUnplugEvent read FDevUnplugEvent write SetDevUnplug;
// the iterator event
property OnEnumerate: TJvHidEnumerateEvent read FEnumerateEvent write SetEnumerate;
end;
procedure Register;
implementation
{$R HidControllerClass.dcr}
type
EControllerError = class(Exception);
var
GlobalInstanceCount: Integer = 0;
//== TJvHidDevice =====================================================
// get the registry path to the individual device info
function DevIDToHardWareKey(DevID: DWORD): string;
var
reg: TRegistry;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_DYN_DATA;
reg.OpenKeyReadOnly('Config Manager\Enum\' + Format('%.8x', [DevID]));
DevIDToHardWareKey := reg.ReadString('HardWareKey');
reg.Free;
end;
// get some static device info from the registry
function HardWareKeyToString(key: string; val: string): string;
var
reg: TRegistry;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKeyReadOnly('Enum\' + key);
HardWareKeyToString := reg.ReadString(val);
reg.Free;
end;
//-- TJvHidDevice: basics and internals -------------------------------
{
* create and fill in a HidDevice object
* the constructor is only accessible from TJvHidController
* DevicePath names the device "file"
* DevID is a handle (registry key from dynamic registry data) to the
* individual device (differs even for two devices of the same kind)
}
constructor TJvHidDevice.Create(DevicePath: string; DevID: DWORD; Unplug: TJvHidUnplugEvent);
var
i: Integer;
len: ULONG;
ptr: PHIDPLinkCollectionNode;
buffer: array [0..256] of WideChar;
ids: array [0..258] of WORD;
name: array [0..256] of Char;
begin
inherited Create;
// initialize private data
FMyController := nil;
FIsPluggedIn := True;
FIsCheckedOut := False;
FIsEnumerated := False;
FDevRegKey := 0;
FDevicePath := DevicePath;
FHardWareKey := '';
FRegDescr := '';
FRegClass := '';
FVendorName := '';
FProductName := '';
FSerialNumber := '';
FDeviceStrings := TStringList.Create;
FLanguageStrings := TStringList.Create;
FPreparsedData := 0;
FNumInputBuffers := 0;
FLinkCollection := nil;
FLinkCollectionNodes := nil;
FMaxDataListLength := 0;
FMaxUsageListLength := 0;
FMaxButtonListLength := 0;
FReportTypeParam := HIDP_Input;
FUsagePageParam := 0;
FLinkCollectionParam := 0;
FUsageParam := 0;
FUnplug := Unplug;
FHidFileHandle := CreateFile(PChar(FDevicePath), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if HidFileHandle <> INVALID_HANDLE_VALUE then
begin
// get all device data through HID functions
// this eliminates the need to redeclare them as methods
FDevRegKey := DevID;
FHardWareKey := DevIDToHardWareKey(DevID);
FRegDescr := HardWareKeyToString(FHardWareKey, 'DeviceDesc');
FRegClass := HardWareKeyToString(FHardWareKey, 'Class');
FAttributes.Size := SizeOf(THIDDAttributes);
HidD_GetPreparsedData(HidFileHandle, FPreparsedData);
HidD_GetAttributes (HidFileHandle, FAttributes);
FConfiguration.cookie := nil;
FConfiguration.size := 0;
FConfiguration.RingBufferSize := 0;
HidD_GetConfiguration(HidFileHandle, FConfiguration, SizeOf(THIDDConfiguration));
HidP_GetCaps(FPreparsedData, FCaps);
if HidD_GetManufacturerString(HidFileHandle, buffer, SizeOf(buffer)) then
FVendorName := WideCharToString(buffer);
if HidD_GetProductString(HidFileHandle, buffer, SizeOf(buffer)) then
FProductName := WideCharToString(buffer);
if HidD_GetSerialNumberString(HidFileHandle, buffer, SizeOf(buffer)) then
FSerialNumber := WideCharToString(buffer);
// put all device strings in a TStringList
i := 1;
while (i < 256) and HidD_GetIndexedString(HidFileHandle, i, buffer, SizeOf(buffer)) do
begin
FDeviceStrings.Add(WideCharToString(buffer));
Inc(i);
end;
// calculate length of StringDescriptor 0
ZeroMemory(@ids,SizeOf(ids));
len := 0;
for i := 0 to 256 do
begin
if HidD_GetIndexedString(HidFileHandle, 0, PWideChar(@ids), i*2) then
Break;
Inc(len);
end;
// transform id in localized language name
for i := 0 to len-1 do
begin
name[0] := #0;
GetLocaleInfo(ids[i],LOCALE_SLANGUAGE,name,SizeOf(name));
FLanguageStrings.Add(name);
end;
// put all LinkCollectionNodes in a TList
FLinkCollectionNodes := TList.Create;
len := FCaps.NumberLinkCollectionNodes;
GetMem(FLinkCollection, len*SizeOf(THIDPLinkCollectionNode));
HidP_GetLinkCollectionNodes(FLinkCollection, len, FPreparsedData);
ptr := FLinkCollection;
for i := 1 to len do
begin
FLinkCollectionNodes.Add(ptr);
ptr := PHIDPLinkCollectionNode((PChar(ptr)+SizeOf(THIDPLinkCollectionNode)));
end;
end;
// the file is closed to stop using up resources
CloseFile;
end;
{
* if a TJvHidDevice is destroyed the TJvHidController has to be informed
* if the device is plugged in this TJvHidDevice instance is destroyed
* but another instance is created in the controller list
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -