potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。

C++20&Win API デバイス情報要素の文字列プロパティ(概要、クラス、クラスGUID、フレンドリーネーム、ハードウェアID)を列挙・整理する

バイス情報要素の文字列プロパティ(概要、クラス、クラスGUID、フレンドリーネーム、ハードウェアID)を列挙するコードです。再利用可能なコードをUtility.hppへ移動しています。

文字列プロパティ(概要、クラス、クラスGUID、フレンドリーネーム、ハードウェアID)を列挙する

Main.cpp

#pragma comment(lib, "SetupAPI.lib")
#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>

#include "Utility.hpp"

#include <iostream>
#include <format>

int main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    auto DevInfoHandle{ unique_SetupDiGetClassDevsW(
        nullptr,
        nullptr,
        nullptr,
        DIGCF_ALLCLASSES) };

    // ループ中で繰り返し使用するバッファーの用意
    std::wstring DevDesc;
    std::wstring DevClass;
    std::wstring DevClassGuid;
    std::wstring DevFriendlyName;
    std::wstring DevHardwareId;

    std::wcout << L"ClassGuid, DevInst, SPDRP_DEVICEDESC, SPDRP_CLASS, SPDRP_CLASSGUID, SPDRP_FRIENDLYNAME, SPDRP_HARDWAREID" << std::endl;
    for (auto& DevInfoData : SetupDiCreateDeviceInfoDataList(DevInfoHandle.get()))
    {
        SetupDiGetDeviceRegistryPropertyAsStringW(DevDesc,
            DevInfoHandle.get(), &DevInfoData, SPDRP_DEVICEDESC);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevClass,
            DevInfoHandle.get(), &DevInfoData, SPDRP_CLASS);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevClassGuid,
            DevInfoHandle.get(), &DevInfoData, SPDRP_CLASSGUID);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevFriendlyName,
            DevInfoHandle.get(), &DevInfoData, SPDRP_FRIENDLYNAME);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevHardwareId,
            DevInfoHandle.get(), &DevInfoData, SPDRP_HARDWAREID);

        std::wcout
            << std::format(L"{}, {}, {}, {}, {}, {}, {}",
                GUIDToWString(DevInfoData.ClassGuid),
                DevInfoData.DevInst,
                DevDesc.c_str(),
                DevClass.c_str(),
                DevClassGuid.c_str(),
                DevFriendlyName.c_str(),
                DevHardwareId.c_str())
            << std::endl;
    }
}

Utility.hpp

//-----------------------------------------------------------------------------
// Utility.hpp
// 再利用可能なコードを定義します。
//-----------------------------------------------------------------------------

#pragma once

#include <bit>
#include <memory>
#include <string>
#include <numeric>
#include <list>

//-----------------------------------------------------------------------------
// GUID -> wstring
//-----------------------------------------------------------------------------

std::wstring GUIDToWString(const GUID& guid)
{
    std::wstring s(38, L'\0');
    if (FAILED(::StringFromGUID2(guid, s.data(), 38)))
        return {};
    return std::move(s);
}

//-----------------------------------------------------------------------------
// Setup API wrappers
//-----------------------------------------------------------------------------

struct HDEVINFO_deleter {
    void operator() (HDEVINFO handle) const noexcept {
        ::SetupDiDestroyDeviceInfoList(handle);
    }
};
using unique_HDEVINFO = std::unique_ptr<std::remove_pointer_t<HDEVINFO>, HDEVINFO_deleter>;

unique_HDEVINFO unique_SetupDiGetClassDevsW(
    const GUID* ClassGuid,
    LPCWSTR Enumerator,
    HWND hWndParent,
    DWORD Flags) noexcept
{
    return unique_HDEVINFO(::SetupDiGetClassDevsW(ClassGuid, Enumerator, hWndParent, Flags));
}

std::list<SP_DEVINFO_DATA> SetupDiCreateDeviceInfoDataList(HDEVINFO DeviceInfoSet)
{
    std::list<SP_DEVINFO_DATA> DevInfoDatas;
    for (DWORD i = 0; i < std::numeric_limits<DWORD>::max(); i++)
    {
        SP_DEVINFO_DATA DevInfoData{ sizeof(SP_DEVINFO_DATA) };
        if (!SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DevInfoData))
            break;
        DevInfoDatas.emplace_back(DevInfoData);
    }
    return std::move(DevInfoDatas);
}

BOOL SetupDiGetDeviceRegistryPropertyAsStringW(
    std::wstring& result,
    HDEVINFO DeviceInfoSet,
    PSP_DEVINFO_DATA DeviceInfoData,
    DWORD Property)
{
    DWORD RequiredSize;
    DWORD PropertyRegDataType;
    if (!::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, &PropertyRegDataType, nullptr, 0, &RequiredSize)
        && ::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return FALSE;
    }
    if (PropertyRegDataType != REG_SZ)
    {
        ::SetLastError(ERROR_INVALID_DATATYPE);
        return FALSE;
    }
    result.resize(RequiredSize);
    return ::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, nullptr, std::bit_cast<PBYTE>(result.data()), result.size(), nullptr);
}

クラスを列挙する

//-----------------------------------------------------------------------------
// Utility.hpp
// 再利用可能なコードを定義します。
//-----------------------------------------------------------------------------

#pragma once

#include <bit>
#include <memory>
#include <string>
#include <numeric>
#include <list>

//-----------------------------------------------------------------------------
// GUID -> wstring
//-----------------------------------------------------------------------------

std::wstring GUIDToWString(const GUID& guid)
{
    std::wstring s(38, L'\0');
    if (FAILED(::StringFromGUID2(guid, s.data(), 38)))
        return {};
    return std::move(s);
}

//-----------------------------------------------------------------------------
// Setup API wrappers
//-----------------------------------------------------------------------------

struct HDEVINFO_deleter {
    void operator() (HDEVINFO handle) const noexcept {
        ::SetupDiDestroyDeviceInfoList(handle);
    }
};
using unique_HDEVINFO = std::unique_ptr<std::remove_pointer_t<HDEVINFO>, HDEVINFO_deleter>;

unique_HDEVINFO unique_SetupDiGetClassDevsW(
    const GUID* ClassGuid,
    LPCWSTR Enumerator,
    HWND hWndParent,
    DWORD Flags) noexcept
{
    return unique_HDEVINFO(::SetupDiGetClassDevsW(ClassGuid, Enumerator, hWndParent, Flags));
}

std::list<SP_DEVINFO_DATA> SetupDiCreateDeviceInfoDataList(HDEVINFO DeviceInfoSet)
{
    std::list<SP_DEVINFO_DATA> DevInfoDatas;
    for (DWORD i = 0; i < std::numeric_limits<DWORD>::max(); i++)
    {
        SP_DEVINFO_DATA DevInfoData{ sizeof(SP_DEVINFO_DATA) };
        if (!SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DevInfoData))
            break;
        DevInfoDatas.emplace_back(DevInfoData);
    }
    return std::move(DevInfoDatas);
}

BOOL SetupDiGetDeviceRegistryPropertyAsStringW(
    std::wstring& result,
    HDEVINFO DeviceInfoSet,
    PSP_DEVINFO_DATA DeviceInfoData,
    DWORD Property)
{
    DWORD RequiredSize;
    DWORD PropertyRegDataType;
    if (!::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, &PropertyRegDataType, nullptr, 0, &RequiredSize)
        && ::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return FALSE;
    }
    if (PropertyRegDataType != REG_SZ)
    {
        ::SetLastError(ERROR_INVALID_DATATYPE);
        return FALSE;
    }
    result.resize(RequiredSize);
    return ::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, nullptr, std::bit_cast<PBYTE>(result.data()),
        static_cast<DWORD>(result.size()), nullptr);
}

クラスに対応するデバイス情報要素の概要を列挙する

#pragma comment(lib, "SetupAPI.lib")
#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>

#include "Utility.hpp"

#include <iostream>
#include <set>

int main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    auto DevInfoHandle{ unique_SetupDiGetClassDevsW(
        nullptr,
        nullptr,
        nullptr,
        DIGCF_ALLCLASSES) };

    std::set<std::wstring> DevClassSet;
    std::wstring Buffer; // ループ中で繰り返し使用するバッファー
    for (auto& DevInfoData : SetupDiCreateDeviceInfoDataList(DevInfoHandle.get()))
    {
        SetupDiGetDeviceRegistryPropertyAsStringW(Buffer,
            DevInfoHandle.get(), &DevInfoData, SPDRP_CLASS);
        DevClassSet.emplace(Buffer.c_str());
    }

    // クラスの列挙
    for (const auto& DevClass : DevClassSet)
    {
        wcout << DevClass << endl;
    }
}