Componentes.Terceros.jvcl/official/3.39/run/JvHidControllerClass.pas
2010-01-18 16:55:50 +00:00

2269 lines
76 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvHidControllerClass.PAS, released on 2001-02-28.
The Initial Developer of the Original Code is Robert Marquardt [robert_marquardt att gmx dott de]
Portions created by Robert Marquardt are Copyright (C) 1999-2003 Robert Marquardt.
All Rights Reserved.
Contributor(s): Michael Beck [mbeck att bigfoot dott com].
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.delphi-jedi.org
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvHidControllerClass.pas 12481 2009-08-26 08:39:55Z obones $
unit JvHidControllerClass;
{$DEFINE DEFAULT_JVCL_INC}
{$I jvcl.inc}
{$I windowsonly.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Windows, Messages, Classes, SysUtils,
JvComponentBase,
DBT, SetupApi, HID;
const
// a version string for the component
cHidControllerClassVersion = '1.0.35';
// strings from the registry for CheckOutByClass
cHidNoClass = 'HIDClass';
type
// forward declarations
TJvHidDeviceController = class;
TJvHidDevice = class;
// the Event function declarations
TJvHidEnumerateEvent = function(HidDev: TJvHidDevice;
const Idx: Integer): Boolean of object;
TJvHidPlugEvent = procedure(HidDev: TJvHidDevice) of object;
TJvHidUnplugEvent = TJvHidPlugEvent;
TJvHidDataEvent = procedure(HidDev: TJvHidDevice; ReportID: Byte;
const Data: Pointer; Size: Word) of object;
TJvHidDataErrorEvent = procedure(HidDev: TJvHidDevice; Error: DWORD) of object;
// check out test function
TJvHidCheckCallback = function(HidDev: TJvHidDevice): Boolean; stdcall;
// open overlapped read or write file handle
TJvHidOpenExMode = (omhRead, omhWrite);
// the physical descriptor
TJvPhysicalDescriptor = array of WORD;
// all USB relevant driver entries in the registry
TJvHidPnPInfo = class(TObject)
private
FDeviceID: DWORD;
FDevicePath: string;
FCapabilities: DWORD;
FClassDescr: string;
FClassGUID: string;
FCompatibleIDs: TStringList;
FConfigFlags: DWORD;
FDeviceDescr: string;
FDriver: string;
FFriendlyName: string;
FHardwareID: TStringList;
FLowerFilters: TStringList;
FMfg: string;
FUpperFilters: TStringList;
FAddress: string;
FBusNumber: DWORD;
FBusType: string;
FCharacteristics: string;
FDevType: DWORD;
FEnumeratorName: string;
FExclusive: DWORD;
FLegacyBusType: DWORD;
FLocationInfo: string;
FPhysDevObjName: string;
FSecuritySDS: string;
FService: string;
FUINumber: DWORD;
FUINumberFormat: string;
function GetRegistryPropertyString(PnPHandle: HDEVINFO;
const DevData: TSPDevInfoData; Prop: DWORD): string;
function GetRegistryPropertyStringList(PnPHandle: HDEVINFO;
const DevData: TSPDevInfoData; Prop: DWORD): TStringList;
function GetRegistryPropertyDWord(PnPHandle: HDEVINFO;
const DevData: TSPDevInfoData; Prop: DWORD): DWORD;
function GetCompatibleIDs: TStrings;
function GetHardwareID: TStrings;
function GetLowerFilters: TStrings;
function GetUpperFilters: TStrings;
public
property DeviceID: DWORD read FDeviceID;
property DevicePath: string read FDevicePath;
// registry values
property Capabilities: DWORD read FCapabilities;
property ClassDescr: string read FClassDescr;
property ClassGUID: string read FClassGUID;
property CompatibleIDs: TStrings read GetCompatibleIDs;
property ConfigFlags: DWORD read FConfigFlags;
property DeviceDescr: string read FDeviceDescr;
property Driver: string read FDriver;
property FriendlyName: string read FFriendlyName;
property HardwareID: TStrings read GetHardwareID;
property LowerFilters: TStrings read GetLowerFilters;
property Mfg: string read FMfg;
property UpperFilters: TStrings read GetUpperFilters;
property Address: string read FAddress;
property BusNumber: DWORD read FBusNumber;
property BusType: string read FBusType;
property Characteristics: string read FCharacteristics;
property DevType: DWORD read FDevType;
property EnumeratorName: string read FEnumeratorName;
property Exclusive: DWORD read FExclusive;
property LegacyBusType: DWORD read FLegacyBusType;
property LocationInfo: string read FLocationInfo;
property PhysDevObjName: string read FPhysDevObjName;
property SecuritySDS: string read FSecuritySDS;
property Service: string read FService;
property UINumber: DWORD read FUINumber;
property UINumberFormat: string read FUINumberFormat;
constructor Create(APnPHandle: HDEVINFO; ADevData: TSPDevInfoData; ADevicePath: PChar);
destructor Destroy; override;
end;
// a thread helper class to implement TJvHidDevice.OnData
TJvHidDeviceReadThread = class(TThread)
private
FErr: DWORD;
procedure DoData;
procedure DoDataError;
constructor CtlCreate(const Dev: TJvHidDevice);
public
Device: TJvHidDevice;
NumBytesRead: Cardinal;
Report: array of Byte;
procedure Execute; override;
constructor Create(CreateSuspended: Boolean);
end;
// the representation of a HID device
TJvHidDevice = class(TObject)
private
// internal control variables
FMyController: TJvHidDeviceController;
FIsPluggedIn: Boolean;
FIsCheckedOut: Boolean;
FIsEnumerated: Boolean;
FHidFileHandle: THandle;
FHidOverlappedRead: THandle;
FHidOverlappedWrite: THandle;
FOvlRead: TOverlapped;
FOvlWrite: TOverlapped;
// internal properties part
FAttributes: THIDDAttributes;
FPnPInfo: TJvHidPnPInfo;
FVendorName: WideString;
FProductName: WideString;
FPhysicalDescriptor: TJvPhysicalDescriptor;
FPreparsedData: PHIDPPreparsedData;
FSerialNumber: WideString;
FLanguageStrings: TStringList;
FNumInputBuffers: Integer;
FNumOverlappedBuffers: Integer;
FThreadSleepTime: Integer;
FLinkCollection: array of THIDPLinkCollectionNode;
FMaxDataListLength: ULONG;
FMaxUsageListLength: ULONG;
FMaxButtonListLength: ULONG;
FReportTypeParam: THIDPReportType;
FUsagePageParam: TUsage;
FLinkCollectionParam: WORD;
FUsageParam: TUsage;
FData: TJvHidDataEvent;
FDataError: TJvHidDataErrorEvent;
FUnplug: TJvHidUnplugEvent;
FHasReadWriteAccess: Boolean;
FDataThread: TJvHidDeviceReadThread;
FTag: Integer;
// tells if access to device is allowed
function IsAccessible: Boolean;
procedure GetMax;
// internal property implementors
function GetDeviceStringAnsi(Idx: Byte): string;
function GetDeviceStringUnicode(Idx: Byte): WideString;
function GetLinkCollectionNode(Idx: WORD): THIDPLinkCollectionNode;
function GetConfiguration: THIDDConfiguration;
function GetPreparsedData: PHIDPPreparsedData;
function GetCaps: THIDPCaps;
function GetVendorName: WideString;
function GetProductName: WideString;
function GetSerialNumber: WideString;
function GetPhysicalDescriptor: TJvPhysicalDescriptor;
function GetLanguageStrings: TStrings;
function GetOverlappedReadResult: DWORD;
function GetOverlappedWriteResult: DWORD;
procedure SetConfiguration(const Config: THIDDConfiguration);
procedure SetDataEvent(const DataEvent: TJvHidDataEvent);
procedure SetNumInputBuffers(const Num: Integer);
procedure SetNumOverlappedBuffers(const Num: Integer);
procedure SetReportTypeParam(const ReportType: THIDPReportType);
procedure SetThreadSleepTime(const SleepTime: Integer);
procedure SetUsagePageParam(const UsagePage: TUsage);
procedure StartThread;
procedure StopThread;
// Constructor is hidden! Only a TJvHidDeviceController can create a TJvHidDevice object.
constructor CtlCreate(const APnPInfo: TJvHidPnPInfo;
const Controller: TJvHidDeviceController);
protected
// internal event implementor
procedure DoUnplug;
public
// dummy constructor
constructor Create;
destructor Destroy; override;
// methods
function CancelIO(const Mode: TJvHidOpenExMode): Boolean;
procedure CloseFile;
procedure CloseFileEx(const Mode: TJvHidOpenExMode);
function DeviceIoControl(IoControlCode: DWORD; InBuffer: Pointer; InSize: DWORD;
OutBuffer: Pointer; OutSize: DWORD;
var BytesReturned: DWORD): Boolean;
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 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: PAnsiChar; UsageValueByteLength: WORD;
var Report; ReportLength: ULONG): NTSTATUS;
function GetValueCaps(ValueCaps: PHIDPValueCaps; var Count: WORD): NTSTATUS;
function OpenFile: Boolean;
function OpenFileEx(Mode: TJvHidOpenExMode): 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: PAnsiChar; 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 ReadFileEx(var Report; ToRead: DWORD;
CallBack: TPROverlappedCompletionRoutine): Boolean;
function WriteFile(var Report; ToWrite: DWORD; var BytesWritten: DWORD): Boolean;
function WriteFileEx(var Report; ToWrite: DWORD;
CallBack: TPROverlappedCompletionRoutine): Boolean;
function CheckOut: Boolean;
// Windows version dependent methods
// added in Win 2000
function GetExtendedAttributes(ReportType: THIDPReportType; DataIndex: Word;
Attributes: PHIDPExtendedAttributes; var LengthAttributes: ULONG): NTSTATUS;
function InitializeReportForID(ReportType: THIDPReportType; ReportID: Byte;
var Report; ReportLength: ULONG): NTSTATUS;
// added in Win XP
function GetInputReport(var Report; const Size: ULONG): Boolean;
function SetOutputReport(var Report; const Size: ULONG): Boolean;
// read only properties
property Attributes: THIDDAttributes read FAttributes;
property Caps: THIDPCaps read GetCaps;
property HasReadWriteAccess: Boolean read FHasReadWriteAccess;
property HidFileHandle: THandle read FHidFileHandle;
property HidOverlappedRead: THandle read FHidOverlappedRead;
property HidOverlappedWrite: THandle read FHidOverlappedWrite;
property HidOverlappedReadResult: DWORD read GetOverlappedReadResult;
property HidOverlappedWriteResult: DWORD read GetOverlappedWriteResult;
property IsCheckedOut: Boolean read FIsCheckedOut;
property IsPluggedIn: Boolean read FIsPluggedIn;
property LanguageStrings: TStrings read GetLanguageStrings;
property MaxButtonListLength: ULONG read FMaxButtonListLength;
property MaxDataListLength: ULONG read FMaxDataListLength;
property MaxUsageListLength: ULONG read FMaxUsageListLength;
property PhysicalDescriptor: TJvPhysicalDescriptor read GetPhysicalDescriptor;
property PnPInfo: TJvHidPnPInfo read FPnPInfo;
property PreparsedData: PHIDPPreparsedData read GetPreparsedData;
property ProductName: WideString read GetProductName;
property SerialNumber: WideString read GetSerialNumber;
property VendorName: WideString read GetVendorName;
// read write properties
property Configuration: THIDDConfiguration read GetConfiguration write SetConfiguration;
property LinkCollectionParam: WORD read FLinkCollectionParam write FLinkCollectionParam;
property NumInputBuffers: Integer read FNumInputBuffers write SetNumInputBuffers;
property NumOverlappedBuffers: Integer read FNumOverlappedBuffers write SetNumOverlappedBuffers;
property ReportTypeParam: THIDPReportType read FReportTypeParam write SetReportTypeParam;
property Tag: Integer read FTag write FTag;
property ThreadSleepTime: Integer read FThreadSleepTime write SetThreadSleepTime;
property UsagePageParam: TUsage read FUsagePageParam write SetUsagePageParam;
property UsageParam: TUsage read FUsageParam write FUsageParam;
// indexed properties
property DeviceStrings[Idx: Byte]: string read GetDeviceStringAnsi;
property DeviceStringsUnicode[Idx: Byte]: WideString read GetDeviceStringUnicode;
property LinkCollectionNodes[Idx: WORD]: THIDPLinkCollectionNode read GetLinkCollectionNode;
// event properties
property OnData: TJvHidDataEvent read FData write SetDataEvent;
property OnDataError: TJvHidDataErrorEvent read FDataError write FDataError;
property OnUnplug: TJvHidUnplugEvent read FUnplug write FUnplug;
end;
// controller class to manage all HID devices
TJvHidDeviceController = class(TJvComponent)
private
// internal properties part
FHidGuid: TGUID;
FArrivalEvent: TJvHidPlugEvent;
FDeviceChangeEvent: TNotifyEvent;
FEnumerateEvent: TJvHidEnumerateEvent;
FDevDataEvent: TJvHidDataEvent;
FDevDataErrorEvent: TJvHidDataErrorEvent;
FDevUnplugEvent: TJvHidUnplugEvent;
FRemovalEvent: TJvHidUnplugEvent;
FDevThreadSleepTime: Integer;
FVersion: string;
FDummy: string;
// internal list of all HID device objects
FList: TList;
// counters for the list
FNumCheckedInDevices: Integer;
FNumCheckedOutDevices: Integer;
FNumUnpluggedDevices: Integer;
// reentrancy
FInDeviceChange: Boolean;
FLParam: LPARAM;
// window to catch WM_DEVICECHANGE
FHWnd: HWND;
// internal worker functions
function CheckThisOut(var HidDev: TJvHidDevice; Idx: Integer; Check: Boolean): Boolean;
procedure EventPipe(var Msg: TMessage);
// internal event implementors
procedure SetDeviceChangeEvent(const Notifier: TNotifyEvent);
procedure SetEnumerate(const Enumerator: TJvHidEnumerateEvent);
procedure SetDevThreadSleepTime(const DevTime: Integer);
procedure SetDevData(const DataEvent: TJvHidDataEvent);
procedure SetDevDataError(const DataErrorEvent: TJvHidDataErrorEvent);
procedure SetDevUnplug(const Unplugger: TJvHidUnplugEvent);
protected
procedure DoArrival(HidDev: TJvHidDevice);
procedure DoRemoval(HidDev: TJvHidDevice);
procedure DoDeviceChange;
function DoEnumerate(HidDev: TJvHidDevice; Idx: Integer): Boolean;
public
// 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; const ClassName: string): Boolean;
function CheckOutByID(var HidDev: TJvHidDevice; const Vid, Pid: Integer): Boolean;
function CheckOutByIndex(var HidDev: TJvHidDevice; const Idx: Integer): Boolean;
function CheckOutByProductName(var HidDev: TJvHidDevice; const ProductName: WideString): Boolean;
function CheckOutByVendorName(var HidDev: TJvHidDevice; const VendorName: WideString): Boolean;
function CheckOutByCallback(var HidDev: TJvHidDevice; Check: TJvHidCheckCallback): Boolean;
// methods to count HID device objects
function CountByClass(const ClassName: string): Integer;
function CountByID(const Vid, Pid: Integer): Integer;
function CountByProductName(const ProductName: WideString): Integer;
function CountByVendorName(const VendorName: WideString): Integer;
function CountByCallback(Check: TJvHidCheckCallback): Integer;
// iterate over the HID devices
function Enumerate: Integer;
class function HidVersion: string;
// just to be complete the GUID
property HidGuid: TGUID read FHidGuid;
property NumCheckedInDevices: Integer read FNumCheckedInDevices;
property NumCheckedOutDevices: Integer read FNumCheckedOutDevices;
property NumUnpluggedDevices: Integer read FNumUnpluggedDevices;
published
property DevThreadSleepTime: Integer read FDevThreadSleepTime write SetDevThreadSleepTime default 100;
property Version: string read FVersion write FDummy stored False;
property OnArrival: TJvHidPlugEvent read FArrivalEvent write FArrivalEvent;
// the iterator event
property OnEnumerate: TJvHidEnumerateEvent read FEnumerateEvent write SetEnumerate;
// the central event for HID device changes
property OnDeviceChange: TNotifyEvent read FDeviceChangeEvent write SetDeviceChangeEvent;
// these events are copied to TJvHidDevices on creation
property OnDeviceData: TJvHidDataEvent read FDevDataEvent write SetDevData;
property OnDeviceDataError: TJvHidDataErrorEvent read FDevDataErrorEvent write SetDevDataError;
property OnDeviceUnplug: TJvHidUnplugEvent read FDevUnplugEvent write SetDevUnplug;
property OnRemoval: TJvHidUnplugEvent read FRemovalEvent write FRemovalEvent;
// to be callable at design time
procedure DeviceChange;
end;
// helpers to check the HID function and method results
function HidCheck(const RetVal: NTSTATUS): NTSTATUS; overload;
function HidCheck(const RetVal: LongBool): LongBool; overload;
function HidError(const RetVal: NTSTATUS): NTSTATUS;
function HidErrorString(const RetVal: NTSTATUS): string;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_39/run/JvHidControllerClass.pas $';
Revision: '$Revision: 12481 $';
Date: '$Date: 2009-08-26 10:39:55 +0200 (mer., 26 août 2009) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
JvResources, JvTypes;
type
EControllerError = class(EJVCLException);
EHidClientError = class(EJVCLException);
//=== these are declared inconsistent in Windows.pas =========================
function ReadFileEx(hFile: THandle; var Buffer; nNumberOfBytesToRead: DWORD;
var Overlapped: TOverlapped; lpCompletionRoutine: TPROverlappedCompletionRoutine): BOOL; stdcall;
external kernel32 name 'ReadFileEx';
function WriteFileEx(hFile: THandle; var Buffer; nNumberOfBytesToWrite: DWORD;
var Overlapped: TOverlapped; lpCompletionRoutine: TPROverlappedCompletionRoutine): BOOL; stdcall;
external kernel32 name 'WriteFileEx';
//=== { TJvHidDeviceReadThread } =============================================
constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
inherited Create(True);
Device := Dev;
NumBytesRead := 0;
SetLength(Report, Dev.Caps.InputReportByteLength);
end;
constructor TJvHidDeviceReadThread.Create(CreateSuspended: Boolean);
begin
raise EControllerError.CreateRes(@RsEDirectThreadCreationNotAllowed);
end;
procedure TJvHidDeviceReadThread.DoData;
begin
Device.OnData(Device, Report[0], @Report[1], NumBytesRead - 1);
end;
procedure TJvHidDeviceReadThread.DoDataError;
begin
if Assigned(Device.FDataError) then
Device.FDataError(Device, FErr);
end;
procedure DummyReadCompletion(ErrorCode: DWORD; Count: DWORD; Ovl: POverlapped); stdcall;
begin
end;
procedure TJvHidDeviceReadThread.Execute;
var
SleepRet: DWORD;
begin
SleepRet := WAIT_IO_COMPLETION;
while not Terminated do
begin
// read data
SleepRet := WAIT_IO_COMPLETION;
FillChar(Report[0], Device.Caps.InputReportByteLength, #0);
if Device.ReadFileEx(Report[0], Device.Caps.InputReportByteLength, @DummyReadCompletion) then
begin
// wait for read to complete
repeat
SleepRet := SleepEx(Device.ThreadSleepTime, True);
until Terminated or (SleepRet = WAIT_IO_COMPLETION);
// show data read
if not Terminated then
begin
NumBytesRead := Device.HidOverlappedReadResult;
if NumBytesRead > 0 then
// synchronizing only works if the component is not instanciated in a DLL
if IsLibrary then
DoData
else
Synchronize(DoData);
end;
end
else
begin
FErr := GetLastError;
Synchronize(DoDataError);
end;
end;
// cancel ReadFileEx call or the callback will
// crash your program
if SleepRet <> WAIT_IO_COMPLETION then
Device.CancelIO(omhRead);
end;
//=== { TJvHidPnPInfo } ======================================================
constructor TJvHidPnPInfo.Create(APnPHandle: HDEVINFO; ADevData: TSPDevInfoData; ADevicePath: PChar);
begin
inherited Create;
FDeviceID := ADevData.DevInst;
FDevicePath := ADevicePath;
// primary information
FCapabilities := GetRegistryPropertyDWord(APnPHandle, ADevData, SPDRP_CAPABILITIES);
FClassDescr := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_CLASS);
FClassGUID := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_CLASSGUID);
FCompatibleIDs := GetRegistryPropertyStringList(APnPHandle, ADevData, SPDRP_COMPATIBLEIDS);
FConfigFlags := GetRegistryPropertyDWord(APnPHandle, ADevData, SPDRP_CONFIGFLAGS);
FDeviceDescr := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_DEVICEDESC);
FDriver := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_DRIVER);
FFriendlyName := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_FRIENDLYNAME);
FHardwareID := GetRegistryPropertyStringList(APnPHandle, ADevData, SPDRP_HARDWAREID);
FLowerFilters := GetRegistryPropertyStringList(APnPHandle, ADevData, SPDRP_LOWERFILTERS);
FMfg := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_MFG);
FUpperFilters := GetRegistryPropertyStringList(APnPHandle, ADevData, SPDRP_UPPERFILTERS);
FService := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_SERVICE);
// secondary information not all likely to exist for a HID device
FAddress := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_ADDRESS);
FBusNumber := GetRegistryPropertyDWord(APnPHandle, ADevData, SPDRP_BUSNUMBER);
FBusType := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_BUSTYPEGUID);
FCharacteristics := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_CHARACTERISTICS);
FDevType := GetRegistryPropertyDWord(APnPHandle, ADevData, SPDRP_DEVTYPE);
FEnumeratorName := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_ENUMERATOR_NAME);
FExclusive := GetRegistryPropertyDWord(APnPHandle, ADevData, SPDRP_EXCLUSIVE);
FLegacyBusType := GetRegistryPropertyDWord(APnPHandle, ADevData, SPDRP_LEGACYBUSTYPE);
FLocationInfo := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_LOCATION_INFORMATION);
FPhysDevObjName := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME);
FSecuritySDS := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_SECURITY_SDS);
FUINumber := GetRegistryPropertyDWord(APnPHandle, ADevData, SPDRP_UI_NUMBER);
FUINumberFormat := GetRegistryPropertyString(APnPHandle, ADevData, SPDRP_UI_NUMBER_DESC_FORMAT);
end;
destructor TJvHidPnPInfo.Destroy;
begin
FCompatibleIDs.Free;
FHardwareID.Free;
FLowerFilters.Free;
FUpperFilters.Free;
inherited Destroy;
end;
function TJvHidPnPInfo.GetCompatibleIDs: TStrings;
begin
Result := FCompatibleIDs;
end;
function TJvHidPnPInfo.GetHardwareID: TStrings;
begin
Result := FHardwareID;
end;
function TJvHidPnPInfo.GetLowerFilters: TStrings;
begin
Result := FLowerFilters;
end;
function TJvHidPnPInfo.GetUpperFilters: TStrings;
begin
Result := FUpperFilters;
end;
// internal helpers to read values from a devices registry area
function TJvHidPnPInfo.GetRegistryPropertyString(PnPHandle: HDEVINFO;
const DevData: TSPDevInfoData; Prop: DWORD): string;
var
BytesReturned: DWORD;
RegDataType: DWORD;
Buffer: array [0..1023] of Char;
begin
BytesReturned := 0;
RegDataType := 0;
Buffer[0] := #0;
SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, Prop,
RegDataType, PByte(@Buffer[0]), SizeOf(Buffer), BytesReturned);
Result := Buffer;
end;
function TJvHidPnPInfo.GetRegistryPropertyStringList(PnPHandle: HDEVINFO;
const DevData: TSPDevInfoData; Prop: DWORD): TStringList;
var
BytesReturned: DWORD;
RegDataType: DWORD;
Buffer: array [0..16383] of Char;
P: PChar;
begin
BytesReturned := 0;
RegDataType := 0;
Buffer[0] := #0;
SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, Prop,
RegDataType, PBYTE(@Buffer[0]), SizeOf(Buffer), BytesReturned);
Result := TStringList.Create;
P := @Buffer[0];
while P[0] <> #0 do
begin
Result.Add(P);
P := P + StrLen(P) + 1;
end;
end;
function TJvHidPnPInfo.GetRegistryPropertyDWord(PnPHandle: HDEVINFO;
const DevData: TSPDevInfoData; Prop: DWORD): DWORD;
var
BytesReturned: DWORD;
RegDataType: DWORD;
begin
BytesReturned := 0;
RegDataType := 0;
Result := 0;
SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, Prop,
RegDataType, PBYTE(@Result), SizeOf(Result), BytesReturned);
end;
//=== { TJvHidDevice } =======================================================
// dummy constructor to catch invalid Create calls
constructor TJvHidDevice.Create;
begin
inherited Create;
FHidFileHandle := INVALID_HANDLE_VALUE;
FHidOverlappedRead := INVALID_HANDLE_VALUE;
FHidOverlappedWrite := INVALID_HANDLE_VALUE;
raise EControllerError.CreateRes(@RsEDirectHidDeviceCreationNotAllowed);
end;
// create and fill in a HidDevice object
// the constructor is only accessible from TJvHidController
// PnPInfo contains all info the JvHidDeviceController collected
// Controller is the devices controller object to be referenced
// internally
constructor TJvHidDevice.CtlCreate(const APnPInfo: TJvHidPnPInfo; const Controller: TJvHidDeviceController);
begin
inherited Create;
// initialize private data
FPnPInfo := APnPInfo;
FMyController := Controller;
FIsPluggedIn := True;
FIsCheckedOut := False;
FIsEnumerated := False;
FHidOverlappedRead := INVALID_HANDLE_VALUE;
FHidOverlappedWrite := INVALID_HANDLE_VALUE;
FVendorName := '';
FProductName := '';
FPreparsedData := nil;
SetLength(FPhysicalDescriptor, 0);
FSerialNumber := '';
FLanguageStrings := TStringList.Create;
FNumInputBuffers := 0;
FNumOverlappedBuffers := 0;
SetLength(FLinkCollection, 0);
FMaxDataListLength := 0;
FMaxUsageListLength := 0;
FMaxButtonListLength := 0;
FReportTypeParam := HIDP_Input;
FThreadSleepTime := 100;
FUsagePageParam := 0;
FLinkCollectionParam := 0;
FUsageParam := 0;
FDataThread := nil;
OnData := Controller.OnDeviceData;
OnUnplug := Controller.OnDeviceUnplug;
FHidFileHandle := CreateFile(PChar(PnPInfo.DevicePath), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
FHasReadWriteAccess := HidFileHandle <> INVALID_HANDLE_VALUE;
// Win2000 hack
if not HasReadWriteAccess then
FHidFileHandle := CreateFile(PChar(PnPInfo.DevicePath), 0,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if HidFileHandle <> INVALID_HANDLE_VALUE then
begin
FAttributes.Size := SizeOf(THIDDAttributes);
if not HidD_GetAttributes(HidFileHandle, FAttributes) then
raise EControllerError.CreateRes(@RsEDeviceCannotBeIdentified);
end
else
raise EControllerError.CreateRes(@RsEDeviceCannotBeOpened);
// 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 to replace it.
destructor TJvHidDevice.Destroy;
var
I: Integer;
TmpOnData: TJvHidDataEvent;
TmpOnUnplug: TJvHidUnplugEvent;
Dev: TJvHidDevice;
begin
// if we need to clone the object
TmpOnData := OnData;
TmpOnUnplug := OnUnplug;
// to prevent strange problems
OnData := nil;
OnUnplug := nil;
// free the data which needs special handling
CloseFile;
CloseFileEx(omhRead);
CloseFileEx(omhWrite);
if FPreparsedData <> nil then
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
Dev := TJvHidDevice.CtlCreate(FPnPInfo, FMyController);
// make it a complete clone
Dev.OnData := TmpOnData;
Dev.OnUnplug := TmpOnUnplug;
Dev.ThreadSleepTime := ThreadSleepTime;
FList.Items[I] := Dev;
// the FPnPInfo has been handed over to the new object
FPnPInfo := nil;
if IsCheckedOut then
begin
Dec(FNumCheckedOutDevices);
Inc(FNumCheckedInDevices);
end;
end
else
begin
FList.Delete(I);
Dec(FNumUnpluggedDevices);
end;
Break;
end;
end;
PnPInfo.Free;
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
FHidFileHandle := CreateFile(PChar(PnPInfo.DevicePath), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
FHasReadWriteAccess := HidFileHandle <> INVALID_HANDLE_VALUE;
// Win2000 hack
if not HasReadWriteAccess then
FHidFileHandle := CreateFile(PChar(PnPInfo.DevicePath), 0,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if HidFileHandle <> INVALID_HANDLE_VALUE then
begin
if NumInputBuffers <> 0 then
HidD_SetNumInputBuffers(HidFileHandle, NumInputBuffers);
HidD_GetNumInputBuffers(HidFileHandle, FNumInputBuffers);
end;
end;
Result := HidFileHandle <> INVALID_HANDLE_VALUE;
end;
// open second device "file" for ReadFileEx and WriteFileEx
function TJvHidDevice.OpenFileEx(Mode: TJvHidOpenExMode): Boolean;
begin
Result := False;
// check if open allowed (propagates this state)
if IsAccessible then
if Mode = omhRead then
begin
if HidOverlappedRead = INVALID_HANDLE_VALUE then // if not already opened
begin
FHidOverlappedRead := CreateFile(PChar(PnPInfo.DevicePath), GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if FHidOverlappedRead <> INVALID_HANDLE_VALUE then
begin
if NumOverlappedBuffers <> 0 then
HidD_SetNumInputBuffers(FHidOverlappedRead, NumOverlappedBuffers);
HidD_GetNumInputBuffers(FHidOverlappedRead, FNumOverlappedBuffers);
end;
end;
Result := FHidOverlappedRead <> INVALID_HANDLE_VALUE;
end
else
begin
if HidOverlappedWrite = INVALID_HANDLE_VALUE then // if not already opened
FHidOverlappedWrite := CreateFile(PChar(PnPInfo.DevicePath), GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
Result := FHidOverlappedWrite <> INVALID_HANDLE_VALUE;
end;
end;
// implement OnUnplug event
procedure TJvHidDevice.DoUnplug;
begin
CloseFile;
CloseFileEx(omhRead);
CloseFileEx(omhWrite);
FIsPluggedIn := False;
// event even for checked in devices
if Assigned(FUnplug) then
FUnplug(Self);
// guarantees that event is only called once
OnUnplug := nil;
end;
// implementing indexed properties read
function TJvHidDevice.GetDeviceStringAnsi(Idx: Byte): string;
begin
Result := WideCharToString(PWideChar(GetDeviceStringUnicode(Idx)));
end;
function TJvHidDevice.GetDeviceStringUnicode(Idx: Byte): WideString;
var
Buffer: array [0..253] of WideChar;
begin
Result := '';
if Idx <> 0 then
if OpenFile then
if HidD_GetIndexedString(HidFileHandle, Idx, Buffer, SizeOf(Buffer)) then
Result := Buffer;
end;
function TJvHidDevice.GetLinkCollectionNode(Idx: WORD): THIDPLinkCollectionNode;
var
Siz: ULONG;
begin
if Length(FLinkCollection) = 0 then
begin
Siz := Caps.NumberLinkCollectionNodes;
SetLength(FLinkCollection, Siz);
HidP_GetLinkCollectionNodes(@FLinkCollection[0], Siz, PreparsedData);
end;
FillChar(Result, SizeOf(THIDPLinkCollectionNode), #0);
if Idx < Length(FLinkCollection) then
Result := FLinkCollection[Idx];
end;
// implementing properties write
procedure TJvHidDevice.SetNumInputBuffers(const Num: Integer);
begin
if (Num <> FNumInputBuffers) and OpenFile then
begin
HidD_SetNumInputBuffers(HidFileHandle, Num);
HidD_GetNumInputBuffers(HidFileHandle, FNumInputBuffers);
end;
end;
procedure TJvHidDevice.SetNumOverlappedBuffers(const Num: Integer);
begin
if (Num <> FNumOverlappedBuffers) and OpenFileEx(omhRead) then
begin
HidD_SetNumInputBuffers(HidOverlappedRead, Num);
HidD_GetNumInputBuffers(HidOverlappedRead, 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.SetThreadSleepTime(const SleepTime: Integer);
begin
// limit to 10 msec .. 10 sec
if (SleepTime >= 10) and (SleepTime <= 10000) then
FThreadSleepTime := SleepTime;
end;
procedure TJvHidDevice.SetUsagePageParam(const UsagePage: TUsage);
begin
FUsagePageParam := UsagePage;
GetMax;
end;
function TJvHidDevice.GetConfiguration: THIDDConfiguration;
begin
Result.cookie := nil;
Result.size := 0;
Result.RingBufferSize := 0;
if OpenFile then
HidD_GetConfiguration(HidFileHandle, Result, SizeOf(THIDDConfiguration));
end;
function TJvHidDevice.GetPreparsedData: PHIDPPreparsedData;
begin
if FPreparsedData = nil then
if OpenFile then
begin
HidD_GetPreparsedData(HidFileHandle, FPreparsedData);
CloseFile;
end;
Result := FPreparsedData;
end;
function TJvHidDevice.GetCaps: THIDPCaps;
begin
FillChar(Result, SizeOf(THIDPCaps), #0);
HidP_GetCaps(PreparsedData, Result);
end;
function TJvHidDevice.GetVendorName: WideString;
var
Buffer: array [0..253] of WideChar;
begin
if FVendorName = '' then
if OpenFile then
begin
FillChar(Buffer, SizeOf(Buffer), #0);
if HidD_GetManufacturerString(HidFileHandle, Buffer, SizeOf(Buffer)) then
FVendorName := Buffer;
CloseFile;
end;
Result := FVendorName;
end;
function TJvHidDevice.GetProductName: WideString;
var
Buffer: array [0..253] of WideChar;
begin
if FProductName = '' then
if OpenFile then
begin
FillChar(Buffer, SizeOf(Buffer), #0);
if HidD_GetProductString(HidFileHandle, Buffer, SizeOf(Buffer)) then
FProductName := Buffer;
CloseFile;
end;
Result := FProductName;
end;
function TJvHidDevice.GetSerialNumber: WideString;
var
I: Integer;
Len: Integer;
IDs: array [0..253] of WORD;
Buffer: array [0..253] of WideChar;
begin
if FSerialNumber = '' then
if OpenFile then
begin
FillChar(Buffer, SizeOf(Buffer), #0);
if HidD_GetSerialNumberString(HidFileHandle, Buffer, SizeOf(Buffer)) then
begin
// calculate length of StringDescriptor 0
FillChar(IDs, SizeOf(IDs), $FF);
Len := 0;
HidD_GetIndexedString(HidFileHandle, 0, PWideChar(@IDs), SizeOf(IDs));
for I := High(IDs) downto 0 do
if IDs[I] <> $FFFF then
begin
if IDs[I] = 0 then
Len := I
else
Len := I + 1;
Break;
end;
// compensate for buggy function
for I := 0 to Len - 1 do
if IDs[I] <> WORD(Buffer[I]) then
begin
FSerialNumber := Buffer;
Break;
end;
end;
CloseFile;
end;
Result := FSerialNumber;
end;
function TJvHidDevice.GetPhysicalDescriptor: TJvPhysicalDescriptor;
var
I: Integer;
begin
if Length(FPhysicalDescriptor) = 0 then
if OpenFile then
begin
I := 0;
SetLength(FPhysicalDescriptor, 2048);
while not HidD_GetPhysicalDescriptor(HidFileHandle, FPhysicalDescriptor[0], I * SizeOf(WORD)) do
begin
Inc(I);
if (I > 2048) or (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
begin
I := 0;
Break;
end;
end;
SetLength(FPhysicalDescriptor, I);
CloseFile;
end;
Result := FPhysicalDescriptor;
end;
function TJvHidDevice.GetLanguageStrings: TStrings;
var
I: Integer;
Len: Integer;
IDs: array [0..253] of WORD;
Name: array [0..255] of Char;
begin
if FLanguageStrings.Count = 0 then
if OpenFile then
begin
// calculate length of StringDescriptor 0
FillChar(IDs, SizeOf(IDs), $FF);
Len := 0;
if HidD_GetIndexedString(HidFileHandle, 0, PWideChar(@IDs), SizeOf(IDs)) then
for I := High(IDs) downto 0 do
if IDs[I] <> $FFFF then
begin
if IDs[I] = 0 then
Len := I
else
Len := I + 1;
Break;
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(RsUnknownLocaleIDFmt, [WORD(IDs[I])]));
end;
CloseFile;
end;
Result := FLanguageStrings;
end;
function TJvHidDevice.GetOverlappedReadResult: DWORD;
begin
Result := 0;
if HidOverlappedRead <> INVALID_HANDLE_VALUE then
if not GetOverlappedResult(HidOverlappedRead, FOvlRead, Result, False) then
Result := 0;
end;
function TJvHidDevice.GetOverlappedWriteResult: DWORD;
begin
Result := 0;
if HidOverlappedWrite <> INVALID_HANDLE_VALUE then
if not GetOverlappedResult(HidOverlappedWrite, FOvlWrite, Result, False) then
Result := 0;
end;
procedure TJvHidDevice.SetConfiguration(const Config: THIDDConfiguration);
begin
if OpenFile then
HidD_SetConfiguration(HidFileHandle, Config, SizeOf(THIDDConfiguration));
end;
procedure TJvHidDevice.SetDataEvent(const DataEvent: TJvHidDataEvent);
begin
// this assignment is a bit tricky because a thread may be running
// kill the thread with the old event still in effect
if not Assigned(DataEvent) then
StopThread;
// assign the new event and start the thread if needed
FData := DataEvent;
StartThread;
end;
procedure TJvHidDevice.StartThread;
begin
if Assigned(FData) and IsPluggedIn and IsCheckedOut and
HasReadWriteAccess and not Assigned(FDataThread) then
begin
FDataThread := TJvHidDeviceReadThread.CtlCreate(Self);
FDataThread.FreeOnTerminate := False;
FDataThread.{$IFDEF COMPILER14_UP}Start{$ELSE}Resume{$ENDIF COMPILER14_UP};
end;
end;
procedure TJvHidDevice.StopThread;
begin
if Assigned(FDataThread) then
begin
FDataThread.Terminate;
FDataThread.WaitFor;
FDataThread.Free;
FDataThread := nil;
end;
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
// cancel asynchronous operations on either HidOverlappedRead or HidOverlappedWrite
function TJvHidDevice.CancelIO(const Mode: TJvHidOpenExMode): Boolean;
function CallCancelIO(Handle: THandle): Boolean;
type
TCancelIOFunc = function(hFile: THandle): BOOL; stdcall;
var
hKernel: HMODULE;
CancelIOFunc: TCancelIOFunc;
begin
hKernel := GetModuleHandle(kernel32);
Result := hKernel <> INVALID_HANDLE_VALUE;
if Result then
begin
@CancelIOFunc := GetProcAddress(hKernel, 'CancelIO');
if Assigned(CancelIOFunc) then
Result := CancelIOFunc(Handle)
else
Result := False;
end;
end;
begin
Result := False;
if (Mode = omhRead) and (HidOverlappedRead <> INVALID_HANDLE_VALUE) then
Result := CallCancelIO(HidOverlappedRead)
else
if (Mode = omhWrite) and (HidOverlappedWrite <> INVALID_HANDLE_VALUE) then
Result := CallCancelIO(HidOverlappedWrite);
end;
// 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(const Mode: TJvHidOpenExMode);
begin
if Mode = omhRead then
begin
if HidOverlappedRead <> INVALID_HANDLE_VALUE then
CloseHandle(HidOverlappedRead);
FNumOverlappedBuffers := 0;
FHidOverlappedRead := INVALID_HANDLE_VALUE;
end
else
begin
if HidOverlappedWrite <> INVALID_HANDLE_VALUE then
CloseHandle(HidOverlappedWrite);
FHidOverlappedWrite := INVALID_HANDLE_VALUE;
end;
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;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_SetData(ReportTypeParam, DataList, DataLength, PreparsedData,
Report, ReportLength);
end;
function TJvHidDevice.GetUsages(UsageList: PUsage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetUsages(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageList, UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.GetButtons(UsageList: PUsage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetButtons(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageList, UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.GetUsagesEx(UsageList: PUsageAndPage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetUsagesEx(ReportTypeParam, LinkCollectionParam, UsageList,
UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.GetButtonsEx(UsageList: PUsageAndPage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetButtonsEx(ReportTypeParam, LinkCollectionParam, UsageList,
UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.SetUsages(UsageList: PUsage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_SetUsages(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageList, UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.SetButtons(UsageList: PUsage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_SetButtons(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageList, UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.UnsetUsages(UsageList: PUsage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_UnsetUsages(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageList, UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.UnsetButtons(UsageList: PUsage; var UsageLength: ULONG;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_UnsetButtons(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageList, UsageLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.GetUsageValue(var UsageValue: ULONG; var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetUsageValue(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageParam, UsageValue, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.GetScaledUsageValue(var UsageValue: Integer;
var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetScaledUsageValue(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageParam, UsageValue, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.GetUsageValueArray(UsageValue: PAnsiChar;
UsageValueByteLength: WORD; var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_GetUsageValueArray(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageParam, UsageValue, UsageValueByteLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.SetUsageValue(UsageValue: ULONG; var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_SetUsageValue(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageParam, UsageValue, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.SetScaledUsageValue(UsageValue: Integer; var Report;
ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_SetScaledUsageValue(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageParam, UsageValue, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.SetUsageValueArray(UsageValue: PAnsiChar;
UsageValueByteLength: WORD; var Report; ReportLength: ULONG): NTSTATUS;
begin
Result := HIDP_STATUS_NULL; // for not plugged in
if IsAccessible then
Result := HidP_SetUsageValueArray(ReportTypeParam, UsagePageParam, LinkCollectionParam,
UsageParam, UsageValue, UsageValueByteLength, PreparsedData, Report, ReportLength);
end;
function TJvHidDevice.DeviceIoControl(IoControlCode: DWORD; InBuffer: Pointer; InSize: DWORD;
OutBuffer: Pointer; OutSize: DWORD; var BytesReturned: DWORD): Boolean;
begin
Result := False;
if OpenFile then
Result := Windows.DeviceIoControl(HidFileHandle, IoControlCode, InBuffer, InSize,
OutBuffer, OutSize, BytesReturned, nil);
end;
function TJvHidDevice.ReadFile(var Report; ToRead: DWORD; var BytesRead: DWORD): Boolean;
begin
Result := False;
if OpenFile then
Result := Windows.ReadFile(HidFileHandle, Report, ToRead, BytesRead, nil);
end;
function TJvHidDevice.WriteFile(var Report; ToWrite: DWORD; var BytesWritten: DWORD): Boolean;
begin
Result := False;
if OpenFile then
Result := Windows.WriteFile(HidFileHandle, Report, ToWrite, BytesWritten, nil);
end;
// the TOverlapped structure is not needed externally
// the hEvent element is used to transport the device object
// to the callback function
// Better not implement a Delphi event with that
function TJvHidDevice.ReadFileEx(var Report; ToRead: DWORD;
CallBack: TPROverlappedCompletionRoutine): Boolean;
begin
Result := False;
if OpenFileEx(omhRead) then
begin
FillChar(FOvlRead, SizeOf(TOverlapped), #0);
FOvlRead.hEvent := DWORD(Self);
Result := JvHidControllerClass.ReadFileEx(HidOverlappedRead, Report, ToRead, FOvlRead, CallBack);
end;
end;
function TJvHidDevice.WriteFileEx(var Report; ToWrite: DWORD;
CallBack: TPROverlappedCompletionRoutine): Boolean;
begin
Result := False;
if OpenFileEx(omhWrite) then
begin
FillChar(FOvlWrite, SizeOf(TOverlapped), #0);
FOvlWrite.hEvent := DWORD(Self);
Result := JvHidControllerClass.WriteFileEx(HidOverlappedWrite, Report, ToWrite, FOvlWrite, CallBack);
end;
end;
function TJvHidDevice.CheckOut: Boolean;
begin
Result := Assigned(FMyController) and IsPluggedIn and not IsCheckedOut;
if Result then
begin
FIsCheckedOut := True;
Inc(FMyController.FNumCheckedOutDevices);
Dec(FMyController.FNumCheckedInDevices);
StartThread;
end;
end;
function TJvHidDevice.GetExtendedAttributes(ReportType: THIDPReportType; DataIndex: Word;
Attributes: PHIDPExtendedAttributes; var LengthAttributes: ULONG): NTSTATUS;
begin
if Assigned(HidP_GetExtendedAttributes) then
Result := HidP_GetExtendedAttributes(ReportType, DataIndex, FPreparsedData,
Attributes, LengthAttributes)
else
Result := HIDP_STATUS_NOT_IMPLEMENTED;
end;
function TJvHidDevice.InitializeReportForID(ReportType: THIDPReportType; ReportID: Byte;
var Report; ReportLength: ULONG): NTSTATUS;
begin
if Assigned(HidP_InitializeReportForID) then
Result := HidP_InitializeReportForID(ReportType, ReportID, FPreparsedData, Report, ReportLength)
else
Result := HIDP_STATUS_NOT_IMPLEMENTED;
end;
function TJvHidDevice.GetInputReport(var Report; const Size: ULONG): Boolean;
begin
Result := False;
if Assigned(HidD_GetInputReport) then
if OpenFile then
Result := HidD_GetInputReport(FHidFileHandle, @Report, Size);
end;
function TJvHidDevice.SetOutputReport(var Report; const Size: ULONG): Boolean;
begin
Result := False;
if Assigned(HidD_SetOutputReport) then
if OpenFile then
Result := HidD_SetOutputReport(FHidFileHandle, @Report, Size);
end;
//=== { TJvHidDeviceController } =============================================
constructor TJvHidDeviceController.Create(AOwner: TComponent);
const
cHidGuid: TGUID = '{4d1e55b2-f16f-11cf-88cb-001111000030}';
begin
inherited Create(AOwner);
FDeviceChangeEvent := nil;
FEnumerateEvent := nil;
FDevUnplugEvent := nil;
FNumCheckedInDevices := 0;
FNumCheckedOutDevices := 0;
FNumUnpluggedDevices := 0;
FDevThreadSleepTime := 100;
FVersion := cHidControllerClassVersion;
FInDeviceChange := False;
FList := TList.Create;
if LoadSetupApi then
LoadHid;
if IsHidLoaded then
begin
HidD_GetHidGuid(FHidGuid);
// only hook messages if there is a HID DLL
FHWnd := AllocateHWnd(EventPipe);
// this one executes after Create completed which ensures
// that all global elements like Application.MainForm are initialized
PostMessage(FHWnd, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, -1);
end
else
FHidGuid := cHidGuid;
end;
// unplug or kill all controlled TJvHidDevices on controller destruction
destructor TJvHidDeviceController.Destroy;
var
I: Integer;
HidDev: TJvHidDevice;
begin
// to prevent strange problems
FDeviceChangeEvent := nil;
FDevUnplugEvent := nil;
OnEnumerate := nil;
// unhook event pipe
if IsHidLoaded then
DeallocateHWnd(FHWnd);
for I := 0 to FList.Count - 1 do
begin
HidDev := FList.Items[I];
with HidDev do
begin
// set to uncontrolled
FMyController := nil;
if IsCheckedOut then
DoUnplug // pull the plug for checked out TJvHidDevices
else
Free; // kill TJvHidDevices which are not checked out
end;
end;
FList.Free;
if IsHidLoaded then
UnloadSetupApi;
UnloadHid;
inherited Destroy;
end;
procedure TJvHidDeviceController.DoArrival(HidDev: TJvHidDevice);
begin
if Assigned(FArrivalEvent) then
begin
HidDev.FIsEnumerated := True;
FArrivalEvent(HidDev);
HidDev.FIsEnumerated := False;
end;
end;
procedure TJvHidDeviceController.DoRemoval(HidDev: TJvHidDevice);
begin
if Assigned(FRemovalEvent) then
begin
HidDev.FIsEnumerated := True;
FRemovalEvent(HidDev);
HidDev.FIsEnumerated := False;
end;
end;
// implement OnDeviceChange event
procedure TJvHidDeviceController.DoDeviceChange;
begin
if Assigned(FDeviceChangeEvent) then
FDeviceChangeEvent(Self);
end;
// gets all the Windows events/messages directly
procedure TJvHidDeviceController.EventPipe(var Msg: TMessage);
begin
// sort out WM_DEVICECHANGE : DBT_DEVNODES_CHANGED
if not (csDestroying in ComponentState) and
(Msg.Msg = WM_DEVICECHANGE) and (TWMDeviceChange(Msg).Event = DBT_DEVNODES_CHANGED) then
if not FInDeviceChange then
begin
FLParam := Msg.LParam;
FInDeviceChange := True;
DeviceChange;
FInDeviceChange := False;
end;
Msg.Result := DefWindowProc(FHWnd, Msg.Msg, Msg.wParam, Msg.lParam);
end;
// implements OnDeviceChange event
// it is published to allow calling at design time
procedure TJvHidDeviceController.DeviceChange;
var
I: Integer;
J: Integer;
HidDev: TJvHidDevice;
Changed: Boolean;
NewList: TList;
// internal worker function to find all HID devices and create their objects
procedure FillInList;
var
PnPHandle: HDEVINFO;
DevData: TSPDevInfoData;
DeviceInterfaceData: TSPDeviceInterfaceData;
FunctionClassDeviceData: PSPDeviceInterfaceDetailData;
Success: LongBool;
Devn: Integer;
BytesReturned: DWORD;
HidDev: TJvHidDevice;
PnPInfo: TJvHidPnPInfo;
begin
if not IsHidLoaded then
Exit;
// Get a handle for the Plug and Play node and request currently active HID devices
PnPHandle := SetupDiGetClassDevs(@FHidGuid, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then
Exit;
Devn := 0;
repeat
DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
// Is there a HID device at this table entry?
Success := SetupDiEnumDeviceInterfaces(PnPHandle, nil, FHidGuid, Devn, DeviceInterfaceData);
if Success then
begin
DevData.cbSize := SizeOf(DevData);
BytesReturned := 0;
SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, nil, 0, BytesReturned, @DevData);
if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
begin
FunctionClassDeviceData := AllocMem(BytesReturned);
FunctionClassDeviceData^.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
if SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData,
FunctionClassDeviceData, BytesReturned, BytesReturned, @DevData) then
begin
// fill in PnPInfo of device
PnPInfo := TJvHidPnPInfo.Create(PnPHandle, DevData, PChar(@FunctionClassDeviceData.DevicePath));
// create HID device object and add it to the device list
try
HidDev := TJvHidDevice.CtlCreate(PnPInfo, Self);
NewList.Add(HidDev);
except
// ignore device if unreadable
end;
Inc(Devn);
end;
FreeMem(FunctionClassDeviceData);
end;
end;
until not Success;
SetupDiDestroyDeviceInfoList(PnPHandle);
end;
begin
// initial auto message always triggers OnDeviceChange event
Changed := (FLParam = -1);
// get new device list
NewList := TList.Create;
FillInList;
// unplug devices in FList which are not in NewList
for I := FList.Count - 1 downto 0 do
begin
HidDev := FList.Items[I];
for J := NewList.Count - 1 downto 0 do
if (TJvHidDevice(NewList.Items[J]).PnPInfo.DeviceID = HidDev.PnPInfo.DeviceID) and
HidDev.IsPluggedIn then
begin
HidDev := nil;
Break;
end;
if HidDev <> nil then
begin
HidDev.DoUnplug;
DoRemoval(HidDev);
// delete from list
if not HidDev.IsCheckedOut then
FList.Delete(I);
Changed := True;
end;
end;
// delete devices from NewList which are in FList
for I := 0 to NewList.Count - 1 do
for J := 0 to FList.Count - 1 do
if (TJvHidDevice(NewList[I]).PnPInfo.DeviceID = TJvHidDevice(FList[J]).PnPInfo.DeviceID) and
TJvHidDevice(FList[J]).IsPluggedIn then
begin
TJvHidDevice(NewList[I]).FMyController := nil; // prevent Free/Destroy from accessing this controller
TJvHidDevice(NewList[I]).Free;
NewList[I] := nil;
Break;
end;
// add the remains in NewList to FList
for I := 0 to NewList.Count - 1 do
if NewList[I] <> nil then
begin
FList.Add(NewList[I]);
Changed := True;
DoArrival(TJvHidDevice(NewList[I]));
end;
// throw away helper list
NewList.Free;
// recount the devices
FNumCheckedInDevices := 0;
FNumCheckedOutDevices := 0;
FNumUnpluggedDevices := 0;
for I := 0 to FList.Count - 1 do
begin
HidDev := FList.Items[I];
Inc(FNumCheckedInDevices, Ord(not HidDev.IsCheckedOut));
Inc(FNumCheckedOutDevices, Ord(HidDev.IsCheckedOut));
Inc(FNumUnpluggedDevices, Ord(not HidDev.IsPluggedIn));
end;
FNumCheckedOutDevices := FNumCheckedOutDevices - FNumUnpluggedDevices;
if Changed then
DoDeviceChange;
end;
class function TJvHidDeviceController.HidVersion: string;
var
Dummy: DWORD;
Size: UINT;
Buf: array of Byte;
Value: PChar;
begin
Result := '';
Size := GetFileVersionInfoSize(HidModuleName, Dummy);
if Size > 0 then
begin
SetLength(Buf, Size);
GetFileVersionInfo(HidModuleName, INVALID_HANDLE_VALUE, Size, @Buf[0]);
if VerQueryValue(@Buf[0], 'StringFileInfo\040904E4\FileVersion', Pointer(Value), Size) then
Result := Value;
end;
end;
// assign OnDeviceChange and immediately fire it
procedure TJvHidDeviceController.SetDeviceChangeEvent(const Notifier: TNotifyEvent);
begin
if @FDeviceChangeEvent <> @Notifier then
begin
FDeviceChangeEvent := Notifier;
if not (csLoading in ComponentState) then
DeviceChange;
end;
end;
// implement OnEnumerate event
function TJvHidDeviceController.DoEnumerate(HidDev: TJvHidDevice; Idx: Integer): Boolean;
begin
Result := False;
if Assigned(FEnumerateEvent) then
begin
HidDev.FIsEnumerated := True;
Result := FEnumerateEvent(HidDev, Idx);
HidDev.FIsEnumerated := False;
if not HidDev.IsCheckedOut then
begin
HidDev.CloseFile;
HidDev.CloseFileEx(omhRead);
HidDev.CloseFileEx(omhWrite);
end;
end;
end;
// assign OnEnumerate event
procedure TJvHidDeviceController.SetEnumerate(const Enumerator: TJvHidEnumerateEvent);
begin
FEnumerateEvent := Enumerator;
end;
// assign DevThreadSleepTime
procedure TJvHidDeviceController.SetDevThreadSleepTime(const DevTime: Integer);
var
I: Integer;
Dev: TJvHidDevice;
begin
if DevTime <> FDevThreadSleepTime then
begin
// change all DevThreadSleepTime with the same old value
for I := 0 to FList.Count - 1 do
begin
Dev := FList.Items[I];
if Dev.ThreadSleepTime = FDevThreadSleepTime then
Dev.ThreadSleepTime := DevTime;
end;
FDevThreadSleepTime := DevTime;
end;
end;
// assign OnDevData event
procedure TJvHidDeviceController.SetDevData(const DataEvent: TJvHidDataEvent);
var
I: Integer;
Dev: TJvHidDevice;
begin
if @DataEvent <> @FDevDataEvent then
begin
// change all OnData events with the same old value
for I := 0 to FList.Count - 1 do
begin
Dev := FList.Items[I];
if @Dev.OnData = @FDevDataEvent then
Dev.OnData := DataEvent;
end;
FDevDataEvent := DataEvent;
end;
end;
// assign OnDevDataError event
procedure TJvHidDeviceController.SetDevDataError(const DataErrorEvent: TJvHidDataErrorEvent);
var
I: Integer;
Dev: TJvHidDevice;
begin
if @DataErrorEvent <> @FDevDataErrorEvent then
begin
// change all OnDataError events with the same old value
for I := 0 to FList.Count - 1 do
begin
Dev := FList.Items[I];
if @Dev.OnDataError = @FDevDataErrorEvent then
Dev.OnDataError := DataErrorEvent;
end;
FDevDataErrorEvent := DataErrorEvent;
end;
end;
// assign OnDevUnplug event
procedure TJvHidDeviceController.SetDevUnplug(const Unplugger: TJvHidUnplugEvent);
var
I: Integer;
Dev: TJvHidDevice;
begin
if @Unplugger <> @FDevUnplugEvent then
begin
// change all OnUnplug events with the same old value
for I := 0 to FList.Count - 1 do
begin
Dev := FList.Items[I];
if @Dev.OnUnplug = @FDevUnplugEvent then
Dev.OnUnplug := Unplugger;
end;
FDevUnplugEvent := Unplugger;
end;
end;
// send an OnEnumerate event for all plugged HidDevices
// it is explicitly allowed to check out any device in the event
function TJvHidDeviceController.Enumerate: Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FList.Count - 1 do
if TJvHidDevice(FList[I]).IsPluggedIn then
begin
Inc(Result);
if not DoEnumerate(FList[I], I) then
Break;
end;
end;
// internal worker function to check out a TJvHidDevice
function TJvHidDeviceController.CheckThisOut(var HidDev: TJvHidDevice; Idx: Integer; Check: Boolean): Boolean;
begin
Result := Check and not TJvHidDevice(FList.Items[Idx]).IsCheckedOut;
if Result then
begin
HidDev := FList[Idx];
HidDev.FIsCheckedOut := True;
Inc(FNumCheckedOutDevices);
Dec(FNumCheckedInDevices);
HidDev.StartThread;
end;
end;
// method CheckOutByProductName hands out the first HidDevice with a matching ProductName
function TJvHidDeviceController.CheckOutByProductName(var HidDev: TJvHidDevice;
const ProductName: WideString): Boolean;
var
I: Integer;
begin
Result := False;
HidDev := nil;
if ProductName <> '' then
for I := 0 to FList.Count - 1 do
begin
Result := CheckThisOut(HidDev, I, ProductName = TJvHidDevice(FList[I]).ProductName);
if Result then
Break;
end;
end;
// method CheckOutByVendorName hands out the first HidDevice with a matching VendorName
function TJvHidDeviceController.CheckOutByVendorName(var HidDev: TJvHidDevice;
const VendorName: WideString): Boolean;
var
I: Integer;
begin
Result := False;
HidDev := nil;
if VendorName <> '' then
for I := 0 to FList.Count - 1 do
begin
Result := CheckThisOut(HidDev, I, VendorName = TJvHidDevice(FList[I]).VendorName);
if Result then
Break;
end;
end;
// method CheckOutByCallback hands out the first HidDevice which is accepted by the Check function
// only checked in devices are presented to the Check function
// the device object is usable like during Enumerate
function TJvHidDeviceController.CheckOutByCallback(var HidDev: TJvHidDevice;
Check: TJvHidCheckCallback): Boolean;
var
I: Integer;
Dev: TJvHidDevice;
begin
Result := False;
HidDev := nil;
for I := 0 to FList.Count - 1 do
begin
Dev := FList[I];
if not Dev.IsCheckedOut then
begin
Dev.FIsEnumerated := True;
Result := CheckThisOut(HidDev, I, Check(Dev));
Dev.FIsEnumerated := False;
if not Result then
begin
Dev.CloseFile;
Dev.CloseFileEx(omhRead);
Dev.CloseFileEx(omhWrite);
end;
if Result then
Break;
end;
end;
end;
// method CheckOutByClass hands out the first HidDevice with a matching Class
// Class comes from the registry (examples: 'Mouse', 'Keyboard')
function TJvHidDeviceController.CheckOutByClass(var HidDev: TJvHidDevice;
const ClassName: string): Boolean;
var
I: Integer;
begin
Result := False;
HidDev := nil;
if ClassName <> '' then
for I := 0 to FList.Count - 1 do
begin
Result := CheckThisOut(HidDev, I, ClassName = TJvHidDevice(FList[I]).PnPInfo.ClassDescr);
if Result then
Break;
end;
end;
// method CheckOutByID hands out the first HidDevice with a matching VendorID and ProductID
// Pid = -1 matches all ProductIDs
function TJvHidDeviceController.CheckOutByID(var HidDev: TJvHidDevice;
const Vid, Pid: Integer): Boolean;
var
I: Integer;
begin
Result := False;
HidDev := nil;
for I := 0 to FList.Count - 1 do
begin
Result := CheckThisOut(HidDev, I, (Vid = TJvHidDevice(FList[I]).Attributes.VendorID) and
((Pid = TJvHidDevice(FList[I]).Attributes.ProductID) or (Pid = -1)));
if Result then
Break;
end;
end;
// method CheckOutByIndex hands out the HidDevice in the list with the named index
// this is mainly for check out during OnEnumerate
function TJvHidDeviceController.CheckOutByIndex(var HidDev: TJvHidDevice;
const Idx: Integer): Boolean;
begin
Result := False;
HidDev := nil;
if (Idx >= 0) and (Idx < FList.Count) then
Result := CheckThisOut(HidDev, Idx, True);
end;
// method CheckOut simply hands out the first available HidDevice in the list
function TJvHidDeviceController.CheckOut(var HidDev: TJvHidDevice): Boolean;
var
I: Integer;
begin
Result := False;
HidDev := nil;
for I := 0 to FList.Count - 1 do
begin
Result := CheckThisOut(HidDev, I, True);
if Result then
Break;
end;
end;
// method CheckIn hands a checked out HidDevice back in
procedure TJvHidDeviceController.CheckIn(var HidDev: TJvHidDevice);
begin
if HidDev <> nil then
begin
HidDev.StopThread;
HidDev.CloseFile;
HidDev.CloseFileEx(omhRead);
HidDev.CloseFileEx(omhWrite);
if HidDev.IsPluggedIn then
begin
HidDev.FIsCheckedOut := False;
Dec(FNumCheckedOutDevices);
Inc(FNumCheckedInDevices);
end
else
HidDev.Free;
HidDev := nil;
end;
end;
function TJvHidDeviceController.CountByClass(const ClassName: string): Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FList.Count - 1 do
if TJvHidDevice(FList[I]).IsPluggedIn and
(ClassName = TJvHidDevice(FList[I]).PnPInfo.ClassDescr) then
Inc(Result);
end;
function TJvHidDeviceController.CountByID(const Vid, Pid: Integer): Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FList.Count - 1 do
if TJvHidDevice(FList[I]).IsPluggedIn and
(Vid = TJvHidDevice(FList[I]).Attributes.VendorID) and
((Pid = TJvHidDevice(FList[I]).Attributes.ProductID) or (Pid = -1)) then
Inc(Result);
end;
function TJvHidDeviceController.CountByProductName(const ProductName: WideString): Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FList.Count - 1 do
if TJvHidDevice(FList[I]).IsPluggedIn and
(ProductName = TJvHidDevice(FList[I]).ProductName) then
Inc(Result);
end;
function TJvHidDeviceController.CountByVendorName(const VendorName: WideString): Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FList.Count - 1 do
if TJvHidDevice(FList[I]).IsPluggedIn and
(VendorName = TJvHidDevice(FList[I]).VendorName) then
Inc(Result);
end;
function TJvHidDeviceController.CountByCallback(Check: TJvHidCheckCallback): Integer;
var
I: Integer;
Dev: TJvHidDevice;
begin
Result := 0;
for I := 0 to FList.Count - 1 do
begin
if TJvHidDevice(FList[I]).IsPluggedIn then
begin
Dev := FList[I];
Dev.FIsEnumerated := True;
if Check(Dev) then
Inc(Result);
Dev.FIsEnumerated := False;
if not Dev.IsCheckedOut then
begin
Dev.CloseFile;
Dev.CloseFileEx(omhRead);
Dev.CloseFileEx(omhWrite);
end;
end;
end;
end;
//============================================================================
// a helper function to check the return values just
// like Win32Check
// the functions return the parameter to be transparent
function HidCheck(const RetVal: NTSTATUS): NTSTATUS;
begin
if RetVal <> HIDP_STATUS_SUCCESS then
HidError(RetVal);
Result := RetVal;
end;
function HidCheck(const RetVal: LongBool): LongBool;
begin
if not RetVal then
raise EHidClientError.CreateRes(@RsEHIDBooleanError);
Result := RetVal;
end;
function HidError(const RetVal: NTSTATUS): NTSTATUS;
var
ErrBuf: string;
begin
ErrBuf := HidErrorString(RetVal);
// only react to HID errors
if ErrBuf <> '' then
raise EHidClientError.Create(ErrBuf);
Result := RetVal;
end;
function HidErrorString(const RetVal: NTSTATUS): string;
begin
Result := '';
// only check HID errors
if ((RetVal and NTSTATUS($00FF0000)) = HIDP_STATUS_SUCCESS) and
((RetVal and NTSTATUS($C0000000)) <> 0) then
begin
case RetVal of
HIDP_STATUS_NULL:
Result := RsHIDP_STATUS_NULL;
HIDP_STATUS_INVALID_PREPARSED_DATA:
Result := RsHIDP_STATUS_INVALID_PREPARSED_DATA;
HIDP_STATUS_INVALID_REPORT_TYPE:
Result := RsHIDP_STATUS_INVALID_REPORT_TYPE;
HIDP_STATUS_INVALID_REPORT_LENGTH:
Result := RsHIDP_STATUS_INVALID_REPORT_LENGTH;
HIDP_STATUS_USAGE_NOT_FOUND:
Result := RsHIDP_STATUS_USAGE_NOT_FOUND;
HIDP_STATUS_VALUE_OUT_OF_RANGE:
Result := RsHIDP_STATUS_VALUE_OUT_OF_RANGE;
HIDP_STATUS_BAD_LOG_PHY_VALUES:
Result := RsHIDP_STATUS_BAD_LOG_PHY_VALUES;
HIDP_STATUS_BUFFER_TOO_SMALL:
Result := RsHIDP_STATUS_BUFFER_TOO_SMALL;
HIDP_STATUS_INTERNAL_ERROR:
Result := RsHIDP_STATUS_INTERNAL_ERROR;
HIDP_STATUS_I8042_TRANS_UNKNOWN:
Result := RsHIDP_STATUS_I8042_TRANS_UNKNOWN;
HIDP_STATUS_INCOMPATIBLE_REPORT_ID:
Result := RsHIDP_STATUS_INCOMPATIBLE_REPORT_ID;
HIDP_STATUS_NOT_VALUE_ARRAY:
Result := RsHIDP_STATUS_NOT_VALUE_ARRAY;
HIDP_STATUS_IS_VALUE_ARRAY:
Result := RsHIDP_STATUS_IS_VALUE_ARRAY;
HIDP_STATUS_DATA_INDEX_NOT_FOUND:
Result := RsHIDP_STATUS_DATA_INDEX_NOT_FOUND;
HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE:
Result := RsHIDP_STATUS_DATA_INDEX_OUT_OF_RANGE;
HIDP_STATUS_BUTTON_NOT_PRESSED:
Result := RsHIDP_STATUS_BUTTON_NOT_PRESSED;
HIDP_STATUS_REPORT_DOES_NOT_EXIST:
Result := RsHIDP_STATUS_REPORT_DOES_NOT_EXIST;
HIDP_STATUS_NOT_IMPLEMENTED:
Result := RsHIDP_STATUS_NOT_IMPLEMENTED;
else
Result := Format(RsUnknownHIDFmt, [RetVal]);
end;
Result := RsHIDErrorPrefix + Result;
end;
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.