unit U_USB;

interface

uses
  Windows, SysUtils, Variants, Classes, ShellApi, Forms, ioctl, FastStrings;

const
  CR_SUCCESS = $00000000;

  MAX_PATH = 260;
  ANYSIZE_ARRAY         = 1;
  SetupApiModuleName    = 'SetupApi.dll';
  setupapi = 'SetupApi.dll';
  DIGCF_PRESENT         = $00000002;
  DIGCF_DEVICEINTERFACE = $00000010;
  GUID_CLASS_USBHUB:TGUID='{f18a0e88-c30c-11d0-8815-00a0c906bed8}';
  GUID_CLASS_USB_DEVICE:TGUID='{A5DCBF10-6530-11D2-901F-00C04FB951ED}';
  GUID_CLASS_USB_HOST_CONTROLLER:TGUID='{3ABF6F2D-71C4-462a-8A92-1E6861E6AF27}';
  GUID_DEVINTERFACE_VOLUME:TGUID='{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}';
  GUID_DEVINTERFACE_DISK:TGUID='{53f56307-b6bf-11d0-94f2-00a0c91efb8b}';
  GUID_DEVCLASS_DISKDRIVE: TGUID = (D1: $4D36E967; D2: $E325; D3: $11CE; D4: ($BF, $C1, $08, $00, $2B, $E1, $03, $18));
  SPDRP_DEVICEDESC                  = $00000000; // DeviceDesc (R/W)
  {$EXTERNALSYM SPDRP_DEVICEDESC}
  SPDRP_HARDWAREID                  = $00000001; // HardwareID (R/W)
  {$EXTERNALSYM SPDRP_HARDWAREID}
  SPDRP_COMPATIBLEIDS               = $00000002; // CompatibleIDs (R/W)
  {$EXTERNALSYM SPDRP_COMPATIBLEIDS}
  SPDRP_UNUSED0                     = $00000003; // unused
  {$EXTERNALSYM SPDRP_UNUSED0}
  SPDRP_SERVICE                     = $00000004; // Service (R/W)
  {$EXTERNALSYM SPDRP_SERVICE}
  SPDRP_UNUSED1                     = $00000005; // unused
  {$EXTERNALSYM SPDRP_UNUSED1}
  SPDRP_UNUSED2                     = $00000006; // unused
  {$EXTERNALSYM SPDRP_UNUSED2}
  SPDRP_CLASS                       = $00000007; // Class (R--tied to ClassGUID)
  {$EXTERNALSYM SPDRP_CLASS}
  SPDRP_CLASSGUID                   = $00000008; // ClassGUID (R/W)
  {$EXTERNALSYM SPDRP_CLASSGUID}
  SPDRP_DRIVER                      = $00000009; // Driver (R/W)
  {$EXTERNALSYM SPDRP_DRIVER}
  SPDRP_CONFIGFLAGS                 = $0000000A; // ConfigFlags (R/W)
  {$EXTERNALSYM SPDRP_CONFIGFLAGS}
  SPDRP_MFG                         = $0000000B; // Mfg (R/W)
  {$EXTERNALSYM SPDRP_MFG}
  SPDRP_FRIENDLYNAME                = $0000000C; // FriendlyName (R/W)
  {$EXTERNALSYM SPDRP_FRIENDLYNAME}
  SPDRP_LOCATION_INFORMATION        = $0000000D; // LocationInformation (R/W)
  {$EXTERNALSYM SPDRP_LOCATION_INFORMATION}
  SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0000000E; // PhysicalDeviceObjectName (R)
  {$EXTERNALSYM SPDRP_PHYSICAL_DEVICE_OBJECT_NAME}
  SPDRP_CAPABILITIES                = $0000000F; // Capabilities (R)
  {$EXTERNALSYM SPDRP_CAPABILITIES}
  SPDRP_UI_NUMBER                   = $00000010; // UiNumber (R)
  {$EXTERNALSYM SPDRP_UI_NUMBER}
  SPDRP_UPPERFILTERS                = $00000011; // UpperFilters (R/W)
  {$EXTERNALSYM SPDRP_UPPERFILTERS}
  SPDRP_LOWERFILTERS                = $00000012; // LowerFilters (R/W)
  {$EXTERNALSYM SPDRP_LOWERFILTERS}
  SPDRP_BUSTYPEGUID                 = $00000013; // BusTypeGUID (R)
  {$EXTERNALSYM SPDRP_BUSTYPEGUID}
  SPDRP_LEGACYBUSTYPE               = $00000014; // LegacyBusType (R)
  {$EXTERNALSYM SPDRP_LEGACYBUSTYPE}
  SPDRP_BUSNUMBER                   = $00000015; // BusNumber (R)
  {$EXTERNALSYM SPDRP_BUSNUMBER}
  SPDRP_ENUMERATOR_NAME             = $00000016; // Enumerator Name (R)
  {$EXTERNALSYM SPDRP_ENUMERATOR_NAME}
  SPDRP_SECURITY                    = $00000017; // Security (R/W, binary form)
  {$EXTERNALSYM SPDRP_SECURITY}
  SPDRP_SECURITY_SDS                = $00000018; // Security (W, SDS form)
  {$EXTERNALSYM SPDRP_SECURITY_SDS}
  SPDRP_DEVTYPE                     = $00000019; // Device Type (R/W)
  {$EXTERNALSYM SPDRP_DEVTYPE}
  SPDRP_EXCLUSIVE                   = $0000001A; // Device is exclusive-access (R/W)
  {$EXTERNALSYM SPDRP_EXCLUSIVE}
  SPDRP_CHARACTERISTICS             = $0000001B; // Device Characteristics (R/W)
  {$EXTERNALSYM SPDRP_CHARACTERISTICS}
  SPDRP_ADDRESS                     = $0000001C; // Device Address (R)
  {$EXTERNALSYM SPDRP_ADDRESS}
  {$IFDEF WINXP_UP}
  SPDRP_UI_NUMBER_DESC_FORMAT       = $0000001D;  // UiNumberDescFormat (R/W)
  {$EXTERNALSYM SPDRP_UI_NUMBER_DESC_FORMAT}
  SPDRP_DEVICE_POWER_DATA           = $0000001E;  // Device Power Data (R)
  {$EXTERNALSYM SPDRP_DEVICE_POWER_DATA}
  SPDRP_REMOVAL_POLICY              = $0000001F;  // Removal Policy (R)
  {$EXTERNALSYM SPDRP_REMOVAL_POLICY}
  SPDRP_REMOVAL_POLICY_HW_DEFAULT   = $00000020;  // Hardware Removal Policy (R)
  {$EXTERNALSYM SPDRP_REMOVAL_POLICY_HW_DEFAULT}
  SPDRP_REMOVAL_POLICY_OVERRIDE     = $00000021;  // Removal Policy Override (RW)
  {$EXTERNALSYM SPDRP_REMOVAL_POLICY_OVERRIDE}
  SPDRP_INSTALL_STATE               = $00000022;  // Device Install State (R)
  {$EXTERNALSYM SPDRP_INSTALL_STATE}

  SPDRP_MAXIMUM_PROPERTY            = $00000023;  // Upper bound on ordinals
  {$EXTERNALSYM SPDRP_MAXIMUM_PROPERTY}
  {$ELSE}
  SPDRP_UI_NUMBER_DESC_FORMAT       = $0000001E; // UiNumberDescFormat (R/W)
  {$EXTERNALSYM SPDRP_UI_NUMBER_DESC_FORMAT}
  SPDRP_MAXIMUM_PROPERTY            = $0000001F; // Upper bound on ordinals
  {$EXTERNALSYM SPDRP_MAXIMUM_PROPERTY}
  {$ENDIF WINXP_UP}
  SPCRP_SECURITY         = $00000017; // Security (R/W, binary form)
  {$EXTERNALSYM SPCRP_SECURITY}
  SPCRP_SECURITY_SDS     = $00000018; // Security (W, SDS form)
  {$EXTERNALSYM SPCRP_SECURITY_SDS}
  SPCRP_DEVTYPE          = $00000019; // Device Type (R/W)
  {$EXTERNALSYM SPCRP_DEVTYPE}
  SPCRP_EXCLUSIVE        = $0000001A; // Device is exclusive-access (R/W)
  {$EXTERNALSYM SPCRP_EXCLUSIVE}
  SPCRP_CHARACTERISTICS  = $0000001B; // Device Characteristics (R/W)
  {$EXTERNALSYM SPCRP_CHARACTERISTICS}
  SPCRP_MAXIMUM_PROPERTY = $0000001C; // Upper bound on ordinals
  {$EXTERNALSYM SPCRP_MAXIMUM_PROPERTY}

type
  HDEVINFON = THandle;

  HDEVINFO = Pointer;

  PSP_DEVINFO_DATA = ^SP_DEVINFO_DATA;
  PSPDevInfoData = ^TSPDevInfoData;
  SP_DEVINFO_DATA = packed record
    cbSize: DWORD;
    ClassGuid: TGUID;
    DevInst: DWORD;
    Reserved: DWORD;
  end;
  TSPDevInfoData = SP_DEVINFO_DATA;

  PSTORAGE_DEVICE_NUMBER = ^STORAGE_DEVICE_NUMBER;
  STORAGE_DEVICE_NUMBER = packed record
    DeviceType: DWORD;
    DeviceNumber: Longint;
    PartitionNumber: Longint;
  end;
  TSTORAGE_DEVICE_NUMBER = STORAGE_DEVICE_NUMBER;

type
  PSPDeviceInterfaceData = ^TSPDeviceInterfaceData;
  SP_DEVICE_INTERFACE_DATA = packed record
    cbSize: DWORD;
    InterfaceClassGuid: TGUID;
    Flags: DWORD;
    Reserved: DWORD;
  end;
  TSPDeviceInterfaceData = SP_DEVICE_INTERFACE_DATA;

type
  PSPDeviceInterfaceDetailData = ^TSPDeviceInterfaceDetailData;
  SP_DEVICE_INTERFACE_DETAIL_DATA_A = packed record
    cbSize: DWORD;
    DevicePath: array [0..ANYSIZE_ARRAY - 1] of AnsiChar;
  end;
  TSPDeviceInterfaceDetailData = SP_DEVICE_INTERFACE_DETAIL_DATA_A;

  TUSBDev = class(TObject)
  private
  public
    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;
    FDevData: TSPDevInfoData;
    FPnPHandle: HDEVINFO;
    constructor Create;
    procedure GetInfoForDev(APnPHandle: HDEVINFO; ADevData: TSPDevInfoData; ADevicePath: PChar);
    function GetRegistryPropertyDWord(PnPHandle: HDEVINFO;
      const DevData: TSPDevInfoData; Prop: DWORD): DWORD;
    function GetRegistryPropertyString(PnPHandle: HDEVINFO;
      const DevData: TSPDevInfoData; Prop: DWORD): string;
    function GetRegistryPropertyStringList(PnPHandle: HDEVINFO;
      const DevData: TSPDevInfoData; Prop: DWORD): TStringList;
  end;

function GetTempDir: string;
function GetDevNum(aPath: string): LongInt;
function USBDeviceFind(GUID:TGUID; aPhysDevObjName: string): TUSBDev;
function GetDEVID(aDevPath: string): string;
function GetDEVIDOther(aDevPath: string): string;
function IsRemoveable(aDevPath: string): Boolean;
procedure EjectDrive(aDrive: string);
procedure SelfRemoteEject;
function EnumDiskDriveVista(aDevSerial: string): string;
function USBDeviceSerial(GUID:TGUID; aPhysDevNumber: LongInt): string;

implementation

function SetupDiGetDeviceRegistryProperty(DeviceInfoSet: HDEVINFO;
    const DeviceInfoData: TSPDevInfoData; Property_: DWORD;
    var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSize: DWORD;
    var RequiredSize: DWORD): LongBool; stdcall; external SetupApiModuleName name 'SetupDiGetDeviceRegistryPropertyA';

function SetupDiGetClassDevs(ClassGuid: PGUID; const Enumerator: PAnsiChar;
    hwndParent: HWND; Flags: DWORD): HDEVINFO; stdcall; external SetupApiModuleName name 'SetupDiGetClassDevsA';

function SetupDiEnumDeviceInfo(DeviceInfoSet: HDEVINFO;
  MemberIndex: DWORD; var DeviceInfoData: TSPDevInfoData): LongBool; stdcall; external SetupApiModuleName name 'SetupDiEnumDeviceInfo';

function SetupDiEnumDeviceInterfaces(DeviceInfoSet: HDEVINFO;
  DeviceInfoData: PSPDevInfoData; const InterfaceClassGuid: TGUID;
  MemberIndex: DWORD; var DeviceInterfaceData: TSPDeviceInterfaceData): LongBool; stdcall; external SetupApiModuleName name 'SetupDiEnumDeviceInterfaces';

function SetupDiGetDeviceInterfaceDetail(DeviceInfoSet: HDEVINFO;
  DeviceInterfaceData: PSPDeviceInterfaceData;
  DeviceInterfaceDetailData: PSPDeviceInterfaceDetailData;
  DeviceInterfaceDetailDataSize: DWORD; var RequiredSize: DWORD;
  Device: PSPDevInfoData): LongBool; stdcall; external SetupApiModuleName name 'SetupDiGetDeviceInterfaceDetailA';

//function GetVolumeNameForVolumeMountPoint(volumeName: string;
//  uniqueVolumeName: PChar;
//  uniqueNameBufferCapacity: integer): LongBool; stdcall; external 'kernel32.dll' name 'GetVolumeNameForVolumeMountPointA';

function SetupDiDestroyDeviceInfoList(DeviceInfoSet: HDEVINFO): LongBool; stdcall; external SetupApiModuleName name 'SetupDiDestroyDeviceInfoList';

function GetVolumeNameForVolumeMountPoint(lpszVolumeMountPoint: LPCSTR;
  lpszVolumeName: LPSTR; cchBufferLength: DWORD): BOOL; stdcall; external 'kernel32.dll' name 'GetVolumeNameForVolumeMountPointA';

function CM_Get_Parent(pdnDevInst: PDWORD; dnDevInst: DWORD; ulFlags: DWORD): DWORD; stdcall; external setupapi;
function CM_Get_Device_ID_Size(pulLen: PDWORD; dnDevInst: DWORD; ulFlags: DWORD): DWORD; stdcall; external setupapi;
function CM_Get_Device_IDA(dnDevInst: DWORD; Buffer: PChar; BufferLen: DWORD; ulFlags: DWORD): DWORD; stdcall; external setupapi;
function CM_Locate_DevNodeA(pdnDevInst: PDWORD; pDeviceID: PChar; ulFlags: DWORD): DWORD; stdcall; external setupapi;
function CM_Request_Device_EjectA(dnDevInst: DWORD; pVetoType: Pointer; pszVetoName: PChar; ulNameLength: DWORD;
ulFlags: DWORD): DWORD; stdcall; external setupapi;
function CM_Query_And_Remove_SubTree(dnDevInst: DWORD; pVetoType: Pointer; pszVetoName: PChar; ulNameLength: DWORD;
ulFlags: DWORD): DWORD; stdcall; external setupapi name 'CM_Query_And_Remove_SubTreeA';


function SetupDiGetClassDevsA(ClassGuid: PGUID; Enumerator: PChar; hwndParent: HWND; Flags: DWORD): HDEVINFON; stdcall; external setupapi name 'SetupDiGetClassDevsA';
function SetupDiEnumDeviceInfoN(DeviceInfoSet: HDEVINFON; MemberIndex: DWORD; DeviceInfoData: PSP_DEVINFO_DATA): boolean; stdcall; external setupapi name 'SetupDiEnumDeviceInfo';
function SetupDiDestroyDeviceInfoListN(DeviceInfoSet: HDEVINFON): boolean; stdcall; external setupapi name 'SetupDiDestroyDeviceInfoList';


function GetDeviceName(aDevInst: THandle): string;
var
  IDLen: Integer;
begin
  Result := '';
  if (CM_Get_Device_ID_Size(@IDLen, aDevInst, 0) = CR_SUCCESS) and (IDLen > 0) then begin
    SetLength(Result, IDLen);
    if CM_Get_Device_IDA(aDevInst, PChar(Result), IDLen + 1, 0) <> CR_SUCCESS then
      Result := ''
  end
end;

// при создании лицензии передается результаты функции GetDEVIDOther(aDrive);
// при проверке - передается из зашифрованого файлика строчка.
function EnumDiskDriveVista(aDevSerial: string): string;
var
  hDevInfoSet: HDEVINFON;
  DevInfo: SP_DEVINFO_DATA;
  i: Integer;
  str1: string;
begin
  result := '';
  DevInfo.cbSize := sizeof(SP_DEVINFO_DATA);
  hDevInfoSet := SetupDiGetClassDevsA(@GUID_DEVCLASS_DISKDRIVE, nil, 0, 2);
  i := 0;
  if hDevInfoSet <> INVALID_HANDLE_VALUE then
  begin
    while (SetupDiEnumDeviceInfoN(hDevInfoSet, i, @DevInfo)) do begin
      str1 := GetDeviceName(DevInfo.DevInst);
      if pos(aDevSerial, AnsiLowerCase(str1)) > 0 then
      begin
        result := AnsiLowerCase(str1);
        break;
      end;
      Inc(i)
    end;
    SetupDiDestroyDeviceInfoListN(hDevInfoSet);
  end
end;


{ TUSBDev }

constructor TUSBDev.Create;
begin
  inherited;
end;

procedure TUSBDev.GetInfoForDev(APnPHandle: HDEVINFO; ADevData: TSPDevInfoData; ADevicePath: PChar);
begin
  FDeviceID := ADevData.DevInst;
  FDevicePath := ADevicePath;

  FDevData := ADevData;
  FPnPHandle := APnPHandle;

  // 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;

function TUSBDev.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;

function TUSBDev.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 TUSBDev.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 GetTempDir: string;
var
  buff: pchar;
  s: string;
begin
  getmem(buff, 1255);
  s := '';
  if (GetEnvironmentVariable('tmp', buff, 1254) > 0)
    and (DirectoryExists(buff)) then
  begin
    s := buff;
  end
  else
  if (GetEnvironmentVariable('temp',buff, 1254) >0)
    and (DirectoryExists(buff)) then
  begin
    s := buff;
  end
  else
    s := 'c:\';
  freemem(buff);
  result := s;
end;

function USBDeviceFind(GUID:TGUID; aPhysDevObjName: string): TUSBDev;
var
  PnPHandle:               HDEVINFO;
  DevData:                 TSPDevInfoData;
  DeviceInterfaceData:     TSPDeviceInterfaceData;
  FunctionClassDeviceData: PSPDeviceInterfaceDetailData;
  Success:                 LongBool;
  Devn:                    Integer;
  BytesReturned:           DWORD;
  aUSBDev: TUSBDev;

begin
  try
    Result := nil;

    PnPHandle:= SetupDiGetClassDevs(@Guid, nil, 0,DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);

    if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then
      Exit;
    try
      Devn := 0;
      repeat
        DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
        Success := SetupDiEnumDeviceInterfaces(PnPHandle, nil, Guid, Devn, DeviceInterfaceData);
        if Success then
        begin
          DevData.cbSize := SizeOf(SP_DEVINFO_DATA);
          DeviceInterfaceData.cbSize := SizeOf(SP_DEVICE_INTERFACE_DATA);
          BytesReturned  := 0;
          SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, nil, 0, BytesReturned, @DevData);
          if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
          begin
            FunctionClassDeviceData := AllocMem(BytesReturned);
           try
            FunctionClassDeviceData.cbSize := 5;
            if SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, FunctionClassDeviceData, BytesReturned, BytesReturned, @DevData) then
            begin
              aUSBDev := TUSBDev.Create;
              aUSBDev.GetInfoForDev(PnPHandle, DevData, @FunctionClassDeviceData.DevicePath);
              if aPhysDevObjName = aUSBDev.FPhysDevObjName then
              begin
                result := aUSBDev;
                break;
              end
              else
                aUSBDev.Free;
            end;
              Inc(Devn);
           finally
            FreeMem(FunctionClassDeviceData);
           end;
          end;
        end;
      until not Success;
    finally
      if result = nil then
        SetupDiDestroyDeviceInfoList(PnPHandle);
    end;
  finally
  end;
end;

function USBDeviceEject(GUID:TGUID; aPhysDevNumber: LongInt): Boolean;
var
  PnPHandle:               HDEVINFO;
  DevData:                 TSPDevInfoData;
  DeviceInterfaceData:     TSPDeviceInterfaceData;
  FunctionClassDeviceData: PSPDeviceInterfaceDetailData;
  Success:                 LongBool;
  Devn:                    Integer;
  BytesReturned:           DWORD;
  i:                       integer;

  aUSBDev: TUSBDev;
  Parent: DWORD;
  VetoName: PChar;
begin
  try
    Result := False;

    PnPHandle:= SetupDiGetClassDevs(@Guid, nil, 0,DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);

    if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then
      Exit;
    try
      Devn := 0;
      repeat
        DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
        Success := SetupDiEnumDeviceInterfaces(PnPHandle, nil, Guid, Devn, DeviceInterfaceData);
        if Success then
        begin
          DevData.cbSize := SizeOf(SP_DEVINFO_DATA);
          DeviceInterfaceData.cbSize := SizeOf(SP_DEVICE_INTERFACE_DATA);
          BytesReturned  := 0;
          SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, nil, 0, BytesReturned, @DevData);
          if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
          begin
            FunctionClassDeviceData := AllocMem(BytesReturned);
           try
            FunctionClassDeviceData.cbSize := 5;
            if SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, FunctionClassDeviceData, BytesReturned, BytesReturned, @DevData) then
            begin
              aUSBDev := TUSBDev.Create;
              aUSBDev.GetInfoForDev(PnPHandle, DevData, @FunctionClassDeviceData.DevicePath);
              if aPhysDevNumber = GetDevNum(aUSBDev.FDevicePath) then
              begin
                if CM_Get_Parent(@Parent, aUSBDev.FDevData.DevInst, 0) = 0 then
                begin
                  sleep(200);
                  VetoName := GetMemory(260);
//                    if (CM_Request_Device_EjectA(aUSBDev.FDevData.DevInst, nil, VetoName, 260, 0) <> 0) then
//                    if (CM_Query_And_Remove_SubTree(aUSBDev.FDevData.DevInst, nil, VetoName, 260, 0) <> 0) then
//                    if (CM_Query_And_Remove_SubTree(Parent, nil, VetoName, 260, 0) <> 0) then
                  if (CM_Request_Device_EjectA(Parent, nil, VetoName, 260, 0) <> 0) then
                  begin
                    if (CM_Locate_DevNodeA(@Parent, VetoName, 0) <> 0) then
                    begin
                      FreeMemory(VetoName);
                      exit;
                    end;
                    FreeMemory(VetoName);
                    if (CM_Request_Device_EjectA(Parent, nil, nil, 0, 0) <> 0) then
                      exit
                    else
                      result := True;
                  end
                  else
                    result := True;
                  FreeMemory(VetoName);
                end;
                aUSBDev.Free;
                break;
              end
              else
                aUSBDev.Free;
            end;
              Inc(Devn);
           finally
            FreeMem(FunctionClassDeviceData);
           end;
          end;
        end;
      until not Success;
    finally
    end;
  finally
    SetupDiDestroyDeviceInfoList(PnPHandle);
  end;
end;

function USBDeviceSerial(GUID:TGUID; aPhysDevNumber: LongInt): string;
var
  PnPHandle:               HDEVINFO;
  DevData:                 TSPDevInfoData;
  DeviceInterfaceData:     TSPDeviceInterfaceData;
  FunctionClassDeviceData: PSPDeviceInterfaceDetailData;
  Success:                 LongBool;
  Devn:                    Integer;
  BytesReturned:           DWORD;
  i:                       integer;

  aUSBDev: TUSBDev;
  Parent: DWORD;
  VetoName: PChar;
begin
  try
    Result := '';

    PnPHandle:= SetupDiGetClassDevs(@Guid, nil, 0,DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);

    if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then
      Exit;
    try
      Devn := 0;
      repeat
        DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
        Success := SetupDiEnumDeviceInterfaces(PnPHandle, nil, Guid, Devn, DeviceInterfaceData);
        if Success then
        begin
          DevData.cbSize := SizeOf(SP_DEVINFO_DATA);
          DeviceInterfaceData.cbSize := SizeOf(SP_DEVICE_INTERFACE_DATA);
          BytesReturned  := 0;
          SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, nil, 0, BytesReturned, @DevData);
          if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
          begin
            FunctionClassDeviceData := AllocMem(BytesReturned);
           try
            FunctionClassDeviceData.cbSize := 5;
            if SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, FunctionClassDeviceData, BytesReturned, BytesReturned, @DevData) then
            begin
              aUSBDev := TUSBDev.Create;
              aUSBDev.GetInfoForDev(PnPHandle, DevData, @FunctionClassDeviceData.DevicePath);
              if aPhysDevNumber = GetDevNum(aUSBDev.FDevicePath) then
              begin
                result := aUSBDev.FDevicePath;
                aUSBDev.Free;
                break;
              end
              else
                aUSBDev.Free;
            end;
              Inc(Devn);
           finally
            FreeMem(FunctionClassDeviceData);
           end;
          end;
        end;
      until not Success;
    finally
    end;
  finally
    SetupDiDestroyDeviceInfoList(PnPHandle);
  end;
end;

function GetDevNum(aPath: string): LongInt;
var
  hFile: THandle;
  DevNum: TSTORAGE_DEVICE_NUMBER;
  tmp: Dword;
begin
  result := 0;
  hFile := CreateFile(PChar(aPath), 0 {GENERIC_READ} { $C0}, FILE_SHARE_READ OR FILE_SHARE_WRITE {4}, nil, OPEN_EXISTING {3}, 0 {FILE_ATTRIBUTE_NORMAL}, 0);
//  hFile := CreateFile(PChar(aPath), $C0, 4, nil, 3, FILE_ATTRIBUTE_NORMAL, 0);
  if hFile > 0 then
  begin
    DeviceIoControl(hFile, IOCTL_STORAGE_GET_DEVICE_NUMBER, nil, 0, @DevNum, sizeof(DevNum), Tmp, nil);
    Result := DevNum.DeviceNumber;
  end;
  CloseHandle(hFile);
end;

function GetDEVID(aDevPath: string): string;
var
  TempStr: string;
  ResDosDev : Array [1..MAX_PATH] of Char;
  aUSBDev: TUSBDev;
  DevNumber: LongInt;
begin
  result := '';
  try
    If (QueryDosDevice(PChar(aDevPath), @ResDosDev, MAX_PATH) <> 0) then
    begin
      TempStr := StrPas(@ResDosDev);
      aUSBDev := USBDeviceFind(GUID_DEVINTERFACE_VOLUME, TempStr);
      if assigned(aUSBDev) then
      begin
        if (Pos('removablemedia', AnsiLowerCase(aUSBDev.FDevicePath)) > 0) or (Pos('usbstor', AnsiLowerCase(aUSBDev.FDevicePath)) > 0) then
        begin
          DevNumber := GetDevNum(aUSBDev.FDevicePath);
          tempstr := AnsiLowerCase(USBDeviceSerial(GUID_DEVINTERFACE_DISK, DevNumber));
        end;
//        tempstr := AnsiLowerCase(aUSBDev.FDevicePath);
        tempstr := copy(tempstr, pos('&rev', tempstr), pos('{', tempstr) - pos('&rev', tempstr) - 1);
        tempstr := FastReplace(tempstr, '#', '\');
        result := tempstr;
        SetupDiDestroyDeviceInfoList(aUSBDev.FPnPHandle);
        aUSBDev.Free;
      end;
    end;
  except
  end;
end;

function GetDEVIDOther(aDevPath: string): string;
var
  TempStr: string;
  ResDosDev : Array [1..MAX_PATH] of Char;
  aUSBDev: TUSBDev;
  DevNumber: LongInt;
begin
  result := '';
  try
    If (QueryDosDevice(PChar(aDevPath), @ResDosDev, MAX_PATH) <> 0) then
    begin
      TempStr := StrPas(@ResDosDev);
      DevNumber := GetDevNum('\\?' + TempStr);
      result := USBDeviceSerial(GUID_DEVINTERFACE_DISK, DevNumber);
      if result <> '' then
      begin
        if Pos('usbstor', AnsiLowerCase(result)) <= 0 then
          result := '';
      end;
    end;
  except
  end;
end;

function IsRemoveable(aDevPath: string): Boolean;
var
  TempStr: string;
  ResDosDev : Array [1..MAX_PATH] of Char;
  aUSBDev: TUSBDev;
  DevNumber: LongInt;
begin
  try
    result := False;
    If (QueryDosDevice(PChar(aDevPath), @ResDosDev, MAX_PATH) <> 0) then
    begin
      TempStr := StrPas(@ResDosDev);
      aUSBDev := USBDeviceFind(GUID_DEVINTERFACE_VOLUME, TempStr);
      if assigned(aUSBDev) then
      begin
        if Pos('removablemedia', AnsiLowerCase(aUSBDev.FDevicePath)) > 0 then
        begin
          Result := True;
        end;
        SetupDiDestroyDeviceInfoList(aUSBDev.FPnPHandle);
        aUSBDev.Free;
      end;
    end;
  except
  end;  
end;

procedure EjectDrive(aDrive: string);
var
  aUSBDev: TUSBDev;
  DevNumber: LongInt;
  ResDosDev : Array [1..MAX_PATH] of Char;
  TempStr: string;
begin
  try
    If (QueryDosDevice(PChar(aDrive), @ResDosDev, MAX_PATH) <> 0) then
    begin
      sleep(500);
      TempStr := StrPas(@ResDosDev);
      aUSBDev := USBDeviceFind(GUID_DEVINTERFACE_VOLUME, TempStr);
      if assigned(aUSBDev) then
      begin
        if Pos('removablemedia', AnsiLowerCase(aUSBDev.FDevicePath)) > 0 then
        begin
          DevNumber := GetDevNum(aUSBDev.FDevicePath);
          USBDeviceEject(GUID_DEVINTERFACE_DISK, DevNumber);
        end;
        SetupDiDestroyDeviceInfoList(aUSBDev.FPnPHandle);
        aUSBDev.Free;
      end;
    end;
  except
  end;
end;

procedure SelfRemoteEject;
var
  TempStr1: string;
  Handle: THandle;
begin
  try
    TempStr1 := ExtractFileDir(Application.ExeName);
    DeleteFile(PChar(GetTempDir + '\EjectUSB.exe'));
//    if Not FileExists(GetTempDir + '\EjectUSB.exe') then
    CopyFile(PChar(TempStr1 + '\EjectUSB.exe'), PChar(GetTempDir + '\EjectUSB.exe'), False);
    TempStr1 := copy(TempStr1, 1, 2);
//    sleep(100);
    Handle := GetDesktopWindow;
    ShellExecute(Handle, PChar('open'), PChar(GetTempDir + '\EjectUSB.exe'), PChar(TempStr1), PChar(GetTempDir), SW_HIDE);
  except
  end;
end;


end.
