0

I'm trying to have some effective code to get vendor, serial number, and model of a USB drive, using its drive letter. I've searched a lot and found several solutions. But I can not determine which one is the best one.

First one is here. Uses hid.pas from JEDI, but I don't know how to use it with a single drive. Specially, function GetHidDeviceInfo is very interesting but requires symbolic link rather that a drive letter. I tried to invite a symbolic link for the drive letter at no avail.

Second one is here. Uses WMI which doesn't seem very clean. My experience tells me that WMI doesn't work on all PCs. The code doesn't work on my own laptop, saying 'The RPC server is unavailable'.

Please advice me on the best way to achieve my goal. Are other ways around?

Update: I'm posting some sample code, combining the results of comments below.

{$APPTYPE CONSOLE}

uses
  Windows, Messages, SysUtils, Variants;

type
  PHIDDAttributes = ^THIDDAttributes;
  HIDD_ATTRIBUTES = record
    Size:          ULONG; // size of structure (set before call)
    VendorID:      Word;
    ProductID:     Word;
    VersionNumber: Word;
    //
    // Additional fields will be added to the end of this structure.
    //
  end;
  THIDDAttributes = HIDD_ATTRIBUTES;

  THIDUSBDeviceInfo = Record   { contains interface level information of each device}
    SymLink            : String;
    BufferSize         : Word;
    Handle             : THandle;
    VID                : DWord;
    PID                : DWord;
    VersionNumber      : Word;
    ManufacturerString : String;
    ProductString      : String;
    SerialNumberString : String;
end;

function GetVolumeNameForVolumeMountPointW(const lpszVolumeMountPoint: LPCWSTR;
  lpszVolumeName: LPWSTR; cchBufferLength: DWORD): BOOL; stdcall;
  external kernel32;

function HidD_GetAttributes(HidDeviceObject: THandle;
  var HidAttrs: THIDDAttributes): LongBool; stdcall;external 'hid.dll' name 'HidD_GetAttributes';
function HidD_GetManufacturerString(HidDeviceObject: THandle;
  Buffer: PWideChar; BufferLength: Integer): LongBool; stdcall;external 'hid.dll' name 'HidD_GetManufacturerString';
function HidD_GetProductString(HidDeviceObject: THandle;
  Buffer: PWideChar; BufferLength: Integer): LongBool; stdcall;external 'hid.dll' name 'HidD_GetProductString';
function HidD_GetSerialNumberString(HidDeviceObject: THandle;
  Buffer: PWideChar; BufferLength: Integer): LongBool; stdcall;external 'hid.dll' name 'HidD_GetSerialNumberString';

function GetVolumeName(Name: string): string;
var
  Volume: array [0..MAX_PATH] of Char;
begin
  FillChar(Volume[0], SizeOf(Volume), 0);
  GetVolumeNameForVolumeMountPointW(PChar(Name), @Volume[0], SizeOf(Volume));
  Result := Volume;
end;

Function GetHidDeviceInfo( Symlink : PChar) : THIDUSBDeviceInfo;
  Var
    pstr          : pWideChar;
    DevHandle    : THandle;
    HidAttrs     : THIDDAttributes;
  Begin
    FillChar(Result, SizeOf( Result), 0);
    Result.SymLink := SymLink+ #0;
    GetMem( pstr, 512);
    DevHandle := CreateFile( Symlink,
                             GENERIC_READ or GENERIC_WRITE,
                             FILE_SHARE_READ or FILE_SHARE_WRITE,
                             nil,
                             OPEN_EXISTING,
                             0,
                             0);
    If DevHandle <> INVALID_HANDLE_VALUE then
    begin
      If HidD_GetAttributes( DevHandle,HidAttrs) then
      begin
        result.VID           := HidAttrs.VendorID;
        result.PID           := HidAttrs.ProductID;
        result.VersionNumber := HidAttrs.VersionNumber;
      end;

      If HidD_GetManufacturerString( DevHandle, pstr, 512) then
        Result.ManufacturerString := pStr;

      If HidD_GetProductString( DevHandle, pstr, 512) then
        Result.ProductString := pStr;

      If HidD_GetSerialNumberString( DevHandle, pstr, 512) then
        Result.SerialNumberString := pStr;

      closeHandle( DevHandle);
    end;
    FreeMem( pStr);
  End;

procedure Main;
var
  VolumeName: string;
  info: THIDUSBDeviceInfo;
begin
  VolumeName:=GetVolumeName('i:\'); //assuming that I: is a USB drive
  info:=GetHidDeviceInfo(pchar(VolumeName));
  Writeln(info.SerialNumberString);
end;

begin
  Main;
  Readln;
end.
Community
  • 1
  • 1
Delphi.Boy
  • 1,199
  • 4
  • 17
  • 38
  • Do you really mean symbolic link? That sounds a little implausible. – David Heffernan Nov 04 '13 at 09:21
  • @DavidHeffernan Yes. Symbolic link really. Take a look at the code of that function. – Delphi.Boy Nov 04 '13 at 09:24
  • 1
    I don't think that you should regard Joop Beunders as the definitive source on this subject. Read the code more closely. Specifically this line: `SymbolicLink := PChar( @(pSpDidd^.DevicePath))` – David Heffernan Nov 04 '13 at 09:26
  • @DavidHeffernan So please help me to use that function. Thanks. – Delphi.Boy Nov 04 '13 at 18:17
  • Isn't your question just how to get the device path from the drive letter? – David Heffernan Nov 05 '13 at 07:56
  • Sure. I would highly appreciate that. – Delphi.Boy Nov 05 '13 at 08:17
  • This MSDN page shows you how: http://msdn.microsoft.com/en-us/library/cc542456(VS.85).aspx – David Heffernan Nov 05 '13 at 09:17
  • @DavidHeffernan I have a Kingston flash drive as my I: drive. `QueryDosDevice('i:', buffer, 255)` returns `\Device\HarddiskVolume8`. So, I used `GetHidDeviceInfo('\Device\HarddiskVolume8')` but it doesn't return anything. In fact, `CreateFile` returns `INVALID_HANDLE_VALUE`. What am I doing wrong? – Delphi.Boy Nov 06 '13 at 14:49
  • Surely you need to use the volume name rather than the device name? Anyway, when CreateFile returns INVALID_HANDLE_VALUE, call GetLastError to find out why. – David Heffernan Nov 06 '13 at 15:02
  • @DavidHeffernan Ok, `GetVolumeNameForVolumeMountPointString('i:\')` returns `\\?\Volume{5ef401e6-324d-11e2-b7bd-002170b0fe84}`. So, I used `GetHidDeviceInfo('\\?\Volume{5ef401e6-324d-11e2-b7bd-002170b0fe84}')` and the invalid handle issue is solved. But now `HidD_GetAttributes`, `HidD_GetManufacturerString` and `HidD_GetProductString` return `false`. Any ideas? – Delphi.Boy Nov 06 '13 at 15:20
  • Not really. Sorry. I don't have the JDEI code. Or your code. – David Heffernan Nov 06 '13 at 15:33
  • @DavidHeffernan You don't need JEDI code! It is just like: `function HidD_GetAttributes; external 'hid.dll' name 'HidD_GetAttributes';`. Thanks anyway. – Delphi.Boy Nov 06 '13 at 16:02
  • OK, maybe it's time for you to prepare an SSCCE and add it to the question. If I can run your code, then I think there's a good chance I won't be able to resist the temptation to try to make it work!! But please try to make the SSCCE as short and possible. – David Heffernan Nov 06 '13 at 16:04
  • @DavidHeffernan I added some sample code to the question. You will be able to run it easily. Thanks. – Delphi.Boy Nov 06 '13 at 16:51
  • OK, I'll try. FWIW, it's always best to give an SSCCE as a console app if possible. Then I have a single complete program that I can paste into my editor. I took the liberty of converting your code into that form. – David Heffernan Nov 06 '13 at 16:52
  • When I run your code, I get INVALID_FILE_HANDLE from CreateFile and last error 3, system cannot find the path specified – David Heffernan Nov 06 '13 at 16:57
  • Oops. Get the return value of `GetVolumeName()` and hardcode it as the parameter of `GetHidDeviceInfo()`. This way it works. It's really weird. – Delphi.Boy Nov 06 '13 at 18:02
  • You don't ever set `HidAttrs.Size`. That's a clear error. – David Heffernan Nov 06 '13 at 18:09
  • You need to strip the trailing `\\` from the volume name – David Heffernan Nov 06 '13 at 18:13
  • And actually I suspect that you do need the device path rather than the volume path. Sorry! Example code here: http://www.procstat.com/?p=122 – David Heffernan Nov 06 '13 at 18:15

1 Answers1

1

You can try to obtain SerialNumber of disk (and more information) using WMI.

Usin WMI and the Win32_DiskDrive class, you can get the Serial Number. The documentation say: "Windows Server 2003 and Windows XP: This property is not available."

In Windows Vista,7/8 works fine.

To try if this method is good for you, try this simple demo on clubdelphi ftp(source included and binary included). In Windows 7 you get information like this:

enter image description here

If you can obtain a correct serial, you can use WMI.

Good library for work with WMI is GLibWMI on Sourceforge. Include a specific component (DiskDriveInfo) that you can use with 0 code lines. See demo.

enter image description here

Regards