DekGenius.com
[ Team LiB ] Previous Section Next Section

28.4 DirectoryEntry Basics

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.

In object-oriented parlance, a property allows you to get or set an attribute of an object. A method typically results in some kind of action being taken on the object.

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.

Table 28-2. DirectoryEntry properties

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.NET

One 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.

Example 28-1. Making a fully functional program
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.

Table 28-3. 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
    [ Team LiB ] Previous Section Next Section