始终检测可移动设备的最佳方法

Dan*_*ane 6 delphi wmi removable-drive delphi-2007 removable

在我之前的问题"如何找到闪存设备的唯一序列号?" 我最终要求找到驱动器号的方法.那个问题解决了.

但是,我的初步问题尚未得到解答.我希望能够将可移动设备(USB驱动器,SD卡,(外部硬盘驱动器?)等)分开,并且在重新连接时始终能够再次识别它们.这也应该可以在任何其他计算机上使用.幸运的是,我不关心正在格式化的驱动器(如果/何时,它们在我的程序中被视为新驱动器),那么我可以使用分区和卷ID作为我识别的一部分吗?我问这是因为PNPDeviceID 不是唯一的.我发现它取决于阅读它的硬件,见下图:

替代文字http://i48.tinypic.com/28uofmc.png

替代文字http://i46.tinypic.com/rk5tv6.jpg

因此,我正在搜索的是一种使用以下方法检测和识别任何计算机上的任何可移动设备的方法:Win32_DiskDrive,Win32_DiskPartition,Win32_LogicalDisk.我要感谢RRUZ的原始代码:

program GetWMI_USBConnectedInfo;

{$APPTYPE CONSOLE}

uses
  Windows,
  Classes,
  ActiveX,
  Variants,
  SysUtils,
  WbemScripting_TLB in '..\..\Documents\RAD Studio\5.0\Imports\WbemScripting_TLB.pas';

procedure  GetUSBDiskDriveInfo;
var
  WMIServices  : ISWbemServices;
  Root,a,b     : ISWbemObjectSet;
  Item,Item2   : Variant;
  i,ii,iii,iiii: Integer;
  start,stop,freq:Int64;
begin
  QueryPerformanceFrequency(freq);
  QueryPerformanceCounter(start);

  WMIServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
  Root := WMIServices.ExecQuery('Select * From Win32_DiskDrive','WQL', 0, nil);
  for i := 0 to Root.Count - 1 do
  begin
    Item := Root.ItemIndex(i);
    for ii := VarArrayLowBound(Item.Capabilities, 1) to VarArrayHighBound(Item.Capabilities, 1) do if (Item.Capabilities[ii] = 7) then begin
      Writeln('Caption      '+VarToStr(Item.Caption));
      Writeln('Name         '+VarToStr(Item.Name));
      Writeln('DeviceID     '+VarToStr(Item.DeviceID));
      Writeln('Partitions   '+VarToStr(Item.Partitions));
      Writeln('PNPDeviceID  '+VarToStr(Item.PNPDeviceID));
      Writeln('SerialNumber '+VarToStr(Item.SerialNumber));
      Writeln('Signature    '+VarToStr(Item.Signature));

      a := WMIServices.ExecQuery('ASSOCIATORS OF {Win32_DiskDrive.DeviceID=''' + VarToStr(Item.DeviceID) + '''} WHERE AssocClass = Win32_DiskDriveToDiskPartition','WQL', 0, nil);
      for iiii := 0 to a.Count - 1 do begin
        b := WMIServices.ExecQuery('ASSOCIATORS OF {Win32_DiskPartition.DeviceID=''' + VarToStr(Variant(a.ItemIndex(iiii)).DeviceID) + '''} WHERE AssocClass = Win32_LogicalDiskToPartition','WQL', 0, nil);
        for iii := 0 to b.Count - 1 do begin
          Item2 := b.ItemIndex(iii);
          Writeln('Drive = ' + Item2.Caption);
        end;
      end;
      Writeln;
      Writeln;
    end;
  end;
  QueryPerformanceCounter(stop);
  if (freq > 0) then
    Writeln('Time took: ' + FloatToStr((stop-start) / freq))
  else
    Writeln('Unable to measure time!');
end;

begin
  try
    CoInitialize(nil);
    GetUSBDiskDriveInfo;
    Readln;
    CoUninitialize;
  except
    on E:Exception do
    Begin
        CoUninitialize;
        Writeln(E.Classname, ': ', E.Message);
        Readln;
    End;
  end;
end.
Run Code Online (Sandbox Code Playgroud)

编辑
我应该补充一点,在插入驱动器时检测驱动器的代码已经可以工作了,尽管它只给了我一个驱动器号.我使用该驱动器号从WMI获取所有其他信息.

最后编辑
我已经读过,开发人员可以安全地使用分区/卷ID进行识别.我可以指望吗?

解决方案:
因此,由于阅读"唯一"ID并不是一个可行的解决方案,因此有两种方法可以解决这个问题:

  1. 使用程序可识别的唯一ID保存驱动器上的隐藏文件(与本地数据库比较).
  2. 以隐藏设置文件的形式将与驱动器相关的所有内容保存在驱动器.我采用这种方法,因为程序本身没有任何设置.所有设置均为每个分区.这也使设置/程序可移植.

ska*_*adt 2

您应该能够使用卷 ID 与磁盘总大小和卷名称配对来确定磁盘是否相同,尽管卷 ID 本身就足够了。

唯一的问题可能是大规模生产的媒体。在某些情况下,所有副本的卷 ID、磁盘大小和卷名称都匹配。

编辑我不确定您是否绝对可以在所有设备上绕过它。每个供应商的硬件都不同,并且规格有待解释,这就是为什么某些设备的序列号为空,而另一些则不是的原因。您唯一的希望是自己提供硬件或需要以可预测的方式运行的特定硬件。

如果您可以写入设备,并且用户可以接受,您可以创建一个包含唯一标识符(例如 guid)的只读系统隐藏文件,并使用该文件进行比较。这只会阻止使用默认设置(隐藏系统文件,并且未选中显示隐藏文件)运行 Windows 的普通用户复制文件,并且在检查中还包括卷 ID、磁盘大小和卷名称会坚持认为仅允许镜像设备。它可能无法获取所有实例,但可能已经足够了。