以前
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メモリのドライブ名を取得する方法がありますでしょうか?
どこかから拾ったコードですが(^^;
これでディスクのタイプが表示されるようです。
これを利用できませんか?
(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;
あ〜すみません、ドライブ名は出てこないみたいですね・・・
とりあえずやってみました
【タイプライブラリが無かったので追加】
メニューの「コンポーネント|コンポーネントのインポート」で、コンポーネントのインポートウィザードを表示し、
次にタイプライブラリのインポートを選択し「次へ」、
一覧から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メモリを指しそうですね
これであとはドライブ名が取得出来れば・・・
こちらでも色々やってみます。
>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
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
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
通りすがりさん
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による 物理ドライブ → 論理ドライブ でした。
ということで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;
取り外しの方は機能するので
USBドライブの指定はユーザーに設定してもらう方法で妥協していましたが
みなさんのサンプルをそれぞれ試してみます。
明日から連休なので、今日中に返信出来ない場合は
返信に時間がかかるかもしれませんのでご了承願います。
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様、通りすがり様、ありがとうございました。
解決忘れました
ツイート | ![]() |