[ Team LiB ] Previous Section Next Section

19.2 Manipulating the Property Cache

There will be times when you need to write a script that queries all the values that have been set in the underlying directory for a particular object. For example, suppose you're one of several systems administrators who work with your company's Active Directory implementation. You need to write a script that queries all the property values that the administrators have set for a particular user.

Discovering the set property values for an object can be a long, tedious job. Fortunately, ADSI provides a quick method. If someone has set a value for a property, it must be in that object's property cache. So all you need to do is walk through the property cache, displaying and optionally modifying each item as you go.

In this section, we'll describe the property cache mechanics and show you how to write scripts that use several ADSI methods and properties to add individual values, add a set of values, walk through the property cache, and write modifications to the cache and to the directory. Although these examples access the Lightweight Directory Access Protocol (LDAP) namespace, you can just as easily substitute the WinNT namespace in any of the scripts and run them against Windows NT servers.

Details of the property cache interfaces can be found at the MSDN Library (http://msdn.microsoft.com/library/) by clicking through the following links: Networking and Directory Services Active Directory, ADSI, Directory Services SDK Documentation Directory Services Active Directory Service Interfaces Active Directory Service Interfaces Reference ADSI Interfaces Property Cache Interfaces.

19.2.1 Property Cache Mechanics

Every object has properties. When you perform an explicit IADs::GetInfo call (or an implicit IADs::GetInfo call using IADs::Get) on an object that you previously bound to, the OS loads all the properties for that specific object into that object's property cache. Consider the property cache a simple list of properties. The PropertyList object represents this list. You can use several IADsPropertyList methods to navigate through the list and access items. For example, you can navigate the list and access each item, every nth item, or one particular item based on its name.

Each item in the property list is a property entry represented by the PropertyEntry object. You use the IADsPropertyEntry interface to access property entries. A property entry can have one or more property values. To access values in a property entry, you use the IADsPropertyValue interface.

To summarize, use IADsPropertyList to navigate through and access property entries in the property list. When you want to manipulate a property, use IADsPropertyEntry. To access the values of that property entry, use IADsPropertyValue.

19.2.2 Adding Individual Values

To show you how to add an individual value, we'll expand on one of the examples from the previous section: the pager property of the User object. The pager property is an array of text strings representing multiple pager numbers.

Consider that any property represents data. Data can take several forms, including a string, an integer, or a Boolean value. In the cache, each property has two attributes: one attribute specifies the type of data the property represents, and the other attribute specifies the value of that data type. For example, each pager property has two attributes: a Unicode string (the type of data) and the pager number (the value of that Unicode string). The User object's lastLogon property, which specifies the time the user last logged on, has the two attributes, a LargeInteger (type of data) and a date/time stamp (the value of that LargeInteger).

The pager and lastLogon properties are instances of the PropertyValue object, so you manipulate them with the method and property methods of the IADsPropertyValue interface. For example, you use the IADsPropertyValue::ADsType property method to set the PropertyValue's type of data. Table 19-3 shows some of the corresponding constant names and values that you can set for the IADsPropertyValue::ADsType property.

Table 19-3. Constants for the IADsPropertyValue::ADsType property

Constant name

IADsPropertyValue property method (if appropriate)

Value

ADSTYPE_INVALID

None

0

ADSTYPE_DN_STRING

IADsPropertyValue::DNString

1

ADSTYPE_CASE_EXACT_STRING

IADsPropertyValue::CaseExactString

2

ADSTYPE_CASE_IGNORE_STRING

IADsPropertyValue::CaseIgnoreString

3

ADSTYPE_PRINTABLE_STRING

IADsPropertyValue::PrintableString

4

ADSTYPE_NUMERIC_STRING

IADsPropertyValue::NumericString

5

ADSTYPE_BOOLEAN

IADsPropertyValue::Boolean

6

ADSTYPE_INTEGER

IADsPropertyValue::Integer

7

ADSTYPE_OCTET_STRING

IADsPropertyValue::OctetString

8

ADSTYPE_UTC_TIME

IADsPropertyValue::UTCTime

9

ADSTYPE_LARGE_INTEGER

IADsPropertyValue::LargeInteger

10

ADSTYPE_PROV_SPECIFIC

None

11

ADSTYPE_OBJECT_CLASS

None

12

ADSTYPE_CASEIGNORE_LIST

None

13

ADSTYPE_OCTET_LIST

None

14

ADSTYPE_PATH

None

15

ADSTYPE_POSTALADDRESS

None

16

ADSTYPE_TIMESTAMP

None

17

ADSTYPE_BACKLINK

None

18

ADSTYPE_TYPEDNAME

None

19

ADSTYPE_HOLD

None

20

ADSTYPE_NETADDRESS

None

21

ADSTYPE_REPLICAPOINTER

None

22

ADSTYPE_FAXNUMBER

None

23

ADSTYPE_EMAIL

None

24

ADSTYPE_NT_SECURITY_DESCRIPTOR

IADsPropertyValue::SecurityDescriptor

25

ADSTYPE_UNKNOWN

None

26

Suppose you want to add a PropertyValue object with the value of "Hi There!" The two attributes are a case-sensitive string (i.e., the type of data, or IADsPropertyValue::ADsType property) and "Hi There!" (i.e., the value of that case-sensitive string or the IADsPropertyValue::CaseExactString property). The constant for the IADsPropertyValue::ADsType of a case-sensitive string is ADSTYPE_CASE_EXACT_STRING, which has a numeric value of 2. As shown in Table 19-3, IADsPropertyValue::CaseExactString is one of a number of IADsPropertyValue property methods that exist, each relating to a specific data type. It is the value in IADsPropertyValue::ADsType that determines which of the many property methods are actually used to get and set the data.

The following script shows how to create this new PropertyValue object. We begin by setting the ADSTYPE_CASE_EXACT_STRING constant to its numeric value (i.e., 2) and declaring the objPropValue variable. As we mentioned earlier, if you use VBScript with WSH, you must either define the constants, as the script does, or use the values directly:

Const ADSTYPE_CASE_EXACT_STRING = 2
   
Dim objPropValue   'An ADSI PropertyValue object
   
Set objPropValue = CreateObject("PropertyValue") 
objPropValue.ADsType = ADSTYPE_CASE_EXACT_STRING
objPropValue.CaseExactString = "Hi There!"

We use VBScript's CreateObject method to create an instance of the PropertyValue object and set it to the objPropValue variable. Then two attributes are assigned to the PropertyValue object. The objPropValue's IADsPropertyValue::ADsType property method is used to assign the property's data type to the ADSTYPE_CASE_EXACT_STRING constant. Finally, we use objPropValue's IADsPropertyValue::CaseExactString property method to assign the property's value to "Hi There!"

19.2.3 Adding Sets of Values

As we mentioned previously, some properties hold one value (e.g., the lastLogon property); others hold multiple values in an array (e.g., the pager property). The PropertyEntry object holds the entire set of values for a property, be it one value or many values.

However, the PropertyEntry object does more than store values. This object's properties dictate how you can manipulate those values. The PropertyEntry object supports the IADsPropertyEntry interface that has four property methods:

IADsPropertyEntry::Name

The IADsPropertyEntry::Name property method sets the name of the property that you want to manipulate (e.g., pager).

IADsPropertyEntry::Values

The IADsPropertyEntry::Values property method sets an array containing those values you want to manipulate (e.g., the pager numbers).

IADsPropertyEntry::ADsType

The IADsPropertyEntry::ADsType property method determines the data type of those values (e.g., Unicode string).

IADsPropertyEntry::ControlCode

The IADsPropertyEntry::ControlCode property method tells the cache whether to overwrite, update, or add to the property's existing values. You use the constants in Table 19-4 with the IADsPropertyEntry::ControlCode property. These constants are the same as the constants for the IADs::PutEx method described earlier. Because IADsPropertyEntry::ControlCode constants work the same way as the IADs::PutEx method constants, we won't go through them again here.

Table 19-4. The constants for the IADsPropertyEntry::ControlCode property method

Constant name

Value

Action

ADS_PROPERTY_CLEAR

1

Use when clearing all values

ADS_PROPERTY_UPDATE

2

Use when replacing all existing values

ADS_PROPERTY_APPEND

3

Use when adding to existing values

ADS_PROPERTY_DELETE

4

Use when deleting specific values

The next script shows how to create a PropertyEntry object from one property value:

Const ADSTYPE_CASE_IGNORE_STRING = 3
Const ADS_PROPERTY_UPDATE = 2
   
Dim objPropValue 'An ADSI PropertyValue object
Dim objPropEntry 'An ADSI PropertyEntry object
   
Set objPropValue = CreateObject("PropertyValue")
objPropValue.ADsType = ADSTYPE_CASE_IGNORE_STRING
objPropValue.CaseIgnoreString = "0123-456-7890"
   
Set objPropEntry = CreateObject("PropertyEntry")
objPropEntry.Name = "pager"
objPropEntry.Values = Array(objPropValue)
objPropEntry.ADsType = ADSTYPE_CASE_IGNORE_STRING
objPropEntry.ControlCode = ADS_PROPERTY_UPDATE

The first part of the script is similar to the previous one. We begin by setting the constants to their numeric values and declaring the variables. Next, we create an instance of the PropertyValue object and set it to the objPropValue variable. We then use the IADsPropertyValue::ADsType property method to assign the property's data type to the ADSTYPE_CASE_IGNORE_STRING constant and the IADsPropertyValue::CaseIgnoreString property method to assign the property's value to 0123-456-7890.

The second part of the script begins by creating an instance of the PropertyEntry object and setting it to the objPropEntry variable. Then all four PropertyEntry properties are set. For the IADsPropertyEntry::Values property, you must use the VBScript Array( ) function to force the values into an array, even if you set only one value. For the IADsPropertyEntry::ControlCode property, you're replacing the existing values with the ones you're passing in.

19.2.4 Walking Through the Property Cache

For any object, the property cache consists of PropertyEntry objects that correspond to each property. When you use the IADs::Get method, it reads the cache's PropertyEntry for that particular property.

As we've previously mentioned, whenever you call GetObject or IADsOpenDSObject::OpenDSObject, as explained later, the object that is returned can use the IADs interface in addition to any interface designed for that object. The IADsPropertyList interface also is directly available for any object. It is of no real use without a call to GetInfo first, without which the property cache will be empty. Once the cache is populated, however, the methods and properties come into their own. Table 19-5 lists the IADsPropertyList methods and properties.

Table 19-5. IADsPropertyList methods and properties

IADsPropertyList methods and properties

Action

Next method

Retrieves the value of the next item in the property list

Skip method

Skips a number of items in the property list

Reset method

Puts the pointer back to the beginning of the list

Add method

Adds a new property to the list

Remove method

Removes a property from the list

Item method

Gets an item from the property list

GetPropertyItem method

Gets an item in the property list

PutPropertyItem method

Puts an item in the property list

ResetPropertyItem method

Resets an item in the property list back to its original value

PurgePropertyList method

Deletes all items in the property list

PropertyCount property

The number of properties in the property list

The PropertyList object represents the entire set of properties for an object. The methods and property methods of the IADsPropertyList interface can be used to manipulate the PropertyList object. Example 19-5 uses several of those methods and property methods to demonstrate three ways of walking through the property cache.

Example 19-5. Walking through the property cache with the IADsPropertyList interface
Option Explicit
   
'**********************************************************************
'Force error checking within the code using the Err.Number property
'method in approaches 2 and 3
'**********************************************************************
On Error Resume Next
   
'**********************************************************************
'Declare the variables
'**********************************************************************
Dim objGroup      'The group whose property list you want to investigate
Dim strText       'A text string that displays results in one message box
Dim intPropCount  'The number of properties
Dim intIndex      'The index used while looping through the property list
Dim objPropEntry  'An individual property entry used in a loop
   
Set objGroup = GetObject("LDAP://cn=Managers,ou=Sales,dc=mycorp,dc=com")
objGroup.GetInfo
   
intPropCount = objGroup.PropertyCount
WScript.Echo "There are " & intPropCount & " values in the property cache."
   
'**********************************************************************
'Approach 1: PropertyCount property method
'**********************************************************************
strText = ""
For intIndex = 0 To (intPropCount-1)
  strText = strText & objGroup.Item(intIndex).Name & vbTab _
  & objGroup.Item(intIndex).ADsType & vbCrLf
Next
WScript.Echo strText
   
'**********************************************************************
'Approach 2: Next method
'**********************************************************************
strText = ""
Set objPropEntry = objGroup.Next
While (Not (IsNull(objPropEntry)) And Err.Number = 0)
  strText = strText & objPropEntry.Name & vbTab & objPropEntry.ADsType 
           & vbCrLf
  Set objPropEntry = objGroup.Next
Wend
WScript.Echo strText
Set objPropEntry = Nothing
   
'**********************************************************************
'Approach 3: Next and Skip methods
'**********************************************************************
strText = ""
objGroup.Reset
Set objPropEntry = objGroup.Next
While (Not (IsNull(objPropEntry)) And Err.Number = 0)
  strText = strText & objPropEntry.Name & vbTab & objPropEntry.ADsType 
           & vbCrLf
  objGroup.Skip(2)
  Set objPropEntry = objGroup.Next
Wend
WScript.Echo strText
Set objPropEntry = Nothing

The script begins by using VBScript's Option Explicit statement (which requires you to declare all variables before using them) and the On Error Resume Next statement (which allows you to do error handling). Then, after declaring the variables, the GetObject method is used to bind to the group whose property cache we want to look at. In this case, we want to view the properties for the Manager group object. Next, the IADs::GetInfo method is called to load the property cache for this group. Since we won't be using the IADs::Get method in the script, the system won't implicitly use the IADs::GetInfo method to load the cache, so we have to explicitly load it in.

Each object in objGroup has a PropertyList object, so we use the IADsPropertyList::PropertyCount property method to count each PropertyList object. We store the count for later use by setting it to the intPropCount variable, and we print it out in a message box using WSH's Echo method.

We now know how many properties objGroup has, but we need to find out the values of those properties. We can use one of three approaches to walk through the property cache to get this information.

19.2.4.1 Approach 1—Using the IADsPropertyList::PropertyCount property method

We begin by walking through the property list by counting the items in the index 0 through intPropCount-1. We need to specify this index, because the property list index starts at 0 rather than 1. For example, a property list with 15 items has an index ranging from 0 to 14.

For each item in the index, you concatenate (&) two property methods to retrieve the property's IADs::Name and IADsPropertyValue::ADsType. The script processes concatenated statements from left to right, so it first uses the IADsPropertyList::Item method with the intIndex value as the item number to retrieve a property entry, to which it applies the IADs::Name property method to get the property's name. The script then uses the same process to retrieve the same property entry, to which it applies the IADsPropertyValue::ADsType property method to get the property's datatype. Forcing the script to process IADsPropertyList::Item twice is inefficient. We processed it twice only to illustrate how to walk through the property list. The concatenated code includes more than just the two property methods. The code also concatenates a tab (vbTab) between the two property methods and a carriage-return line-feed (vbCrLf), or new line, after the second property method. But even more important, the code first concatenates the existing strText variable onto the front (i.e., strText = strText & property method 1 & property method 2), which means that, in the output, these property values are appended to the existing strText string. As a result, the WSH displays all the property values in one message box if you use WSH's wscript.exe scripting engine to run the script. If you're using WSH's cscript.exe scripting engine, using this append technique makes no difference. If you don't concatenate the strText variable (i.e., strText = property method 1 & property method 2), WSH displays a separate message box for each property.

When the script finishes looping through the property list index, it prints the appended strText string in the message box. Approaches 2 and 3 also use the append technique to display all their output in one message box.

19.2.4.2 Approach 2—Using the IADsPropertyList::Next method

We start this approach by resetting the strText variable to a zero-length string to ensure that no values from the previous approach are left in the string. Then the IADsPropertyList::Next method is called to retrieve a copy of the first property entry and set the result to the objPropEntry variable. Because we called the IADsPropertyList::Next method, we can use a while loop to iterate through the cache until we encounter a null value, which specifies that we're at the end of the list.

Providing that the first property entry isn't a null entry, we enter the while loop. The And Err.Number = 0 code designates a test to see whether an error has occurred. A value of 0 indicates no error; any other value specifies an error. If a valid entry (i.e., not a null entry) is retrieved and an error hasn't occurred (i.e., the error number is equal to 0), we enter the loop. Within the loop, the property name and data type are appended to the strText string in a similar manner as before. To move to the next property entry in the property cache, we again call the IADsPropertyList::Next method. As long as this value isn't null and isn't generating an error code, the process continues until it hits a null entry, which means we're at the end of the list. The wend keyword signifies the end of the while loop. Finally, the results are printed.

19.2.4.3 Approach 3—Using the IADsPropertyList::Next and IADsPropertyList::Skip methods

The code in this approach is identical to the code used in Approach 2, except for the addition of two lines. The IADsPropertyList::Reset property method sets the property list pointer to the first property entry in the cache. If we don't use the IADsPropertyList::Reset property method, the pointer will be at the end of the cache, which would generate a null entry. The IADsPropertyList::Skip code tells the IADsPropertyList::Next property method to skip the next two property entries. In other words, the IADsPropertyList::Next property method is retrieving every third property, so this approach returns only property entries 1, 4, 7, 10, and so on.

19.2.5 Writing the Modifications

Now that we've shown how to walk through the cache, next we will review how to write modifications to the cache and back to the directory. Example 19-6 illustrates these procedures. This script is an amalgam of the code in the earlier examples. As such, it shows how to assemble the pieces of code into a usable script.

Example 19-6. Writing modifications to the cache and back to the directory
Option Explicit
   
'**********************************************************************
'Force error checking within the code using the Err.Number property
'method in approaches 2 and 3
'**********************************************************************
On Error Resume Next
   
'**********************************************************************
'Declare the constants and variables
'**********************************************************************
Const ADSTYPE_CASE_IGNORE_STRING = 3
Const ADS_PROPERTY_UPDATE = 2
   
Dim objPropValue  'An ADSI PropertyValue object
Dim objPropEntry  'An ADSI PropertyEntry object
Dim objUser       'The user whose property list you want to investigate
Dim strText       'A text string that displays results in one message box
Dim intPropCount  'The number of properties
Dim intIndex      'The index used while looping through the property list
   
Set objUser = GetObject("LDAP://cn=AlistairGLN,ou=Sales,dc=mycorp,dc=com")
objUser.GetInfo
   
'**********************************************************************
'Section A: Calculate the property count, and enumerate each 
'property's name and datatype
'**********************************************************************
intPropCount = objUser.PropertyCount
WScript.Echo "There are " & intPropCount _
  & " values in the property cache before adding the new one."
   
strText = ""
For intIndex = 0 To (intPropCount-1)
  strText = strText & objUser.Item(intIndex).Name & vbTab _
    & objUser.Item(intIndex).ADsType & vbCrLf
Next
WScript.Echo strText
   
'**********************************************************************
'Section B: Create a property entry, and write it to the cache
'**********************************************************************
Set objPropValue = CreateObject("PropertyValue")
objPropValue.ADsType = ADSTYPE_CASE_IGNORE_STRING
objPropValue.CaseExactString = "0123-456-7890"
   
Set objPropEntry = CreateObject("PropertyEntry")
objPropEntry.Name = "pager"
objPropEntry.Values = Array(objPropValue)
objPropEntry.ADsType = ADSTYPE_CASE_IGNORE_STRING
objPropEntry.ControlCode = ADS_PROPERTY_UPDATE
   
objUser.PutPropertyItem(objPropEntry)
   
'**********************************************************************
'Section C: Write out the cache to Active Directory and read the new
'cache explicitly back in from the object
'**********************************************************************
objUser.SetInfo
objUser.GetInfo
   
'**********************************************************************
'Section D: Recalculate the property count, and re-enumerate each
'property's name and datatype to see the changes
'**********************************************************************
intPropCount = objUser.PropertyCount
   
WScript.Echo "There are " & intPropCount _
  & " values in the property cache before adding the new one."
   
strText = ""
For intIndex = 0 To (intPropCount-1)
  strText = strText & objUser.Item(intIndex).Name _
    & vbTab & objUser.Item(intIndex).ADsType & vbCrLf
Next
WScript.Echo strText

The script begins with Option Explicit and On Error Resume Next, after which it sets the constants, declares the variables, and sets the objUser variable to the AlistairGLN user object. The script then divides into four sections:

Section A

Determines the User object's property count and lists each property's name and data type.

Section B

Creates a property entry and writes it to the cache. The last line uses the IADsPropertyList::PutPropertyItem method to write the new property entry for objUser to the cache. However, the IADs::SetInfo method must be used to write this entry to the directory.

Section C

Contains new code. The first line uses the IADs::SetInfo method to write the cache to the directory. The second line uses the explicit IADs::GetInfo method to read it back into the cache. Although the second line might not seem necessary, it is. If we don't use an explicit IADs::GetInfo call, we'll be accessing the same cache that we accessed before we added the new property entry. The explicit IADs::GetInfo call retrieves any new properties that anyone else has updated since the last implicit or explicit IADs::GetInfo call.

Section D

Recalculates the property count and reenumerates each property's name and data type so that we can see the modifications. If we see the property count increase by one after we write the cache to the directory, the script has successfully executed.

19.2.6 Walking the Property Cache—The Solution

Example 19-7 is quite long. It walks through the property cache for an object and prints the name, data type, and values of each entry. Some of the properties are not printable strings, so printing them in a text format makes little sense. Thus, this script prints only the text strings. We used a VBScript dictionary object to map the data type integers (ADsType) to descriptive names. A dictionary is similar in nature to an associative array or hash, which are common in other programming languages. After instantiating a dictionary object, you can use the Add method to add new key value pairs to it.

The script also illustrates how you can just as easily use the WinNT namespace rather than the LDAP namespace to display properties of objects, and how you can run the script against Windows NT domains and Windows NT or later member servers rather than Active Directory.

Example 19-7. Walking through the property cache of an object
Option Explicit
On Error Resume Next
   
'**********************************************************************
'Declare the hash (dictionary), constants and variables
'**********************************************************************
Dim dicADsType
Set dicADsType = CreateObject("Scripting.Dictionary")
dicADsType.Add 0, "INVALID"
dicADsType.Add 1, "DN_STRING"
dicADsType.Add 2, "CASE_EXACT_STRING"
dicADsType.Add 3, "CASE_IGNORE_STRING"
dicADsType.Add 4, "PRINTABLE_STRING"
dicADsType.Add 5, "NUMERIC_STRING"
dicADsType.Add 6, "BOOLEAN"
dicADsType.Add 7, "INTEGER"
dicADsType.Add 8, "OCTET_STRING"
dicADsType.Add 9, "UTC_TIME"
dicADsType.Add 10, "LARGE_INTEGER"
dicADsType.Add 11, "PROV_SPECIFIC"
dicADsType.Add 12, "OBJECT_CLASS"
dicADsType.Add 13, "CASEIGNORE_LIST"
dicADsType.Add 14, "OCTET_LIST"
dicADsType.Add 15, "PATH"
dicADsType.Add 16, "POSTALADDRESS"
dicADsType.Add 17, "TIMESTAMP"
dicADsType.Add 18, "BACKLINK"
dicADsType.Add 19, "TYPEDNAME"
dicADsType.Add 20, "HOLD"
dicADsType.Add 21, "NETADDRESS"
dicADsType.Add 22, "REPLICAPOINTER"
dicADsType.Add 23, "FAXNUMBER"
dicADsType.Add 24, "EMAIL"
dicADsType.Add 25, "NT_SECURITY_DESCRIPTOR"
dicADsType.Add 26, "UNKNOWN"
   
Const ADS_PROPERTY_CLEAR = 1
Const ADS_PROPERTY_UPDATE = 2
Const ADS_PROPERTY_APPEND = 3
Const ADS_PROPERTY_DELETE = 4
   
Dim objPropValue  'An individual property value within a loop
Dim objPropEntry  'An ADSI PropertyEntry object
Dim objObject     'The object whose property list we wish to investigate
Dim strText       'A text string used to display results in one go
Dim intPropCount  'The number of properties in 
Dim intIndex      'The index used while looping through the property list
Dim intCount      'Used to display property values in a numbered sequence
   
'**********************************************************************
'Uncomment one of these lines and modify it to your own environment.
'The first uses the LDAP namespace; the second uses the WinNT namespace.
'**********************************************************************
' Set objObject = GetObject("LDAP://cn=administrator,cn=users,dc=mycorp,dc=com")
' Set objObject = GetObject("WinNT://WINDOWS/Managers,Group")
objObject.GetInfo
if (Err.Number > 0) Then
   Wscript.Echo "Object not found, returning..."
   Wscript.Quit
End if
   
'**********************************************************************
'Write out the current property cache total to the string that is 
'storing output
'**********************************************************************
intPropCount = objObject.PropertyCount
strText = "There are " & intPropCount & _
           " values in the property cache." & vbCrLf
   
'**********************************************************************
'The extra vbTabs used in the first loop are to space the results so 
'that they are nicely formatted with the list of values in the second loop
'**********************************************************************
For intIndex = 0 To (intPropCount-1)
   
  Set objPropEntry = objObject.Item(intIndex)
  strText = strText & objPropEntry.Name & vbCrLf
   
  strText = strText & vbTab & "Type:" & vbTab & vbTab & _
            dicADsType.Item(objPropEntry.ADsType) & vbCrLf
   
  '**********************************************************************
  'Go through each property value in the property entry and use the AdsType
  'to print out the appropriate value, prefixed by a count (intCount), i.e.:
  '
  '  Value #1: Vicky Launders
  '  Value #2: Alistair Lowe-Norris
  '  Value #3: Robbie Allen
  '**********************************************************************
  intCount = 1
   
  For Each objPropValue In objPropEntry.Values  
   
    If (dicADsType(objPropValue.ADsType) = "STRING") Then
      strText = strText & vbTab & "Value #" & intCount & ":" _
        & vbTab & objPropValue.DNString & vbCrLf
   
    ElseIf (dicADsType(objPropValue.ADsType) = "CASE_EXACT_STRING") Then
      strText = strText & vbTab & "Value #" & intCount & ":" _
        & vbTab & objPropValue.CaseExactString & vbCrLf
   
    ElseIf (dicADsType(objPropValue.ADsType) = "CASE_IGNORE_STRING") Then
      strText = strText & vbTab & "Value #" & intCount & ":" _
        & vbTab & objPropValue.CaseIgnoreString & vbCrLf
   
    ElseIf (dicADsType(objPropValue.ADsType) = "PRINTABLE_STRING") Then
      strText = strText & vbTab & "Value #" & intCount & ":" _
        & vbTab & objPropValue.PrintableString & vbCrLf
   
    ElseIf (dicADsType(objPropValue.ADsType) = "NUMERIC_STRING") Then
      strText = strText & vbTab & "Value #" & intCount & ":" _
        & vbTab & objPropValue.NumericString & vbCrLf
   
    ElseIf (dicADsType(objPropValue.ADsType) = "BOOLEAN") Then
      strText = strText & vbTab & "Value #" & intCount & ":" _
        & vbTab & CStr(objPropValue.Boolean) & vbCrLf
   
    ElseIf (dicADsType(objPropValue.ADsType) = "INTEGER") Then
      strText = strText & vbTab & "Value #" & intCount & ":" _
        & vbTab & objPropValue.Integer & vbCrLf
   
    End If
   
    intCount=intCount+1
   
  Next
Next
   
WScript.Echo strText

This script displays every value in the property cache for an object. However, there may come a time when you wish to see the entire potential property cache for an object and list which of all possible values have been set. To do that, you need to query the formal schema class definition for the object. This leads us to the final section on the property cache.

19.2.7 Walking the Property Cache Using the Formal Schema Class Definition

There is one other way to walk the property list for a particular object: using its schema class details. Chapter 4 explained how the schema is the blueprint for objects in Active Directory. As each schema class actually is stored in Active Directory, you can navigate the object's properties by using the IADsClass interface to display each individual item according to its formal name in the schema class. To do this, we first obtain a reference to the object in the normal manner. We then obtain a reference to the schema class for that object. We can do this using the IADs::Schema property method, which returns the full ADsPath of the schema class. For example, the user objectclass in the mycorp.com domain has the following schema ADsPath:

LDAP://cn=User,cn=Schema,cn=Configuration,dc=mycorp,dc=com

Then we can use the IADsClass::MandatoryProperties and IADsClass::OptionalProperties methods to retrieve the appropriate properties. The following example nicely brings together IADs::GetEx for retrieving multiple properties and writing to a file, which is required due to the large number of properties.

Example 19-8 uses On Error Resume Next because all properties may not display, and the program will fail if any do not. The script also differs from the previous script in that it lists all possible properties and whether they've been set. The previous example listed only those that had been set. The script is also generic; it will print out the property cache for any object class. Just change the ADsPath passed to GetObject.

Example 19-8. Walking the property cache using the formal schema class definition
Option Explicit
'**********************************************************************
'Force error checking within the code using the Err.Number property
'method in approaches 2 and 3
'**********************************************************************
On Error Resume Next
   
'**********************************************************************
'Declare the constants and variables
'**********************************************************************
Dim objObject    'Active Directory object
Dim objClass     'ADSI Class object
Dim objProp      'An individual property
Dim intCount     'Incremental counter for display
Dim fileadsect   'A FileSystemObject
Dim outTextFile  'A TextStream Object
   
'**********************************************************************
'Create a VBScript file object and use it to open a text file. The
'second parameter specifies to overwrite any existing file that exists.
'**********************************************************************
Set fileadsect = CreateObject("Scripting.FileSystemObject")
Set outTextFile = fileadsect.CreateTextFile("c:\out.txt", TRUE)
   
'**********************************************************************
'Bind to the object and get a pointer to the appropriate schema class,
'i.e., User in this case
'**********************************************************************
Set objObject = 
           GetObject("LDAP://cn=administrator,cn=Users,dc=mycorp,dc=com")
Set objClass = GetObject(objObject.Schema)
   
intCount = 1
   
'**********************************************************************
'Iterate through all the mandatory properties
'**********************************************************************
For Each objProp in objClass.MandatoryProperties
  EnumerateProperties objProp, outTextFile, objObject
  intCount=intCount+1
Next
   
'**********************************************************************
'Iterate through all the optional properties
'**********************************************************************
For Each objProp in objClass.OptionalProperties
  EnumerateProperties objProp
  intCount=intCount+1
Next
   
outTextFile.Close
   
'**********************************************************************
'Subroutine EnumerateProperties
'**********************************************************************
Sub EnumerateProperties(ByVal objProp, ByVal tsFile, ByVal objObj)
   
  Dim objProperty  'ADSI Property object
  Dim arrElement   'Array of elements
   
  '**********************************************************************
  'Get pointer to the schema property object
  '**********************************************************************
  Set objProperty = GetObject("LDAP://Schema/" & objProp)
   
  '**********************************************************************
  'Check whether property requires GetEx using IADsProperty::MultiValued
  '**********************************************************************
  If objProperty.MultiValued Then
    tsFile.WriteLine intCount & ") " & objProp & _
      " (" & objProperty.Syntax & ") (MULTI-VALUED)"
   
    '**********************************************************************
    'Check whether array returned from GetEx is empty using VBScript 
     function
    '**********************************************************************
    If (IsEmpty(objObj.GetEx(objProp))) Then
      tsFile.WriteLine vbTab & "= " & "NO VALUES SET!"
    Else
      For Each arrElement in objObj.GetEx(objProp)
        tsFile.WriteLine vbTab & "= " & arrElement
      Next
    End If
   
  Else
    tsFile.WriteLine intCount & ") " & objProp _
      & " (" & objProperty.Syntax & ")"
   
    Err.Clear
    If Err=0 Then
      tsFile.WriteLine vbTab & "= " & objObj.Get(objProp)
    Else
      tsFile.WriteLine vbTab & "= " & "Not Set!"        
    End If
   
  End If
End Sub
    [ Team LiB ] Previous Section Next Section