Windows 無類似 Android ConnectivityManager.getActiveNetworkInfo() 的直接 API 獲取「當前應用使用的網絡類型(Wi-Fi/有線/蜂窩)」,需自行綜合多個介面特征判斷。
GetAdaptersAddresses() (優於 GetAdaptersInfo) 獲取所有網卡:IfType(IF_TYPE_IEEE80211=Wi-Fi, IF_TYPE_ETHERNET_CSMACD=有線)、OperStatus、Ipv4/IPv6 地址、FriendlyName(含 "Wi-Fi"/"Wireless" 關鍵字)。getsockname() 取得本地綁定的源 IP。IfType、名稱關鍵字、媒體連接狀態綜合判斷。// 簡化邏輯,實際需處理多 IP、多網卡、VPN、虛擬網卡等邊界情況
DWORD GetCurrentNetworkType() {
// 1. 連接目標服務器(或發送 UDP 包)獲取本地源 IP
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in dest = { AF_INET, htons(53), inet_addr("8.8.8.8") }; // Google DNS
connect(s, (sockaddr*)&dest, sizeof(dest));
sockaddr_in local; int len = sizeof(local);
getsockname(s, (sockaddr*)&local, &len);
closesocket(s);
ULONG srcIp = local.sin_addr.S_un.S_addr;
// 2. 枚舉網卡匹配
ULONG bufSize = 15000; PIP_ADAPTER_ADDRESSES pAddr = (PIP_ADAPTER_ADDRESSES)malloc(bufSize);
GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddr, &bufSize);
for (PIP_ADAPTER_ADDRESSES p = pAddr; p; p = p->Next) {
if (p->OperStatus != IfOperStatusUp) continue;
for (PIP_ADAPTER_UNICAST_ADDRESS ua = p->FirstUnicastAddress; ua; ua = ua->Next) {
if (ua->Address.lpSockaddr->sa_family == AF_INET) {
auto ip = ((sockaddr_in*)ua->Address.lpSockaddr)->sin_addr.S_un.S_addr;
if (ip == srcIp) {
// 3. 判斷類型
if (p->IfType == IF_TYPE_IEEE80211) return NET_WIFI;
if (p->IfType == IF_TYPE_ETHERNET_CSMACD) return NET_WIRED;
// 輔助:名稱含 "Wi-Fi"/"Wireless"/"802.11"
wstring name(p->FriendlyName);
if (name.find(L"Wi-Fi") != wstring::npos || name.find(L"Wireless") != wstring::npos)
return NET_WIFI;
return NET_UNKNOWN;
}
}
}
}
free(pAddr);
return NET_UNKNOWN;
}
IfType 可能為 IF_TYPE_TUNNEL、IF_TYPE_PPP,需識別並排除或標記為 VPN。GetAdaptersAddresses 無需管理員權限,但部分 OID 查詢需提權。結論:Windows 需自建「特征庫」(IfType、名稱關鍵字、媒體狀態)配合源 IP 匹配,無現成 API 一鍵獲取,屬系統級網絡編程進階課題。