The best way to illustrate how to use the various IADsPropertyXXX interfaces is with a program. Listing 7-2, from PropertyList.bas on the companion CD, shows a sample that does it all. It's a good starting point for learning how to work with the IADsPropertyXXX interfaces. The PropertyList.bas sample does the following:
Here's the code:
` Property List Navigation Example`
` Shows the use of IADsPropertyList, IADsPropertyEntry,
` IADsPropertyValue and IADsPropertyValue2
`
Public Sub Main()
` Define which ADSI interfaces to use
Dim objRootDSE As IADs
Dim objADs As IADs
Dim objADsPropList As IADsPropertyList
Dim objADsPropEntry As PropertyEntry
Dim objADsPropValue As PropertyValue
Dim objADsPropValue2 As IADsPropertyValue2
` Data type interfaces
Dim objADsLargeInteger As LargeInteger
Dim objADsSecurityDescriptor As SecurityDescriptor
Dim objADsDNWithString As DNWithString
Dim objADsDNWithBinary As DNWithBinary
Dim strADsPath As String
Dim nPropCount As Long
Dim nPropIndex As Long
Dim strOperationCode As String Dim varVal As Variant
Dim strValue As String
Dim strType As String
Dim strName As String
` Connect to the LDAP server's root object
Set objRootDSE = GetObject("LDAP://RootDSE")
` Form a path to the root domain object
strADsPath = "LDAP://" & objRootDSE.Get("defaultNamingContext")
` Bind to the object
Set objADs = GetObject(strADsPath)
` Explicitly call GetInfo to populate the cache
objADs.GetInfo
` Get the IADsPropertyList interface for the bound object
Set objADsPropList = objADs
` PropertyCount is the current number of attributes in the cache
nPropCount = objADsPropList.PropertyCount
` Display some information
Debug.Print "Object " & objADs.Name & " at " & objADs.ADsPath
Debug.Print "Cache contains " & nPropCount
` Loop through all the properties
For nPropIndex = 0 To nPropCount - 1
` The Item method accepts a text name or index number
` and returns a IADsPropertyEntry object
Set objADsPropEntry = objADsPropList.Item(nPropIndex)
With objADsPropEntry
` Display PropertyEntry information
Debug.Print "Name/Type/Code:" & vbTab;
Debug.Print .Name & " / " & .ADsType & " / ";
` What is the status of the property?
` If non-zero, then the entry has been updated but not committed
Select Case .ControlCode
Case 0:
strOperationCode = "Property entry has not been updated"
Case ADS_PROPERTY_CLEAR:
strOperationCode = "ADS_PROPERTY_CLEAR" Case ADS_PROPERTY_UPDATE:
strOperationCode = "ADS_PROPERTY_UPDATE"
Case ADS_PROPERTY_APPEND
strOperationCode = "ADS_PROPERTY_APPEND"
Case ADS_PROPERTY_DELETE
strOperationCode = "ADS_PROPERTY_DELETE"
Case Else
strOperationCode = "Unknown ADSI property operation"
End Select
` Display the text name of the status/control code
Debug.Print strOperationCode
` Display PropertyValue information
Debug.Print "Type / Value: " & vbTab;
` Loop through each value for this property
For Each varVal In .Values
` Check for types that returned as objects
Select Case .ADsType
` 64-bit number
Case ADSTYPE_LARGE_INTEGER:
` Use IADsPropertyValue for this type
Set objADsPropValue = varVal
` Store the value in a LargeInteger object
Set objADsLargeInteger = objADsPropValue.LargeInteger
` Convert the LargeInteger object to a string
strValue = "&H" & Hex(objADsLargeInteger.HighPart) & _
Hex(objADsLargeInteger.LowPart)
strType = "ADSTYPE_LARGE_INTEGER"
` String representing a Windows NT/Windows 2000
` security descriptor
Case ADSTYPE_NT_SECURITY_DESCRIPTOR:
` Use IADsPropertyValue for this type
Set objADsPropValue = varVal
` Store the value in a SecurityDescriptor object
Set objADsSecurityDescriptor = _
objADsPropValue.SecurityDescriptor
` Build string with information from object
strValue = objADsSecurityDescriptor.Owner & _
" of group " & objADsSecurityDescriptor.Group
strType = "ADSTYPE_NT_SECURITY_DESCRIPTOR"
Case ADSTYPE_DN_WITH_STRING:
` NOTE: Default Active Directory schema does not
` use this syntax.
` Type not supported by IADsPropertyValue, use
` IADsPropertyValue2
Set objADsPropValue2 = varVal
` Use GetObjectProperty to return a reference
Set objADsDNWithString = _
objADsPropValue2.GetObjectProperty(.ADsType)
` Get the string and binary portions
strValue = "DN: `" & objADsDNWithString.DNString & _
"` String: `" & objADsDNWithString.StringValue
strType = "ADSTYPE_DN_WITH_STRING"
Case ADSTYPE_DN_WITH_BINARY:
` Type not exposed by IADsPropertyValue, use
` IADsPropertyValue2
Set objADsPropValue2 = varVal
` Use GetObjectProperty to return a reference
Set objADsDNWithBinary = _
objADsPropValue2.GetObjectProperty(.ADsType)
` Get the string and binary portions
strValue = objADsDNWithBinary.DNString & " (" & _
objADsDNWithBinary.BinaryValue & ")"
strType = "ADSTYPE_DN_WITH_BINARY"
Case Else:
` Use the IADsPropertyValue2 interface to get all
` other types of variants
Set objADsPropValue2 = varVal
` Use GetObjectProperty to return a variant
strValue = objADsPropValue2.GetObjectProperty(.ADsType)
strType = TypeName( _
objADsPropValue2.GetObjectProperty(.ADsType))
End Select ` Print the type and value
Debug.Print strType & " / " & strValue
` Indent the next line
Debug.Print String(4, vbTab);
Next
` End of values, terminate the list
Debug.Print
End With
Next
` Purge the property cache
`
` Example of the PurgePropertyList
` method to dump the entire cache
`---------------------------------
Debug.Print "Purging the cache..."
` Purge the property cache
objADsPropList.PurgePropertyList
` Display number of items in property list cache
Debug.Print "Property list has " & objADsPropList.PropertyCount & _
" entries."
` Add an entry to the property list
`
` Example of creating new property entries
` and associated values in the local cache
`-----------------------------------------
Debug.Print "Add an entry to the property list..."
` We'll use the description attribute, which is multivalued
strName = "description"
strValue = ">>> Temporary attribute - Safe to delete <<<"
` Create new entry object and fill in the properties
Set objADsPropEntry = New PropertyEntry
` Set the attribute name
objADsPropEntry.Name = strName
` Specify to add new values to an existing set
objADsPropEntry.ControlCode = ADS_PROPERTY_APPEND ` The values will be a case-insensitive string
objADsPropEntry.ADsType = ADSTYPE_CASE_IGNORE_STRING
` Create a value for this entry
Set objADsPropValue = New PropertyValue
` Set the data type based on the property entry
objADsPropValue.ADsType = objADsPropEntry.ADsType
` Set the value using the type property
objADsPropValue.CaseIgnoreString = strValue
` Add the value to the entry
objADsPropEntry.Values = Array(objADsPropValue)
` Add this property entry to the property list
objADsPropList.PutPropertyItem objADsPropEntry
` Count should now be 1
Debug.Print "Property list has " & objADsPropList.PropertyCount & _
" entries."
` Entries and values are not saved until SetInfo is called
Debug.Print "Saving new entry to server..."
objADs.SetInfo
` Saving resets the property cache, use GetInfoEx to reload specific
` properties
objADs.GetInfoEx Array("description", "name", "distinguishedName"), 0
` Count should now be 3
Debug.Print "Property list has " & objADsPropList.PropertyCount & _
" entries."
` Remove property entries one-by-one
`
` Example of how to loop through the property cache
` using the Next property and delete entries using
` the ResetPropertyItem method
`--------------------------------------------------
Debug.Print "Move to start of list, then remove each entry..."
` Start at the beginning of the cache
objADsPropList.Reset ` The Next property fails at the end, must trap this error
On Error Resume Next
` Loop through the entire cache using Next property
` Note, cannot use For loop with index since the number of items will
` be changing
Set objADsPropEntry = objADsPropList.Next
` Ensure we got an entry by checking for no error
While (Err.Number = 0)
` Turn off error checking
On Error GoTo 0
` Display the unsaved property entry
Debug.Print "Property Entry: " & objADsPropEntry.Name
` Remove the entry from the list
` This only removes the entry from the cache, not from the server object
objADsPropList.ResetPropertyItem (objADsPropEntry.Name)
On Error Resume Next
` Get the next entry in the cache
Set objADsPropEntry = objADsPropList.Next
Wend
` Turn off error checking
On Error GoTo 0
` Display number of items in property list cache
Debug.Print "Property list has " & objADsPropList.PropertyCount & _
" entries."
` Refresh Cache
`
`--------------
Debug.Print "Refresh cache with GetInfo..."
` Explicit call to GetInfo will overwrite dirty entry
objADs.GetInfo
` Display number of items in property list cache
Debug.Print "Property list has " & objADsPropList.PropertyCount & _
" entries."
` Delete a property value
`
` Example showing how to delete the value
` we added to description. Very similar
` to adding values, but with a different
` operations control code.
`----------------------------------------
` Get the entry we added earlier
Set objADsPropEntry = objADsPropList.GetPropertyItem( _
strName, ADSTYPE_CASE_IGNORE_STRING)
` Specify to remove values from an existing set
objADsPropEntry.ControlCode = ADS_PROPERTY_DELETE
` Create a value for this entry
Set objADsPropValue = New PropertyValue
` Specify the value to remove without regard to case
objADsPropValue.CaseIgnoreString = strValue
` Set the data type based on the property entry
objADsPropValue.ADsType = objADsPropEntry.ADsType
` Add the value to the entry
objADsPropEntry.Values = Array(objADsPropValue)
` Remove this property entry from the property list
objADsPropList.PutPropertyItem objADsPropEntry
` Commit changes of the property list to the directory
objADs.SetInfo
End Sub
Listing 7-2 PropertyList.bas demonstrates some of the capabilities of the IADsPropertyXXX interfaces.
When you run the code in Listing 7-2, you'll see a long list related to the properties of the object and the progress of manipulating the property cache. The following is an abbreviated example of the output.
Object DC=coppersoftware at LDAP://DC=coppersoftware,DC=com
Cache contains 39
Name/Type/Code: masteredBy / 1 / Property entry has not been updated
Type / Value: String / CN=NTDS Settings,CN=COPPER1,CN=Servers,
CN=Default-First-Site-Name,CN=Sites,CN=Configuration,
DC=coppersoftware,DC=com
Name/Type/Code: auditingPolicy / 8 / Property entry has not been updated
Type / Value: Byte() / A
Name/Type/Code: creationTime / 10 / Property entry has not been updated
Type / Value: ADSTYPE_LARGE_INTEGER / &H1C073D5A2D6E216
...
Name/Type/Code: wellKnownObjects / 27 / Property entry has not been updated
Type / Value: ADSTYPE_DN_WITH_BINARY /
CN=Deleted Objects,DC=coppersoftware,DC=com (????????)
ADSTYPE_DN_WITH_BINARY /
CN=Infrastructure,DC=coppersoftware,DC=com (????????)
ADSTYPE_DN_WITH_BINARY /
CN=LostAndFound,DC= coppersoftware,DC=com (????????)
ADSTYPE_DN_WITH_BINARY /
CN=System,DC= coppersoftware,DC=com (????????)
ADSTYPE_DN_WITH_BINARY /
OU=Domain Controllers,DC= coppersoftware,DC=com (????????)
ADSTYPE_DN_WITH_BINARY /
CN=Computers,DC= coppersoftware,DC=com (????????)
ADSTYPE_DN_WITH_BINARY /
CN=Users,DC= coppersoftware,DC=com (????????)
Name/Type/Code: whenChanged / 9 / Property entry has not been updated
Type / Value: Date / 1/1/2001 9:34:00 AM
Name/Type/Code: whenCreated / 9 / Property entry has not been updated
Type / Value: Date / 1/1/2001 9:27:20 AM
Purging the cache...
Property list has 0 entries.
Add an entry to the property list...
Property list has 1 entries.
Saving new entry to server...
Property list has 3 entries.
Move to start of list, then remove each entry...
Property Entry: description
Property Entry: distinguishedName
Property Entry: name
Property list has 0 entries.
Refresh cache with GetInfo...
Property list has 40 entries.
That's how you deal with objects in the property cache and the interfaces that manage it. Now let's turn our attention to an ADSI interface that ignores the property cache altogether and accesses Active Directory objects directly.