⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unit2.pas

📁 基于DELPHI的API
💻 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 + -