6

以前の質問で「フラッシュデバイスの一意のシリアル番号を見つける方法は?」ドライブ文字を取得する方法を尋ねることになりました。その問題は解決されました。

しかし、私の最初の質問には答えられていません。リムーバブルデバイス(USBドライブ、SDカード、(外付けHDD?)など)を区別し、再接続したときに常に認識できるようにしたかったのです。これは他のコンピュータでも可能です。幸い、ドライブがフォーマットされているかどうかは気にしないので(プログラムで新しいドライブとして扱われる場合)、認識の一部としてパーティションIDとボリュームIDを使用できますか?PNPDeviceIDは一意ではないため、これを求めています。私はそれを読んでいるハードウェアに依存することを発見しました、下の写真を見てください:

代替テキスト

代替テキスト

したがって、私が探しているのは、Win32_DiskDriveWin32_DiskPartitionWin32_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.

編集
ドライブが挿入されたときにドライブを検出するコードはすでに機能していることを追加する必要がありますが、ドライブ文字しか表示されません。そのドライブ文字を使用して、WMIから他のすべての情報を取得します。

最終編集
私は、開発者が認識のためにパーティション/ボリュームIDを安全に使用できることを読みました。頼りにできますか?

解決策:
つまり、「一意の」IDを読み取ることは実行可能な解決策ではないため、これを回避するには2つの方法があります。

  1. プログラムが認識できる一意のIDを使用して隠しファイルをドライブに保存します(ローカルデータベースと比較してください)。
  2. ドライブに関連するすべてのものを非表示の設定ファイルの形式でドライブに保存します。プログラム自体には設定がないので、このアプローチを採用しました。すべての設定はパーティションごとです。これにより、設定/プログラムも移植可能になります。
4

2 に答える 2

2

USBデバイスには一意のIDが必要です。私は過去に.NETアプリからこれをうまく使用しましたが、Delphiアプリでも同様に機能するはずです。

タイプライブラリエディタを使用してDelphiにWMICOMオブジェクトをインポートできるはずです。そうすれば、バリアントの代わりにアーリーバインディングを使用できます。

実際のサンプルが必要な場合は、C#コードを検索する必要があります。必要に応じて、プライベートメッセージまたはメールを送ってください。

.NETでこれを行う方法をざっと見てみました。ネイティブの世界では利用できない ManagementStronglyTyped Class Generator(Mgmtclassgen.exe)を使用しています。

USB以外のデバイスについてはよくわかりません。PNP IDはデバイスで同じであると期待しますが、そうではないと述べています。ただし、例では、異なるデバイスで同じPNP IDが表示されていません(もちろん、ここで明らかなことを見落とす可能性があります)。

--jeroen

USBデバイスには一意のIDがあります。

すべてのUSBデバイスを一覧表示するC#コードのいくつかの部分が見つかりました。

    public static List<DiskDrive> GetUsbDiskDrives()
    {
        DiskDrive.DiskDriveCollection diskDrives = DiskDrive.GetInstances("InterfaceType = 'USB'");
        return DiskDriveList(diskDrives);
    }

    public static List<DiskDrive> DiskDriveList(DiskDrive.DiskDriveCollection diskDrives)
    {
        List<DiskDrive> result = new List<DiskDrive>();
        foreach (DiskDrive diskDrive in diskDrives)
        {
            result.Add(diskDrive);
        }
        return result;
    }

    public static string Serial(DiskDrive diskDrive)
    {
        // pick the last portion of diskDrive.PNPDeviceID:
        string[] splitted = diskDrive.PNPDeviceID.Split('\\'); // note this becomes one backslash
        string result = splitted[splitted.Length - 1];
        return result;
    }

上記の部分では、.NET System.Collections.Generic名前空間を使用しているため、汎用リストを作成できます。

DiskDriveは、次のコマンドで生成されたC#ファイルのWin32.WMI名前空間にあります。

MgmtClassGen.exe Win32_DiskDrive /oWin32.WMI
于 2009-12-22T22:02:02.793 に答える
2

ボリュームIDだけで十分ですが、ボリュームIDを合計ディスクサイズおよびボリューム名と組み合わせて使用​​して、ディスクが同じかどうかを判断できるはずです。

唯一の問題は大量生産されたメディアかもしれません。場合によっては、ボリュームID、ディスクサイズ、およびボリューム名がすべてのコピーで一致します。

編集一般的にすべてのデバイスで絶対に回避できるかどうかはわかりません。各ベンダーのハードウェアは異なり、仕様は解釈の余地があります。そのため、一部のデバイスのシリアル番号はnullであり、一部のデバイスはnullではありません。あなたの唯一の望みは、ハードウェアを自分で供給するか、予測可能な方法で動作する特定のハードウェアを要求することです。

デバイスに書き込むことができ、それがユーザーに受け入れられる場合は、一意の識別子(GUIDなど)を含む読み取り専用のシステム隠しファイルを作成し、そのファイルを比較に使用できます。これは、デフォルトでウィンドウを実行する平均的なユーザー(システムファイルを非表示にし、非表示のファイルを表示するチェックボックスをオンにしない)のみがファイルをコピーするのを停止し、ボリュームID、ディスクサイズ、およびボリューム名もチェックに含めると、ミラーリングされたデバイスにのみ許可されます。すべてのインスタンスを取得できるわけではありませんが、十分に取得できる可能性があります。

于 2009-12-22T21:43:52.477 に答える