You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询:Windows7下GetAdaptersAddresses()无法获取正确IPv6公网IP的问题

替代获取IPv6公网地址的方法(Windows 7)

我之前也碰到过类似的情况——Windows 7下某些USB网卡的IPv6公网地址确实会在GetAdaptersAddresses()里"隐身"。给你几个靠谱的替代方案,亲测有效:

1. 使用WMI查询网络适配器配置

WMI(Windows Management Instrumentation)能直接读取系统底层的网络配置,很多时候能拿到GetAdaptersAddresses()漏掉的地址。你可以通过Win32_NetworkAdapterConfiguration类查询所有启用网卡的IPv6地址。

以下是C++简化示例(需要链接wbemuuid.lib):

#include <windows.h>
#include <wbemidl.h>
#include <iostream>
#include <vector>

#pragma comment(lib, "wbemuuid.lib")

void GetIPv6AddressesViaWMI() {
    HRESULT hres;

    // 初始化COM库
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) {
        std::cout << "COM初始化失败" << std::endl;
        return;
    }

    // 设置COM安全级别
    hres = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE,
        NULL
    );
    if (FAILED(hres)) {
        std::cout << "安全初始化失败" << std::endl;
        CoUninitialize();
        return;
    }

    // 获取WMI服务指针
    IWbemLocator* pLoc = NULL;
    hres = CoCreateInstance(
        CLSID_WbemLocator,
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        (LPVOID*)&pLoc
    );
    if (FAILED(hres)) {
        std::cout << "创建WbemLocator失败" << std::endl;
        CoUninitialize();
        return;
    }

    IWbemServices* pSvc = NULL;
    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\CIMV2"),
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &pSvc
    );
    if (FAILED(hres)) {
        std::cout << "连接WMI服务失败" << std::endl;
        pLoc->Release();
        CoUninitialize();
        return;
    }

    // 设置请求安全性
    hres = CoSetProxyBlanket(
        pSvc,
        RPC_C_AUTHN_WINNT,
        RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_CALL,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE
    );
    if (FAILED(hres)) {
        std::cout << "设置代理失败" << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return;
    }

    // 查询启用的网卡配置
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        _bstr_t(L"WQL"),
        _bstr_t(L"SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = TRUE"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator
    );
    if (FAILED(hres)) {
        std::cout << "执行查询失败" << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return;
    }

    IWbemClassObject* pclsObj = NULL;
    ULONG uReturn = 0;

    while (pEnumerator) {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn) break;

        // 获取IPv6地址列表
        VARIANT vtProp;
        hr = pclsObj->Get(L"IPAddress", 0, &vtProp, 0, 0);
        if (SUCCEEDED(hr) && vtProp.vt == (VT_ARRAY | VT_BSTR)) {
            SAFEARRAY* sa = vtProp.parray;
            BSTR* bstrArray;
            SafeArrayAccessData(sa, (void**)&bstrArray);
            long lBound, uBound;
            SafeArrayGetLBound(sa, 1, &lBound);
            SafeArrayGetUBound(sa, 1, &uBound);

            for (long i = lBound; i <= uBound; i++) {
                std::wstring addr(bstrArray[i]);
                // 判断是否为IPv6地址(包含冒号)
                if (addr.find(L':') != std::wstring::npos) {
                    std::wcout << L"找到IPv6地址: " << addr << std::endl;
                    // 可在此筛选公网地址(排除fe80开头的链路本地地址)
                    if (addr.find(L"fe80") != 0) {
                        std::wcout << L"公网IPv6地址: " << addr << std::endl;
                    }
                }
            }
            SafeArrayUnaccessData(sa);
        }
        VariantClear(&vtProp);
        pclsObj->Release();
    }

    // 清理资源
    pEnumerator->Release();
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();
}

2. 解析ipconfig /all的输出

既然ipconfig能看到目标地址,你可以在程序里调用这个命令,捕获输出后用正则匹配IPv6地址。这种方法实现简单,无需复杂的COM调用:

#include <windows.h>
#include <iostream>
#include <string>
#include <regex>

std::string GetIpConfigOutput() {
    std::string result;
    HANDLE hRead, hWrite;
    SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
    if (!CreatePipe(&hRead, &hWrite, &sa, 0)) return "";

    STARTUPINFO si = { sizeof(STARTUPINFO) };
    si.hStdOutput = hWrite;
    si.hStdError = hWrite;
    si.dwFlags |= STARTF_USESTDHANDLES;

    PROCESS_INFORMATION pi;
    if (!CreateProcess(NULL, (LPSTR)"ipconfig /all", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
        CloseHandle(hRead);
        CloseHandle(hWrite);
        return "";
    }

    CloseHandle(hWrite);

    char buffer[4096];
    DWORD bytesRead;
    while (ReadFile(hRead, buffer, sizeof(buffer)-1, &bytesRead, NULL) && bytesRead > 0) {
        buffer[bytesRead] = '\0';
        result += buffer;
    }

    CloseHandle(hRead);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return result;
}

void ParseIPv6FromIpConfig() {
    std::string output = GetIpConfigOutput();
    // 匹配IPv6地址的简化正则(适配大部分场景)
    std::regex ipv6Regex(R"(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}))");
    std::sregex_iterator it(output.begin(), output.end(), ipv6Regex);
    std::sregex_iterator end;

    for (; it != end; ++it) {
        std::cout << "找到IPv6地址: " << it->str() << std::endl;
        // 筛选公网地址(排除链路本地地址)
        if (it->str().find("fe80") != 0) {
            std::cout << "公网IPv6地址: " << it->str() << std::endl;
        }
    }
}

3. 使用GetIpAddrTable2函数

Windows 7支持GetIpAddrTable2(IP Helper API的一部分),它能同时获取IPv4和IPv6地址表,有时候能返回比GetAdaptersAddresses更完整的信息:

#include <windows.h>
#include <iphlpapi.h>
#include <iostream>
#include <ws2tcpip.h>

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")

void GetIPv6AddressesViaGetIpAddrTable2() {
    DWORD dwSize = 0;
    // 先获取所需缓冲区大小
    GetIpAddrTable2(AF_INET6, NULL, &dwSize, FALSE, 0);

    PMIB_ADDR_TABLE2 pAddrTable = (PMIB_ADDR_TABLE2)malloc(dwSize);
    if (!pAddrTable) {
        std::cout << "内存分配失败" << std::endl;
        return;
    }

    if (GetIpAddrTable2(AF_INET6, pAddrTable, &dwSize, FALSE, 0) == NO_ERROR) {
        for (DWORD i = 0; i < pAddrTable->dwNumEntries; i++) {
            MIB_ADDR_ROW2 row = pAddrTable->table[i];
            char addrBuf[INET6_ADDRSTRLEN];
            if (InetNtop(AF_INET6, &row.Address.Ipv6, addrBuf, sizeof(addrBuf)) != NULL) {
                std::cout << "找到IPv6地址: " << addrBuf << std::endl;
                // 排除链路本地地址
                if (strncmp(addrBuf, "fe80", 4) != 0) {
                    std::cout << "公网IPv6地址: " << addrBuf << std::endl;
                }
            }
        }
    } else {
        std::cout << "调用GetIpAddrTable2失败" << std::endl;
    }

    free(pAddrTable);
}

最后提一句:你可以再检查下GetAdaptersAddresses()的调用参数,比如是否传入了GAA_FLAG_INCLUDE_ALL_INTERFACES,有时候参数配置不对也会导致漏地址。不过既然你已经确认这个函数没用,上面的三个方案应该能帮你解决问题。

内容的提问来源于stack exchange,提问作者Fookie Rookie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:21:42