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 结尾的字符串的指针,该字符串包含主机 (节点) 名称或主机地址数字字符串。 对于 Internet 协议,数字主机地址字符串是点十进制 IPv4 地址或 IPv6 十六进制地址。

[in, optional] pServiceName

指向以 NULL 结尾的可选字符串的指针,该字符串包含表示为字符串的服务名称或端口号。

服务名称是端口号的字符串别名。 例如,“http”是由 Internet 工程任务组定义的端口 80 的别名, (IETF) 作为 Web 服务器用于 HTTP 协议的默认端口。 以下文件中列出了未指定端口号时 pServiceName 参数的可能值:

%WINDIR%\system32\drivers\etc\services

[in] dwNameSpace

一个可选的命名空间标识符,用于确定要查询的命名空间提供程序。 传递特定的命名空间标识符将导致仅查询支持指定命名空间的命名空间提供程序。 指定 NS_ALL 将导致查询所有已安装的和活动命名空间提供程序。

winsock2.h include 文件中列出了 dwNameSpace 参数的选项。 Windows Vista 及更高版本上添加了多个命名空间提供程序。 可以安装其他命名空间提供程序,因此以下可能的值仅是通常可用的值。 许多其他值是可能的。

含义
NS_ALL
0
所有已安装的和活动命名空间。
NS_DNS
12
域名系统 (DNS) 命名空间。
NS_NETBT
13
NETBIOS over TCP/IP (NETBT) 命名空间。
NS_WINS
14
Windows Internet 命名服务 (NS_WINS) 命名空间。
NS_NLA
15
网络位置感知 (NLA) 命名空间。

Windows XP 及更高版本支持此命名空间标识符。

NS_BTH
16
蓝牙命名空间。

Windows Vista 及更高版本支持此命名空间标识符。

NS_NTDS
32
Windows NT目录服务 (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_canonnameai_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 函数来检索操作的结果。

在Windows 8和Windows Server 2012,每当未定义 UNICODE_UNICODE 宏时,此参数当前是保留的,必须设置为 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 参数的值来调用完成例程。 OVERLAPPED 结构的 Pointer 成员将设置为原始调用的 ppResult 参数的值。 如果 Pointer 成员指向 指向 addrinfoex 结构的非 NULL 指针,则调用方负责调用 FreeAddrInfoEx 以释放 addrinfoex 结构。 传递给完成例程的 dwError 参数将设置为 Winsock 错误代码。 dwBytes 参数保留供将来使用,必须忽略。

在Windows 8和Windows Server 2012,每当未定义 UNICODE_UNICODE 宏时,此参数当前是保留的,必须设置为 NULL

在 Windows 7 和 Windows Server 2008 R2 或更早版本上,此参数当前是保留的,并且必须设置为 NULL ,因为不支持异步操作。

[out, optional] lpNameHandle

仅用于异步操作的可选指针。

仅当在调用 GetAddrInfoEx 函数之前已在源中定义了 UNICODE_UNICODE 宏时,才支持此参数。

在Windows 8和Windows Server 2012,如果 GetAddrInfoEx 函数将以异步方式完成,则此字段中返回的指针可与 GetAddrInfoExCancel 函数一起使用。 当 GetAddrInfoEx 返回时,返回的句柄有效,直到调用完成例程、触发事件或使用此句柄调用 GetAddrInfoExCancel 函数。

在Windows 8和Windows Server 2012,每当未定义 UNICODE_UNICODE 宏时,此参数当前是保留的,必须设置为 NULL

在 Windows 7 和 Windows Server 2008 R2 或更早版本上,此参数当前是保留的,并且必须设置为 NULL ,因为不支持异步操作。

返回值

成功后, GetAddrInfoEx 将返回 NO_ERROR (0) 。 失败将返回非零 Windows 套接字错误代码,如 Windows 套接字错误代码中所示。

GetAddrInfoEx 函数返回的大多数非零错误代码映射到 Internet 工程任务组概述的错误集, (IETF) 建议。 下表显示了这些错误代码及其 WSA 等效项。 建议使用 WSA 错误代码,因为它们为 Winsock 程序员提供熟悉且全面的错误信息。

错误值 WSA 等效项 说明
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 pHints 参数的指定ai_socktype成员不支持 pServiceName 参数。
EAI_SOCKTYPE WSAESOCKTNOSUPPORT 不支持 pHints 参数的 ai_socktype 成员。
 

使用 gai_strerror 函数根据 GetAddrInfoEx 函数返回的 EAI 代码打印错误消息。 提供 gai_strerror 函数是为了符合 IETF 建议,但它不是线程安全的。 因此,建议使用传统的 Windows 套接字函数,例如 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
数据库查找期间发生不可恢复的错误。 如果名称解析中发生不可恢复的错误,则返回此错误。
WSANOTINITIALIZED
在使用此函数之前,必须成功调用 WSAStartup
WSASERVICE_NOT_FOUND
无法识别此类服务。 在指定的名称空间中找不到该服务。 如果未为 dwNameSpace 参数中指定的命名空间找到 pName 或 pServiceName 参数,或者未安装 dwNameSpace 参数中指定的命名空间,则返回此错误。
WSATRY_AGAIN
这通常是主机名解析期间的临时错误,意味着本地服务器未接收到来自授权服务器的响应。 发生名称解析的临时失败时,将返回此错误。
WSATYPE_NOT_FOUND
未找到指定的类。 pHints 参数指向的 addrinfoex 结构的指定ai_socktype成员不支持 pServiceName 参数。

注解

GetAddrInfoEx 函数提供从主机名到地址以及从服务名称到端口号的与协议无关的转换。 GetAddrInfoEx 函数是 getaddrinfoGetAddrInfoW 函数的增强版本。 GetAddrInfoEx 函数允许指定命名空间提供程序来解析查询。

GetAddrInfoEx 函数聚合多个命名空间提供程序并返回结果,除非指定了特定的命名空间提供程序。 若要与 IPv6 和 IPv4 协议一起使用,名称解析可以由域名系统 (DNS) 、本地 主机 文件、电子邮件提供商 (NS_EMAIL 命名空间) 或其他命名机制进行。

定义 UNICODE 或 _UNICODE 时, 将 GetAddrInfoEx 定义为 GetAddrInfoExW,即此函数的 Unicode 版本。 字符串参数定义为 PWSTR 数据类型,并使用 ADDRINFOEXW 结构。 在Windows 8和Windows Server 2012时,timeoutlpOverlappedlpCompletionRoutinelpNameHandle 参数可用于调用 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 参数包含空字符串,则返回本地计算机上的所有注册地址。

在 Windows Server 2003 及更高版本中,如果 pName 参数指向等于“.”的字符串。localmachine“,返回本地计算机上所有已注册的地址。

如果 pName 参数引用群集虚拟服务器名称,则仅返回虚拟服务器地址。 在 Windows Vista 及更高版本上,这些地址将包括 GetUnicastIpAddressTableGetUnicastIpAddressEntry 函数返回的所有单播 IP 地址,其中 SkipAsSource 成员在 MIB_UNICASTIPADDRESS_ROW 结构中设置为 true。 有关聚类分析的详细信息,请参阅 Windows 群集

具有 Service Pack 1 的 Windows 7 (SP1) 和 Windows Server 2008 R2 with Service Pack 1 (SP1) 为 Netsh.exe 添加在 IP 地址上设置 SkipAsSource 属性的支持。 这还会更改行为,使得如果MIB_UNICASTIPADDRESS_ROW结构中的 SkipAsSource 成员设置为 false,则会在 DNS 中注册 IP 地址。 如果 SkipAsSource 成员设置为 true,则不会在 DNS 中注册 IP 地址。

修补程序适用于 Windows 7 和 Windows Server 2008 R2,它增加了对 Netsh.exe 在 IP 地址上设置 SkipAsSource 属性的支持。 此修补程序还会更改行为,使得如果 MIB_UNICASTIPADDRESS_ROW 结构中的 SkipAsSource 成员设置为 false,则会在 DNS 中注册 IP 地址。 如果 SkipAsSource 成员设置为 true,则不会在 DNS 中注册 IP 地址。 有关详细信息,请参阅 知识库 (知识库) 2386184

Service Pack 2 (SP2) Windows Vista 和 Windows Server 2008 Service Pack 2 (SP2) 也提供了类似的修补程序,为 Netsh.exe 在 IP 地址上设置 SkipAsSource 属性添加了支持。 此修补程序还会更改行为,使得如果 MIB_UNICASTIPADDRESS_ROW 结构中的 SkipAsSource 成员设置为 false,则会在 DNS 中注册 IP 地址。 如果 SkipAsSource 成员设置为 true,则不会在 DNS 中注册 IP 地址。

GetAddrInfoEx 函数的调用方可以提供有关通过 pHints 参数指向的 addrinfoex 结构支持的套接字类型的提示。 使用 pHints 参数时,以下规则适用于其关联的 addrinfoex 结构:

  • ai_family 的 AF_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 的结构和函数定义。
 

国际化域名

Internet 主机名通常由一组非常受限的字符组成:
  • 英文字母表中的大小写 ASCII 字母。
  • 从 0 到 9 的数字。
  • ASCII 连字符。

随着 Internet 的发展,越来越需要为非 ASCII 字符集表示的其他语言标识 Internet 主机名。 促进此需求并允许非 ASCII 字符 (Unicode) 表示为特殊 ASCII 字符串的标识符称为国际化域名 (IDN) 。 (IDNA) 中名为“将应用程序中的域名国际化”的机制用于以标准方式处理 IDN。 INTERNET 工程任务组 (IETF ) 发布的 RFC 3490RTF 5890RFC 6365 中记录了 IDN 和 IDNA 的规范。

在Windows 8和Windows Server 2012上,GetAddrInfoEx 函数支持国际化域名 (IDN) 分析应用于 pName 参数中传递的名称。 Winsock 执行 Punycode/IDN 编码和转换。 可以使用下面讨论的 AI_DISABLE_IDN_ENCODING 标志禁用此行为。

在 Windows 7 和 Windows Server 2008 R2 或更早版本上, GetAddrInfoEx 函数当前不支持应用于 pName 参数中传递的名称的 IDN 分析。 根据 RFC 3490,GetAddrInfoEx 函数的宽字符版本不使用 Punycode 转换 IDN Punycode 格式。 查询 DNS 时 ,GetAddrInfoEx 函数的宽字符版本将 Unicode 名称编码为 UTF-8 格式,这是 Microsoft DNS 服务器在企业环境中使用的格式。

Windows Vista 和更高版本上的多个函数支持将 IDN 中的 Unicode 标签转换为 ASCII 等效项。 每个 Unicode 标签的结果表示形式仅包含 ASCII 字符,并且以 xn 开头- 前缀(如果 Unicode 标签包含任何非 ASCII 字符)。 这样做的原因是支持 Internet 上的现有 DNS 服务器,因为某些 DNS 工具和服务器仅支持 ASCII 字符, (请参阅 RFC 3490) 。

IdnToAscii 函数使用 Punycode 使用 RFC 3490 中定义的标准算法将 IDN 转换为原始 Unicode 字符串的 ASCII 表示形式。 IdnToUnicode 函数将 IDN 的 ASCII 形式转换为正常的 Unicode UTF-16 编码语法。 有关相关标准草案的详细信息和链接,请参阅处理国际化 域名 (IDN)

IdnToAscii 函数可用于将 IDN 名称转换为 ASCII 形式,然后在未定义 UNICODE 和 _UNICODE) 时 (,此函数的 ASCII 版本被使用 pName 参数传递给 GetAddrInfoEx 函数。 若要在) 定义 UNICODE 或 (_UNICODE 时使用此函数的宽字符版本时将此 IDN 名称传递给 GetAddrInfoEx 函数,可以使用 MultiByteToWideChar 函数将 CHAR 字符串转换为 WCHAR 字符串。

在 hints 参数中使用ai_flags

hints 参数中提供的可选 addrinfoex 结构的 ai_flags 成员中的标志修改函数的行为。

这些标志位在适用于 Windows 7 的 Microsoft Windows 软件开发工具包 (Windows SDK) (SDK) 上的 Ws2def.h 头文件中定义。 这些标志位在 Windows Server 2008 和 Windows Vista Windows SDK上的 Ws2tcpip.h 头文件中定义。 这些标志位在适用于 Windows Server 2003 和 Windows XP 的平台软件开发工具包 (SDK) 上的 Ws2tcpip.h 头文件中定义。

标志位可以是以下各项的组合:

标记位 说明
AI_PASSIVE 设置 AI_PASSIVE 标志表示调用方打算在 调用绑定 函数时使用返回的套接字地址结构。 设置 AI_PASSIVE 标志且 pNameNULL 指针时,套接字地址结构的 IP 地址部分将设置为 IPv4 地址的INADDR_ANY ,对于 IPv6 地址 IN6ADDR_ANY_INIT

如果未设置 AI_PASSIVE 标志,则返回的套接字地址结构已准备好调用面向连接的协议的 connect 函数,或者已准备好调用无连接协议的 connectsendtosend 函数。 在这种情况下,如果 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 终止字符串。

注意设置 AI_CANONNAME 标志时,GetAddrInfoEx 函数可以返回成功,但关联的 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上定义。 AI_NON_AUTHORITATIVE 标志在 Windows Vista 及更高版本上受支持,并且仅适用于 NS_EMAIL 命名空间。

AI_SECURE 如果设置了 AI_SECURE 位, NS_EMAIL 命名空间提供程序将返回以增强的安全性获取的结果,以最大程度地减少可能的欺骗。

AI_SECURE标志在 Windows Vista 及更高版本的Windows SDK上定义。 AI_SECURE标志在 Windows Vista 及更高版本上受支持,并且仅适用于NS_EMAIL命名空间。

AI_RETURN_PREFERRED_NAMES 如果设置了 AI_RETURN_PREFERRED_NAMES ,则不应在 pName 参数中提供名称。 NS_EMAIL命名空间提供程序将返回发布的首选名称。

AI_RETURN_PREFERRED_NAMES标志在 Windows Vista 及更高版本的Windows SDK上定义。 AI_RETURN_PREFERRED_NAMES标志在 Windows Vista 及更高版本上受支持,并且仅适用于NS_EMAIL命名空间。

AI_FQDN 如果设置了 AI_FQDN ,并且指定了单标签) (平面名称, 则 GetAddrInfoEx 将返回该名称最终解析为的完全限定域名。 完全限定的域名在关联的 addrinfoex 结构的 ai_canonname 成员中返回。 这不同于 AI_CANONNAME 位标志,后者返回在 DNS 中注册的规范名称,该规范名称可能与平面名称解析到的完全限定域名不同。

设置 AI_FQDN 位后, pName 参数不能为 NULL。 否则, GetAddrInfoEx 函数将失败并 WSANO_RECOVERY

在Windows 8和Windows Server 2012上,可以设置AI_FQDN位和AI_CANONNAME位。 如果使用AI_FQDN位和AI_CANONNAME位调用 GetAddrInfoEx 函数,则 ppResult 参数返回指向 addrinfoex2 结构的指针,而不是 addrinfoex 结构。

在 Windows 7 和 Windows Server 2008 R2 上,只能设置 AI_FQDN 位和 AI_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 标头将 GetAddrInfoEx 定义为别名,该别名根据 UNICODE 预处理器常量的定义自动选择此函数的 ANSI 或 Unicode 版本。 将非特定编码别名与非非特定编码的代码混合使用可能会导致不匹配,从而导致编译或运行时错误。 有关详细信息,请参阅 函数原型的约定

要求

要求
最低受支持的客户端 Windows Vista [桌面应用 | UWP 应用]
最低受支持的服务器 Windows Server 2008 [桌面应用 | UWP 应用]
目标平台 Windows
标头 ws2tcpip.h
Library Ws2_32.lib
DLL Ws2_32.dll

另请参阅

FreeAddrInfoEx

GetAddrInfoExCancel

GetAddrInfoExOverlappedResult

GetAddrInfoW

IdnToAscii

IdnToUnicode

WSAEnumNameSpaceProviders

WSAGetLastError

Windows 套接字错误代码

addrinfoex

addrinfoex2

gai_strerror

getaddrinfo