potisanのプログラミングメモ

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

C++20&Win API IPv4アドレスを列挙する

WindowsIPv4アドレスを列挙するサンプルコードです。

GetIpAddrTable関数はメモリに構造体とそこから参照される文字列をまとめて配置します。このコードではそれらをまとめて管理するためにクラスを使用しています。なお、この関数はループバックアドレスも返します。

#include <bit> // bit_cast
#include <format>
#include <iostream>
#include <string>
#include <vector>

#define STRICT
#include <WinSock2.h> // 最初にインクルードしないとコンパイルエラー
#include <Windows.h>
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")

// GetIpAddrTable (iphlpapi.h)の結果を管理します。
class IPAddressTable
{
public:
    IPAddressTable()
    {
        DWORD bufferSize{ 0 };
        m_lastErr = ::GetIpAddrTable(nullptr, &bufferSize, FALSE);
        if (m_lastErr != ERROR_INSUFFICIENT_BUFFER)
            return;

        m_buffer.resize(bufferSize);
        m_lastErr = ::GetIpAddrTable(
            std::bit_cast<PMIB_IPADDRTABLE>(m_buffer.data()),
            &bufferSize, FALSE);
        if (m_lastErr != NO_ERROR)
        {
            m_buffer.clear();
            return;
        }
    }

    constexpr const MIB_IPADDRTABLE* GetIPAddressTable() const noexcept {
        return std::bit_cast<const MIB_IPADDRTABLE*>(m_buffer.data());
    }

    constexpr DWORD GetNumberOfTable() const noexcept {
        return GetIPAddressTable()->dwNumEntries;
    }

    constexpr DWORD GetLastError() const noexcept {
        return m_lastErr;
    }

    // range-based for対応
    constexpr const MIB_IPADDRROW* begin() const noexcept {
        return &GetIPAddressTable()->table[0];
    }

    constexpr const MIB_IPADDRROW* end() const noexcept {
        return &GetIPAddressTable()->table[GetNumberOfTable()];
    }

    // ranges対応
    constexpr size_t size() const noexcept {
        return GetNumberOfTable();
    }

private:
    std::vector<BYTE> m_buffer;
    DWORD m_lastErr;

private:
    IPAddressTable(const IPAddressTable&) = delete;
    IPAddressTable(IPAddressTable&&) = delete;
};

// MIB_IPADDRROW_XP構造体のwTypeメンバーを対応する定数の名前に変換します。
// 不明の場合は空文字列を返します。
std::wstring IPAddrRowXPTypeToStringW(unsigned short type)
{
    std::wstring s;
    if (type & MIB_IPADDR_PRIMARY) s += L"MIB_IPADDR_PRIMARY, ";
    if (type & MIB_IPADDR_DYNAMIC) s += L"MIB_IPADDR_DYNAMIC, ";
    if (type & MIB_IPADDR_DISCONNECTED) s += L"MIB_IPADDR_DISCONNECTED, ";
    if (type & MIB_IPADDR_DELETED) s += L"MIB_IPADDR_DELETED, ";
    if (type & MIB_IPADDR_TRANSIENT) s += L"MIB_IPADDR_TRANSIENT, ";
    if (type & MIB_IPADDR_DNS_ELIGIBLE) s += L"MIB_IPADDR_DNS_ELIGIBLE, ";
    return s.size() != 0 ? s.substr(0, s.size() - 2) : {};
}

// IPV4アドレスを0.0.0.0形式の文字列に変換します。
std::wstring IPv4ToStringW(DWORD ipv4) noexcept
{
    auto b = std::bit_cast<const BYTE*>(&ipv4);
    return std::format(L"{}.{}.{}.{}", b[0], b[1], b[2], b[3]);
}

int main()
{
    for (auto& table : IPAddressTable())
    {
        std::wcout
            << L"dwAttr: " << IPv4ToStringW(table.dwAddr) << std::endl
            << L"dwIndex: " << table.dwIndex << std::endl
            << L"dwMask: " << IPv4ToStringW(table.dwMask) << std::endl
            << L"dwBCastAddr: " << IPv4ToStringW(table.dwBCastAddr) << std::endl
            << L"dwReasmSize: " << table.dwReasmSize << std::endl
            << L"unused1: " << table.unused1 << std::endl
            << L"wType: " << IPAddrRowXPTypeToStringW(table.wType) << std::endl
            << std::endl;
    }

    return 0;
}