Containers

As I've mentioned earlier in this book, in Active Directory, all objects, except the root object, are grouped under another object, known as a container. The container is often referred to as the parent object, while each object within the container is considered a child object. Active Directory documentation also refers to objects that have no children as leaves or leaf nodes.

In a virtual sense, a container object is simply a regular Active Directory object that is marked as a container. While the Active Directory schema provides a container class object, this does not mean that only container class objects can be containers. You need to understand the importance of this because any object can become a container object, not just objects of the class container. An excellent example of this is the objects representing computers on the network. These are objects of the class computer and are normally contained in the Computers or Domain Controllers container. When a printer is published in Active Directory, an object representing the print queue is created in the directory. The computer that physically hosts the printer is marked as a container, and the new printQueue object is made a child of the computer object.

Figure 6-5 shows a printer as a child of the computer object COPPER2. You need to select a View option in the Active Directory Users and Computers snap-in to show children of users, groups, and computers.

Figure 6-5 Computer object COPPER2 is shown as a container for a printer object.

The concept of containers has many benefits for developers. By allowing any object to be made a container, information that is relevant to all the child objects can be stored in the parent object. For example, information about the organizational unit, such as location information and group policy, can be stored within the parent, an organizationalUnit class object.

Enumerating Containers

Enumeration is the process of retrieving each object within a container. Automation, coupled with Visual Basic, makes this process very easy.

 Set objContainer = GetObject("LDAP://CN=Users,DC=coppersoftware,DC=com")

For Each objUser in objContainer
    Debug.Print objUser.Name
Next

In this code, each iteration through the For Each loop assigns the variable objUser with an object that is part of the container referenced by the variable objContainer.

This enumeration is made possible by the IADsContainer interface. This interface is made available by ADSI for any object that contains other objects. IADsContainer is a versatile interface supplying several functions for operations such as enumerating container objects, creating and deleting objects, and moving objects. Tables 6-7 and 6-8 list the properties and methods of the IADsContainer interface.

IADsContainer Property Returned Data Type Description

Count

Long

A count of the number of objects in the container. ad-only. Not supported under Active Directory.

Filter

Variant array of strings

An array of class names to filter on. When this property is set, only objects of the class that matches the filter are enumerated. The default is to enumerate all classes.

Hints

Variant array of strings

An array of attribute names to retrieve for each object when enumerating a container.

get__NewEnum (Not exposed in Visual Basic)

Object

Creates a new enumerator object that supports the IEnumVARIANT interface. Called indirectly from Visual Basic using the For Each statement. Returns an IUnknown interface pointer. Note that there are two underscore (_) characters in the method name.

Table 6-7 Properties of the IADsContainer interface.

IADsContainer Method Description

Create

Creates a new object in the container of the class specified. Also accepts a name. Returns an IDispatch interface from which IADs properties and methods can be called. The object is not stored in the directory until the SetInfo method is called.

Delete

Deletes the object in the container specified by name. You must also supply the class name. The object will not be deleted from the directory until the SetInfo method is called.

CopyHere

Creates a new copy of the specified object in the container. Not supported under Active Directory.

GetObject

Binds to an object from the container using the provided relative name. Can also specify class name as added identification. Not required with Active Directory. Returns an IDispatch interface from which IADs properties and methods can be called.

MoveHere

Moves an object from another container to the current container, optionally providing a new name. Can be used to rename objects within the same container.

Table 6-8 Methods of the IADsContainer interface.

IADsContainer is unique among the ADSI interfaces in that it provides an enumerator object. An enumerator is a special COM object that allows a program to enumerate through a collection of other objects of the same type. Using the Visual Basic and VBScript For Each statement, you can use this enumerator object. JScript provides support for enumerators through the Enumerator object.

Visual Basic, VBScript, and JScript all handle enumerators transparently. Developers working with C or C++, as usual when it comes to COM, have a little extra work involved. Behind the scenes and hidden from the developer, Visual Basic and the scripting languages use the get__NewEnum property of IADsContainer to have ADSI create the enumerator object. In C and C++, you must call get__NewEnum yourself and then use QueryInterface to get the IEnumVARIANT interface. ADSI provides some help with utility functions that perform some of the steps required in working with enumerators. The ADsBuildEnumerator, ADsEnumerateNext, and ADsFreeEnumerator functions perform some of the work involved with calling get__NewEnum and the IEnumVARIANT interface directly. Enumeration is generic among all C and C++ programs using COM, so I won't go into the details here.

If you are working with C or C++, be careful to note that two underscore characters appear in get__NewEnum. Technically, the IADs-Container interface defines a read-only property called _NewEnum, with one underscore. The single underscore indicates a special restricted property. Since you only need to use _NewEnum from C or C++, and in those environments you are required to prefix method calls to properties with either get_ or put_, I've listed it here as get__NewEnum, which is the name of the function you need to call from the IADsContainer interface.

Controlling Enumeration

The IADsContainer interface has two properties that you can set to help control the enumeration process. The Filter property is used when you just want to enumerate certain classes of objects. Doing this is extremely helpful when a container has a large number of objects and you are interested only in a certain type. The Filter property actually takes an array of strings, where each string is the class name that should be included in the enumeration.

The filtering is done on the client-side. The client must still access all the objects of the container; however, only objects matching the filter are enumerated. Filtering on the client might be a performance issue with large containers. Take, for example, the enumeration of group objects in the Users container of a company with 40,000 employees and hundreds of groups. In those kinds of cases, letting the server do the filtering is best. This approach was discussed in Chapter 5, "Searching Active Directory."

The Hints property is another way to increase performance. Like the Filter property, it takes an array of strings, each with the name of an attribute to retrieve. When enumerating, ADSI usually binds to each enumerated object and loads all the attributes of the object using the GetInfo method. This can be a huge waste of time if you're only interested in a certain number of attributes of the object. Listing 6-4, from the EnumContainer.bas sample on the companion CD, shows how to use the Filter and Hints properties in Visual Basic to enumerate all the Organizational Unit containers in the directory. By removing or commenting the line varClasses = Array("organizationalUnit") and by deleting the reference to varClasses(0) in the Debug.Print line, you can enumerate all objects in the directory recursively. (For a C++ sample that shows how to enumerate objects in a container, see the EnumContainers folder on the companion CD.)

 Option Explicit
Dim nIndentLevel As Double ` Global variable to track nesting level

Public Sub Main()
` Enumerate all the containers in the directory partion
Dim objRootDSE As IADs
Dim strPath As String
Dim objContainer As IADsContainer
Dim varClasses As Variant
    
    ` Connect to the LDAP server's root object
    Set objRootDSE = GetObject("LDAP://RootDSE")
    
    ` Form a ADsPath string to the name of the default domain
    strPath = "LDAP://" + objRootDSE.Get("defaultNamingContext")
    
    ` Connect to the directory specified in the path
    Set objContainer = GetObject(strPath)
        
    ` Display the name of the object
    Debug.Print objContainer.Name
        
    ` Setup array of classes to enumerate
    varClasses = Array("organizationalUnit")
    
    ` Display ADsPath being used
    Debug.Print "Listing " & varClasses(0) & " objects at " _
        & strPath & "..."     ` Enumerate the container
    Call EnumContainer(objContainer, varClasses)

End Sub

Public Function EnumContainer(objContainer As IADsContainer, _
varSchemaClasses As Variant)

Dim objADs As IADs
Dim objClass As IADsClass
    
    ` Increase the indent level
    nIndentLevel = nIndentLevel + 1
    
    ` Only enumerate certain classes
    objContainer.Filter = varSchemaClasses
    
    ` Retrieve only the Name and Schema attributes
    objContainer.Hints = Array("Name", "Schema")
    
    ` Loop through each object in the container
    For Each objADs In objContainer
        
        ` Indent text according the nesting level
        Debug.Print String(nIndentLevel, vbTab);
        
        ` Display the name of the object
        Debug.Print objADs.Name
    
        ` Get the class of object
        Set objClass = GetObject(objADs.Schema)
        
        ` Check whether this class is a container
        If (objClass.Container = True) Then
            
            ` Recurse to enumerate this container
            Call EnumContainer(objADs, varSchemaClasses)
        End If
    Next
    
    ` After going through a container, reduce the indent level
    nIndentLevel = nIndentLevel - 1
    
End Function

Listing 6-4 EnumContainer.bas shows how to use the Filter and Hints properties to enumerate all the Organizational Unit containers in the directory.

Adding Objects

The IADsContainer interface is also the interface you use to add objects to the directory. Once again, ADSI makes this a near trivial task. The function below accepts two strings, one a name for the new object and the other the class name for the new object. Active Directory uses the class name to determine what attributes are available for the object. You must call SetInfo to update the directory, otherwise the object is only created locally and will be discarded when the object reference is released.

The name of an object is given as the RDN for the new object. Generally this is "CN=MyObject". In the case of an Organizational Unit container, this would be "OU=MyOrgUnit". The following function shows how to create new objects in a container:

 ` Creates a new object in the container specified
Public Function CreateObject(strClass As String, strName As String, _
strADsPath As String) As IADs
Dim objContainer As IADsContainer
Dim objClass As IADsClass
Dim objNewObject As IADs
    ` Bind to the object at the path given
    Set objContainer = GetObject(strADsPath)
    
    ` Get the class of object
    Set objClass = GetObject(objContainer.Schema)
    
    ` Check whether this class is a container
    If (objClass.Container = True) Then
    
        ` Create new object in the container
        Set objNewObject = objContainer.Create(strClass, strName)
        
        ` Write the new object to the directory
        objNewObject.SetInfo
        
        ` Return the new object back to the caller
        Set CreateObject = objNewObject
        
    Else
        ` Not a container, exit
        Debug.Print objContainer.Name & " at "; _
            strADsPath; " is not a container."
        CreateObject = Nothing
    End If
End Function

Deleting Objects

Deleting an object is nearly identical to creating one. Once again, the class name and RDN are used to identify the object within the container. Actually, under Active Directory and the ADSI LDAP provider, the object's RDN is sufficient to uniquely identify the object within a container. However, other directory services require both parameters and, as such, both are required when you call the Delete method of the IADsContainer interface.

To delete an object, you must ask the object's container to delete the object; you cannot perform the operation directly on the object itself. In the following function, the object to be deleted is named objADsToDelete, and I use its Parent method (from the IADs interface) to get the distinguished name string to its parent container. Then I use the Class and Name properties to identify the object to the container when calling the Delete method.

 ` Deletes the object
Public Function DeleteObject(objADsToDelete As IADs)
Dim objContainer As IADsContainer
    ` Get the parent of the object
    Set objContainer = GetObject(objADsToDelete.Parent)
    
    ` Call the parent container's Delete method
    ` Specify object class and name
    objContainer.Delete objADsToDelete.Class, objADsToDelete.Name
    
    ` Explicitly discard object reference, no longer valid
    Set objADsToDelete = Nothing
    
    ` Update the directory
    objContainer.SetInfo
    
End Function

If the object is a container itself, it must be empty or Active Directory will report an error. You must delete all the contained objects first. Fortunately, ADSI helps out again, supplying an interface you can use to wipe out the container and all the objects contained within it: the IADsDeleteOps interface.

Easy Deleting with IADsDeleteOps

The IADsDeleteOps interface is used for performing sweeping deletion operations in the directory. It can delete the current object and all objects contained within it, which is useful for removing chunks of the directory tree without having to enumerate each child object and delete it using the Delete method of IADsContainer.

The IADsDeleteOps interface has only one member, the DeleteObject method. This method uses the current object—which supports the IADsDeleteOps interface—enumerates any child objects and deletes the current object. After the call to DeleteObject, the reference to the object is no longer valid and should be released by setting the variable to Nothing (in Visual Basic and VBScript) or by calling the Release method (in C or C++).

The DeleteObject method accepts a number as a parameter, but at this time the parameter is unused. You can use 0 when calling DeleteObject. The following function shows how to use the IADsDeleteOps interface and the DeleteObject method.

 ` Deletes the current object and any subobjects
Public Function DeleteTree(objTreeToDelete As IADsDeleteOps)
    ` Call the parent containers Delete method
    ` Specify object class and name
    objTreeToDelete.DeleteObject (0)
    
    ` Explicitly discard object reference, no longer valid
    Set objTreeToDelete = Nothing
        
End Function

Creating and Deleting Objects Sample

The following code, from the CreateDelete sample on the companion CD, shows how to call the functions shown earlier to create and delete objects. It creates two randomly named organizational unit containers in the root of Active Directory and then proceeds to delete them.

 Option Explicit
` Create then delete a series of objects
Sub Main()
Dim objRootDSE As IADs
Dim strADsPath As String
Dim objContainer As IADsContainer
Dim objADs As IADs
Dim strName As String
    
    ` Connect to the LDAP server's root object
    Set objRootDSE = GetObject("LDAP://RootDSE")
    
    ` Form an ADsPath string to the name of the default domain
    strADsPath = "LDAP://" + adsRootDSE.Get("defaultNamingContext")
    
    ` Connect to the directory specified in the path
    Set objContainer = GetObject(strADsPath)
        
    ` Display the name of the object
    Debug.Print objContainer.Name
    
    ` Create, then delete 2 organizational unit objects in the 
    ` directory root
    Dim X As Long
    For X = 1 To 2
        ` Assign a name to the new object using a random number
        Randomize
        strName = "Test" & Int((1000 * Rnd) + 1)
            
        ` Call function to create object
        ` Note that name must be an RDN
        Set objADs = CreateObject("organizationalUnit", _
            "OU=" & strName, strADsPath)
        
        ` Display info about new object
        Debug.Print objADs.Name, objADs.Guid
        
        ` Delete the object
        Call DeleteObject(objADs)
        
        ` Delete the object and all objects contained within it
        `Call DeleteTree(objADs)
        
        ` Discard the reference
        Set objADs = Nothing
        
    Next X
End Sub


MicrosoftR WindowsR 2000 Active DirectoryT Programming
MicrosoftR WindowsR 2000 Active DirectoryT Programming
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net