potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20&Win API 無線ネットワークのシグナル品質を列挙する

ネイティブWiFi APIを使って無線ネットワークのシグナル強度を列挙するコードです。いくつかのネイティブWiFi APIは独自メモリに確保した構造体に可変長配列を持ちますが、std::unique_ptrstd::spanを使えば可読性と安全性をまとめて担保できます。

シグナル品質とデシベルの変換はWLAN_ASSOCIATION_ATTRIBUTES構造体のドキュメントを参照ください。

#include <span>
#include <string>
#include <memory>
#include <optional>

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

namespace win32
{
    struct wlanhandle_delete sealed {
        inline void operator() (HANDLE handle) const noexcept {
            ::WlanCloseHandle(handle, nullptr);
        }
    };
    using unique_wlanhandle = std::unique_ptr<std::remove_pointer_t<HANDLE>, wlanhandle_delete>;

    unique_wlanhandle WlanOpenHandle(
        DWORD dwClientVersion,
        PDWORD pdwNegotiatedVersion = nullptr) noexcept
    {
        HANDLE handle;
        DWORD negotiatedVersion;
        if (::WlanOpenHandle(dwClientVersion, nullptr, &negotiatedVersion, &handle) != ERROR_SUCCESS)
            return {};
        if (pdwNegotiatedVersion)
            *pdwNegotiatedVersion = negotiatedVersion;
        return unique_wlanhandle{handle};
    }

    struct wlanmem_delete sealed {
        inline void operator() (LPVOID p) const noexcept {
            ::WlanFreeMemory(p);
        }
    };
    template <typename T>
    using unique_wlanmem = std::unique_ptr<T, wlanmem_delete>;

    unique_wlanmem<WLAN_INTERFACE_INFO_LIST> WlanEnumInterfaces(
        HANDLE hClientHandle)
    {
        PWLAN_INTERFACE_INFO_LIST p;
        if (::WlanEnumInterfaces(hClientHandle, nullptr, &p) != ERROR_SUCCESS)
            return {};
        return unique_wlanmem<WLAN_INTERFACE_INFO_LIST>{p};
    }

    unique_wlanmem<WLAN_INTERFACE_CAPABILITY> WlanGetInterfaceCapability(
        HANDLE hClientHandle,
        CONST GUID* pInterfaceGuid)
    {
        PWLAN_INTERFACE_CAPABILITY p;
        if (::WlanGetInterfaceCapability(hClientHandle, pInterfaceGuid, nullptr, &p) != ERROR_SUCCESS)
            return {};
        return unique_wlanmem<WLAN_INTERFACE_CAPABILITY>{p};
    }

    unique_wlanmem<WLAN_PROFILE_INFO_LIST> WlanGetProfileList(
        HANDLE hClientHandle,
        CONST GUID* pInterfaceGuid)
    {
        PWLAN_PROFILE_INFO_LIST p;
        if (::WlanGetProfileList(hClientHandle, pInterfaceGuid, nullptr, &p) != ERROR_SUCCESS)
            return {};
        return unique_wlanmem<WLAN_PROFILE_INFO_LIST>{p};
    }

    std::wstring_view WLAN_INTERFACE_TYPE_ToString(WLAN_INTERFACE_TYPE x) noexcept
    {
        const wchar_t* names[] = {
            L"wlan_interface_type_emulated_802_11",
            L"wlan_interface_type_native_802_11",
            L"wlan_interface_type_invalid"
        };
        if (0 <= x && x <= std::ssize(names))
            return names[x];
        return {};
    }

    std::optional<WLAN_CONNECTION_ATTRIBUTES> WlanQueryInterfaceConnectionAttributes(
        HANDLE hClientHandle,
        const GUID* pInterfaceGuid)
    {
        DWORD size{};
        PWLAN_CONNECTION_ATTRIBUTES pdata{};
        auto ret = ::WlanQueryInterface(hClientHandle, pInterfaceGuid,
            WLAN_INTF_OPCODE::wlan_intf_opcode_current_connection,
            nullptr, &size, reinterpret_cast<void**>(&pdata), nullptr);
        if (ret != ERROR_SUCCESS)
            return {};
        auto attrs = *pdata;
        ::WlanFreeMemory(pdata);
        return attrs;

    }
}

#include <iostream>

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

    auto handle{win32::WlanOpenHandle(WLAN_API_VERSION)};
    if (!handle) return -1;

    auto interfaces{win32::WlanEnumInterfaces(handle.get())};
    for (const auto& interfaceInfo : std::span(interfaces->InterfaceInfo, interfaces->dwNumberOfItems))
    {
        auto capability{win32::WlanGetInterfaceCapability(handle.get(), &interfaceInfo.InterfaceGuid)};
        auto connectAttrs{win32::WlanQueryInterfaceConnectionAttributes(handle.get(), &interfaceInfo.InterfaceGuid)};

        auto interfaceType{capability ? win32::WLAN_INTERFACE_TYPE_ToString(capability->interfaceType) : L"(不明)"};
        auto signalQuality{connectAttrs ? std::to_wstring(connectAttrs->wlanAssociationAttributes.wlanSignalQuality) : L"(不明)"};

        std::wcout << interfaceInfo.strInterfaceDescription << std::endl;
        std::wcout << L" Interface Type: " << interfaceType << std::endl;
        std::wcout << L" Signal Quality: " << signalQuality << std::endl;
        std::wcout << L" Profiles:" << std::endl;
        auto profiles{win32::WlanGetProfileList(handle.get(), &interfaceInfo.InterfaceGuid)};
        for (const auto& profileInfo : std::span(profiles->ProfileInfo, profiles->dwNumberOfItems))
        {
            std::wcout << L"  " << profileInfo.strProfileName << std::endl;
        }
    }

    return 0;
}