如何获取 USB 闪存驱动器的制造商序列号?

Posted

技术标签:

【中文标题】如何获取 USB 闪存驱动器的制造商序列号?【英文标题】:How to get manufacturer serial number of an USB flash drive? 【发布时间】:2011-05-16 14:20:22 【问题描述】:

如何在 Delphi 中检索 USB 闪存驱动器的制造商序列号?

我试过这个:

function GetDiskVolSerialID(ADriveName: Char): Cardinal;
var
  DiskDrive: string;
  FileSystemFlags: DWORD;
  VolumeSerialNumber: DWORD;
  MaximumComponentLength: DWORD;
begin
  DiskDrive := ADriveName + ':\';
  GetVolumeInformation(PChar(DiskDrive),
                       nil,
                       0,
                       @VolumeSerialNumber,
                       MaximumComponentLength,
                       FileSystemFlags,
                       nil,
                       0);
  Result := VolumeSerialNumber;
end;

但它没有返回正确的结果!

【问题讨论】:

硬盘格式化时要windows分配的序列号还是厂家的序列号? 厂商的序列号! WMI 的创建是为了方便访问包括硬件在内的信息系统,WMI 是完成此类任务的完美工具,因为使用起来非常简单。我不知道您不使用 WMI 的动机是什么。你能解释一下吗? 在delphi中很难实现我试过但如果你有一些代码失败了我会很感激;) 我也同意 RRUZ。您同意删除 WMI 约束吗?如果不是,还有其他原因吗?你请求帮助解决一个人为的要求,这可能会使它变得更难,只是因为你认为它应该更容易。 【参考方案1】:

opc0de,根据你的 cmets,我会给你一个使用 WMI 的示例。

首先,您发布的代码(使用GetVolumeInformation 函数)返回格式化磁盘时Windows分配的序列号。

好消息是存在两个 wmi 类,它们公开了一个名为 SerialNumber 的属性,其中存储了 the Number allocated by the manufacturer to identify the physical media. 这些类是 Win32_DiskDriveWin32_PhysicalMedia

现在有个坏消息,不幸的是这个类与逻辑磁盘的字母(C,D,E,F...)没有直接关联,因为你必须调用另一个 wmi 类来找到它们之间的链接逻辑驱动器号和物理驱动器。

因此您必须先找到此链接才能获取序列号。找到这个关联的顺序是这样的。

Win32_DiskPartition -> Win32_LogicalDiskToPartition -> Win32_DiskDrive

这是使用Win32_DiskDrive 类获取USB 序列号的代码。

program GetWMI_Info;

$APPTYPE CONSOLE

uses
  SysUtils,
  StrUtils,
  ActiveX,
  ComObj,
  Variants;

function VarArrayToStr(const vArray: variant): string;

    function _VarToStr(const V: variant): string;
    var
    Vt: integer;
    begin
    Vt := VarType(V);
        case Vt of
          varSmallint,
          varInteger  : Result := IntToStr(integer(V));
          varSingle,
          varDouble,
          varCurrency : Result := FloatToStr(Double(V));
          varDate     : Result := VarToStr(V);
          varOleStr   : Result := WideString(V);
          varBoolean  : Result := VarToStr(V);
          varVariant  : Result := VarToStr(Variant(V));
          varByte     : Result := char(byte(V));
          varString   : Result := String(V);
          varArray    : Result := VarArrayToStr(Variant(V));
        end;
    end;

var
i : integer;
begin
    Result := '[';
     if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);

    Result:=Result+']';
end;

function VarStrNull(const V:OleVariant):string; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=VarToStr(V);
  end;
end;


function GetWMIObject(const objectName: String): IDispatch; //create the Wmi instance
var
  chEaten: Integer;
  BindCtx: IBindCtx;
  Moniker: IMoniker;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
  OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;



function GetUsbDriveSerial(const Drive:AnsiChar):string;
var
  objWMIService  : OLEVariant;
  colDiskDrives  : OLEVariant;
  colLogicalDisks: OLEVariant;
  colPartitions  : OLEVariant;
  objDiskDrive   : OLEVariant;
  objPartition   : OLEVariant;
  objLogicalDisk : OLEVariant;
  oEnumDiskDrive : IEnumvariant;
  oEnumPartition : IEnumvariant;
  oEnumLogical   : IEnumvariant;
  iValue         : LongWord;
  DeviceID       : string;
begin;
  Result:='';
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2'); //Connect to the WMI
  //colDiskDrives := objWMIService.ExecQuery('SELECT DeviceID,SerialNumber FROM Win32_DiskDrive WHERE InterfaceType="USB"','WQL',0); 
  colDiskDrives := objWMIService.ExecQuery('SELECT * FROM Win32_DiskDrive WHERE InterfaceType="USB"','WQL',0);
  oEnumDiskDrive:= IUnknown(colDiskDrives._NewEnum) as IEnumVariant;
  while oEnumDiskDrive.Next(1, objDiskDrive, iValue) = 0 do
  begin
     DeviceID        := StringReplace(VarStrNull(objDiskDrive.DeviceID),'\','\\',[rfReplaceAll]); //Escape the `\` chars in the DeviceID value because the '\' is a reserved character in WMI.
     colPartitions   := objWMIService.ExecQuery(Format('ASSOCIATORS OF Win32_DiskDrive.DeviceID="%s" WHERE AssocClass = Win32_DiskDriveToDiskPartition',[DeviceID]));//link the Win32_DiskDrive class with the Win32_DiskDriveToDiskPartition class 
     oEnumPartition  := IUnknown(colPartitions._NewEnum) as IEnumVariant;
      while oEnumPartition.Next(1, objPartition, iValue) = 0 do
       begin
            colLogicalDisks := objWMIService.ExecQuery('ASSOCIATORS OF Win32_DiskPartition.DeviceID="'+VarStrNull(objPartition.DeviceID)+'" WHERE AssocClass = Win32_LogicalDiskToPartition'); //link the Win32_DiskPartition class with theWin32_LogicalDiskToPartition class.
            oEnumLogical  := IUnknown(colLogicalDisks._NewEnum) as IEnumVariant;
              while oEnumLogical.Next(1, objLogicalDisk, iValue) = 0 do
              if VarStrNull(objLogicalDisk.DeviceID)=(Drive+':')  then //compare the device id
              begin
                  Result:=VarStrNull(objDiskDrive.SerialNumber);
                  Exit;
              end;
       end;
  end;
end;

begin
 try
    CoInitialize(nil);
    try
      Writeln(GetUsbDriveSerial('F'));
      Readln;
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

顺便说一下,前段时间我写了一个名为WMI Delphi Code Creator 的应用程序,它可以帮助您生成delphi 代码以使用WMI 访问系统信息。

更新

某些 USB 磁盘驱动程序不会在 Win32_DiskDrive.SerialNumber 属性中公开制造商序列号,因此在这种情况下,您可以从 PnPDeviceID 属性中提取序列号。

检查此示例代码。

$APPTYPE CONSOLE

uses
  SysUtils,
  StrUtils,
  ActiveX,
  ComObj,
  Variants;


function VarStrNull(const V:OleVariant):string; //avoid issues with null variants
begin
  Result:='';
  if not VarIsNull(V) then
    Result:=VarToStr(V);
end;


function GetUsbDriveSerial(const Drive:AnsiChar):string;
var
 FSWbemLocator   : OleVariant;
  objWMIService  : OLEVariant;
  colDiskDrives  : OLEVariant;
  colLogicalDisks: OLEVariant;
  colPartitions  : OLEVariant;
  objDiskDrive   : OLEVariant;
  objPartition   : OLEVariant;
  objLogicalDisk : OLEVariant;
  oEnumDiskDrive : IEnumvariant;
  oEnumPartition : IEnumvariant;
  oEnumLogical   : IEnumvariant;
  iValue         : LongWord;
  DeviceID       : string;
begin;
  Result:='';
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  objWMIService := FSWbemLocator.ConnectServer('.', 'root\CIMV2', '', '');
  colDiskDrives := objWMIService.ExecQuery('SELECT * FROM Win32_DiskDrive WHERE InterfaceType="USB"','WQL',0);
  oEnumDiskDrive:= IUnknown(colDiskDrives._NewEnum) as IEnumVariant;
  while oEnumDiskDrive.Next(1, objDiskDrive, iValue) = 0 do
  begin
     DeviceID        := StringReplace(VarStrNull(objDiskDrive.DeviceID),'\','\\',[rfReplaceAll]); //Escape the `\` chars in the DeviceID value because the '\' is a reserved character in WMI.
     colPartitions   := objWMIService.ExecQuery(Format('ASSOCIATORS OF Win32_DiskDrive.DeviceID="%s" WHERE AssocClass = Win32_DiskDriveToDiskPartition',[DeviceID]));//link the Win32_DiskDrive class with the Win32_DiskDriveToDiskPartition class
     oEnumPartition  := IUnknown(colPartitions._NewEnum) as IEnumVariant;
      while oEnumPartition.Next(1, objPartition, iValue) = 0 do
       begin
        colLogicalDisks := objWMIService.ExecQuery('ASSOCIATORS OF Win32_DiskPartition.DeviceID="'+VarStrNull(objPartition.DeviceID)+'" WHERE AssocClass = Win32_LogicalDiskToPartition'); //link the Win32_DiskPartition class with theWin32_LogicalDiskToPartition class.
        oEnumLogical  := IUnknown(colLogicalDisks._NewEnum) as IEnumVariant;
          while oEnumLogical.Next(1, objLogicalDisk, iValue) = 0 do
          begin
            if  SameText(VarStrNull(objLogicalDisk.DeviceID),Drive+':')  then //compare the device id
            begin
              Result:=VarStrNull(objDiskDrive.PnPDeviceID);
              if AnsiStartsText('USBSTOR', Result) then
              begin
               iValue:=LastDelimiter('\', Result);
                Result:=Copy(Result, iValue+1, Length(Result));
              end;
              objLogicalDisk:=Unassigned;
              Exit;
            end;
            objLogicalDisk:=Unassigned;
          end;
          objPartition:=Unassigned;
       end;
       objDiskDrive:=Unassigned;
  end;
end;

begin
 try
    CoInitialize(nil);
    try
      Writeln(GetUsbDriveSerial('F'));
      Readln;
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

【讨论】:

在运行代码时出现以下异常:Invalid Query any ideea why? ok , 那是由于你的 WMI 版本将这一行 colDiskDrives := objWMIService.ExecQuery('SELECT DeviceID,SerialNumber FROM Win32_DiskDrive WHERE InterfaceType="USB"','WQL',0); 更改为 colDiskDrives := objWMIService.ExecQuery('SELECT * FROM Win32_DiskDrive WHERE InterfaceType="USB"','WQL',0); 代码被编辑为兼容旧版本的 WMI。 这是因为某些U盘驱动没有在Win32_DiskDrive.SerialNumber属性上暴露厂商序列号。 @JustMarc, 1.) 是的,您可以在代码中使用此示例,2) 从技术上讲可以伪造序列号,为避免这种情况,您可以尝试使用处理器等硬件信息的组合,以 Bios、RAM 等为例,试试这个链接theroadtodelphi.wordpress.com/2010/12/02/…【参考方案2】:

您可以尝试使用组件TDiskInfo from GLib 来获取序列号。 它不使用 WMI,但在某些系统(磁盘类型)中无法检索数字。 试试吧。免费。

问候。

【讨论】:

以上是关于如何获取 USB 闪存驱动器的制造商序列号?的主要内容,如果未能解决你的问题,请参考以下文章

UDEV - 在 USB 闪存驱动器插入上运行程序 [关闭]

如何在 Linux 下的 C 中确定 USB 存储(USB 闪存驱动器)“设备路径”

如何模拟 USB 闪存驱动器并同时读取数据

如何在 USB 闪存驱动器插入上运行 Python 脚本

必恩威 U盘 怎么量产

markdown 创建一个可启动的USB闪存驱动器.md