일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- matter
- 홈네트워크
- ConnectedHomeIP
- 힐스테이트 광교산
- 나스닥
- MQTT
- 매터
- 해외주식
- 빅데이터분석기사
- 미국주식
- esp32
- 배당
- 파이썬
- homebridge
- Espressif
- Python
- 애플
- 공모주
- 월패드
- 현대통신
- Apple
- 국내주식
- RS-485
- SK텔레콤
- Bestin
- 라즈베리파이
- Home Assistant
- 주식
- raspberry pi
- cluster
- Today
- Total
YOGYUI
MFC::SetupAPI - 장치관리자(Device Manager) 정보 얻기 본문
윈도 OS가 설치된 PC에서 소프트웨어 구동 시 특정 장치의 드라이버가 설치되어있는지 여부를 확인해야 한다는 고객 요구사항이 있어 이리저리 구글링해본뒤 직접 구현해보았다
그 중 개발자들에게 도움이 될만한 기본적인 내용을 간단히 적어본다
MFC의 Win32 API 중 SetupAPI를 활용하면 제어판의 장치 관리자에서 접근 가능한 거의 모든 정보를 열람할 수 있다
https://docs.microsoft.com/en-us/windows/win32/api/setupapi/
API 문서를 참고해서 다음과 같이 구현해봤다 (GitHub 링크)
※ 유니코드 문자집합 사용
외부종속성은 setupapi.lib 하나만 링크해주면 된다
#define _AFXDLL
#include <iostream>
#include <afxwin.h>
#include <SetupAPI.h>
#include <locale.h>
#pragma comment(lib, "setupapi.lib")
void test() {
HDEVINFO hDevInfo;
SP_DEVINFO_DATA stDevInfoData = SP_DEVINFO_DATA();
hDevInfo = SetupDiGetClassDevs(
0L,
0L,
0L,
DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_PROFILE
);
if (hDevInfo == INVALID_HANDLE_VALUE)
return;
stDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &stDevInfoData); i++) {
TCHAR szInstanceId [MAX_PATH] = { 0 };
TCHAR szClassName [MAX_PATH] = { 0 };
TCHAR szFriendlyName [MAX_PATH] = { 0 };
TCHAR szClassDescription [MAX_PATH] = { 0 };
TCHAR szDeviceDescription[MAX_PATH] = { 0 };
// Get Device Instance ID
BOOL bResult = SetupDiGetDeviceInstanceId(
hDevInfo,
&stDevInfoData,
szInstanceId,
_countof(szInstanceId),
0
);
if (!bResult) {
_tprintf(_T("Failed to get device instance ID\n"));
continue;
}
(VOID)SetupDiGetDeviceRegistryProperty(
hDevInfo,
&stDevInfoData,
SPDRP_CLASS,
0,
(PBYTE)szClassName,
_countof(szClassName),
0
);
(VOID)SetupDiGetDeviceRegistryProperty(
hDevInfo,
&stDevInfoData,
SPDRP_DEVICEDESC,
0,
(PBYTE)szDeviceDescription,
_countof(szDeviceDescription),
0
);
(VOID)SetupDiGetDeviceRegistryProperty(
hDevInfo,
&stDevInfoData,
SPDRP_FRIENDLYNAME,
0,
(PBYTE)szFriendlyName,
_countof(szFriendlyName),
0
);
(VOID)SetupDiGetClassDescription(
&stDevInfoData.ClassGuid,
szClassDescription,
_countof(szClassDescription),
0
);
_tprintf(_T("[%d]\n"), i);
_tprintf(_T("-- Class: %s\n"), szClassName);
_tprintf(_T("-- Friendly Name: %s\n"), szFriendlyName);
_tprintf(_T("-- Instance ID: %s\n"), szInstanceId);
_tprintf(_T("-- Class Description: %s\n"), szClassDescription);
_tprintf(_T("-- Device Description: %s\n"), szDeviceDescription);
_tprintf(_T("\n"));
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
int main() {
setlocale(LC_ALL, "korean");
_wsetlocale(LC_ALL, L"korean");
test();
}
실행해보면 다음과 같이 콘솔창에 디바이스의 정보가 출력된다
[0]
-- Class: System
-- Friendly Name:
-- Instance ID: PCI\VEN_8086&DEV_6F90&SUBSYS_6F908086&REV_01\3&103A9D54&0&48
-- Class Description: 시스템 장치
-- Device Description: Intel(R) Xeon(R) E7 v4/Xeon(R) E5 v4/Xeon(R) E3 v4/Xeon(R) D QPI Link 1 - 6F90
[1]
-- Class: System
-- Friendly Name:
-- Instance ID: PCI\VEN_8086&DEV_6FD1&SUBSYS_6FD18086&REV_01\3&103A9D54&0&B9
-- Class Description: 시스템 장치
-- Device Description: Intel(R) Xeon(R) E7 v4/Xeon(R) E5 v4/Xeon(R) E3 v4/Xeon(R) D Memory Controller 1 - Channel 1 Thermal Control - 6FD1
[2]
-- Class: System
-- Friendly Name:
-- Instance ID: PCI\VEN_8086&DEV_6F68&SUBSYS_6F688086&REV_01\3&103A9D54&0&B0
-- Class Description: 시스템 장치
-- Device Description: Intel(R) Xeon(R) E7 v4/Xeon(R) E5 v4/Xeon(R) E3 v4/Xeon(R) D Target Address/Thermal/RAS - 6F68
[3]
-- Class: System
-- Friendly Name:
-- Instance ID: PCI\VEN_8086&DEV_6FBA&SUBSYS_00000000&REV_01\3&1C6B4348&0&BE
-- Class Description: 시스템 장치
-- Device Description: Intel(R) Xeon(R) E7 v4/Xeon(R) E5 v4/Xeon(R) E3 v4/Xeon(R) D DDRIO Channel 2/3 Interface - 6FBA
[4]
-- Class: VolumeSnapshot
-- Friendly Name:
-- Instance ID: STORAGE\VOLUMESNAPSHOT\HARDDISKVOLUMESNAPSHOT100
-- Class Description: 저장소 볼륨 섀도 복사본
-- Device Description: 일반 볼륨 섀도 복사본
(... 후략 ...)
디바이스 하나 골라서 제어판 장치관리자의 정보들과 비교해보자
[870]
-- Class: USB
-- Friendly Name:
-- Instance ID: USB\VID_0403&PID_601F&MI_00\6&1F7B2494&0&0000
-- Class Description: 범용 직렬 버스 컨트롤러
-- Device Description: FTDI FT601 USB 3.0 Bridge Device
SetupDiGetDeviceRegistryProperty 함수의 세번째 인자 (DWORD Property) 값을 바꿔가면서 다양한 정보를 얻을 수 있다
// SetupAPI.h
//
// Device registry property codes
// (Codes marked as read-only (R) may only be used for
// SetupDiGetDeviceRegistryProperty)
//
// These values should cover the same set of registry properties
// as defined by the CM_DRP codes in cfgmgr32.h.
//
// Note that SPDRP codes are zero based while CM_DRP codes are one based!
//
#define SPDRP_DEVICEDESC (0x00000000) // DeviceDesc (R/W)
#define SPDRP_HARDWAREID (0x00000001) // HardwareID (R/W)
#define SPDRP_COMPATIBLEIDS (0x00000002) // CompatibleIDs (R/W)
#define SPDRP_UNUSED0 (0x00000003) // unused
#define SPDRP_SERVICE (0x00000004) // Service (R/W)
#define SPDRP_UNUSED1 (0x00000005) // unused
#define SPDRP_UNUSED2 (0x00000006) // unused
#define SPDRP_CLASS (0x00000007) // Class (R--tied to ClassGUID)
#define SPDRP_CLASSGUID (0x00000008) // ClassGUID (R/W)
#define SPDRP_DRIVER (0x00000009) // Driver (R/W)
#define SPDRP_CONFIGFLAGS (0x0000000A) // ConfigFlags (R/W)
#define SPDRP_MFG (0x0000000B) // Mfg (R/W)
#define SPDRP_FRIENDLYNAME (0x0000000C) // FriendlyName (R/W)
#define SPDRP_LOCATION_INFORMATION (0x0000000D) // LocationInformation (R/W)
#define SPDRP_PHYSICAL_DEVICE_OBJECT_NAME (0x0000000E) // PhysicalDeviceObjectName (R)
#define SPDRP_CAPABILITIES (0x0000000F) // Capabilities (R)
#define SPDRP_UI_NUMBER (0x00000010) // UiNumber (R)
#define SPDRP_UPPERFILTERS (0x00000011) // UpperFilters (R/W)
#define SPDRP_LOWERFILTERS (0x00000012) // LowerFilters (R/W)
#define SPDRP_BUSTYPEGUID (0x00000013) // BusTypeGUID (R)
#define SPDRP_LEGACYBUSTYPE (0x00000014) // LegacyBusType (R)
#define SPDRP_BUSNUMBER (0x00000015) // BusNumber (R)
#define SPDRP_ENUMERATOR_NAME (0x00000016) // Enumerator Name (R)
#define SPDRP_SECURITY (0x00000017) // Security (R/W, binary form)
#define SPDRP_SECURITY_SDS (0x00000018) // Security (W, SDS form)
#define SPDRP_DEVTYPE (0x00000019) // Device Type (R/W)
#define SPDRP_EXCLUSIVE (0x0000001A) // Device is exclusive-access (R/W)
#define SPDRP_CHARACTERISTICS (0x0000001B) // Device Characteristics (R/W)
#define SPDRP_ADDRESS (0x0000001C) // Device Address (R)
#define SPDRP_UI_NUMBER_DESC_FORMAT (0X0000001D) // UiNumberDescFormat (R/W)
#define SPDRP_DEVICE_POWER_DATA (0x0000001E) // Device Power Data (R)
#define SPDRP_REMOVAL_POLICY (0x0000001F) // Removal Policy (R)
#define SPDRP_REMOVAL_POLICY_HW_DEFAULT (0x00000020) // Hardware Removal Policy (R)
#define SPDRP_REMOVAL_POLICY_OVERRIDE (0x00000021) // Removal Policy Override (RW)
#define SPDRP_INSTALL_STATE (0x00000022) // Device Install State (R)
#define SPDRP_LOCATION_PATHS (0x00000023) // Device Location Paths (R)
#define SPDRP_BASE_CONTAINERID (0x00000024) // Base ContainerID (R)
#define SPDRP_MAXIMUM_PROPERTY (0x00000025) // Upper bound on ordinals
장치 설명 (Device Description) 문자열을 기반으로 존재하는지 여부는 다음과 같이 구현하면 간편하다
#include <vector>
void test2(std::vector<CString>& avecDesc)
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA stDevInfoData = SP_DEVINFO_DATA();
hDevInfo = SetupDiGetClassDevs(
0L,
0L,
0L,
DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_PROFILE
);
if (hDevInfo == INVALID_HANDLE_VALUE)
return;
stDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &stDevInfoData); i++) {
TCHAR szDeviceDescription[MAX_PATH] = { 0 };
(VOID)SetupDiGetDeviceRegistryProperty(
hDevInfo,
&stDevInfoData,
SPDRP_DEVICEDESC,
0,
(PBYTE)szDeviceDescription,
_countof(szDeviceDescription),
0
);
// _tprintf(_T("%s\n"), szDeviceDescription);
avecDesc.push_back(szDeviceDescription);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
int main()
{
LARGE_INTEGER liStart;
LARGE_INTEGER liEnd;
LARGE_INTEGER liFreq;
QueryPerformanceFrequency(&liFreq);
QueryPerformanceCounter(&liStart);
std::vector<CString> vecDev;
test2(vecDev);
QueryPerformanceCounter(&liEnd);
double fTimeElapsedMs = (double)(liEnd.QuadPart - liStart.QuadPart) / (double)liFreq.QuadPart * 1000;
_tprintf(_T("Total Device Count: %lld, Elapsed: %f msec\n"), vecDev.size(), fTimeElapsedMs);
auto idx = std::find(vecDev.begin(), vecDev.end(), _T("FTDI FT601 USB 3.0 Bridge Device"));
if (idx == vecDev.end()) {
_tprintf(_T("Cannot find device"));
}
else {
_tprintf(_T("Found device (index: %lld)"), idx - vecDev.begin());
}
}
실행결과
Total Device Count: 909, Elapsed: 2118.421500 msec
Found device (index: 870)
909개 디바이스들의 장치 설명(SPDRP_DEVICEDESC)을 가져오는데 2초 정도 소요된다
시간이 적지 않게 걸리므로 어플리케이션 초기화 시 호출하거나, 사용자에 의한 refresh 명령이 호출될때만 구동하는 것이 좋을 것 같다
벡터에 문자열들을 집어넣은 뒤, std::find 함수로 존재하는지 여부를 확인할 수 있다
참 쉽쥬?
여유 시간 짬짬이 장치관리자를 트리뷰같은걸 써서 직접 만들어보는 것도 재밌을 것 같다
[참고]
https://www.codeproject.com/Articles/14469/Simple-Device-Manager
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=blue7red&logNo=100063168724
'Software > C, C++' 카테고리의 다른 글
C++::chrono - 현재 날짜/시간 가져오기 (밀리초 포함) (0) | 2022.07.24 |
---|---|
MFC::프로그램으로 PC 전원 끄기 (Windows OS) (0) | 2022.02.23 |
C/C++::CRC8, CRC16, CRC32 계산 라이브러리 깃허브 등록 (DLL) (0) | 2021.10.22 |
C++::CRC-16 계산 알고리즘 구현 (소스코드) (2) | 2021.10.13 |
std::vector 내부 요소에서 시퀀스 찾기 (find sub-sequence) (0) | 2021.10.12 |