Click here to Skip to main content
15,881,281 members
Articles / Programming Languages / C#
Article

Simple Active Directory Browser

Rate me:
Please Sign up or sign in to vote.
4.30/5 (23 votes)
16 Dec 2003CPOL 171.7K   4.8K   43   29
A simple Active Directory Browser for beginners.

Sample Image - ActiveDirectoryBrowser.jpg

Introduction

Ever wondered how to write a simple Active Directory Browser? Well, then look at my example code here. Its quite simple and straightforward. I am utilizing System.DirectoryServices to connect to AD and enumerate objects.

Of course, this is a simple example which doesn't include threading to make the user interface respond faster. But it will give you an idea of how to connect to AD and do some simple browsing.

I also used my listview column sorter class, which is available here.

Code Basics

To connect to Active Directory is quite simple:

C#
// connect to active directory 
DirectoryEntry rootDSE = new 
  DirectoryEntry("LDAP<A href="ldap://rootDSE/">://rootDSE/</A>"); 
DirectoryEntry root = new DirectoryEntry("LDAP://" + 
  (string)rootDSE.Properties["defaultNamingContext"].Value);

The property defaultNamingContext contains the domain name in format: "dc=test,dc=com" (for domain name "test.com") which we use to connect to the test.com domain.

History

  • 2003-11-04 - First edition, more updates will come later.
  • 2003-11-13 - Dominic found a bug. I uploaded a new example project that fixes the bug.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) a large company
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionrefresh button Pin
abu sabha19-May-15 23:36
abu sabha19-May-15 23:36 
AnswerRe: refresh button Pin
Sven So.20-Jul-15 22:06
Sven So.20-Jul-15 22:06 
QuestionLicense Terms Pin
Manual178921-Dec-12 3:14
Manual178921-Dec-12 3:14 
AnswerRe: License Terms Pin
Sven So.2-Jan-13 23:20
Sven So.2-Jan-13 23:20 
QuestionHaving trouble figuring out images Pin
David Bayer23-Sep-09 3:10
David Bayer23-Sep-09 3:10 
AnswerRe: Having trouble figuring out images Pin
Sven So.23-Sep-09 4:42
Sven So.23-Sep-09 4:42 
GeneralRe: Having trouble figuring out images Pin
David Bayer23-Sep-09 5:16
David Bayer23-Sep-09 5:16 
GeneralRe: Having trouble figuring out images Pin
David Bayer23-Sep-09 5:30
David Bayer23-Sep-09 5:30 
GeneralRe: Having trouble figuring out images Pin
Sven So.23-Sep-09 22:26
Sven So.23-Sep-09 22:26 
GeneralDisplay Properties Pin
R.Palanivel3-Jun-09 0:03
R.Palanivel3-Jun-09 0:03 
QuestionRe: Display Properties Pin
Sven So.5-Jun-09 3:58
Sven So.5-Jun-09 3:58 
AnswerRe: Display Properties Pin
R.Palanivel7-Jun-09 0:53
R.Palanivel7-Jun-09 0:53 
GeneralGood GUI Pin
Zhefu Zhang2-Jan-04 14:27
Zhefu Zhang2-Jan-04 14:27 
GeneralBitmaps Pin
bruccutler16-Dec-03 11:35
bruccutler16-Dec-03 11:35 
GeneralRe: Bitmaps Pin
Sven So.16-Dec-03 19:39
Sven So.16-Dec-03 19:39 
GeneralRe: Bitmaps Pin
Andreas Saurwein17-Dec-03 2:59
Andreas Saurwein17-Dec-03 2:59 
GeneralRe: Bitmaps Pin
Sven So.4-Jan-04 21:27
Sven So.4-Jan-04 21:27 
GeneralDomains Pin
sevenstorydrop15-Nov-03 9:56
sevenstorydrop15-Nov-03 9:56 
GeneralRe: Domains Pin
scatter15-Nov-03 10:09
scatter15-Nov-03 10:09 
GeneralRe: Domains Pin
sevenstorydrop16-Nov-03 13:36
sevenstorydrop16-Nov-03 13:36 
GeneralRe: Domains Pin
Sven So.17-Nov-03 19:40
Sven So.17-Nov-03 19:40 
GeneralRe: Domains Pin
dromano1110-Mar-05 10:40
dromano1110-Mar-05 10:40 
I haven't been able to figure out how to do it with the DirectoryServices namespace so I decided to go down and dirty and get it using WinAPIs, I did not place any error handling on this so you will have to do this. Also if you need more info on return values I included a few links on the remarks and comments to help you out. This should be able to return all of the Domain Names in LDAP Format (e.g. dc=domain,dc=ext) for all domains registered in your Global Catalog Server, Also since I'm repeating the process for every site you might need to look for duplicated entries in the DomainNames string array. Good LuckLaugh | :laugh:

<------ Code Begin ------>

using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;

namespace UserCreatorAD
{
/// <summary>
/// Summary description for ADEnumerator.
/// </summary>
[SecurityPermissionAttribute(SecurityAction.LinkDemand, UnmanagedCode=true)]
public class ADEnumerator
{
/// <summary>
/// WinAPI DsBind
/// </summary>
/// <param name="DomainController">[in] The name of the domain
/// controller formated "\\name.domain.domain this should
/// be left null so we can bind to the Global Catalog</param>
/// <param name="DnsDomain">[in] The name of the domain you want
/// to bind to this should also be left null in order
/// to bind to the Global Catalog</param>
/// <param name="phDS">[out] pointer to the Directory Services
/// Bind Handle</param>
/// <returns>Error code</returns>
/// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ad/ad/dsbind.asp</remarks>
[DllImport("ntdsapi.dll")]
private static extern int DsBind(
string DomainController,
string DnsDomain,
out System.IntPtr phDS);
/// <summary>
/// WinAPI DsUnBind
/// </summary>
/// <param name="phDs">[in] The pointer to the Directory
/// Services Bind Handle</param>
/// <returns>Error Code</returns>
/// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ad/ad/dsunbind.asp</remarks>
[DllImport("ntdsapi.dll")]
private static extern int DsUnBind(
System.IntPtr phDs);
/// <summary>
/// WinAPI DsListSites
/// </summary>
/// <param name="hDS">[in] Pointer to the Directory
/// Services Bind handle</param>
/// <param name="ppSites">[out] pointer to a structure
/// of type DsNameResult which contains an array of
/// DsNameResultItem structures with the LDAP names
/// of the sites in the Global Catalog.</param>
/// <returns>Error Code</returns>
/// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ad/ad/dslistserversinsite.asp</remarks>
[DllImport("ntdsapi.dll")]
private static extern int DsListSites(
System.IntPtr hDS,
out System.IntPtr ppSites);
/// <summary>
/// WinAPI DsListDomainsInSite
/// </summary>
/// <param name="hDS">[in] Pointer to Directory
/// Services Bind Handle.</param>
/// <param name="sSite">The name of the site aquired
/// from DsListSites WinAPI</param>
/// <param name="ppDomains">pointer to the handle of
/// a structure of type DsNameResult</param>
/// <returns>Error Code</returns>
/// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ad/ad/dslistdomainsinsite.asp</remarks>
[DllImport("ntdsapi.dll")]
private static extern int DsListDomainsInSite(
System.IntPtr hDS,
string sSite,
out System.IntPtr ppDomains);
/// <summary>
/// WinAPI DsFreeNameResult
/// </summary>
/// <param name="pResult">[in] The Pointer to the
/// DsNameResult structure to be freed from
/// memory</param>
[DllImport("ntdsapi.dll")]
private static extern void DsFreeNameResult(
System.IntPtr pResult);
/// <summary>
/// The pointer to the Directory Service bind handle.
/// </summary>
private System.IntPtr DSBindHandle;
private Error thisLastError;

public ADEnumerator()
{

}
/// <summary>
/// Finds the domain names
/// in LDAP format for all
/// the domains in the global
/// catalog server
/// </summary>
/// <param name="DomainNames">[out] An array
/// of strings containing the domain names</param>
/// <returns>true for success false for failure.</returns>
public bool FindDomains(out string[] DomainNames)
{
//For Error handling.
int MyError;
//Pointer to the DsNameResult structure for the sites.
System.IntPtr pSites = IntPtr.Zero;
//Pointer to the DsNameResult structure for the Domains.
System.IntPtr pDomains = IntPtr.Zero;
//The structure which will hold the name of the sites.
DsNameResult rSites;
//The structure which will hold the name of the domains.
DsNameResult rDomains;

//Lets bind the program to the GCS.
//and find the sites it contains.
MyError = DsBind(null,null,out DSBindHandle);
DsListSites(DSBindHandle, out pSites);
//Now we marshal the pointer to the DsNameResult Structure.
rSites = (DsNameResult)
Marshal.PtrToStructure(
pSites,
typeof(DsNameResult));
//Lets fin how many sites are there and point
//to the first Item in the Array.
int ItemCount = rSites.cItems;
System.IntPtr pItems = rSites.rItems;
//This varibles will hold the names of the sites.
DsNameResultItem rItem;
string[] SiteNames = new string[ItemCount];
//Now we loop through all of the items in the array
//and save their names.
for(int i = 0; i < ItemCount; i++)
{
//Again we marshal the pointer
//but this time to the DsNameResultItem
//Structure so we can read the names
//of the sites.
rItem = (DsNameResultItem)
Marshal.PtrToStructure(
pItems,
typeof(DsNameResultItem));
//we save the name in our own array.
SiteNames[i] = rItem.pName;
//we find the size of the object.
int SizeOfItem = Marshal.SizeOf(rItem);
//And now we add the size to the existing pointer
//so we can move to the next structure in the
//unmanaged Array.
pItems = (System.IntPtr)((int)pItems + SizeOfItem);
}
//since we don't know how many domains are in each site
//it is easier for us to use an ArrayList object to
//store those since we can add items dynamically to it.
System.Collections.ArrayList sDN = new System.Collections.ArrayList();
//Now we loop through all of the sites and find
//the domains that are in it.
//The entire process is almost the same as the sites
//so I'm not going to bother commenting it all,
//but we have a loop within a loop to find all domains.
foreach(string thisSite in SiteNames)
{
MyError = DsListDomainsInSite(DSBindHandle,thisSite,out pDomains);
rDomains = (DsNameResult)Marshal.PtrToStructure(pDomains,typeof(DsNameResult));
pItems = rDomains.rItems;
for(int i = 0; i < rDomains.cItems; i++)
{
rItem = (DsNameResultItem)Marshal.PtrToStructure(pItems,typeof(DsNameResultItem));
sDN.Add(rItem.pName);
int SizeOfItem = Marshal.SizeOf(rItem);
pItems = (System.IntPtr)((int)pItems + SizeOfItem);
}
//Since we don't need the unmanaged structure
//anymore we free that memory.
DsFreeNameResult(pDomains);
}
//And now that we don't need the sites anymore either
//we free that memory as well.
DsFreeNameResult(pSites);
//Unbind the program from the GC.
DsUnBind(DSBindHandle);
//Save the anems in the out array.
DomainNames = new string[sDN.Count];
for(int i = 0; i < sDN.Count; i++)
{
DomainNames[i] = (string)sDN[i];
}
//and we are done.
return true;
}

public Error LastError
{
get{return thisLastError;}
}
}
public struct Error
{
public int ErrNumber;
public string ErrMsg;
}
/// <summary>
/// Created to mimic the unmanaged structure
/// DS_NAME_RESULT_ITEM.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct DsNameResultItem
{
public int status;
public string pDomain;
public string pName;
}
/// <summary>
/// Created to mimic the unmanged structure
/// DS_NAME_RESULT.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct DsNameResult
{
public int cItems;
public System.IntPtr rItems;
}
}
<------ Code End ------>

GeneralSorting columns error Pin
jsk647814-Nov-03 3:40
jsk647814-Nov-03 3:40 
GeneralRe: Sorting columns error Pin
Sven So.17-Nov-03 19:38
Sven So.17-Nov-03 19:38 
GeneralRe: Sorting columns error Pin
jsk647818-Dec-03 5:38
jsk647818-Dec-03 5:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.