枚举域控制器

在早期版本的 Windows 中,应用程序只能通过调用 DsGetDcName 在域中获取单个域控制器。 无法预测要检索的域控制器或获取域控制器列表。 Windows 允许应用程序使用 DsGetDcOpenDsGetDcNextDsGetDcClose 函数枚举域中的域控制器。

若要枚举域控制器,请调用 DsGetDcOpen。 此函数采用用于定义要枚举的域和其他枚举选项的参数。 DsGetDcOpen 提供域枚举上下文句柄,用于在调用 DsGetDcNextDsGetDcClose 时标识枚举操作。

使用域枚举上下文句柄调用 DsGetDcNext 函数,以检索枚举中的下一个域控制器。 首次调用此函数时,将检索枚举中的第一个域控制器。 第二次调用此函数时,将检索枚举中的第二个域控制器。 此过程将重复,直到 DsGetDcNext 返回 ERROR_NO_MORE_ITEMS,指示枚举的末尾。

DsGetDcNext 函数将枚举两个组中的域控制器。 第一个组包含涵盖在其中执行函数的计算机站点的域控制器,第二个组包含不涵盖在其中执行函数的计算机站点的域控制器。 如果在 DsGetDcOpenOptionFlags 参数中指定了 DS_NOTIFY_AFTER_SITE_RECORDS 标志,则检索到所有特定于站点的域控制器后,DsGetDcNext 函数将返回 ERROR_FILEMARK_DETECTED。 然后,DsGetDcNext 将开始枚举第二个组,该组包含域中的所有域控制器,包括第一组中包含的特定于站点的域控制器。

首先枚举处理在其中执行函数的计算机站点的域控制器,然后枚举不涵盖在其中执行函数的计算机站点的域控制器。 如果将域控制器配置为驻留在该站点中,或者域控制器位于就配置的站点间链路成本而言,离站点最近的站点中,则表示域控制器涵盖站点。 如果涵盖的两组域控制器包含任何域控制器,以及不涵盖计算机站点的域控制器组,则会按照 DNS 中指定的已配置优先级和权重在组中返回域控制器。 首先在组中返回具有较低数值优先级的域控制器。 如果站点相关组中有多个具有相同优先级的域控制器的子组,则会按加权随机顺序返回域控制器,其中首先返回具有较高权重的域控制器的可能性更大。 站点、优先级和权重由域管理员配置,以在域中提供的多个域控制器之间实现有效的性能和负载均衡。 因此,使用 DsGetDcOpen/DsGetDcNext/DsGetDcClose 函数的应用程序会自动利用这些优化。

当枚举完成或不再需要枚举时,必须使用域枚举上下文句柄调用 DsGetDcClose 来关闭枚举。

若要重置枚举,必须通过调用 DsGetDcClose 关闭当前枚举,然后再次调用 DsGetDcOpen 重新打开枚举。

示例

以下代码示例演示如何使用这些函数枚举本地域中的域控制器。

DWORD dwRet;
PDOMAIN_CONTROLLER_INFO pdcInfo;

// Get a domain controller for the domain this computer is on.
dwRet = DsGetDcName(NULL, NULL, NULL, NULL, 0, &pdcInfo);
if(ERROR_SUCCESS == dwRet)
{
    HANDLE hGetDc;
    
    // Open the enumeration.
    dwRet = DsGetDcOpen(    pdcInfo->DomainName,
                            DS_NOTIFY_AFTER_SITE_RECORDS,
                            NULL,
                            NULL,
                            NULL,
                            0,
                            &hGetDc);
    if(ERROR_SUCCESS == dwRet)
    {
        LPTSTR pszDnsHostName;

        /*
        Enumerate each domain controller and print its name to the 
        debug window.
        */
        while(TRUE)
        {
            ULONG ulSocketCount;
            LPSOCKET_ADDRESS rgSocketAddresses;

            dwRet = DsGetDcNext(
                hGetDc, 
                &ulSocketCount, 
                &rgSocketAddresses, 
                &pszDnsHostName);
            
            if(ERROR_SUCCESS == dwRet)
            {
                OutputDebugString(pszDnsHostName);
                OutputDebugString(TEXT("\n"));
                
                // Free the allocated string.
                NetApiBufferFree(pszDnsHostName);

                // Free the socket address array.
                LocalFree(rgSocketAddresses);
            }
            else if(ERROR_NO_MORE_ITEMS == dwRet)
            {
                // The end of the list has been reached.
                break;
            }
            else if(ERROR_FILEMARK_DETECTED == dwRet)
            {
                /*
                DS_NOTIFY_AFTER_SITE_RECORDS was specified in 
                DsGetDcOpen and the end of the site-specific 
                records was reached.
                */
                OutputDebugString(
                  TEXT("End of site-specific domain controllers\n"));
                continue;
            }
            else
            {
                // Some other error occurred.
                break;
            }
        }
        
        // Close the enumeration.
        DsGetDcClose(hGetDc);
    }
    
    // Free the DOMAIN_CONTROLLER_INFO structure. 
    NetApiBufferFree(pdcInfo);
}