USBデバイスをWindowsコンピューターに接続し、ほとんど待つことなく画面に現れる瞬間には、独特の満足感があります。Windowsはハードウェアを識別し、適切なドライバーを読み込み、ストレージデバイスにドライブ文字を割り当てます。そして1秒か2秒もすれば、USBメモリを使用できる状態になります。
ほとんどのユーザーは、そのわずかな数秒間に何が起きたのかを考える必要はありません。しかし、開発者、IT管理者、ハードウェアエンジニア、そしてUSBデバイスと連携するソフトウェアを作成する人にとって、この自動処理は重要な疑問を生みます。Windowsは、デバイスを識別するための情報をどこから取得しているのでしょうか。
USBメモリは、複数の識別情報を返すことができます。USBレベルでは、デバイスは一般にVIDと呼ばれるVendor IDと、一般にPIDと呼ばれるProduct IDを返します。WindowsがそのデバイスをUSBマスストレージとして認識した後は、ストレージデバイスからSCSI Vendor文字列、SCSI Product文字列、製品リビジョンも返される場合があります。
これらの値は同じ物理デバイスを説明しているため互いに関係していますが、同じ場所から取得されるわけではありません。相互に置き換えられるものでもなく、常に同じメーカーや製品名を示すとも限りません。
Windows 10には、この情報を表示または取得するための方法が複数用意されています。コンピューターの前にいる人が直接確認するための方法もあれば、スクリプト、資産管理システム、診断ソフトウェア、USBストレージデバイスを自動識別する必要があるアプリケーションに適した方法もあります。
USBのVIDとPID、SCSIのVendorとProductの違い
Windowsで利用できる各方法を見る前に、関係する2種類の識別情報を理解しておくと役立ちます。
USB Vendor IDとProduct IDは、デバイスのUSBディスクリプタから取得されます。これらの値は、USBデバイスが最初に接続され、WindowsがUSB列挙処理を開始したときに返されます。
一般的なUSBハードウェアIDは、次のような形式になります。
USB\VID_090C&PID_1000&REV_1100
USB\VID_090C&PID_1000
この例の値は次のとおりです。
USB VID = 090C
USB PID = 1000
VIDはUSBベンダーを識別するための値であり、PIDはそのベンダーが割り当てた製品または製品ファミリーを識別します。どちらも16進数です。
接続されたデバイスがUSB Mass StorageデバイスであるとWindowsが判断すると、Windowsはストレージコマンド層を通じてデバイスと通信します。標準的なSCSI Inquiry応答には、Vendor文字列、Product文字列、Revision文字列が含まれる場合があります。
ストレージのハードウェアIDは、次のような形式になります。
USBSTOR\Disk&Ven_Generic&Prod_Flash_Disk&Rev_8.07
この例の値は次のとおりです。
SCSI Vendor = Generic
SCSI Product = Flash Disk
SCSI Revision = 8.07
USBのVIDとPIDは、USB列挙中に使用される数値識別子です。一方、SCSI VendorとProductフィールドは、ストレージインターフェースを通じて返されるテキスト文字列です。デバイスメーカーは、一方の識別情報を変更しても、必ずしももう一方を変更する必要はありません。
そのため、USBメモリがコントローラーメーカーに関連するUSB VIDを返している一方で、SCSI Productフィールドには小売製品名、顧客名、または「USB Flash Disk」のような一般的な名称が表示されることがあります。
Windowsが2種類のデバイス識別情報を構築する仕組み
この処理は、2つの段階に分けると最も理解しやすくなります。Windowsは最初に、接続されたUSBハードウェアを識別します。その後、そのUSB接続を通じて動作しているストレージデバイスを識別します。
USBのVIDとPIDは、最初の段階で利用可能になります。SCSI Vendor、Product、Revisionフィールドは、USBマスストレージドライバーが読み込まれ、Windowsがストレージデバイスへ問い合わせを行った後に利用可能になります。
この違いを理解すると、USBデバイスを取り外した後もWindowsがそのデバイスを記憶できる理由も分かりやすくなります。Windowsが過去に接続されたUSBデバイスの履歴を保存する理由についての記事で説明したように、Windowsはデバイス列挙中に収集した情報を保存し、同じハードウェアが次に接続されたときに認識して管理できるようにしています。
なぜ両方の識別情報が必要になるのか
簡単なトラブルシューティングであれば、USBのVIDとPIDだけで十分な場合があります。しかし、デバイス資産管理、製造、診断、ソフトウェア開発では、両方の識別情報を収集することで、より完全な全体像を得られます。
資産管理アプリケーションでは、USBのVIDとPIDを使用してデバイスをハードウェアプラットフォームごとに分類しながら、SCSI VendorとProductの文字列をユーザーに表示できます。製造用ユーティリティでは、処理を実行する前に、対応するUSBコントローラーが接続されていることを確認できます。診断プログラムでは、異なるデバイスの結果を後から比較できるように、すべての識別子を記録できます。
デジタルフォレンジックツールでは、デバイス記録の一部として識別子を収集することがあります。USB検証ソフトウェアでは、デバイスが返している識別情報と、テスト中に観察された動作を比較できます。USBデバイスが申告する名前、容量、性能だけでは、内部に実際にどのようなハードウェアが搭載されているかを証明できるとは限らないため、この比較が役立ちます。
同じ考え方は、データの完全性にも当てはまります。識別情報は、そのデバイスが何であると名乗っているかを示し、テストは実際にどのように動作するかを示します。USBデータ検証に電源の入れ直しが必要になる場合がある理由を解説した記事では、すぐに返された情報と、デバイスを取り外して再接続した後に実証された情報との間にある、同様の違いを取り上げています。
方法1:Windowsのデバイスマネージャーを使用する
デバイスマネージャーは、コマンドライン、スクリプト、プログラミングを必要としないため、最も簡単に始められる方法です。重要なのは、USBの識別情報とストレージの識別情報が、通常は2つの異なるデバイスエントリに表示されるという点です。
USBのVIDとPIDを確認する
デバイスマネージャーを開き、ユニバーサル シリアル バス コントローラーという項目を展開します。対象のドライブに対応するUSB Mass Storage Deviceを見つけ、右クリックしてプロパティを選択します。
詳細タブを選択し、プロパティのドロップダウンリストからハードウェアIDを選びます。
デバイスマネージャー
→ ユニバーサル シリアル バス コントローラー
→ USB Mass Storage Device
→ プロパティ
→ 詳細
→ ハードウェアID
一般的な結果は、次のようになります。
USB\VID_090C&PID_1000&REV_1100
USB\VID_090C&PID_1000
VID_に続く4文字がUSB Vendor IDです。PID_に続く4文字がUSB Product IDです。
SCSI VendorとProductを確認する
デバイスマネージャーに戻り、ディスク ドライブを展開します。USBメモリを見つけて右クリックし、プロパティを選択します。もう一度詳細タブを開き、ハードウェアIDを選択します。
デバイスマネージャー
→ ディスク ドライブ
→ USBメモリ
→ プロパティ
→ 詳細
→ ハードウェアID
結果は、次のような形式になります。
USBSTOR\Disk&Ven_Generic&Prod_Flash_Disk&Rev_8.07
Windowsは、これらの値をPlug and PlayハードウェアIDとして整形しています。
Ven_Generic = SCSI Vendor
Prod_Flash_Disk = SCSI Product
Rev_8.07 = SCSI Revision
デバイスマネージャーは、1台のデバイスを手作業で調べる場合には非常に優れた選択肢です。ただし、複数のUSBメモリが同時に接続されていると、正しいUSB Mass Storageのエントリと、正しいDisk Driveのエントリを対応付ける作業が分かりにくくなる場合があります。
方法2:PowerShellを使用する
情報を繰り返し収集する必要がある場合、レポートに表示する場合、または複数のコンピューターから取得する場合は、PowerShellがより適しています。Windows 10には、PowerShellと、基本的なデバイス確認に必要なPlug and Play cmdletが含まれています。
VIDとPIDを含む、現在接続中のUSBデバイスを一覧表示する
PowerShellを開き、次のコマンドを実行します。
Get-PnpDevice -PresentOnly |
Where-Object {
$_.InstanceId -match '^USB\\VID_'
} |
Select-Object FriendlyName, Class, InstanceId
出力には、次のようなデバイスインスタンスIDが含まれます。
USB\VID_090C&PID_1000\1234567890
次の例では、VIDとPIDを個別の列へ抽出します。
Get-PnpDevice -PresentOnly |
Where-Object {
$_.InstanceId -match '^USB\\VID_'
} |
ForEach-Object {
if ($_.InstanceId -match 'VID_([0-9A-F]{4})&PID_([0-9A-F]{4})') {
[PSCustomObject]@{
DeviceName = $_.FriendlyName
VID = $matches[1]
PID = $matches[2]
InstanceId = $_.InstanceId
}
}
}
これは、長いWindowsデバイスインスタンス文字列を、デバイス名、VID、PIDを含む、より読みやすい結果へ変換できるため便利です。
USBストレージ情報を一覧表示する
次のPowerShellコマンドは、Windowsのディスクドライブクラスへ問い合わせを行い、USBストレージデバイスだけを抽出します。
Get-CimInstance Win32_DiskDrive |
Where-Object {
$_.InterfaceType -eq 'USB' -or
$_.PNPDeviceID -like 'USBSTOR*'
} |
Select-Object DeviceID,
Manufacturer,
Model,
FirmwareRevision,
PNPDeviceID
結果は、次のような形式になります。
DeviceID : \\.\PHYSICALDRIVE2
Manufacturer : Generic
Model : Flash Disk USB Device
FirmwareRevision: 8.07
PNPDeviceID : USBSTOR\DISK&VEN_GENERIC&PROD_FLASH_DISK&REV_8.07...
ManufacturerフィールドとModelフィールドは便利ですが、すべてのUSBストレージデバイスが一貫して値を設定するわけではありません。多くの場合、PNPDeviceIDが、Windowsが受け取ったVendor、Product、Revisionの値を最も明確に表します。
PowerShellは、コンパイル済みアプリケーションを必要としないため、汎用的な選択肢として非常に優れています。難しいのは、複数のデバイスが接続されている場合に、USBレベルのデバイスエントリと対応する物理ディスクを関連付けることです。より高度なスクリプトであれば、Windowsの親子デバイス関係をたどり、その対応付けを行えます。
方法3:WMIまたはWMICコマンドを使用する
一般にWMIと略されるWindows Management Instrumentationは、ハードウェアやオペレーティングシステムの情報を取得するために、長年使用されてきました。多くのWindows 10コンピューターには、従来のWMICコマンドラインユーティリティも含まれています。
WMICは簡単なテストや古いスクリプトの維持に便利ですが、新しい開発では、一般的にPowerShellとCIMの方が適しています。
WMICでUSBディスク情報を表示する
コマンドプロンプトを開き、次のコマンドを実行します。
wmic diskdrive get DeviceID,InterfaceType,Manufacturer,Model,FirmwareRevision,PNPDeviceID
結果をUSBインターフェースを使用するディスクドライブに限定するには、次のコマンドを実行します。
wmic diskdrive where "InterfaceType='USB'" get DeviceID,Manufacturer,Model,FirmwareRevision,PNPDeviceID
一般的な結果は、次のようになります。
DeviceID Manufacturer Model FirmwareRevision
\\.\PHYSICALDRIVE2 Generic Flash Disk USB Device 8.07
PNPDeviceID列には、より長い値が含まれる場合があります。
USBSTOR\DISK&VEN_GENERIC&PROD_FLASH_DISK&REV_8.07\...
USBのVIDとPIDを含むPlug and Playエントリを検索する
次のWMICコマンドは、USB VIDを含むエントリをPlug and Playデバイスから検索します。
wmic path Win32_PnPEntity where "PNPDeviceID like 'USB%%VID_%%'" get Name,PNPDeviceID
2つ連続したパーセント記号は、WMICのクエリ内でワイルドカード文字として使用されます。
WMICは、新しいWindows環境では存在しないか、有効になっていない場合があります。また、Microsoftは管理用スクリプトをPowerShellへ移行しつつあります。それでも、Windows 10用ユーティリティや既存の企業向けスクリプトでは、WMIから有用な情報を取得できます。
方法4:Windows SetupAPIを使用する
CまたはC++で作成されたソフトウェアは、SetupAPIを通じてWindowsのPlug and Playデバイス情報を取得できます。これは、ユーザーにデバイスマネージャーを調べてもらうのではなく、USBデバイスを自動検出する必要があるデスクトップアプリケーションにとって実用的な方法です。
完全なSetupAPIアプリケーションには、エラー処理、動的バッファ、デバイスプロパティの解析、リソースの解放処理が含まれます。次の簡略化した例は、関係する主なWindows関数を示しています。
デバイス情報セットを作成する
#include <windows.h>
#include <setupapi.h>
HDEVINFO deviceInfoSet = SetupDiGetClassDevsW(
nullptr,
L"USB",
nullptr,
DIGCF_ALLCLASSES | DIGCF_PRESENT
);
これにより、現在コンピューターに存在するUSBデバイスを含むデバイス情報セットが作成されます。
デバイスエントリを列挙する
SP_DEVINFO_DATA deviceInfoData{};
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (
DWORD index = 0;
SetupDiEnumDeviceInfo(
deviceInfoSet,
index,
&deviceInfoData
);
++index
) {
// ここでデバイスプロパティを取得します。
}
ハードウェアIDプロパティを読み取る
WCHAR hardwareIds[4096]{};
DWORD requiredSize = 0;
if (SetupDiGetDeviceRegistryPropertyW(
deviceInfoSet,
&deviceInfoData,
SPDRP_HARDWAREID,
nullptr,
reinterpret_cast<PBYTE>(hardwareIds),
sizeof(hardwareIds),
&requiredSize
)) {
// hardwareIds内で次を検索します。
// VID_xxxx
// PID_xxxx
}
返されるハードウェアIDプロパティには、null文字で区切られた1つ以上の文字列が含まれる場合があります。USBデバイスエントリには、次のようなIDが含まれることがあります。
USB\VID_090C&PID_1000&REV_1100
アプリケーションは、その文字列からVID_フィールドとPID_フィールドを検索し、その後に続く4文字の16進数を抽出できます。
処理が完了したら、デバイス情報セットを解放する必要があります。
SetupDiDestroyDeviceInfoList(deviceInfoSet);
SetupAPIは強力ですが、重要な点があります。Windowsは、USBデバイスと物理ディスクをデバイスツリー内の別々のノードとして表します。USBのVIDとPIDを取得すること自体は比較的簡単です。しかし、その値を正しいディスク、ドライブ文字、SCSI識別情報へ関連付けるには、親子デバイス関係をたどる必要がある場合があります。
短いデモコードから想像されるよりも、商用診断アプリケーションに多くのコードが必要になる理由の1つがこれです。個々のプロパティは取得できますが、すべての情報を正しく結合し、1つのデバイス記録としてまとめる部分が本当の作業になります。
方法5:ストレージデバイスへ直接問い合わせる
Windowsアプリケーションは、デバイスを開き、IOCTL_STORAGE_QUERY_PROPERTY要求を送信することで、物理ディスクへ問い合わせを行えます。この方法は、Vendor、Product、Revision、シリアル番号、バスタイプなどのストレージ情報を取得する場合に便利です。
次の例ではPhysicalDrive2を開きます。実際の物理ドライブ番号は、コンピューターと、調査対象となるUSBデバイスによって異なります。
物理ドライブを開く
HANDLE drive = CreateFileW(
L"\\\\.\\PhysicalDrive2",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
0,
nullptr
);
if (drive == INVALID_HANDLE_VALUE) {
// エラーを処理します。
}
ストレージクエリを準備する
STORAGE_PROPERTY_QUERY query{};
query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;
Windowsへクエリを送信する
BYTE buffer[4096]{};
DWORD bytesReturned = 0;
BOOL result = DeviceIoControl(
drive,
IOCTL_STORAGE_QUERY_PROPERTY,
&query,
sizeof(query),
buffer,
sizeof(buffer),
&bytesReturned,
nullptr
);
返されたストレージディスクリプタを解釈する
if (result) {
auto descriptor =
reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(buffer);
const char* vendor =
descriptor->VendorIdOffset
? reinterpret_cast<const char*>(
buffer + descriptor->VendorIdOffset
)
: "";
const char* product =
descriptor->ProductIdOffset
? reinterpret_cast<const char*>(
buffer + descriptor->ProductIdOffset
)
: "";
const char* revision =
descriptor->ProductRevisionOffset
? reinterpret_cast<const char*>(
buffer + descriptor->ProductRevisionOffset
)
: "";
const char* serial =
descriptor->SerialNumberOffset
? reinterpret_cast<const char*>(
buffer + descriptor->SerialNumberOffset
)
: "";
}
文字列は、構造体の固定部分に直接保存されているわけではありません。構造体には、その代わりに、返されたバッファ内で各文字列が保存されている位置を示すバイトオフセットが格納されています。
物理ドライブのハンドルを使い終えたら、ハンドルを閉じます。
CloseHandle(drive);
ストレージクエリが成功すると、次のような情報を取得できます。
Vendor = Generic
Product = Flash Disk
Revision = 8.07
Serial = 1234567890
Bus Type = USB
この方法は、デバイスマネージャーからフレンドリ名を読み取る方法や、キャッシュされたレジストリ値へ問い合わせる方法よりも、ストレージデバイスに近い位置から情報を取得します。ただし、USBのVIDとPIDが自動的に返されるわけではありません。アプリケーションは、物理ディスクとそのUSB親デバイスを関連付け、USBハードウェアIDを別途取得する必要があります。通常はSetupAPIまたはWindows Configuration Managerの関数を使用します。
どのWindowsの方法を選ぶべきか
すべての状況に適した唯一の最良方法があるわけではありません。適切な選択肢は、目的が手作業による確認、スクリプト処理、ソフトウェア開発、低レベルのデバイス解析のどれであるかによって異なります。
WindowsのUSB識別方法の比較を開く
| 方法 | USB VID/PID | SCSI Vendor/Product | コーディングの必要性 | 最適な用途 |
|---|---|---|---|---|
| デバイスマネージャー | はい | はい | いいえ | 1台のデバイスを手作業で確認する場合 |
| PowerShell | はい | はい | 簡単なスクリプト | 資産管理と繰り返し作成するレポート |
| WMIまたはWMIC | はい | はい | 簡単なスクリプト | 古いシステムと既存のスクリプト |
| SetupAPI | はい | デバイスの関連付けによって取得 | はい | WindowsアプリケーションとUSBユーティリティ |
| ストレージクエリ | 直接は取得不可 | はい | はい | 診断とストレージ情報の直接取得 |
1台のUSBメモリを確認するだけであれば、通常はデバイスマネージャーで十分です。多数のコンピューターから情報を収集する管理者であれば、PowerShellが最も適した出発点になるでしょう。Windowsユーティリティを構築するソフトウェア開発者であれば、SetupAPIとストレージプロパティクエリを組み合わせることで、最も完全な方法を実現できます。
Windowsレジストリも別の方法なのか
WindowsはUSB列挙情報をレジストリに保存しており、USBエントリとUSBストレージエントリの両方をそこから見つけることができます。
USBデバイスのエントリは、一般に次の場所にあります。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB
USBストレージのエントリは、一般に次の場所にあります。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR
これらの場所は、トラブルシューティングや履歴解析には便利ですが、レジストリを情報の元の発生源と混同してはいけません。Windowsは、USBデバイスを列挙し、ストレージインターフェースへ問い合わせを行った後で、これらのエントリを作成および更新します。
元のUSB VIDとPIDはUSBディスクリプタから取得されます。元のストレージVendor、Product、Revisionの値は、ストレージデバイスのInquiry応答から取得されます。レジストリは、Windowsがそれらの結果を後で使用するために記録している場所です。
レジストリのアクセス許可によって、ソフトウェアからの直接アクセスが扱いにくくなる場合もあります。現在接続されているデバイスの情報だけが必要なアプリケーションでは、通常、Windowsが正式に提供するデバイスAPIを使用する方が適しています。
USB名とSCSI名が一致しない理由
すべての識別子に同じメーカー名が表示されると考えるのは自然です。しかし実際には、USBメモリは複数のハードウェア層とファームウェア層で構成されており、それぞれの層が異なる情報を返す場合があります。
小売ブランドのUSBメモリを販売する企業が、別のメーカーからコントローラーを購入している場合があります。USB VIDはコントローラーベンダー、またはファームウェアを担当した企業を示し、SCSI Productフィールドには小売ブランド名が含まれていることがあります。それ以外の場合は、SCSI文字列が完全に一般的な名称のまま残ることもあります。
あるデバイスは、次のような値を返す場合があります。
USB VID = 090C
USB PID = 1000
SCSI Vendor = Generic
SCSI Product = Flash Disk
別のドライブは、次のような値を返す場合があります。
USB VID = 0951
USB PID = 1666
SCSI Vendor = Kingston
SCSI Product = DataTraveler 3.0
どちらの形式であっても、その製品が正規品であることを自動的に証明するものではありません。これらの識別子は、識別や関連付けには役立ちますが、ファームウェアによって返される値です。十分に改造されたデバイスや偽造デバイスであれば、プログラムされた任意の値を返す可能性があります。
これは、デバイス検証ソフトウェアを開発する際に重要な違いです。識別値は記録するべきですが、容量テスト、書き込みと読み取りの検証、性能テスト、その他の動作解析の代わりに使用するべきではありません。
実用的なデバイス記録
USBメモリの資産管理やテストを行うソフトウェアでは、実用的なデバイス記録に4項目以上の情報を含めることができます。
USB VID
USB PID
USB Revision
USB Serial Number
SCSI Vendor
SCSI Product
SCSI Revision
Storage Serial Number
Physical Drive Number
Drive Letter
Reported Capacity
Bus Type
Connection Speed
すべてのUSBメモリが、すべての値を提供するわけではありません。シリアル番号が存在しない、重複している、またはUSBレベルとストレージレベルで異なる形で返されることがあります。メーカー文字列が空欄になる場合もあります。製品名が一般的な名称の場合もあります。ソフトウェアは、すべてのフィールドが常に利用できると仮定するのではなく、不完全な情報が返されることを想定する必要があります。
最も堅実な方法は、利用可能な識別子を収集し、元の値をそのまま保持したうえで、アプリケーションが生成する性能、容量、検証結果とその値を関連付けることです。
最後にもう1つ
Windows 10には、USBメモリのVID、PID、SCSI Vendor、SCSI Product情報を確認するための信頼できる方法が複数あります。デバイスマネージャーなら、手作業ですぐに確認できます。PowerShellとWMIなら、スクリプトからアクセスできます。SetupAPIを使えば、WindowsアプリケーションからPlug and Playデバイスツリーへアクセスでき、ストレージプロパティへ直接問い合わせることで、物理ディスクに関連する情報を取得できます。
重要なのは、USBストレージデバイスには複数の識別情報があるという点です。USBのVIDとPIDは、USB列挙層から取得されます。SCSI Vendor、Product、Revisionの値は、ストレージ層から取得されます。片方だけを確認した場合、得られるのは情報の半分だけかもしれません。
この違いを理解すると、Windowsに表示される一見矛盾した名称も意味が通るようになります。デバイスが必ずしも識別情報を変えているわけではありません。Windowsが、同じハードウェアの異なる2つの部分から収集した情報を表示しているだけです。
編集注記:この記事は、Windowsシステムを使用して実際に行ったUSBデバイス列挙とストレージ識別の作業に基づいています。実際の出力は、USBコントローラー、ファームウェア、Windowsドライバー、ストレージデバイスの実装によって異なる場合があります。画像は、情報をより分かりやすく伝えるため、人工知能を利用した画像作成ツールの支援を受けて作成しました。