如何找到闪存设备的唯一序列号?

4

我找到了几个代码片段和.pas文件,可以检测USB闪存驱动器何时插入和拔出。有些提供各种好的信息,但是我需要的是设备的唯一序列号,而不是卷的序列号。

我的当前.pas文件(我不记得在哪里找到)似乎也可以检测SD卡(这很好)。如果你想看看,可以在这里找到它(它只返回驱动器号码和插入/拔出):

unit UsbDetector;

interface

uses Classes;

type
  TUsbDriveChanged = procedure (Sender: TObject; Drive: string; Attached: boolean) of object;

procedure StartUsbDetector(NotifyProc: TUsbDriveChanged);
procedure StopUsbDetector;

implementation

uses Windows, Messages, Forms, SysUtils;

type
  TUSBDetector = class(TObject)
  private
    fUsbDriveChanged: TUsbDriveChanged;
  protected
    procedure DeviceChanged(Msg: UINT; wParam, lParam: Longint);
    procedure DoUsbDriveChanged(Drive: string; Attached: Boolean); dynamic;
  public
    constructor Create(NotifyProc: TUsbDriveChanged);
    destructor Destroy; override;
    property OnUsbDriveChanged: TUsbDriveChanged read fUsbDriveChanged;
  end;

var mUSBDetector: TUSBDetector;

procedure StartUsbDetector(NotifyProc: TUsbDriveChanged);
begin
  if not Assigned(mUsbDetector) then
    mUsbDetector := TUsbDetector.Create(NotifyProc);
end;

procedure StopUsbDetector;
begin
  FreeAndNil(mUsbDetector);
end;

{----------------------------------------------------------------------------}
// Device constants
const
  DBT_DEVICEARRIVAL          =  $00008000;
  DBT_DEVICEREMOVECOMPLETE   =  $00008004;
  DBT_DEVTYP_VOLUME          =  $00000002;

// Device structs
type
  _DEV_BROADCAST_HDR         =  packed record
     dbch_size:              DWORD;
     dbch_devicetype:        DWORD;
     dbch_reserved:          DWORD;
  end;
  DEV_BROADCAST_HDR          =  _DEV_BROADCAST_HDR;
  TDevBroadcastHeader        =  DEV_BROADCAST_HDR;
  PDevBroadcastHeader        =  ^TDevBroadcastHeader;

type
  _DEV_BROADCAST_VOLUME      =  packed record
     dbch_size:              DWORD;
     dbch_devicetype:        DWORD;
     dbch_reserved:          DWORD;
     dbcv_unitmask:          DWORD;
     dbcv_flags:             WORD;
  end;
  DEV_BROADCAST_VOLUME       =  _DEV_BROADCAST_VOLUME;
  TDevBroadcastVolume        =  DEV_BROADCAST_VOLUME;
  PDevBroadcastVolume        =  ^TDevBroadcastVolume;

var
  fPrevWndProc: TFNWndProc = nil;

function UsbWndProc(hWnd: HWND; Msg: UINT; wParam, lParam: Longint): Longint; stdcall;
begin
  Result := CallWindowProc(fPrevWndProc, hWnd, Msg, wParam, lParam);
  if (Msg = WM_DEVICECHANGE) and (mUsbDetector <> nil) then
    mUsbDetector.DeviceChanged(Msg, wParam, lParam);
end;

constructor TUSBDetector.Create(NotifyProc: TUsbDriveChanged);
begin
  inherited Create;
  fUsbDriveChanged := NotifyProc;
  if not Assigned(fPrevWndProc) then 
  begin
    fPrevWndProc := TFNWndProc(GetWindowLong(Application.Handle, GWL_WNDPROC));
    SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(@UsbWndProc));
  end;
end;

destructor TUSBDetector.Destroy;
begin
  //SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(@fPrevWndProc));
  inherited Destroy;
end;

procedure TUSBDetector.DeviceChanged(Msg: UINT; wParam, lParam: LongInt);
var
  lpdbhHeader: PDevBroadcastHeader;
  lpdbvData: PDevBroadcastVolume;
  dwIndex: Integer;
  lpszDrive: string;
begin
  // Get the device notification header
  lpdbhHeader := PDevBroadcastHeader(lParam);
  // Handle the message
  lpszDrive := '';
  case WParam of
    DBT_DEVICEARRIVAL:    {a USB drive was connected}
    begin
      if lpdbhHeader^.dbch_devicetype = DBT_DEVTYP_VOLUME then
      begin
        lpdbvData := PDevBroadcastVolume(lParam);
        for dwIndex := 0 to 25 do
        begin
          if (lpdbvData^.dbcv_unitmask shr dwIndex) = 1 then
          begin
            lpszDrive := lpszDrive + Chr(65 + dwIndex) + ':\';
            break;
          end;
        end;
        DoUsbDriveChanged(lpszDrive, True);
      end;
    end;
    DBT_DEVICEREMOVECOMPLETE:    {a USB drive was removed}
    begin
      if lpdbhHeader^.dbch_devicetype = DBT_DEVTYP_VOLUME then
      begin
        lpdbvData := PDevBroadcastVolume(lParam);
        for dwIndex := 0 to 25 do
        begin
          if (lpdbvData^.dbcv_unitmask shr dwIndex) = 1 then
          begin
            lpszDrive := lpszDrive + Chr(65 + dwIndex) + ':\';
            break;
          end;
        end;
        DoUsbDriveChanged(lpszDrive, False);
      end;
    end;
  end;
end;

procedure TUSBDetector.DoUsbDriveChanged(Drive: string; Attached: Boolean);
begin
  if Assigned(fUsbDriveChanged) then
    fUsbDriveChanged(Self, Drive, Attached);
end;

end.

顺便说一下,代码高亮出现了问题。

总之,在插入/拔出可移动设备时,获取驱动器号和其唯一序列号。或许可以将已有的代码与WMI调用“where Index=found_index”相结合。

****编辑!**** 我从RRUZ给出的代码中删除了“where”子句。我终于找到了如何处理数组,所以我使用它来查找Capabilities [i] = 7以获取所有可移动介质。现在我只需要将此代码与上面的代码连接起来。我考虑使用Index,但我不知道如何使用GetDrive MapInfo。如果您能提供一个获取驱动器号的示例,我的问题就解决了。


1
@Daniel,请查看这个问题https://dev59.com/DErSa4cB1Zd3GeqPUCx1 - RRUZ
我确实这样做了,但是我忘了告诉你我希望有一种标准的方法(如果存在的话)。我想如果没有其他选择,我可以使用这个。 - Daniel-Dane
好的,它已经可以工作了。但是它只能检测到USB闪存驱动器。而且我无法将那个和我的现有设备连接起来。 - Daniel-Dane
我发现可以使用Capabilities[7]获取所有可移动驱动器(USB笔+SD卡等),但我不知道如何在WMI查询中使用数组。此外,如果我能获得驱动器字母就太棒了。虽然有一个索引,但我找不到GetDriveMapInfo。 - Daniel-Dane
1个回答

4
你可以使用Magenta Systems提供的WMI库,它可以帮助您更轻松地使用WMI查询。免费下载包括源代码和示例项目,让您可以自由地使用API和查询。您需要保留链接到官方Microsoft API文档,以帮助您确定要执行哪个查询以获取所需信息...您可以使用类似SQL语法的查询来查询类。
例如,执行以下查询:
SELECT * FROM Win32_DiskDrive Where InterfaceType = 'USB'

返回有关当前连接到计算机的所有USB设备的大量信息。然后,您可以使用PNPDeviceID作为唯一标识符。

编辑 检查我手头唯一的USB设备返回了一个硬件序列号“u”,但看起来非常长且有效的PNPDeviceID似乎包括序列号,这就是我建议使用该字段的原因。

编辑 您可以通过对Win32_LogicalDisk执行查询来获取驱动器号,还可以查询Win32_DiskDriveToDiskPartition,其中包含Win32_DiskDriveWin32_DiskPartition之间的映射。最后,Win32_LogicalDiskToPartition将逻辑磁盘映射到分区,从而使您能够将物理USB驱动器链接到特定的驱动器号。


是的,大致上就是这样,但我希望您查看一下问题的评论;我想要所有类型的可移动设备(包括SD卡)。此外,当插入(和拔出)此类设备时,应该可以获取此信息,也许可以将其与问题中的代码结合起来? - Daniel-Dane
您不必在where子句中添加任何内容,它将返回连接到计算机的所有磁盘驱动器设备的记录。 - skamradt
很可能不会,但操作系统支持它,所以这是可能的。 - skamradt
抱歉唤醒旧的主题,但是...问题:基于Win32_DiskDrive获取该序列号以进行许可实际上有多安全,也就是说它是否可以以某种方式被伪造?谢谢! - That Marc
我相信只要有足够的时间和资源,任何你所做的事情都可以被绕过。最完美的计划也可以很容易地被聪明的黑客挫败,他们直接修改你的exe并用一堆NOP指令替换你的例程,以便跳过你的关键检查。你能做的最好的事情就是让事情对于普通人来说有些难以绕过...试图做更多的事情会浪费你的时间,并增加实际付费客户出现误报的可能性。 - skamradt
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接