咨询: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
相关产品推荐
相关产品推荐

