回答

1

這裏是具有DC的沒有硬編碼的碼樣品。歡迎評論和批評。

/// <summary> 
    /// For best results ensure all hosts are pingable, and turned on. 
    /// </summary> 
    /// <returns>An ordered list of DCs with the PDCE first</returns> 
    static LinkedList<DomainController> GetNearbyDCs() 
    { 
     LinkedList<DomainController> preferredDCs = new LinkedList<DomainController>(); 
     List<string> TestedDCs = new List<string>(); 

     using (var mysite = ActiveDirectorySite.GetComputerSite()) 
     { 
      using (var currentDomain = Domain.GetCurrentDomain()) 
      { 
       DirectoryContext dctx = new DirectoryContext(DirectoryContextType.Domain, currentDomain.Name); 
       var listOfDCs = DomainController.FindAll(dctx, mysite.Name); 

       foreach (DomainController item in listOfDCs) 
       { 
        Console.WriteLine(item.Name); 
        if (IsConnected(item.IPAddress)) 
        { 
         // Enumerating "Roles" will cause the object to bind to the server 
         ActiveDirectoryRoleCollection rollColl = item.Roles; 
         if (rollColl.Count > 0) 
         { 
          foreach (ActiveDirectoryRole roleItem in rollColl) 
          { 
           if (!TestedDCs.Contains(item.Name)) 
           { 
            TestedDCs.Add(item.Name); 
            if (roleItem == ActiveDirectoryRole.PdcRole) 
            { 
             preferredDCs.AddFirst(item); 
             break; 
            } 
            else 
            { 

             if (preferredDCs.Count > 0) 
             { 
              var tmp = preferredDCs.First; 
              preferredDCs.AddBefore(tmp, item); 
             } 
             else 
             { 
              preferredDCs.AddFirst(item); 
             } 
             break; 
            } 
           } 

          } 
         } 
         else 
         { 
          // The DC exists but has no roles 
          TestedDCs.Add(item.Name); 
          if (preferredDCs.Count > 0) 
          { 
           var tmp = preferredDCs.First; 
           preferredDCs.AddBefore(tmp, item); 
          } 
          else 
          { 
           preferredDCs.AddFirst(item); 
          } 
         } 
        } 
        else 
        { 
         preferredDCs.AddLast(item); 
        } 
       } 
      } 
     } 
     return preferredDCs; 
    } 
    static bool IsConnected(string hostToPing) 
    { 
     string pingurl = string.Format("{0}", hostToPing); 
     string host = pingurl; 
     bool result = false; 
     Ping p = new Ping(); 
     try 
     { 
      PingReply reply = p.Send(host, 3000); 
      if (reply.Status == IPStatus.Success) 
       return true; 
     } 
     catch { } 
     return result; 
    } 
+0

問題給任何人都可以回答:客戶端是否需要處置每個'DomainController'實例? – LamonteCristo

+0

由於類實現了iDisposable我會建議它。這些DirectoryEntry類傾向於泄漏內存。 – Peter

6

所有這些麻煩都可能是浪費精力。除非遇到用於查找域控制器的內置邏輯的問題,否則應該使用返回一個的內置方法。根據微軟它會自動嘗試找到關閉的一個:http://technet.microsoft.com/en-us/library/cc978016.aspx

只需使用靜態DomainController.FindOne方法,並將您的directorycontext

更新
好吧,試試下面的代碼,讓我知道它是如何工作的。它ping每個,返回往返時間,如果-1(無連接)它跳過它。標誌PDC狀態(如果存在)。通過PDC狀態訂單,然後ping往返。

static void Main(string[] args) 
    { 
     var dcsInOrder = (from DomainController c in Domain.GetCurrentDomain().DomainControllers 
          let responseTime = Pinger(c.Name) 
          where responseTime >=0 
          let pdcStatus = c.Roles.Contains(ActiveDirectoryRole.PdcRole) 
          orderby pdcStatus, responseTime 
          select new {DC = c, ResponseTime = responseTime} 
         ).ToList(); 

     foreach (var dc in dcsInOrder) 
     { 
      System.Console.WriteLine(dc.DC.Name + " - " + dc.ResponseTime); 
     } 

     System.Console.ReadLine(); 
    } 

    private static int Pinger(string address) 
    { 
     Ping p = new Ping(); 
     try 
     { 
      PingReply reply = p.Send(address, 3000); 
      if (reply.Status == IPStatus.Success) return (int)reply.RoundtripTime; 
     } 
     catch { } 

     return -1; 

    } 
+0

問題是我沒有信心FindOne以technet描述的方式工作。運行代碼似乎表明服務器是隨機選擇的。 – LamonteCristo

+0

@ makerofthings7 - 更新了我的答案,以包含一個我認爲可以爲您工作的例子。 – Peter

+0

@ makerofthings7 - 你有機會嘗試一下嗎? – Peter

2

首先,我會回答你居然問了一個問題:

System.DirectoryServices.ActiveDirectory.ActiveDirectorySite.GetComputerSite().Servers 

但好像你問如何確保你所談論到最近的域控制器可能。 Windows並沒有完全提供這種功能,它所要做的最好的事情就是在代碼運行的同一站點上爲您提供一個域控制器。

我認爲首先要檢查的事情是,你有你的站點和子網配置正確。運行Active Directory站點和服務,並確保將子網和域控制器分配到正確的站點。

This MSDN page(以及Peter的答案中的Technet文章)表示,您必須通過DNS名稱搜索DC定位器以嘗試在當前站點中查找DC。我不知道Domain類的Name屬性是否是DNS域名。

我不得不假定DomainController.FindOne是DsGetDcName的包裝。在該鏈接中,您可以找到如何打開該功能的跟蹤。如果你仍然有問題,你可以使用它,或者你應該只是PInvoke這個功能。

0

下面是使用PowerShell我的做法,但我敢肯定,它在C#等簡單的實現。如果DHCP設置正確,在你的子網中的主DNS服務器應該是最近的域控制器。因此,下面的代碼應該抓住第一個DNS IP並將其解析爲最近的DC的主機名。這不需要RSAT或憑證,也不包含當前域的特定屬性。

$NetItems = @(Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled = 'True'" -ComputerName $env:COMPUTERNAME) 
foreach ($objItem in $NetItems) 
{ 
    if ($objItem.{DNSServerSearchOrder}.Count -ge 1) 
    { 
     $PrimaryDNS = $objItem.DNSServerSearchOrder[0] 
     $domain = $objItem.DNSDomain 
     break 
    } 
} 
[System.Net.Dns]::GetHostbyAddress($PrimaryDNS).hostname -replace ".$($domain)",""