📄 unit2.pas
字号:
// this sample program illustrates the way from the plug event(s) on
// application level via the registry entries to the Hid-functions
// to talk to the device.
//
// On the way it unhides some nice problems of Windows.
// You get a plug message for the usb device AND for this same device as Hid device.
// There is no simple way to match these events.
// The bookkeeping message of Windows (DEVNODES_CHANGED) follows!
// Between these messages Windows is theoretically instable, because you cannot
// access the device when you get the plug event.
// If you look harder then you can even see that Windows does its bookkeeping
// between the two plug events but does not send the bookkeeping event there.
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Registry, Menus,
DBT;
type
// a bag to collect the info on HID devices
THidDeviceData = record
VendorID : Word;
ProductID : Word;
HardWareKey : String;
VendorName : String;
ProductName : String;
SerialNumber: String;
PhysDescr : String;
DevicePath : String;
end;
// the simple Form we show
TDevEventForm = class(TForm)
ListBox1 : TListBox;
MainMenu : TMainMenu;
QuitMenuItem: TMenuItem;
FileMenu : TMenuItem;
procedure QuitMenuItemClick(Sender: TObject);
private
{ Private declarations }
public
procedure WMDeviceChange(var msg: TWMDeviceChange); message WM_DEVICECHANGE;
{ Public declarations }
end;
var
DevEventForm: TDevEventForm;
implementation
uses Hid, SetupAPI;
{$R *.DFM}
// gets the registry key which points to further information
// from HKEY_DYN_DATA\Config Manager\Enum\<DevID>
//
// <DevID> we get from the structure brought to us by the plug event
// follow the registry keys for yourself with REGEDIT to see
// what Windows stores in the registry for the device
function DevIDToHardWareKey(DevID: DWORD): String;
var
reg: TRegistry;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_DYN_DATA;
reg.OpenKey('Config Manager\Enum\' + Format('%.8x',[DevID]),False);
DevIDToHardWareKey := reg.ReadString('HardWareKey');
reg.CloseKey;
end;
// follows HardWareKey (see above) to
// HKEY_LOCAL_MACHINE\Enum\<HardWareKey> where more
// info for the device (interface) is stored
//
// the description (DeviceDescr) stored there is always from the INF script
// or generated by Windows no info from the USB device is used
function HardWareKeyToDeviceDescr(key: String): String;
var
reg: TRegistry;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey('Enum\' + key,False);
HardWareKeyToDeviceDescr := reg.ReadString('DeviceDesc');
reg.CloseKey;
end;
// extracts a 4 digit hex number from the <HardWareKey>
// prefix is a substring in key which precedes the number
function ExtractID(prefix: String; key: String): Integer;
begin
ExtractID := StrToInt('$'+Copy(key,Pos(prefix,key)+Length(prefix),4));
end;
// extracts the device type ('HID', 'USB' etc) from <HardWareKey>
function ExtractType(key: String): String;
begin
ExtractType := Copy(key,1,Pos('\',key)-1);
end;
// here the SetupDi* and some of the HidD_* functions are used
// to collect the info on the plugged devices
// this is the sample for using all the HidD_ and HidP_ functions
procedure DeviceFillIn(var HidDeviceData: THidDeviceData; key: String);
var
HidGuid: TGUID;
PnPHandle: HDEVINFO;
HidHandle: THandle;
DeviceInterfaceData: TSPDeviceInterfaceData;
FunctionClassDeviceData: PSPDeviceInterfaceDetailData;
ThisHidDevice: THIDDAttributes;
Success: LongBool;
Success1: LongBool;
devn: Integer;
BytesReturned: DWORD;
buffer: array [0..255] of WideChar;
buf: array [0..255] of Char;
begin
// initialize our bag of info
with HidDeviceData do
begin
HardWareKey := key;
VendorID := ExtractID('VID_',key);
ProductID := ExtractID('PID_',key);
VendorName := '';
ProductName := '';
SerialNumber := '';
PhysDescr := '';
DevicePath := '';
end;
// First, get class identifier
HidD_GetHidGuid(HidGuid);
// Get a handle for the Plug and Play node and request currently active HID devices
PnPHandle := SetupDiGetClassDevs(@HidGuid,Nil,0,DIGCF_PRESENT OR DIGCF_DEVICEINTERFACE);
if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then
begin
ShowMessage('Could not attach to PnP node');
Exit;
end;
devn := 0;
repeat
DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
// Is there a HID device at this table entry
Success := SetupDiEnumDeviceInterfaces(PnPHandle,Nil,HidGuid,devn,DeviceInterfaceData);
if Success then
begin
FunctionClassDeviceData := nil;
BytesReturned := 0;
Success1 := SetupDiGetDeviceInterfaceDetail(PnPHandle,@DeviceInterfaceData,nil,0,@BytesReturned,nil);
if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
begin
FunctionClassDeviceData := AllocMem(BytesReturned);
FunctionClassDeviceData.cbSize := 5;
Success1 := SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, FunctionClassDeviceData, BytesReturned, @BytesReturned, nil);
end;
if not Success1 then
begin
ShowMessage('Could not find the system name for this HID device');
Exit;
end;
HidHandle := CreateFile(FunctionClassDeviceData.DevicePath, GENERIC_READ OR GENERIC_WRITE,FILE_SHARE_READ OR FILE_SHARE_WRITE, Nil,OPEN_EXISTING,0,0);
if HidHandle <> INVALID_HANDLE_VALUE then
begin
ThisHidDevice.Size := SizeOf(THIDDAttributes);
HidD_GetAttributes(HidHandle,ThisHidDevice);
if (ThisHidDevice.VendorID = HidDeviceData.VendorID) and
(ThisHidDevice.ProductID = HidDeviceData.ProductID) then
begin
// collect the info
HidDeviceData.DevicePath := FunctionClassDeviceData.DevicePath;
if HidD_GetManufacturerString(HidHandle,buffer,SizeOf(buffer)) then
HidDeviceData.VendorName := WideCharToString(buffer);
if HidD_GetProductString(HidHandle,buffer,SizeOf(buffer)) then
HidDeviceData.ProductName := WideCharToString(buffer);
if HidD_GetSerialNumberString(HidHandle,buffer,SizeOf(buffer)) then
HidDeviceData.SerialNumber := WideCharToString(buffer);
if HidD_GetPhysicalDescriptor(HidHandle,buf,SizeOf(buf)) then
HidDeviceData.PhysDescr := buf;
end;
CloseHandle(HidHandle);
end
else
ShowMessage('could not open DevicePath');
Inc(devn);
FreeMem(FunctionClassDeviceData);
end;
until not Success;
SetupDiDestroyDeviceInfoList(PnPHandle);
end;
// catch the WM_DEVICECHANGE event
procedure TDevEventForm.WMDeviceChange(var msg: TWMDeviceChange);
var
HidDeviceData: THidDeviceData;
p: string;
begin
// we are only interested in plug and unplug events
case msg.Event of
DBT_DEVICEARRIVAL:
begin
// msg.Device points to a TDevBroadcastHeader structure
// if DevType = DBT_DEVTYP_DEVNODE then it is a PTDevBroadcastDevnode structure
if PDevBroadcastHdr(msg.dwData)^.dbch_devicetype = DBT_DEVTYP_DEVNODE then
begin
// this may fail since the DEVNODE is not guaranteed to be created yet
DeviceFillIn(HidDeviceData,DevIDToHardWareKey(PDevBroadcastDevNode(msg.dwData)^.dbcd_devnode));
p := 'Plug of ' + ExtractType(HidDeviceData.HardWareKey) + ': ';
p := p + Format('VID=%x PID=%x ',[HidDeviceData.VendorID,HidDeviceData.ProductID]);
// get description from registry
p := p + HardWareKeyToDeviceDescr(HidDeviceData.HardWareKey);
// if DeviceFillIn succeeded and a VendorName is in the hardware
// add a better description
if HidDeviceData.VendorName <> '' then
p := p + ' (' + HidDeviceData.VendorName + ': ' + HidDeviceData.ProductName + ')';
ListBox1.Items.Add(p);
end;
end;
DBT_DEVICEREMOVECOMPLETE:
begin
if PDevBroadcastHdr(msg.dwData)^.dbch_devicetype = DBT_DEVTYP_DEVNODE then
begin
// this may fail since the DEVNODE may be killed already
DeviceFillIn(HidDeviceData,DevIDToHardWareKey(PDevBroadcastDevNode(msg.dwData)^.dbcd_devnode));
p := 'Unplug of ' + ExtractType(HidDeviceData.HardWareKey) + ': ';
p := p + Format('VID=%x PID=%x ',[HidDeviceData.VendorID,HidDeviceData.ProductID]);
// get description from registry
p := p + HardWareKeyToDeviceDescr(HidDeviceData.HardWareKey);
// if DeviceFillIn succeeded and a VendorName is in the hardware
// add a better description
if HidDeviceData.VendorName <> '' then
p := p + ' (' + HidDeviceData.VendorName + ': ' + HidDeviceData.ProductName + ')';
ListBox1.Items.Add(p);
end;
end;
DBT_DEVNODES_CHANGED:
// only here the DEVNODE is in existence
// this is a simple sample
// for correct function a list of the DBT_DEVICEARRIVALs
// has to be created and DeviceFillIn called here on them
ListBox1.Items.Add('DEVNODES_CHANGED');
end;
end;
procedure TDevEventForm.QuitMenuItemClick(Sender: TObject);
begin
Close;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -