もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
掲題の通り、PCに接続されているデバイスの名前とデバイスインスタンスパス、あとエラーがあるかどうかを表示したい。
というのをcopilotにお願いすると、まんま動くコードをくれたので、それ見ながら勉強する。
コード(by copilot)
#include <windows.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <iostream>
#include <vector>
#include <string>
#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "cfgmgr32.lib")
int main()
{
std::setlocale(LC_ALL, "ja_JP.UTF-8");
std::wcout.imbue(std::locale("ja_JP.UTF-8"));
// すべてのデバイスを取得
HDEVINFO hDevInfo = SetupDiGetClassDevsA(
nullptr, // すべてのクラス
nullptr,
nullptr,
DIGCF_ALLCLASSES | DIGCF_PRESENT
);
if (hDevInfo == INVALID_HANDLE_VALUE) {
std::cerr << "SetupDiGetClassDevsA failed." << std::endl;
return 1;
}
DWORD index = 0;
SP_DEVINFO_DATA devInfoData;
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
while (SetupDiEnumDeviceInfo(hDevInfo, index, &devInfoData)) {
++index;
// FriendlyName取得
WCHAR friendlyName[256];
DWORD requiredSize = 0;
BOOL hasFriendlyName = SetupDiGetDeviceRegistryPropertyW(
hDevInfo,
&devInfoData,
SPDRP_FRIENDLYNAME,
nullptr,
(PBYTE)friendlyName,
sizeof(friendlyName),
&requiredSize
);
if (!hasFriendlyName) {
// FriendlyNameがない場合はDeviceDescを試す
hasFriendlyName = SetupDiGetDeviceRegistryPropertyW(
hDevInfo,
&devInfoData,
SPDRP_DEVICEDESC,
nullptr,
(PBYTE)friendlyName,
sizeof(friendlyName),
&requiredSize
);
}
// Device Instance ID取得
WCHAR deviceId[MAX_DEVICE_ID_LEN];
if (CM_Get_Device_IDW(devInfoData.DevInst, deviceId, MAX_DEVICE_ID_LEN, 0) != CR_SUCCESS) {
wcscpy_s(deviceId, L"(取得失敗)");
}
// デバイスの状態取得
ULONG status = 0, problem = 0;
CONFIGRET cr = CM_Get_DevNode_Status(&status, &problem, devInfoData.DevInst, 0);
// エラーのあるものだけ表示
// エラーコード:https://support.microsoft.com/ja-jp/topic/windows-%E3%81%AE%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9-%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC-%E3%82%B3%E3%83%BC%E3%83%89-524e9e89-4dee-8883-0afa-6bca0456324e
/*if (!(status & DN_HAS_PROBLEM))
continue;*/
// 結果表示
std::wcout << L"----------------------------------------" << std::endl;
std::wcout << L"Device Instance ID: " << deviceId << std::endl;
if (hasFriendlyName) {
std::wcout << L"FriendlyName: " << friendlyName << std::endl;
}
else {
std::wcout << L"FriendlyName: (取得不可)" << std::endl;
}
if (cr == CR_SUCCESS && (status & DN_HAS_PROBLEM)) {
std::wcout << L"エラー: あり (Problem code: " << problem << L")" << std::endl;
}
else if (cr == CR_SUCCESS) {
std::wcout << L"エラー: なし" << std::endl;
}
else {
std::wcout << L"エラー状態取得失敗" << std::endl;
}
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}
エラーコードについて
👇のコードで取れる「プロブレムコード」の値がなにを表すかは、
ULONG status = 0, problem = 0;
CONFIGRET cr = CM_Get_DevNode_Status(&status, &problem, devInfoData.DevInst, 0);
👇のページで取れる。
知ったことメモ
デバイスインターフェースとは
- 「デバイスインターフェース」というものがある。
- デバイスがOSやアプリケーションとやり取りするために公開する“機能の入り口”
- Windowsでは、1つの物理デバイスが複数の「デバイスインターフェース」を持つことがある。
- USBメモリは、以下のような複数のインターフェースを持てる。
- 「ストレージボリューム」としてのインターフェース(GUID_DEVINTERFACE_VOLUME)
- USBデバイスとしてのインターフェース(GUID_DEVINTERFACE_USB_DEVICE)
- USBメモリは、以下のような複数のインターフェースを持てる。
- 「デバイスインターフェース」は、アプリケーションやドライバがデバイスにアクセスするための論理的な窓口となるもの。
- 物理デバイスそのものではなく、「この機能を持つデバイスの一覧がほしい」といった用途で使われる。
例:
SetupDiGetClassDevsA(&GUID_DEVINTERFACE_VOLUME, ...)
→ 「ボリューム(論理ドライブ)」としてアクセス可能なデバイスのインターフェース一覧を取得
SetupDiGetClassDevsA(&GUID_DEVINTERFACE_USB_DEVICE, ...)
→ 「USBデバイス」としてアクセス可能なインターフェース一覧を取得
つまり、
SetupDiGetClassDevsで、クラス(≒種類)ごとにデバイスをとるときに、ある機能を示すデバイスインターフェースを指定して取得すれば、その機能を持ったデバイスを取得できるということ。
つぎやりたいこと
デバマネの「デバイス(接続別)」で表示したツリー構造のように、
接続を改装で合わらすような情報を取得したい。