The DirectoryEntry class contains several properties to access the attributes of Active Directory objects. The following code shows how to display the currentTime attribute of the RootDSE:
Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE") Console.WriteLine(objRootDSE.Properties("currentTime")(0))
In the code, once we instantiated the DirectoryEntry object, we can access the currentTime attribute by passing it to the Properties property. The Properties property actually returns a collection of values for the attribute in the form of a PropertyCollection class, which is why we needed to specify an index of 0 to get at a specific value. If the currentTime attribute was multivalued, we could get at the other values by incrementing the index to 1 and so on.
|
Now let's look at how to display all of the values for all of the attributes of an object in Active Directory. Again we will use RootDSE as the object we want to display:
Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE") Dim strAttrName As String Dim objValue As Object For Each strAttrName In objRootDSE.Properties.PropertyNames For Each objValue In objRootDSE.Properties(strAttrName) Console.WriteLine(strAttrName & " : " & objValue.ToString) Next objValue Next strAttrName
As you can see, the Properties property, which returns a PropertyCollection, has a PropertyNames property that returns a collection of attribute names for the Active Directory object. We loop over each attribute name and then loop over each value for that attribute to ensure we print out values for all single- and multivalued attributes. The ToString property converts whatever value is stored in the attribute to a printable string.
There are several properties available in the DirectoryEntry class. Table 28-2 contains a list of them.
Property name |
Description |
---|---|
AuthenticationType |
Gets or sets the type of authentication to use when accessing the directory. |
Children |
Gets a DirectoryEntries class that contains the child objects of this object. |
Guid |
Gets the GUID for the object (e.g., in Active Directory the objectGUID attribute). |
Name |
Gets the relative distinguished name of the object. |
NativeGuid |
Gets the GUID of the object as returned by the provider. |
NativeObject |
Gets the native ADSI object. |
Parent |
Gets the object's parent in Active Directory. |
Password |
Gets or sets the password to use when authenticating. |
Path |
Gets or sets the ADsPath for the object. |
Properties |
Gets a PropertyCollection class containing the attributes on the object. |
SchemaClassName |
Gets the objectclass of the object. |
SchemaEntry |
Gets the DirectoryEntry class of the object's objectclass. |
UsePropertyCache |
Gets or sets the flag indicating whether the property cache should be committed after each operation. |
Username |
Gets or sets the username to use when authenticating. |
One interesting property to note is Children, which returns a DirectoryEntries collection containing each child object. Using the Children property, you can quickly traverse a directory tree. The following code prints out the entire directory tree rooted at dc=mycorp,dc=com:
Sub Main( ) Dim objADObject As New DirectoryEntry("LDAP://dc=mycorp,dc=com") DisplayChildren(objADObject, " ") End Sub Sub DisplayChildren(ByVal objADObject As DirectoryEntry, _ ByVal strSpaces As String) Console.WriteLine(strSpaces & objADObject.Name) Dim objChild As New DirectoryEntry( ) For Each objChild In objADObject.Children DisplayChildren(objChild, strSpaces & " ") Next objChild End Sub
The DisplayChildren( ) subroutine is recursive. For each child that is found, DisplayChildren( ) is called again, and so on until no child objects are found. The strSpaces variable is used to indent each child so that you can see the hierarchy when printed out.
Now let's say that we wanted to traverse the tree but print out only the Organizational Units. To do that, we can use the SchemaClassName property for each object and only print out the entry if its SchemaClassName equals organizationalUnit, which is the objectClass value for OUs.
Sub Main( ) Dim objADObject As New DirectoryEntry("LDAP://dc=mycorp,dc=com") DisplayChildren(objADObject, " ") End Sub Sub DisplayChildren(ByVal objADObject As DirectoryEntry, _ ByVal strSpaces As String) If objADObject.SchemaClassName = "organizationalUnit" Then Console.WriteLine(strSpaces & objADObject.Name) End If Dim objChild As New DirectoryEntry( ) For Each objChild In objADObject.Children DisplayChildren(objChild, strSpaces & " ") Next objChild End Sub
Error Handling in VB.NETOne of the important new features of VB.NET is robust error handling. VB.NET supports a new Try Catch statement that allows you to easily catch exceptions as they happen and perform certain actions based on the type of exception that was thrown. Typically in .NET, if an error is encountered, an exception is thrown. Using a Try Catch statement allows you to handle errors gracefully, much as you could with the On Error directive in Visual Basic 6.0. In fact, if you use On Error with VB.NET, the compiler actually translates it into Try Catch statements. Let's take a look at a code snippet we used earlier to print the currentTime attribute of the RootDSE: Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE") Console.WriteLine(objRootDSE.Properties("currentTime")(0)) As you can see, there is no error handling. If there is a problem accessing the RootDSE, the program will abort gracelessly. Using a Try Catch statement, we can change the code to die gracefully or even continue execution of the rest of the program: Try Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE") Console.WriteLine(objRootDSE.Properties("currentTime")(0)) Catch objExp As Exception Console.WriteLine("Error retrieving RootDSE: " & _ objExp.Message) End Try One of the nice features of the Try Catch statement is you can catch different types of errors. For example, let's pretend that we wanted to write to a file after we retrieved the currentTime from the RootDSE. Interacting with a file can generate certain IO exceptions. We can insert an additional catch into the Try End Try block to catch IO exceptions as follows: Try Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE") Console.WriteLine(objRootDSE.Properties("currentTime")(0)) ' write to a file Catch objIOExp as IOException Console.WriteLine("File IO Error: " & objIOExp.Message) Catch objExp As Exception Console.WriteLine("Error retrieving RootDSE: " & _ objExp.Message) End Try You can also generate your own exceptions with the Throw statement. Here is an example: Try If objADObject.Exists(strADsPath) = False Then Throw (New Exception("Object does not exist")) End If Catch exp As Exception Console.WriteLine("Error retrieving object: " & _ strADsPath) End Try The Try Catch statement is very powerful and flexible. For more information on the properties and methods available to exception objects, check out the System.Exception namespace. |
We are now going to take many of the concepts described so far and make a fully functional program. Let's expand on the first example we covered that printed the attributes and values for the RootDSE. We are going to turn it into a program that can accept a command-line argument, which should be the ADsPath of an object, and then display all of the attributes and values for that object. Example 28-1 contains the code.
Imports System Imports System.DirectoryServices Module Module1 Sub Main( ) Dim cmd As String ' Read the commandline and get the number of arguments passed Dim intArgs As Integer Try intArgs = Environment.GetCommandLineArgs( ).Length( ) Catch exp As Exception ' Set intArgs to 0 if no arguments were passed intArgs = 0 End Try ' If an argument was specified on the commandline, set ' strADsPath to that, if not default to query the RootDSE Dim strADsPath As String If intArgs > 1 Then strADsPath = Environment.GetCommandLineArgs( )(1) Else strADsPath = "LDAP://RootDSE" End If ' We need to see if the object in strADsPath exists ' and if not, print an error and return Dim objADObject As New DirectoryEntry( ) Try If objADObject.Exists(strADsPath) = False Then Throw (New Exception("Object does not exist")) End If Catch exp As Exception Console.WriteLine("Error retrieving object: " & strADsPath) Return End Try ' Iterate over each attribute of the object and print its values Dim strAttrName As String Dim objValue As Object Try objADObject.Path = strADsPath Console.WriteLine("Displaying " & objADObject.Path) For Each strAttrName In objADObject.Properties.PropertyNames For Each objValue In objADObject.Properties(strAttrName) Console.WriteLine(strAttrName & " : " & objValue.ToString) Next objValue Next strAttrName Catch exp As Exception Console.WriteLine("Fatal error accessing: " & strADsPath) Return End Try End Sub End Module
The first two lines, which use the Imports keyword, allow us to specify class names contained within those namespaces without fully qualifying them. For example, by using Imports we can use the following code:
New DirectoryEntry( )
instead of :
New System.DirectoryServices.Directory( )
For simplicity, we put the rest of the code directly in the Main( ) subroutine. The first part of the code attempts to read the command line using the System.Environment namespace to see if a parameter was specified. A Try Catch statement was used because the call to Environment.GetCommandLineArgs( ).Length( ) will throw an exception if no parameters are passed on the command line. For more information on error handling, see the "Error Handling in VB.NET" sidebar. Note that the intArgs variable will contain the number of arguments passed to the script including the script name as the first argument. To see if the user actually passed the ADsPath we have to check whether intArgs > 1. It is for this reason that we set the strADsPath variable to the value specified on the command line and if one wasn't, default to the RootDSE. Next we use the Exists( ) method (not property) to determine if the object specified in strADsPath actually exists. The DirectoryEntry class contains a host of methods in addition to the properties we showed earlier. Table 28-3 contains a list of all the DirectoryEntry methods.
Method name |
Description |
---|---|
Close |
Closes the DirectoryEntry and releases any system resources associated with the component |
CommitChanges |
Saves any changes to the object in Active Directory (similar to SetInfo) |
CopyTo |
Creates a copy of the object |
DeleteTree |
Deletes the object and any children |
Equals |
Determines whether two objects are the same |
Exists |
Determines whether the object exists in Active Directory |
Invoke |
Allows you to invoke a native ADSI method |
MoveTo |
Moves an object to a different location |
RefreshCache |
Refreshes the property cache for the object |
Rename |
Renames the relative distinguished name of the object |
ToString |
String representation of the object |
If the Exists( ) check fails, we generate an
exception using Throw( ). If the object exists, we
proceed to iterate over each attribute, printing the values for it.
To turn the code into an executable, you can compile the program by
selecting Build Build Solution from the VS.NET
menu. If any errors are found, they are displayed in the bottom pane.
If none are found, you can then execute the program. If we named the
project EntryQuery, an example command line would
look like the following:
D:\Visual Studio Projects\EntryQuery\EntryQuery\bin> entryquery.exe LDAP:// dc=mycorp,dc=com