USBメモリのドライブ名を取得する方法

解決


take  2015-10-06 18:03:28  No: 47659

以前
https://www.petitmonte.com/bbs/answers?question_id=6974

にてUSBメモリの取り外しを質問したのですが

同様に「SetupDiEnumDeviceInfo」を利用して
デバイス一覧から USBメモリのドライブを取得する処理が
Windows7だとうまく機能しません

【環境】
OS:Windows7 64bit  Delphi2007

C:HDD
D:HDD
G:USBメモリ

【デバイス一覧取得結果】
必要な所だけ抜粋

1.
  Node:USB 大容量記憶装置
  DisplayName:(メーカー名)USB Flash Disk USB Device
  Removable:True
  DriveNames:空

2.
  Node:汎用ボリューム
  DisplayName:汎用ボリューム
  Removable:False
  DriveNames: G

3.
  Node:汎用ボリューム
  DisplayName:汎用ボリューム シャドウ コピー
  Removable:False
  DriveNames:C

4.
  Node:汎用ボリューム
  DisplayName:汎用ボリューム
  Removable:False
  DriveNames:D

WindowsXP だと (1)と(2)のノード名が一致していたので
取り外し出来るドライブは G という判定が出来たのですが
この結果からは関連づけが出来ません。

USBメモリのドライブ名を取得する方法がありますでしょうか?


deldel  2015-10-06 19:13:43  No: 47660

どこかから拾ったコードですが(^^;
これでディスクのタイプが表示されるようです。
これを利用できませんか?
(D5,XP)

WbemScripting_TLB, ActiveX, ComObj

var
  Locator: ISWbemLocator;
  Services: ISWbemServices;
  OsSet: ISWbemObjectSet;
  tmpEnum: IEnumVariant;
  Value:   Cardinal;
  OS: OleVariant;
  il: Longint;
begin
  Locator := CreateOleObject('WbemScripting.SWbemLocator') as ISWbemLocator;
  Services := Locator.ConnectServer('.', '', '', '', '', '', 0, nil);
  OsSet := Services.ExecQuery('SELECT * FROM Win32_DiskDrive', 'WQL', wbemFlagReturnImmediately, nil);
  tmpEnum:= OsSet._NewEnum as IEnumVariant;

  while True do begin
    il := tmpEnum.Next(1, OS, Value);
    if il <> 0 then Break;

    try
      Memo1.Lines.Add(OS.PNPDeviceID);
    except
    end;
  end;
end;


deldel  2015-10-06 20:37:52  No: 47661

あ〜すみません、ドライブ名は出てこないみたいですね・・・


take  2015-10-06 20:51:59  No: 47662

とりあえずやってみました

【タイプライブラリが無かったので追加】
メニューの「コンポーネント|コンポーネントのインポート」で、コンポーネントのインポートウィザードを表示し、
次にタイプライブラリのインポートを選択し「次へ」、
一覧からMicrosoft WMI Scripting V1.2 Library を選択し「次へ」、ユニットの作成を選択し「完了」ボタンをクリック。

【得られた結果】
IDE\DISKVOLUME01.0.00__\4&10D80AFA&0&0.0.0
USBSTOR\DISK&VEN_(メーカー名)&PROD_USB_FLASH_DISK&REV_A0A\0009121150000D&0

「USBSTOR」がUSBメモリを指しそうですね

これであとはドライブ名が取得出来れば・・・
こちらでも色々やってみます。


Harry  2015-10-08 18:23:04  No: 47663

https://www.petitmonte.com/bbs/answers?question_id=6974
以前の↑この投稿にある、おかぽん(ぽむぽむ)さんのページは、以下に移転しちゃってます。
http://www42.tok2.com/home/okapony/download/delphi/tips3.html
ダウンロードは以下から探す、と。
http://www42.tok2.com/home/okapony/download/delphi/

こちら↓でMr.XRAYさんが教えてくれてました。
USBメモリの取り外しについて
https://www.petitmonte.com/bbs/answers?question_id=7653
-----------------------------------------------------------------------------
WMIは、この↓サイトが非常に詳しいです。
http://www.wmifun.net/library/

また、WMI Explorer を使うと、すべての情報を取得できて便利です。
https://wmie.codeplex.com/
-----------------------------------------------------------------------------
takeさん、その後進展はありましたでしょうか?
面白そうなので自分もやってみました。

WMI Explorer で調べるとUSBはクエリだけで特定できそうだと判り、また、
  Win32_DiskDrive → Win32_DiskDriveToDiskPartition → Win32_LogicalDiskToPartition → Win32_LogicalDisk
という流れで行けるのでは思い、まずはVBSで組み始めたんですが…、途中の二つがリンク専用で、
その参照をうまく比較できませんでした。
困ったので検索してみると、な、なんと、ほぼそのまま使えそうなサンプル↓がありました。

Hey, Scripting Guy! 論理ドライブと物理ディスクを相互に関連付ける方法はありますか
https://gallery.technet.microsoft.com/scriptcenter/1abfce9f-d531-440e-9500-b9d7d2e454df

これにInterfaceTypeをUSBに限定したクエリにしてWin32_DiskDriveを取り出し、Capabilitiesから
リムーバブルの有無を確認することで目的に達せたようです。
VBSで失礼しますが、次の投稿にてそのコードを載せておきます。
なお、ドライブ名だけを取得する関数としてDelphiにも翻訳途中ですので、ちょっとだけお待ちを。

余談:
リムーバブルの判定だけなら CreateObject("Scripting.FileSystemObject").GetDrive("F:").DriveType
とかでも出来ます(参考例↓)。これはWMIと同じ結果を出してくれました。
http://www.vbaexpress.com/forum/showthread.php?8312-Solved-Drive-letters-for-Devices-with-Removable-Storage


Harry  2015-10-08 18:30:04  No: 47664

DelphiじゃなくてVBSですみません。が、これはこれで何か役に立ちそうな気がするので。

出力例:
IDE
  DiskDrive: \\.\PHYSICALDRIVE0
    DiskPartition: Disk #0, Partition #0
      LogicalDisk: C:
    DiskPartition: Disk #0, Partition #1
      LogicalDisk: E:
USB
  DiskDrive: \\.\PHYSICALDRIVE1
    DiskPartition: Disk #1, Partition #0
      LogicalDisk: F:
  DiskDrive: \\.\PHYSICALDRIVE2 *Removable Media*
    DiskPartition: Disk #2, Partition #0
      LogicalDisk: G:

'GetDrivesInfoByType.vbs
InterfaceTypes=Array("SCSI", "HDC", "IDE", "USB", "1394")
InfoStr=""
For Each InterfaceType in InterfaceTypes
  GetDrivesInfoByType InterfaceType, InfoStr
Next
Wscript.Echo InfoStr
Wscript.Quit

Function GetDrivesInfoByType(Value, ByRef Str)
  GetDrivesInfoByType=False
  LinesAdd Str, ""
  LinesAdd Str, Value

  strComputer="."
  Set WMI=GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

  Query="SELECT * FROM Win32_DiskDrive WHERE InterfaceType='" & Value & "'"
  Set DiskDrives=WMI.ExecQuery(Query)

  If DiskDrives.Count=0 Then LinesAdd Str, "  DiskDrives None."

  For Each DiskDrive in DiskDrives
    Removable=""
    For Each Capability in DiskDrive.Capabilities
      If Capability=7 Then Removable=" *Removable Media*"
    Next
    LinesAdd Str, "  DiskDrive: " & DiskDrive.DeviceID & Removable

    strDeviceID=Replace(DiskDrive.DeviceID, "\", "\\")
    Query="ASSOCIATORS OF {Win32_DiskDrive.DeviceID=""" & strDeviceID
    Query=Query & """} WHERE AssocClass=Win32_DiskDriveToDiskPartition"
    Set DiskPartitions=WMI.ExecQuery(Query)

    If DiskPartitions.Count=0 Then LinesAdd Str, "    DiskPartitions None."

    For Each DiskPartition in DiskPartitions
      LinesAdd Str, "    DiskPartition: " & DiskPartition.DeviceID

      Query="ASSOCIATORS OF {Win32_DiskPartition.DeviceID=""" & DiskPartition.DeviceID
      Query=Query & """} WHERE AssocClass=Win32_LogicalDiskToPartition"
      Set LogicalDisks=WMI.ExecQuery(Query)

      If LogicalDisks.Count=0 Then LinesAdd Str, "      LogicalDisks None."

      For Each LogicalDisk In LogicalDisks
        LinesAdd Str, "      LogicalDisk: " & LogicalDisk.DeviceID
        GetDrivesInfoByType=True
      Next
      Set LogicalDisks=Nothing

    Next
    Set DiskPartitions=Nothing

  Next
  Set DiskDrives=Nothing
  Set WMI=Nothing

End Function

Sub LinesAdd(ByRef S, V)
  S=S & V & vbNewLine
End Sub


通りすがり  2015-10-08 19:23:52  No: 47665

Delphiでおk

WMI Delphi Code Creator | The Road to Delphi
https://theroadtodelphi.wordpress.com/wmi-delphi-code-creator/

RRUZ/wmi-delphi-code-creator
https://github.com/RRUZ/wmi-delphi-code-creator


Harry  2015-10-09 07:23:51  No: 47666

通りすがりさん
WMI Delphi Code Creator は、WMI専用統合開発環境のようですね。
FPC、C++、C#、Oxygeneにも対応してるのに、ちょっとネーミングで損してるような。
ちょうど、この作者 Rodrigo Ruz さんのページも参考にしてました。

Implementing a Delphi for..in loop on COM collections and Variant Arrays | The Road to Delphi
http://theroadtodelphi.wordpress.com/2011/11/04/implementing-a-delphi-for-in-loop-on-com-collections-and-variant-arrays/

バリアントのコレクションで、IEnumVariantを使った分かりにくいイテレートから、for .. in
ループに変えられるテクニックを紹介してるページなんですが、な、なんとここでも偶然に
そのサンプルコードの内容がWMIによる 物理ドライブ → 論理ドライブ でした。


Harry  2015-10-09 07:28:18  No: 47667

ということでtakeさん、Delphi版が出来ましたのでお試しください。
なお、タイプライブラリ WbemScripting_TLB は使用してません。(使うとかえって面倒なので。)

下記サンプルの出力例
USB Drives:
F:
G:
USB Drives Supports Removable Media:
G:
IDE Drives:
C:
E:

☆InterfaceTypeとかCapabilitiesの定数はこちら
Win32_DiskDrive class (Windows)
http://msdn.microsoft.com/en-us/library/aa394132%28v=vs.85%29.aspx

WQL (SQL for WMI) (Windows)
http://msdn.microsoft.com/en-us/library/aa394606%28v=vs.85%29.aspx

implementation

{$R *.dfm}

uses
  ComObj, ActiveX;

// Win32_DiskDrive.InterfaceType  'SCSI', 'HDC', 'IDE', 'USB', '1394'
function GetDriveNamesByType(DriveNames: TStrings; InterfaceType: String;
  OnlyRemovable: Boolean): Boolean;
var
  WMI: OleVariant;
  Query: String;
  DiskDrives,  DiskPartitions, LogicalDisks: OleVariant;
  DiskDrivesEnum, DiskPartitionsEnum, LogicalDisksEnum: IEnumVariant;
  DiskDrive, DiskPartition, LogicalDisk: OleVariant;
  iValue : LongWord;
  strDeviceID: String;
  I: Integer;
begin
  Result:=False;

  WMI:=OleVariant(CreateOleObject('WbemScripting.SWbemLocator')).ConnectServer;

  Query:='SELECT * FROM Win32_DiskDrive WHERE InterfaceType='''+InterfaceType+'''';
  DiskDrives:=WMI.ExecQuery(Query);

  DiskDrivesEnum:=IUnknown(DiskDrives._NewEnum) as IEnumVariant;
  while DiskDrivesEnum.Next(1, DiskDrive, iValue)=0 do begin

    if OnlyRemovable then begin
      for I:=VarArrayLowBound(DiskDrive.Capabilities, 1) to VarArrayHighBound(DiskDrive.Capabilities, 1) do begin
        if DiskDrive.Capabilities[I]=7 then Break; // 7: Supports Removable Media
      end;
      if I=VarArrayHighBound(DiskDrive.Capabilities, 1)+1 then Continue;
    end;

    strDeviceID:=StringReplace(VarToStr(DiskDrive.DeviceID), '\', '\\', [rfReplaceAll]);
    Query:='ASSOCIATORS OF {Win32_DiskDrive.DeviceID="'+strDeviceID+
           '"} WHERE AssocClass=Win32_DiskDriveToDiskPartition';
    DiskPartitions:=WMI.ExecQuery(Query);

    DiskPartitionsEnum:=IUnknown(DiskPartitions._NewEnum) as IEnumVariant;
    while DiskPartitionsEnum.Next(1, DiskPartition, iValue)=0 do begin
      Query:='ASSOCIATORS OF {Win32_DiskPartition.DeviceID="'+
             VarToStr(DiskPartition.DeviceID)+
             '"} WHERE AssocClass=Win32_LogicalDiskToPartition';
      LogicalDisks:=WMI.ExecQuery(Query);

      LogicalDisksEnum:=IUnknown(LogicalDisks._NewEnum) as IEnumVariant;
      while LogicalDisksEnum.Next(1, LogicalDisk, iValue)=0 do begin
        strDeviceID:=VarToStr(LogicalDisk.DeviceID);
        if strDeviceID<>'' then begin
          DriveNames.Add(strDeviceID);
          Result:=True;
        end;
      end;
      LogicalDisks:=Unassigned;

    end;
    DiskPartitions:=Unassigned;

  end;
  DiskDrives:=Unassigned;
  WMI:=Unassigned;

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Add('USB Drives:');
  GetDriveNamesByType(Memo1.Lines, 'USB', False);
  Memo1.Lines.Add('USB Drives Supports Removable Media:');
  GetDriveNamesByType(Memo1.Lines, 'usb', True);
  Memo1.Lines.Add('IDE Drives:');
  GetDriveNamesByType(Memo1.Lines, 'IDE', False);
end;


take  2015-10-09 17:00:21  No: 47668

取り外しの方は機能するので
USBドライブの指定はユーザーに設定してもらう方法で妥協していましたが
みなさんのサンプルをそれぞれ試してみます。

明日から連休なので、今日中に返信出来ない場合は
返信に時間がかかるかもしれませんのでご了承願います。


take  2015-10-09 19:32:12  No: 47669

Harry様へ
ユニットに「Variants」が必要なようでしたが
それ以外は動作しました。

【何も刺していないとき】
USB Drives:
USB Drives Supports Removable Media:
IDE Drives:

※IDEに何も表示されないのはSSDだからかな?という感じです。
  HDDの環境で試すと表示されました。

【USBメモリを指したとき】
USB Drives:
G:
USB Drives Supports Removable Media:
G:
IDE Drives:

【USB HDDを刺したとき
USB Drives:
F:
USB Drives Supports Removable Media:
IDE Drives:

完璧な動作です。

参考になるサイトを紹介して頂けるだけでもありがたいのに
まさか作って頂けるとは・・・

Harry様に感謝です。

参考サイトや助言を頂きました
deldel様、通りすがり様、ありがとうございました。


take  2015-10-09 19:32:38  No: 47670

解決忘れました


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加