📄 hidcontrollerclass.pas
字号:
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
FHidFileHandle := CreateFile(PChar(FDevicePath), 0,
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
FDeviceID := DevID;
FRegDescr := DevDesc;
FRegClass := ClassDesc;
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);
// calculate length of StringDescriptor 0
FillChar(IDs, SizeOf(IDs), #0);
Len := 0;
for I := 0 to 256 do
begin
if HidD_GetIndexedString(HidFileHandle, 0, PWideChar(@IDs), I*SizeOf(WORD)) then
Break;
Inc(Len);
end;
// transform id into localized language name
for I := 0 to Len - 1 do
begin
Name[0] := #0;
if GetLocaleInfo(WORD(IDs[I]), LOCALE_SLANGUAGE, Name, SizeOf(Name)) <> 0 then
FLanguageStrings.Add(Name)
else
FLanguageStrings.Add(Format('unknown Locale ID $%.4x',[WORD(IDs[I])]));
end;
if HidD_GetManufacturerString(HidFileHandle, Buffer, SizeOf(Buffer)) then
FVendorName := WideCharToString(Buffer);
if HidD_GetProductString(HidFileHandle, Buffer, SizeOf(Buffer)) then
FProductName := WideCharToString(Buffer);
if HidD_GetPhysicalDescriptor(HidFileHandle, Buffer, SizeOf(Buffer)) then
FPhysicalDescriptor := WideCharToString(Buffer);
// compensate for buggy function
if HidD_GetSerialNumberString(HidFileHandle, Buffer, SizeOf(Buffer)) then
for I := 0 to Len - 1 do
if IDs[I] <> WORD(Buffer[I]) then
begin
FSerialNumber := WideCharToString(Buffer);
Break;
end;
Len := FCaps.NumberLinkCollectionNodes;
SetLength(FLinkCollection, Len);
HidP_GetLinkCollectionNodes(@FLinkCollection[0], Len, FPreparsedData);
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
destructor TJvHidDevice.Destroy;
var
I: Integer;
begin
// to prevent strange problems
OnUnplug := nil;
// free the data which needs special handling
CloseFile;
CloseFileEx;
HidD_FreePreparsedData(FPreparsedData);
FLanguageStrings.Free;
// if controller exists
if FMyController <> nil then
with FMyController do
begin
// delete device from controller list
for I := 0 to FList.Count - 1 do
if FList.Items[I] = Self then
begin
// if device is plugged in create a checked in copy
if IsPluggedIn then
begin
FList.Items[I] := TJvHidDevice.Create(DevicePath, DeviceID,
RegDescr, RegClass, FMyController);
if IsCheckedOut then
begin
Dec(FNumCheckedOutDevices);
Inc(FNumCheckedInDevices);
end;
end
else
begin
FList.Delete(I);
Dec(FNumUnpluggedDevices);
end;
Break;
end;
end;
inherited Destroy;
end;
// if check changes change check only here
function TJvHidDevice.IsAccessible: Boolean;
begin
Result := IsPluggedIn and (IsCheckedOut or FIsEnumerated);
end;
// open the device "file" (for the other methods)
function TJvHidDevice.OpenFile: Boolean;
begin
// check if open allowed (propagates this state)
if IsAccessible then
if HidFileHandle = INVALID_HANDLE_VALUE then // if not already opened
begin
FNumInputBuffers := 0;
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
HidD_GetNumInputBuffers(HidFileHandle, FNumInputBuffers);
end;
Result := HidFileHandle <> INVALID_HANDLE_VALUE;
end;
// open second device "file" for ReadFileEx and WriteFileEx
function TJvHidDevice.OpenFileEx: Boolean;
begin
// check if open allowed (propagates this state)
if IsAccessible then
if HidOverlappedHandle = INVALID_HANDLE_VALUE then // if not already opened
begin
FNumOverlappedBuffers := 0;
FHidOverlappedHandle := CreateFile(PChar(FDevicePath), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if FHidOverlappedHandle <> INVALID_HANDLE_VALUE then
HidD_GetNumInputBuffers(FHidOverlappedHandle, FNumOverlappedBuffers);
end;
Result := FHidOverlappedHandle <> INVALID_HANDLE_VALUE;
end;
// implement OnUnplug event
procedure TJvHidDevice.DoUnplug;
begin
CloseFile;
CloseFileEx;
FIsPluggedIn := False;
// event even for checked in devices
if Assigned(FUnplug) then
FUnplug(Self);
// guarantees that event is only called once
OnUnplug := nil;
end;
// assign the OnUnplug event
procedure TJvHidDevice.SetUnplug(const Event: TJvHidUnplugEvent);
begin
if @Event <> @FUnplug then
FUnplug := Event;
end;
// implementing indexed properties read
function TJvHidDevice.GetDeviceString(Idx: Byte): string;
var
Buffer: array [0..256] of WideChar;
begin
Result := '';
if Idx <> 0 then
if OpenFile then
if HidD_GetIndexedString(HidFileHandle, Idx, Buffer, SizeOf(Buffer)) then
Result := WideCharToString(Buffer);
end;
function TJvHidDevice.GetLinkCollectionNode(Idx: WORD): THIDPLinkCollectionNode;
begin
FillChar(Result, SizeOf(THIDPLinkCollectionNode), #0);
if (Idx > 0) and (Idx <= Length(FLinkCollection)) then
Result := FLinkCollection[Idx-1];
end;
// implementing properties write
procedure TJvHidDevice.SetNumInputBuffers(const Num: Integer);
begin
if OpenFile then
begin
HidD_SetNumInputBuffers(HidFileHandle, Num);
HidD_GetNumInputBuffers(HidFileHandle, FNumInputBuffers);
end;
end;
procedure TJvHidDevice.SetNumOverlappedBuffers(const Num: Integer);
begin
if OpenFileEx then
begin
HidD_SetNumInputBuffers(HidOverlappedHandle, Num);
HidD_GetNumInputBuffers(HidOverlappedHandle, FNumOverlappedBuffers);
end;
end;
// internal helper for the following functions
procedure TJvHidDevice.GetMax;
begin
if IsAccessible then
begin
FMaxDataListLength := HidP_MaxDataListLength (ReportTypeParam, PreparsedData);
FMaxUsageListLength := HidP_MaxUsageListLength (ReportTypeParam, UsagePageParam, PreparsedData);
FMaxButtonListLength := HidP_MaxButtonListLength(ReportTypeParam, UsagePageParam, PreparsedData);
end;
end;
procedure TJvHidDevice.SetReportTypeParam(const ReportType: THIDPReportType);
begin
FReportTypeParam := ReportType;
GetMax;
end;
procedure TJvHidDevice.SetUsagePageParam(const UsagePage: TUsage);
begin
FUsagePageParam := UsagePage;
GetMax;
end;
procedure TJvHidDevice.SetConfiguration(Config: THIDDConfiguration);
begin
if OpenFile then
if HidD_SetConfiguration(HidFileHandle, Config, SizeOf(THIDDConfiguration)) then
FConfiguration := Config;
end;
//-- TJvHidDevice methods -----------------------------------------------
// generally the parameter count of the methods is reduced with the Param properties
// first assign the Param properties the desired value then call a method
// normally you will address the same Usage, UsagePage, ReportType or LinkCollection
// with more than one method
//
// the methods will open the device file when needed
// this file is not closed until unplug or destruction to speed up access
// close the device "file"
// if you want to open the file directly close this
// to get undisturbed access
procedure TJvHidDevice.CloseFile;
begin
if HidFileHandle <> INVALID_HANDLE_VALUE then
CloseHandle(HidFileHandle);
FNumInputBuffers := 0;
FHidFileHandle := INVALID_HANDLE_VALUE;
end;
// same for the other device "file"
procedure TJvHidDevice.CloseFileEx;
begin
if HidOverlappedHandle <> INVALID_HANDLE_VALUE then
CloseHandle(HidOverlappedHandle);
FNumOverlappedBuffers := 0;
FHidOverlappedHandle := INVALID_HANDLE_VALUE;
end;
// all the methods which directly map to a HID-function
function TJvHidDevice.FlushQueue: Boolean;
begin
Result := False;
if OpenFile then
Result := HidD_FlushQueue(HidFileHandle);
end;
function TJvHidDevice.GetFeature(var Report; const Size: Integer): Boolean;
begin
Result := False;
if OpenFile then
Result := HidD_GetFeature(HidFileHandle, Report, Size);
end;
function TJvHidDevice.SetFeature(var Report; const Size: Integer): Boolean;
begin
Result := False;
if OpenFile then
Result := HidD_SetFeature(HidFileHandle, Report, Size);
end;
function TJvHidDevice.GetSpecificButtonCaps(ButtonCaps: PHIDPButtonCaps; var Count: WORD): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetSpecificButtonCaps(ReportTypeParam, UsagePageParam, LinkCollectionParam, UsageParam, ButtonCaps, Count, PreparsedData);
end;
function TJvHidDevice.GetButtonCaps(ButtonCaps: PHIDPButtonCaps; var Count: WORD): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetButtonCaps(ReportTypeParam, ButtonCaps, Count, PreparsedData);
end;
function TJvHidDevice.GetSpecificValueCaps(ValueCaps: PHIDPValueCaps; var Count: WORD): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetSpecificValueCaps(ReportTypeParam, UsagePageParam, LinkCollectionParam, UsageParam, ValueCaps, Count, PreparsedData);
end;
function TJvHidDevice.GetValueCaps(ValueCaps: PHIDPValueCaps; var Count: WORD): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetValueCaps(ReportTypeParam, ValueCaps, Count, PreparsedData);
end;
function TJvHidDevice.GetData(DataList: PHIDPData; var DataLength: ULONG; var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetData(ReportTypeParam, DataList, DataLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.SetData(DataList: PHIDPData; var DataLength: ULONG; var Report; ReportLength: ULONG): NTSTATUS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -