GetAddrInfoExA 函式 (ws2tcpip.h)

GetAddrInfoEx 函式會提供與通訊協議無關的名稱解析,並提供其他參數來限定哪些命名空間提供者應該處理要求。

語法

INT WSAAPI GetAddrInfoExA(
  [in, optional]  PCSTR                              pName,
  [in, optional]  PCSTR                              pServiceName,
  [in]            DWORD                              dwNameSpace,
  [in, optional]  LPGUID                             lpNspId,
  [in, optional]  const ADDRINFOEXA                  *hints,
  [out]           PADDRINFOEXA                       *ppResult,
  [in, optional]  timeval                            *timeout,
  [in, optional]  LPOVERLAPPED                       lpOverlapped,
  [in, optional]  LPLOOKUPSERVICE_COMPLETION_ROUTINE lpCompletionRoutine,
  [out, optional] LPHANDLE                           lpNameHandle
);

參數

[in, optional] pName

NULL 終止字串的指標,其中包含主機 (節點) 名稱或數值主機位址字串。 對於因特網通訊協議,數值主機位址字串是虛線十進位IPv4位址或IPv6 十六進位位址。

[in, optional] pServiceName

選擇性 NULL 終止字串的指標,其中包含以字串表示的服務名稱或埠號碼。

服務名稱是埠號碼的字串別名。 例如,「HTTP」 是因特網工程工作組 (IETF) 所定義之埠 80 的別名,做為 Web 伺服器用於 HTTP 通訊協定的預設埠。 未指定埠號碼時 ,pServiceName 參數的可能值會列在下列檔案中:

%WINDIR%\system32\drivers\etc\services

[in] dwNameSpace

選擇性命名空間標識碼,決定要查詢哪些命名空間提供者。 傳遞特定命名空間識別碼將只會產生支援所查詢指定命名空間的命名空間提供者。 指定 NS_ALL 會導致查詢所有已安裝且作用中的命名空間提供者。

dwNameSpace 參數的選項會列在 Winsock2.h include 檔案中。 Windows Vista 和更新版本上會新增數個命名空間提供者。 您可以安裝其他命名空間提供者,因此只有下列可能的值才可供使用。 可能有許多其他值。

意義
NS_ALL
0
所有已安裝且作用中的命名空間。
NS_DNS
12
功能變數名稱系統 (DNS) 命名空間。
NS_NETBT
13
NetBIOS over TCP/IP (NETBT) 命名空間。
NS_WINS
14
Windows 因特網命名服務 (NS_WINS) 命名空間。
NS_NLA
15
NLA) 命名空間 (網路位置感知。

Windows XP 和更新版本支援此命名空間識別碼。

NS_BTH
16
藍牙命名空間。

Windows Vista 和更新版本支援此命名空間標識碼。

NS_NTDS
32
Windows NT Directory Services (NS_NTDS) 命名空間。
NS_EMAIL
37
電子郵件命名空間。

Windows Vista 和更新版本支援此命名空間標識碼。

NS_PNRPNAME
38
特定對等名稱的點對點命名空間。

Windows Vista 和更新版本支援此命名空間標識碼。

NS_PNRPCLOUD
39
對等名稱集合的點對點命名空間。

Windows Vista 和更新版本支援此命名空間標識碼。

[in, optional] lpNspId

特定命名空間提供者選擇性 GUID 的指標,可在多個命名空間提供者註冊於單一命名空間下,例如 NS_DNS。 傳遞特定命名空間提供者的 GUID 將只會查詢指定的命名空間提供者。 您可以呼叫 WSAEnumNameSpaceProviders 函 式來擷取命名空間提供者的 GUID。

[in, optional] hints

addrinfoex 結構的指標,提供呼叫端所支援套接字類型的提示。

pHints 參數所指向之 addrinfoex 結構的ai_addrlenai_canonname、ai_addrai_next成員必須是零或 NULL。 否則 GetAddrInfoEx 函式將會 失敗,並WSANO_RECOVERY。

如需詳細資訊,請參閱。

[out] ppResult

一或多個 addrinfoex 結構鏈接清單的指標,其中包含主機的響應資訊。

[in, optional] timeout

選擇性參數,指出在中止呼叫之前,等候命名空間提供者的回應,以毫秒為單位。

只有在呼叫 GetAddrInfoEx 函式之前已在來源中定義 UNICODE_UNICODE 宏時,才支援此參數。 否則,此參數目前已保留,而且必須設定為 NULL ,因為不支援 逾時 選項。

[in, optional] lpOverlapped

用於異步操作之重疊結構的選擇性指標。

只有在呼叫 GetAddrInfoEx 函式之前已在來源中定義 UNICODE_UNICODE 宏時,才支援此參數。

在 Windows 8 和 Windows Server 2012 上,如果未指定任何 lpCompletionRoutine 參數,則 OVERLAPPED 結構的 hEvent 成員必須設定為在異步呼叫時呼叫的手動重設事件。 如果已指定完成例程, hEvent 成員必須是 NULL。 設定 hEvent 指定的事件時,呼叫 GetAddrInfoExOverlappedResult 函式即可擷取作業的結果。

每當未定義 UNICODE 或_UNICODE宏時,Windows 8 和 Windows Server 2012,此參數目前會保留且必須設定為 NULL

在 Windows 7 和 Windows Server 2008 R2 或更早版本上,此參數目前已保留,而且必須設定為 NULL ,因為不支援異步操作。

[in, optional] lpCompletionRoutine

異步操作成功完成時要叫用之函式的選擇性指標。

只有在呼叫 GetAddrInfoEx 函式之前已在來源中定義 UNICODE_UNICODE 宏時,才支援此參數。

如果指定此參數,它必須是具有下列簽章之函式的指標:

typedef   
void   
(CALLBACK * LPLOOKUPSERVICE_COMPLETION_ROUTINE)(   
    __in      DWORD    dwError,   
    __in      DWORD    dwBytes,   
    __in      LPWSAOVERLAPPED lpOverlapped   
    );   

當異步操作完成時,將會叫用完成例程,並將 lpOverlapped 參數設定為傳遞至 GetAddrInfoExlpOverlapped 參數值。 重疊結構的 Pointer 成員將會設定為原始呼叫之 ppResult 參數的值。 如果 Pointer 成員指向 addrinfoex 結構的非 NULL 指標,則呼叫端必須負責呼叫 FreeAddrInfoEx 以釋放 addrinfoex 結構。 傳遞至完成例程的 dwError 參數將會設定為 Winsock 錯誤碼。 dwBytes 參數會保留供日後使用,必須予以忽略。

每當未定義 UNICODE 或_UNICODE宏時,Windows 8 和 Windows Server 2012,此參數目前會保留且必須設定為 NULL

在 Windows 7 和 Windows Server 2008 R2 或更早版本上,此參數目前已保留,而且必須設定為 NULL ,因為不支援異步操作。

[out, optional] lpNameHandle

選擇性指標,僅用於異步操作。

只有在呼叫 GetAddrInfoEx 函式之前已在來源中定義 UNICODE_UNICODE 宏時,才支援此參數。

在 Windows 8 和 Windows Server 2012 上,如果 GetAddrInfoEx 函式會以異步方式完成,則此字段中傳回的指標可能會與 GetAddrInfoExCancel 函式搭配使用。 當 GetAddrInfoEx 傳回直到呼叫完成例程、觸發事件或使用此句柄呼叫 GetAddrInfoExCancel 函式時,傳回的句柄有效。

每當未定義 UNICODE 或_UNICODE宏時,Windows 8 和 Windows Server 2012,此參數目前會保留且必須設定為 NULL

在 Windows 7 和 Windows Server 2008 R2 或更早版本上,此參數目前已保留,而且必須設定為 NULL ,因為不支援異步操作。

傳回值

成功時, GetAddrInfoEx傳回 NO_ERROR (0) 。 失敗會傳回非零的 Windows Sockets 錯誤碼,如 Windows Sockets 錯誤碼中找到。

GetAddrInfoEx 函式傳回的大部分非零錯誤碼都會對應至 Internet Engineering Task Force (IETF) 建議中所述的錯誤集。 下表顯示這些錯誤碼及其 WSA 對等專案。 建議使用 WSA 錯誤碼,因為它們為 Winsock 程式設計人員提供熟悉且完整的錯誤資訊。

錯誤值 WSA 對等專案 Description
EAI_AGAIN WSATRY_AGAIN 發生名稱解析的暫時失敗。
EAI_BADFLAGS WSAEINVAL 提供無效的參數。 如果任何保留參數不是 NULL,就會傳回此錯誤。 如果為 pHints 參數ai_flags成員提供無效的值,也會傳回此錯誤。
EAI_FAIL WSANO_RECOVERY 發生無法復原的名稱解析失敗。
EAI_FAMILY WSAEAFNOSUPPORT 不支援 pHints 參數ai_family成員。
EAI_MEMORY WSA_NOT_ENOUGH_MEMORY 發生記憶體配置失敗。
EAI_NONAME WSAHOST_NOT_FOUND 未針對提供的參數解析名稱,或未提供 pNamepServiceName 參數。
EAI_SERVICE WSATYPE_NOT_FOUND pServiceName 參數不支援 pHints 參數的指定ai_socktype成員。
EAI_SOCKTYPE WSAESOCKTNOSUPPORT 不支援 pHints 參數ai_socktype成員。
 

使用 gai_strerror 函式,根據 GetAddrInfoEx 函式傳回的 EAI 代碼來列印錯誤訊息。 提供gai_strerror函式以符合 IETF 建議,但不是安全線程。 因此,建議使用傳統 Windows Sockets 函式,例如 WSAGetLastError

錯誤碼 意義
WSA_NOT_ENOUGH_MEMORY
記憶體不足,無法執行作業。
WSAEAFNOSUPPORT
已使用與要求通訊協定不相容的位址。 如果不支援 pHints 參數所指向的 addrinfoex 結構ai_family成員,就會傳回此錯誤。
WSAEINVAL
提供的引數無效。 如果為 pHints 參數所指向之 addrinfoex 結構ai_flags成員提供無效的值,就會傳回此錯誤。 當 dwNameSpace 參數NS_PNRPNAME或NS_PNRPCLOUD且對等名稱服務未運作時,也會傳回此錯誤。
WSAESOCKTNOSUPPORT
這個地址家族中不存在對指定通訊端類型的支援。 如果不支援 pHints 參數所指向的 addrinfoex 結構ai_socktype成員,就會傳回此錯誤。
WSAHOST_NOT_FOUND
沒有已知的此類主機。 如果未針對提供的參數解析名稱,或未提供 pNamepServiceName 參數,則會傳回此錯誤。
WSANO_DATA
要求的名稱有效,但找不到所要求類型的數據。
WSANO_RECOVERY
資料庫尋查期間發生無法復原的錯誤。 如果發生名稱解析中無法復原的錯誤,就會傳回此錯誤。
WSANOTINITIALISED
使用此函式之前,必須先進行成功的 WSAStartup 呼叫。
WSASERVICE_NOT_FOUND
不知道這類服務。 在指定的名稱空間中找不到服務。 如果在 dwNameSpace 參數中指定的命名空間找不到 pName 或 pServiceName 參數,或未安裝 dwNameSpace 參數中指定的命名空間,就會傳回這個錯誤。
WSATRY_AGAIN
這通常為主機名稱解析期間的暫時錯誤,表示本機伺服器未收到授權伺服器的回應。 發生名稱解析暫時失敗時,就會傳回此錯誤。
WSATYPE_NOT_FOUND
找不到指定的類別。 pServiceName 參數不支援 pHints 參數所指向之 addrinfoex 結構的指定ai_socktype成員。

備註

GetAddrInfoEx 函式提供從主機名到地址的通訊協定獨立轉譯,以及從服務名稱到埠號碼。 GetAddrInfoEx 函式是 getaddrinfoGetAddrInfoW 函式的增強版本。 GetAddrInfoEx 函式允許指定命名空間提供者來解析查詢。

GetAddrInfoEx 函式會匯總並傳回多個命名空間提供者的結果,除非指定特定的命名空間提供者。 為了與 IPv6 和 IPv4 通訊協定搭配使用,名稱解析可以是域名系統 (DNS) 、本機 主機 檔案、電子郵件提供者 (NS_EMAIL 命名空間) 或其他命名機制。

定義 UNICODE 或_UNICODE時, GetAddrInfoEx 會定義為 GetAddrInfoExW,這是此函式的 Unicode 版本。 字串參數會定義至 PWSTR 資料類型,並使用 ADDRINFOEXW 結構。 在 Windows 8 和 Windows Server 2012 上,逾時lpOverlappedlpCompletionRoutinelpNameHandle 參數可用來呼叫 GetAddrInfoEx 函式,以便以異步方式完成。

未定義 UNICODE 或_UNICODE時, GetAddrInfoEx 會定義為 GetAddrInfoExA,這是此函式的 ANSI 版本。 字串參數屬於 PCSTR 數據類型,並使用 ADDRINFOEXA 結構。 timeoutlpOverlappedlpCompletionRoutinelpNameHandle 參數必須設定為 NULL

pNamepServiceName 參數的一或兩者都必須指向 NULL 終止的字串。 通常會提供這兩者。

成功時,ppResult 參數會傳回 addrinfoex 結構的連結清單。 您可以依照每個傳回 addrinfoex 結構的ai_next成員中提供的指標來處理清單直到遇到 NULL 指標為止。 在每個傳回 的 addrinfoex 結構中, ai_familyai_socktypeai_protocol 成員會對應到 套接字WSASocket 函數調用中的個別自變數。 此外,每個傳回 addrinfoex 結構中的ai_addr成員都會指向填入的套接字地址結構,其ai_addrlen成員中指定的長度。

如果 pName 參數指向電腦名稱,則會傳回可用來做為來源地址的電腦的所有永久位址。 在 Windows Vista 和更新版本上,這些位址會包含 GetUnicastIpAddressTableGetUnicastIpAddressEntry 函式傳回的所有單播 IP 位址, 其中 SkipAsSource 成員在 MIB_UNICASTIPADDRESS_ROW 結構中設定為 false。

如果 pName 參數指向等於 「localhost」 的字串,則會傳回本機電腦上的所有回送位址。

如果 pName 參數包含空字串,則會傳回本機電腦上所有已註冊的位址。

如果 pName 參數指向等於 「. 的字串,在 Windows Server 2003 和更新版本上。localmachine“,會傳回本機計算機上所有已註冊的位址。

如果 pName 參數參考叢集虛擬伺服器名稱,則只會傳回虛擬伺服器位址。 在 Windows Vista 和更新版本上,這些位址會包含 GetUnicastIpAddressTableGetUnicastIpAddressEntry 函式傳回的所有單播 IP 位址, 其中 SkipAsSource 成員在 MIB_UNICASTIPADDRESS_ROW 結構中設定為 true。 如需叢集的詳細資訊,請參閱 Windows 叢集

Windows 7 Service Pack 1 (SP1) 和 Windows Server 2008 R2 with Service Pack 1 (SP1) 新增支援至 Netsh.exe,以在 IP 位址上設定 SkipAsSource 屬性。 這也會變更行為,如此一來,如果 MIB_UNICASTIPADDRESS_ROW 結構中的SkipAsSource成員設定為 false,IP 位址將會在 DNS 中註冊。 如果 SkipAsSource 成員設定為 true,則不會在 DNS 中註冊 IP 位址。

Hotfix 適用於 Windows 7 和 Windows Server 2008 R2,可新增對 Netsh.exe 的支援,以在 IP 位址上設定 SkipAsSource 屬性。 此 Hotfix 也會變更行為,如此一來,如果 MIB_UNICASTIPADDRESS_ROW 結構中的 SkipAsSource 成員設定為 false,IP 位址將會在 DNS 中註冊。 如果 SkipAsSource 成員設定為 true,則不會在 DNS 中註冊 IP 位址。 如需詳細資訊,請參閱 知識庫 (KB) 2386184

Windows Vista 的 Service Pack 2 (SP2) 和 Windows Server 2008 Service Pack 2 (SP2) 也提供類似的 Hotfix,可新增對 Netsh.exe 的支援,以在 IP 位址上設定 SkipAsSource 屬性。 此 Hotfix 也會變更行為,如此一來,如果 MIB_UNICASTIPADDRESS_ROW 結構中的 SkipAsSource 成員設定為 false,IP 位址將會在 DNS 中註冊。 如果 SkipAsSource 成員設定為 true,則不會在 DNS 中註冊 IP 位址。

GetAddrInfoEx 函式的呼叫端可以提供 pHints 參數所指向之 addrinfoex 結構所支援的套接字類型提示。 使用 pHints 參數時,下列規則會套用至其相關聯的 addrinfoex 結構:

  • ai_familyAF_UNSPEC 值表示呼叫端只會接受AF_INETAF_INET6位址系列。 請注意, AF_UNSPECPF_UNSPEC 相同。
  • ai_socktype的值為零,表示呼叫端將接受任何套接字類型。
  • ai_protocol 的值為零,表示呼叫端將接受任何通訊協定。
  • ai_addrlen成員必須設定為零。
  • ai_canonname成員必須設定為NULL
  • ai_addr成員必須設定為NULL
  • ai_next成員必須設定為NULL

pHints 參數中提供的 addrinfoex 結構中的其他值表示特定需求。 例如,如果呼叫端只處理 IPv4 且未處理 IPv6, 則ai_family 成員應該設定為 AF_INET。 另一個範例是,如果呼叫端只處理 TCP 且未處理 UDP, 則ai_socktype 成員應設定為 SOCK_STREAM

如果 pHints 參數是 NULL 指標,GetAddrInfoEx 函式會將它視為 pHints 中的 addrinfoex 結構已初始化,並將其ai_family成員設定為 AF_UNSPEC,而所有其他成員都設定為 NULL 或零。

從服務呼叫 GetAddrInfoEx 時,如果作業是呼叫服務的用戶進程結果,服務應該模擬使用者。 這是為了允許正確強制執行安全性。

GetAddrInfoEx 函式可用來將IP位址的文字字串表示轉換為addrinfoex結構,其中包含IP位址的sockaddr結構和其他資訊。 若要以此方式使用,pName 參數所指向的字串必須包含IP位址的文字表示,而 pHints 參數所指向的 addrinfoex 結構必須設定ai_flags成員中的AI_NUMERICHOST旗標。 pName 參數指向的字串可能包含 IPv4 或 IPv6 位址的文字表示。 文字 IP 位址會轉換成 ppResult 參數所指向的 addrinfoex 結構。 傳回的 addrinfoex 結構包含 IP 位址的 sockaddr 結構,以及 IP 位址的其他資訊。

多個命名空間提供者可以安裝在相同命名空間的本機計算機上。 例如,基底 Windows TCP/IP 網路軟體會註冊NS_DNS命名空間。 Microsoft Forefront Threat Management Gateway (TMG) 和較舊的 Microsoft Internet Security and Acceleration (ISA) Server 包含防火牆客戶端軟體,也會註冊NS_DNS命名空間。 當 dwNameSpace 參數設定為值 (NS_DNS 時,例如) 和 lpNspId 參數為 NULL,GetAddrInfoEx 函式傳回的結果就是所有登錄指定命名空間且排除重複結果之命名空間的合併結果。 只有在查詢單一命名空間提供者時, lpNspId 參數應該設定為特定命名空間提供者的 GUID。

如果 pNameSpace 參數設定為 NS_ALL,則會合併並傳回查詢所有命名空間提供者的結果。 在此情況下,如果多個命名空間提供者傳回相同的資訊, 可能會在ppResult 參數指向的結果中傳回重複的回應。

在 Windows 8 和 Windows Server 2012 上,如果 GetAddrInfoEx 函式會以異步方式完成,lpNameHandle 參數中傳回的指標可能會與 GetAddrInfoExCancel 函式搭配使用。 當 GetAddrInfoEx 傳回直到呼叫完成例程、觸發事件或使用此句柄呼叫 GetAddrInfoExCancel 函式時,傳回的句柄有效。

從動態配置釋放地址資訊

會動態配置 ppResult 參數所指向之 GetAddrInfoEx 函式所傳回的所有資訊,包括 addrinfoex 結構、套接字地址結構和標準主機名字符串所指向的 addrinfoex 結構。 成功呼叫此函式所配置的記憶體必須釋放,且後續呼叫 FreeAddrInfoEx

範例程序代碼

下列範例示範 如何使用 GetAddrInfoEx 函 式。
#ifndef UNICODE
#define UNICODE
#endif

#include <winsock2.h>
#include <ws2tcpip.h>
#include <objbase.h>
#include <stdio.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")

// Need to link with Ole32.lib to print GUID
#pragma comment(lib, "ole32.lib")

int __cdecl wmain(int argc, wchar_t ** argv)
{

    //-----------------------------------------
    // Declare and initialize variables
    WSADATA wsaData;
    int iResult;

    DWORD dwRetval;

    int i = 1;

    DWORD dwNamespace = NS_ALL;
    LPGUID lpNspid = NULL;

    ADDRINFOEX *result = NULL;
    ADDRINFOEX *ptr = NULL;
    ADDRINFOEX hints;

    // LPSOCKADDR sockaddr_ip;
    struct sockaddr_in *sockaddr_ipv4;
    struct sockaddr_in6 *sockaddr_ipv6;

    // DWORD ipbufferlength = 46;
    wchar_t ipstringbuffer[46];

    // variables needed to print namespace provider GUID
    int iRet = 0;
    WCHAR GuidString[40] = { 0 };

    // Validate the parameters
    if (argc != 4) {
        wprintf(L"usage: %ws <hostname> <servicename> <namespace>\n", argv[0]);
        wprintf(L"getaddrinfoex provides protocol-independent translation\n");
        wprintf(L"   from a host name to an IP address\n");
        wprintf(L"%ws example usage\n", argv[0]);
        wprintf(L"   %ws www.contoso.com 0 12\n", argv[0]);
        wprintf(L"   looks up the www.contoso.com in the NS_DNS namespace\n",
                argv[0]);
        return 1;
    }
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        wprintf(L"WSAStartup failed: %d\n", iResult);
        return 1;
    }
    //--------------------------------
    // Setup the hints address info structure
    // which is passed to the getaddrinfo() function
    ZeroMemory(&hints, sizeof (hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    dwNamespace = (DWORD) _wtoi(argv[3]);

    wprintf(L"Calling GetAddrInfoEx with following parameters:\n");
    wprintf(L"\tName = %ws\n", argv[1]);
    wprintf(L"\tServiceName (or port) = %ws\n", argv[2]);
    wprintf(L"\tNamespace = %s (", argv[3]);
    switch (dwNamespace) {
    case NS_ALL:
        wprintf(L"(NS_ALL)\n");
        break;
    case NS_DNS:
        wprintf(L"(NS_DNS)\n");
        break;
    case NS_NETBT:
        wprintf(L"NS_NETBT");
        break;
    case NS_WINS:
        wprintf(L"NS_WINS");
        break;
    case NS_NLA:
        wprintf(L"NS_NLA");
        break;
    case NS_BTH:
        wprintf(L"NS_BTH");
        break;
    case NS_NTDS:
        wprintf(L"NS_NTDS");
        break;
    case NS_EMAIL:
        wprintf(L"NS_EMAIL");
        break;
    case NS_PNRPNAME:
        wprintf(L"NS_PNRPNAME");
        break;
    case NS_PNRPCLOUD:
        wprintf(L"NS_PNRPCLOUD");
        break;
    default:
        wprintf(L"Other");
        break;
    }
    wprintf(L")\n\n");

//--------------------------------
// Call getaddrinfoex(). If the call succeeds,
// the result variable will hold a linked list
// of addrinfo structures containing response
// information
    dwRetval =
        GetAddrInfoEx(argv[1], argv[2], dwNamespace, lpNspid, &hints, &result,
                      NULL, NULL, NULL, NULL);
    if (dwRetval != 0) {
        wprintf(L"GetAddrInfoEx failed with error: %d\n", dwRetval);
        WSACleanup();
        return 1;
    }

    wprintf(L"GetAddrInfoEx returned success\n");

    // Retrieve each address and print out the hex bytes
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        wprintf(L"GetAddrInfoEx response %d\n", i++);
        wprintf(L"\tFlags: 0x%x\n", ptr->ai_flags);
        wprintf(L"\tFamily: ");
        switch (ptr->ai_family) {
        case AF_UNSPEC:
            wprintf(L"Unspecified\n");
            break;
        case AF_INET:
            wprintf(L"AF_INET (IPv4)\n");
            // the InetNtop function is available on Windows Vista and later
            sockaddr_ipv4 = (struct sockaddr_in *) ptr->ai_addr;
            wprintf(L"\tIPv4 address %ws\n",
                    InetNtop(AF_INET, &sockaddr_ipv4->sin_addr, ipstringbuffer,
                             46));

            // We could also use the WSAAddressToString function
            // sockaddr_ip = (LPSOCKADDR) ptr->ai_addr;
            // The buffer length is changed by each call to WSAAddresstoString
            // So we need to set it for each iteration through the loop for safety
            // ipbufferlength = 46;
            // iRetval = WSAAddressToString(sockaddr_ip, (DWORD) ptr->ai_addrlen, NULL, 
            //    ipstringbuffer, &ipbufferlength );
            // if (iRetval)
            //    wprintf(L"WSAAddressToString failed with %u\n", WSAGetLastError() );
            // else    
            //    wprintf(L"\tIPv4 address %ws\n", ipstringbuffer);
            break;
        case AF_INET6:
            wprintf(L"AF_INET6 (IPv6)\n");
            // the InetNtop function is available on Windows Vista and later
            sockaddr_ipv6 = (struct sockaddr_in6 *) ptr->ai_addr;
            wprintf(L"\tIPv6 address %ws\n",
                    InetNtop(AF_INET6, &sockaddr_ipv6->sin6_addr,
                             ipstringbuffer, 46));

            // We could also use WSAAddressToString which also returns the scope ID
            // sockaddr_ip = (LPSOCKADDR) ptr->ai_addr;
            // The buffer length is changed by each call to WSAAddresstoString
            // So we need to set it for each iteration through the loop for safety
            // ipbufferlength = 46;
            //iRetval = WSAAddressToString(sockaddr_ip, (DWORD) ptr->ai_addrlen, NULL, 
            //    ipstringbuffer, &ipbufferlength );
            //if (iRetval)
            //    wprintf(L"WSAAddressToString failed with %u\n", WSAGetLastError() );
            //else    
            //    wprintf(L"\tIPv6 address %ws\n", ipstringbuffer);
            break;
        default:
            wprintf(L"Other %ld\n", ptr->ai_family);
            break;
        }
        wprintf(L"\tSocket type: ");
        switch (ptr->ai_socktype) {
        case 0:
            wprintf(L"Unspecified\n");
            break;
        case SOCK_STREAM:
            wprintf(L"SOCK_STREAM (stream)\n");
            break;
        case SOCK_DGRAM:
            wprintf(L"SOCK_DGRAM (datagram) \n");
            break;
        case SOCK_RAW:
            wprintf(L"SOCK_RAW (raw) \n");
            break;
        case SOCK_RDM:
            wprintf(L"SOCK_RDM (reliable message datagram)\n");
            break;
        case SOCK_SEQPACKET:
            wprintf(L"SOCK_SEQPACKET (pseudo-stream packet)\n");
            break;
        default:
            wprintf(L"Other %ld\n", ptr->ai_socktype);
            break;
        }
        wprintf(L"\tProtocol: ");
        switch (ptr->ai_protocol) {
        case 0:
            wprintf(L"Unspecified\n");
            break;
        case IPPROTO_TCP:
            wprintf(L"IPPROTO_TCP (TCP)\n");
            break;
        case IPPROTO_UDP:
            wprintf(L"IPPROTO_UDP (UDP) \n");
            break;
        default:
            wprintf(L"Other %ld\n", ptr->ai_protocol);
            break;
        }
        wprintf(L"\tLength of this sockaddr: %d\n", ptr->ai_addrlen);
        wprintf(L"\tCanonical name: %s\n", ptr->ai_canonname);

        if (ptr->ai_blob == NULL)
            wprintf(L"\tBlob: (null)\n");
        else    
            wprintf(L"\tLength of the blob: %u\n",
                    (DWORD) ptr->ai_bloblen);

        if (ptr->ai_provider == NULL)
            wprintf(L"\tNamespace provider GUID: (null)\n");
        else {
            iRet =
                StringFromGUID2(*(ptr->ai_provider), (LPOLESTR) & GuidString,
                                39);
            // For c rather than C++ source code, the above line needs to be
            // iRet = StringFromGUID2(&ptr.ai_provider, (LPOLESTR) &GuidString, 39); 
            if (iRet == 0)
                wprintf(L"StringFromGUID2 failed\n");
            else {
                wprintf(L"\tNamespace provider: %ws\n", GuidString);
            }
        }
    }

    FreeAddrInfoEx(result);
    WSACleanup();

    return 0;
}


下列範例示範如何使用 GetAddrInfoEx 函式異步將名稱解析為 IP 位址。

//
//    This sample demonstrates how to use asynchronous GetAddrInfoEx to
//    resolve a name to an IP address.
//
//    ResolveName <QueryName>
//

#ifndef UNICODE
#define UNICODE
#endif

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")

#define MAX_ADDRESS_STRING_LENGTH   64

//
//  Asynchronous query context structure.
//

typedef struct _QueryContext
{
    OVERLAPPED      QueryOverlapped;
    PADDRINFOEX     QueryResults;
    HANDLE          CompleteEvent;
}QUERY_CONTEXT, *PQUERY_CONTEXT;

VOID
WINAPI
QueryCompleteCallback(
    _In_ DWORD Error,
    _In_ DWORD Bytes,
    _In_ LPOVERLAPPED Overlapped
    );

int
__cdecl
wmain(
    _In_ int Argc, PWCHAR Argv[]
    )
{
    INT                 Error = ERROR_SUCCESS;
    WSADATA             wsaData;
    BOOL                IsWSAStartupCalled = FALSE;
    ADDRINFOEX          Hints;
    QUERY_CONTEXT       QueryContext;
    HANDLE              CancelHandle = NULL;
    DWORD               QueryTimeout = 5 * 1000; // 5 seconds

    ZeroMemory(&QueryContext, sizeof(QueryContext));

    //
    //  Validate the parameters
    //

    if (Argc != 2)
    {
        wprintf(L"Usage: ResolveName <QueryName>\n");
        goto exit;
    }

    //
    //  All Winsock functions require WSAStartup() to be called first
    //

    Error = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (Error != 0)
    {
        wprintf(L"WSAStartup failed with %d\n", Error);
        goto exit;
    }

    IsWSAStartupCalled = TRUE;

    ZeroMemory(&Hints, sizeof(Hints));
    Hints.ai_family = AF_UNSPEC;

    //
    //  Note that this is a simple sample that waits/cancels a single
    //  asynchronous query. The reader may extend this to support
    //  multiple asynchronous queries.
    //

    QueryContext.CompleteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (QueryContext.CompleteEvent == NULL)
    {
        Error = GetLastError();
        wprintf(L"Failed to create completion event: Error %d\n",  Error);
        goto exit;
    }

    //
    //  Initiate asynchronous GetAddrInfoExW.
    //
    //  Note GetAddrInfoEx can also be invoked asynchronously using an event
    //  in the overlapped object (Just set hEvent in the Overlapped object
    //  and set NULL as completion callback.)
    //
    //  This sample uses the completion callback method.
    //

    Error = GetAddrInfoExW(Argv[1],
                           NULL,
                           NS_DNS,
                           NULL,
                           &Hints,
                           &QueryContext.QueryResults,
                           NULL,
                           &QueryContext.QueryOverlapped,
                           QueryCompleteCallback,
                           &CancelHandle);

    //
    //  If GetAddrInfoExW() returns  WSA_IO_PENDING, GetAddrInfoExW will invoke
    //  the completion routine. If GetAddrInfoExW returned anything else we must
    //  invoke the completion directly.
    //

    if (Error != WSA_IO_PENDING)
    {
        QueryCompleteCallback(Error, 0, &QueryContext.QueryOverlapped);
        goto exit;
    }

    //
    //  Wait for query completion for 5 seconds and cancel the query if it has
    //  not yet completed.
    //

    if (WaitForSingleObject(QueryContext.CompleteEvent,
                            QueryTimeout)  == WAIT_TIMEOUT )
    {

        //
        //  Cancel the query: Note that the GetAddrInfoExCancelcancel call does
        //  not block, so we must wait for the completion routine to be invoked.
        //  If we fail to wait, WSACleanup() could be called while an
        //  asynchronous query is still in progress, possibly causing a crash.
        //

        wprintf(L"The query took longer than %d seconds to complete; "
                L"cancelling the query...\n", QueryTimeout/1000);

        GetAddrInfoExCancel(&CancelHandle);

        WaitForSingleObject(QueryContext.CompleteEvent,
                            INFINITE);
    }

exit:

    if (IsWSAStartupCalled)
    {
        WSACleanup();
    }

    if (QueryContext.CompleteEvent)
    {
        CloseHandle(QueryContext.CompleteEvent);
    }

    return Error;
}

//
// Callback function called by Winsock as part of asynchronous query complete
//

VOID
WINAPI
QueryCompleteCallback(
    _In_ DWORD Error,
    _In_ DWORD Bytes,
    _In_ LPOVERLAPPED Overlapped
    )
{
    PQUERY_CONTEXT  QueryContext = NULL;
    PADDRINFOEX     QueryResults = NULL;
    WCHAR           AddrString[MAX_ADDRESS_STRING_LENGTH];
    DWORD           AddressStringLength;

    UNREFERENCED_PARAMETER(Bytes);

    QueryContext = CONTAINING_RECORD(Overlapped,
                                     QUERY_CONTEXT,
                                     QueryOverlapped);

    if (Error != ERROR_SUCCESS)
    {
        wprintf(L"ResolveName failed with %d\n", Error);
        goto exit;
    }

    wprintf(L"ResolveName succeeded. Query Results:\n");

    QueryResults = QueryContext->QueryResults;

    while(QueryResults)
    {
        AddressStringLength = MAX_ADDRESS_STRING_LENGTH;

        WSAAddressToString(QueryResults->ai_addr,
                           (DWORD)QueryResults->ai_addrlen,
                           NULL,
                           AddrString,
                           &AddressStringLength);

        wprintf(L"Ip Address: %s\n", AddrString);
        QueryResults = QueryResults->ai_next;
    }

exit:

    if (QueryContext->QueryResults)
    {
        FreeAddrInfoEx(QueryContext->QueryResults);
    }

    //
    //  Notify caller that the query completed
    //

    SetEvent(QueryContext->CompleteEvent);
    return;
}

注意 請確定開發環境分別以最新版的 Ws2tcpip.h 為目標,其中包含 addrinfoexGetAddrInfoEx 的結構和函式定義。
 

國際化功能變數名稱

因特網主機名通常包含一組非常受限的字元:
  • 大寫和小寫的英文 ASCII 字母。
  • 0 到 9 位數。
  • ASCII 連字元字元。

隨著因特網的成長,需要識別非 ASCII 字元集所代表之其他語言的因特網主機名。 有助於此需求的標識符,並允許非 ASCII 字元 (Unicode) 表示為特殊 ASCII 字元字串,稱為國際功能變數名稱 (IDN) 。 在應用程式 (IDNA) 中稱為國際化功能變數名稱的機制,可用來以標準方式處理IDN。 IDN 和 IDNA 的規格記載於 INTERNET Engineering Task Force (IETF) 所發佈的 RFC 3490RTF 5890RFC 6365 中。

在 Windows 8 和 Windows Server 2012 上,GetAddrInfoEx 函式支持國際化功能變數名稱 (IDN) 剖析套用至 pName 參數中傳遞的名稱。 Winsock 會執行 Punycode/IDN 編碼和轉換。 您可以使用下面討論的 AI_DISABLE_IDN_ENCODING 旗標來停用此行為。

在 Windows 7 和 Windows Server 2008 R2 或更早版本上, GetAddrInfoEx 函式目前不支援套用至 pName 參數中所傳遞名稱的 IDN 剖析。 GetAddrInfoEx 函式的寬字元版本不會根據 RFC 3490 使用 Punycode 來轉換 IDN Punycode 格式。 查詢 DNS 時 ,GetAddrInfoEx 函式的寬字元版本會以 UTF-8 格式編碼 Unicode 名稱,這是 Microsoft DNS 伺服器在企業環境中所使用的格式。

Windows Vista 和更新版本的數個函式支援在 IDN 中的 Unicode 標籤與其 ASCII 對等專案之間的轉換。 每個 Unicode 標籤的結果表示只包含 ASCII 字元,如果 Unicode 標籤包含任何非 ASCII 字元,則開頭為 xn-- 前置詞。 這是因為支援因特網上現有的 DNS 伺服器,因為某些 DNS 工具和伺服器僅支援 ASCII 字元 (請參閱 RFC 3490) 。

IdnToAscii 函式會使用 Punycode,使用 RFC 3490 中定義的標準演算法,將 IDN 轉換為原始 Unicode 字串的 ASCII 表示法。 IdnToUnicode 函式會將IDN的 ASCII形式轉換成一般 Unicode UTF-16 編碼語法。 如需相關草稿標準的詳細資訊和連結,請參閱 處理國際化功能變數名稱 (IDN)

IdnToAscii 函式可用來將 IDN 名稱轉換成 ASCII 形式,然後在未) 定義 UNICODE 和 _UNICODE 時,將 pName 參數傳入 GetAddrInfoEx (函式至 GetAddrInfoEx 函式。 若要將這個 IDN 名稱傳遞給 GetAddrInfoEx 函式,當此函式的寬字元版本在定義 UNICODE 或_UNICODE) 時 (,您可以使用 MultiByteToWideChar 函式將 CHAR 字串轉換成 WCHAR 字串。

在 hints 參數中使用ai_flags

hints 參數中提供的選擇性 addrinfoex 結構ai_flags成員中的旗標會修改函式的行為。

這些旗標位定義於適用於 Windows 7 的 Microsoft Windows 軟體開發套件 (SDK) Ws2def.h 頭檔中。 這些旗標位定義於 Windows Server 2008 和 Windows Vista Windows SDK 上的 Ws2tcpip.h 頭檔中。 這些旗標位定義於適用於 Windows Server 2003 和 Windows XP 的 Platform Software Development Kit (SDK) Ws2tcpip.h 頭檔中。

旗標位可以是下列各項的組合:

標幟位 Description
AI_PASSIVE 設定 AI_PASSIVE 旗標表示呼叫端想要在系 函式的呼叫中使用傳回的套接字地址結構。 設定 AI_PASSIVE 旗標且 pNameNULL 指標時,套接字地址結構的 IP 位址部分會設定為 IPv4 位址的 INADDR_ANY ,而 IPv6 位址 IN6ADDR_ANY_INIT

未設定AI_PASSIVE旗標時,傳回的套接字地址結構已準備好呼叫連線導向通訊協定的 connect 函式,或準備好呼叫連線傳送或傳送無連線通訊協定的函式。 如果 pName 參數在此案例中是 NULL 指標,套接字地址結構的 IP 位址部分會設定為回送位址。

AI_CANONNAME 如果未使用AI_CANONNAMEAI_NUMERICHOST,GetAddrInfoEx 函式會嘗試解析。 如果傳遞常值字串 GetAddrInfoEx 嘗試轉換字串串,而且如果傳遞主機名, GetAddrInfoEx 函式會嘗試將名稱解析為位址或多個位址。

設定 AI_CANONNAME 位時, pName 參數不能是 NULL。 否則 GetAddrInfoEx 函式將會 失敗,並WSANO_RECOVERY。

設定AI_CANONNAME位且 GetAddrInfoEx 函式傳回成功時,ppResult 參數中的ai_canonname成員會指向包含指定節點標準名稱的 NULL 終止字元串。

注意GetAddrInfoEx 函式可以在設定AI_CANONNAME旗標時傳回成功,但相關聯 addrinfo 結構中的ai_canonname成員為 NULL。 因此,建議使用AI_CANONNAME旗標包括測試相關聯 addrinfoex 結構中的ai_canonname成員是否為 NULL
 
AI_NUMERICHOST 設定 AI_NUMERICHOST 位時, pName 參數必須包含非 NULL 數值主機位址字串,否則會傳回 EAI_NONAME 錯誤。 此旗標可防止呼叫名稱解析服務。
AI_NUMERICSERV 設定 AI_NUMERICSERV 位時, pServiceName 參數必須包含非 NULL 數值埠號碼,否則會傳回 EAI_NONAME 錯誤。 此旗標可防止呼叫名稱解析服務。

AI_NUMERICSERV旗標是在 Windows Vista 和更新版本的 Windows SDK 上定義。 Microsoft 提供者不支援 AI_NUMERICSERV 旗標。

AI_ALL 如果設定 AI_ALL 位,則會針對具有 AI_V4MAPPED的 IPv6 位址和 IPv4 位址提出要求。

AI_ALL旗標是在 Windows Vista 和更新版本的 Windows SDK 上定義。 Windows Vista 和更新版本支援 AI_ALL 旗標。

AI_ADDRCONFIG 如果 設定AI_ADDRCONFIG 位,只有在設定全域位址時 ,GetAddrInfoEx 才會解析。 如果指定 AI_ADDRCONFIG 旗標,只有在本機系統上設定IPv4位址時,才會傳回IPv4位址,而且只有在本機系統上設定IPv6 位址時,才會傳回 IPv6 位址。 IPv4 或 IPv6 回送位址不會被視為有效的全域位址。

AI_ADDRCONFIG旗標是在 Windows Vista 和更新版本的 Windows SDK 上定義。 Windows Vista 和更新版本支援 AI_ADDRCONFIG 旗標。

AI_V4MAPPED 如果 已設定AI_V4MAPPED 位,且 IPv6 位址的要求失敗,則會對 IPv4 位址提出名稱服務要求,而且這些位址會轉換成 IPv4 對應 IPv6 位址格式。

AI_V4MAPPED旗標是在 Windows Vista 和更新版本的 Windows SDK 上定義。 Windows Vista 和更新版本支援 AI_V4MAPPED 旗標。

AI_NON_AUTHORITATIVE 如果 已設定AI_NON_AUTHORITATIVE 位, 則NS_EMAIL 命名空間提供者會同時傳回授權和非授權結果。 如果未設定 AI_NON_AUTHORITATIVE 位, 則NS_EMAIL 命名空間提供者只會傳回授權結果。

AI_NON_AUTHORITATIVE旗標是在 Windows Vista 和更新版本的 Windows SDK 上定義。 Windows Vista 和更新版本支援 AI_NON_AUTHORITATIVE 旗標,並僅適用於 NS_EMAIL 命名空間。

AI_SECURE 如果 已設定AI_SECURE 位, NS_EMAIL 命名空間提供者會傳回以增強安全性取得的結果,以將可能的詐騙降到最低。

AI_SECURE旗標是在 Windows Vista 和更新版本的 Windows SDK 上定義。 Windows Vista 和更新版本支援 AI_SECURE 旗標,僅適用於 NS_EMAIL 命名空間。

AI_RETURN_PREFERRED_NAMES 如果 已設定AI_RETURN_PREFERRED_NAMES ,則不應該在 pName 參數中提供名稱。 NS_EMAIL命名空間提供者會傳回發行集的慣用名稱。

AI_RETURN_PREFERRED_NAMES旗標是在 Windows Vista 和更新版本的 Windows SDK 上定義。 Windows Vista 和更新版本支援 AI_RETURN_PREFERRED_NAMES 旗標,僅適用於 NS_EMAIL 命名空間。

AI_FQDN 如果 已設定AI_FQDN ,且指定單一卷標 (一般名稱 ) ,GetAddrInfoEx 將會傳回最終解析名稱的完整功能變數名稱。 完整功能變數名稱會在相關聯 addrinfoex 結構的ai_canonname成員中傳回。 這與傳回 DNS 中註冊標準名稱的 AI_CANONNAME 位旗標不同,可能與一般名稱解析為的完整功能變數名稱不同。

設定 AI_FQDN 位時, pName 參數不能是 NULL。 否則 GetAddrInfoEx 函式將會失敗,並 WSANO_RECOVERY

在 Windows 8 和 Windows Server 2012 上,可以設定AI_FQDNAI_CANONNAME位。 如果使用 AI_FQDNAI_CANONNAME 位呼叫 GetAddrInfoEx 函式,ppResult 參數會傳回 addrinfoex2 結構的指標,而不是 addrinfoex 結構。

在 Windows 7 和 Windows Server 2008 R2 上,只能設定其中一個 AI_FQDNAI_CANONNAME 位。 如果兩個旗標都出現EAI_BADFLAGS,GetAddrInfoEx 函式將會失敗。

Windows 7: AI_FQDN旗標是在 Windows 7 和更新版本的 Windows SDK 上定義。 Windows 7 和更新版本支援 AI_FQDN 旗標。

AI_FILESERVER 如果 已設定AI_FILESERVER ,這是要查詢的主機名用於檔案共用案例的命名空間提供者提示。 命名空間提供者可能會忽略此提示。

Windows 7: AI_FILESERVER旗標是在 Windows 7 和更新版本的 Windows SDK 上定義。 Windows 7 和更新版本支援 AI_FILESERVER 旗標。

AI_DISABLE_IDN_ENCODING 如果 已設定AI_DISABLE_IDN_ENCODING ,這會在 GetAddrInfoEx 函式所呼叫的名稱解析函式中使用 Punycode 停用自動國際功能變數名稱編碼。

Windows 8:AI_DISABLE_IDN_ENCODING旗標是在 Windows 8 和更新版本的 Windows SDK 上定義。 Windows 8 和更新版本支援AI_DISABLE_IDN_ENCODING旗標。

 

注意

ws2tcpip.h 標頭會根據 UNICODE 預處理器常數的定義,將 GetAddrInfoEx 定義為別名,自動選取此函式的 ANSI 或 Unicode 版本。 混合使用編碼中性別名與非編碼中性的程序代碼,可能會導致編譯或運行時間錯誤不符。 如需詳細資訊,請參閱 函式原型的慣例

規格需求

需求
最低支援的用戶端 Windows Vista [傳統型應用程式 |UWP 應用程式]
最低支援的伺服器 Windows Server 2008 [傳統型應用程式 |UWP 應用程式]
目標平台 Windows
標頭 ws2tcpip.h
程式庫 Ws2_32.lib
Dll Ws2_32.dll

另請參閱

FreeAddrInfoEx

GetAddrInfoExCancel

GetAddrInfoExOverlappedResult

GetAddrInfoW

IdnToAscii

IdnToUnicode

WSAEnumNameSpaceProviders

WSAGetLastError

Windows Sockets 錯誤碼

addrinfoex

addrinfoex2

gai_strerror

getaddrinfo