In the majority of your dealings with ADSI, you won't need to work with the property cache directly. The IADs interface provides all the functionality that is required to read, modify, and delete object property values. However, it's useful to understand how ADSI works under the covers, which always comes in handy when your code isn't working as expected.
The IADsPropertyXXX family of interfaces are a convenient way to examine the properties and values of a directory object contained in the cache. They also allow a program to create and remove cache entries that can then be applied to the object on the server. However, the property cache interfaces work only on the local property cache. Changes are not saved, nor is new data retrieved, unless you specifically request it. To make working with the property cache easier, ADSI exposes it as a collection of entries, one for each property of the object that contains a value. The property list is managed using the IADsPropertyList interface. Each entry is a PropertyEntry object that supports the IADsPropertyEntry interface. Likewise, the entries contain one or more values. Each value is packaged with its own PropertyValue object that supports two interfaces: IADsPropertyValue and IADsPropertyValue2. Table 7-1 describes the function of each of these interfaces.
Another ADSI interface, named IADsProperty, represents the schema definition of an attribute in the directory. Despite its name, this interface is not related to the local property cache. I'll discuss IADsProperty in Chapter 9, but I mention it now to avoid confusion with the IADsPropertyXXX family of interfaces.
IADsPropertyXXX Interface | Description |
---|---|
IADsPropertyList | Manages the list of PropertyEntry objects in the property cache of an object. Used to enumerate the list of properties, read and modify property values, and add and remove properties from the property cache. |
IADsPropertyEntry | Used to read, modify, and delete property values for an entry in the property cache. Supported by the PropertyEntry object. |
IADsPropertyValue | Used to read and write a property value as a specific data type. Supported by the PropertyValue object. |
IADsPropertyValue2 | Used to read and write a property value as a specific data type. Similar to IADsPropertyValue , this interface supports any data type, including custom data types. Supported by the PropertyValue object. |
Table 7-1 The IADsPropertyXXX family of interfaces.
To get an idea of the relationship between the property cache interfaces and which parts of the property cache they are associated with, Figure 7-1 shows a loose representation of these interfaces and their corresponding objects in the property cache.
Figure 7-1 Loose representation of IADsPropertyXXX interfaces and the property cache.
When ADSI populates the property cache with information from the server, it creates a PropertyEntry object in the cache for each property that's associated with the current object. ADSI exposes the property cache as a list of PropertyEntry objects. Using the IADsPropertyList interface, your application can manage the property cache—reading, modifying, adding, and removing PropertyEntry objects independently of the server.
Remember that the property cache is a snapshot of the properties of the object in the directory at the time when the IADs GetInfo method was called (either explicitly or implicitly). When working with the local cache, everything occurs within the confines of your application. Only when you call the IADs SetInfo method is Active Directory contacted and the object updated en masse with property and value data from the cache. Only at that time does Active Directory validate any changes. Keep this in mind because a change to a property in one place in a program might cause an error in a separate part of the application that calls SetInfo. This kind of error can happen if you delete a property from the cache that is required by the object class, resulting in a "Constraint Violation" when SetInfo is called. It's also wise to keep the local cache refreshed using the GetInfo or GetInfoEx methods because your application won't be notified of changes made by other clients to the same object.
Table 7-2 lists the methods of the IADsPropertyList interface. Note that the interface refers to property entries as items.
IADsPropertyList Method | Description |
---|---|
Next | Returns the next property item in the list. |
Skip | Skips a specified number of property items in the list. |
Reset | Moves back to the start of the list. |
Item | Returns the property item specified by name or index (zero based). |
GetPropertyItem | Returns the property item specified by name. The item is returned by specifying a particular data type. |
PutPropertyItem | Updates or adds a property item in the list. |
ResetPropertyItem | Removes the property item specified by name or index (zero-based). |
PurgePropertyList | Removes all items from the list. |
Table 7-2 Methods for the IADsPropertyList interface.
The IADsPropertyList interface has one property, named PropertyCount. This property is read-only and indicates the number of items in the list. Its data type is a Long.
The IADsPropertyList interface does not support enumerator objects or the IEnumVARIANT interface, which allows enumeration using the Visual Basic and VBScript For Each statement. Instead, to enumerate the entries, you must use a For Next loop starting at 0 for the first item (a zero-based index) and loop up to PropertyCount-1 for the last item. The PropertyCount value will change as you delete or add entries to the list.
The IADsPropertyList methods Next, Item, and GetPropertyItem all return a reference to an ADSI PropertyEntry object, which represents a single property of the current directory object. The PropertyEntry object is different from other ADSI objects we've worked with earlier because it is not contained in the directory itself; it only exists locally. The PropertyEntry object supports the Automation interface IADsPropertyEntry with four properties that describe and control the entry. All of these properties can be read and written to. Table 7-3 lists the properties of the IADsPropertyEntry interface.
IADsPropertyEntry Property | Type | Description |
---|---|---|
Name | String | The name of the property. In Active Directory, this will be the LDAP display name of the attribute (i.e. adminDescription ). |
ADsType | Long | The data type of the entry. See the ADSTYPEENUM enumeration (Table 7-5) later in this chapter for a list of supported data types. |
ControlCode | Long | Controls how the property should be treated when being written to the server. Uses one of the constants defined by the ADS_PROPERTY_OPERATION_ENUM enumeration: ADS_PROPERTY_APPEND ADS_PROPERTY_CLEAR ADS_PROPERTY_DELETE ADS_PROPERTY_UPDATE These control codes are described in more detail in Chapter 6. |
Values | Variant array | Variant array of PropertyValue objects containing the individual values for this property. |
Table 7-3 IADsPropertyEntry properties.
New PropertyEntry objects can be created using the New keyword or the CreateObject function in Visual Basic. In VBScript the CreateObject function can be used. C and C++ developers can use the COM function CoCreateInstance. A new PropertyEntry object can be added to the property list using the PutPropertyItem method of the IADsPropertyList interface. The directory object is updated with the new property when the SetInfo method is called.
The Values property returns a collection of one or more PropertyValue objects that represent the value or values of the property. The PropertyValue object represents a single value of the property. Multivalued attributes will be represented with an array of PropertyValue objects, each exposing the IADsPropertyValue interface. In addition to containing the actual value of the property, the PropertyValue object contains information about the data type of the value.
The data type is stored in the ADsType property of the IADsPropertyValue interface. The contents of this property correspond to a data type defined in the ADSTYPEENUM enumeration. Depending on the data type returned, you retrieve the value using the appropriate IADsPropertyValue property. For example, if the ADsType property is equal to the number defined by ADSTYPE_OCTET_STRING, you should use the OctetString property to retrieve and set the value. In some cases, the value is represented as another object, such as a LargeInteger object supplied by ADSI. In those cases, the value returned by the appropriate method is a reference to an object representing the value. I'll discuss the data types and interfaces in the next section. Table 7-4 lists the properties of the IADsPropertyValue interface.
IADsPropertyValue Property | Type | Description |
---|---|---|
ADsType | Long | The data type of the value. One of the constants defined by ADSTYPEENUM (Table 7-5). |
Boolean | Long | Returns the value as a Boolean (ADSTYPE_BOOLEAN). |
CaseExactString | String | Returns the value as a case-sensitive string (ADSTYPE_CASE_EXACT_STRING). |
CaseIgnoreString | String | Returns the value as a case-insensitive string (ADSTYPE_CASE_IGNORE_STRING). |
DNString | String | Returns the value as a distinguished name (ADSTYPE_DN_STRING). |
Integer | Long | Returns the value as an integer (ADSTYPE_INTEGER). |
LargeInteger | Object | Returns the value as a LargeInteger object (ADSTYPE_LARGE_INTEGER). |
NumericString | String | Returns the value as a string of numeric characters (ADSTYPE_NUMERIC_STRING). |
OctetString | Variant array of single-byte characters | Returns the value as a byte array (ADSTYPE_OCTET_STRING). |
PrintableString | String | Returns the value as a printable string (ADSTYPE_PRINTABLE_STRING). |
SecurityDescriptor | Object | Returns the value as a SecurityDescriptor object. (ADSTYPE_NT_SECURITY_DESCRIPTOR). |
UTCTime | Date | Returns the value as a coordinated universal time (ADSTYPE_UTC_TIME). |
Table 7-4 IADsPropertyValue properties.
The IADsPropertyValue interface has one method, named Clear, which clears the PropertyValue object's current value. Calling any of the other properties after clearing the object will return empty strings or variants.
In a number of places, the old school of LDAP and the more modern world of COM collide and result in confusion. LDAP, and thus Active Directory, supports data types known as syntaxes. A syntax defines what kind of data a particular directory attribute can contain. Active Directory defines 23 different syntaxes, such as an integer, printable string, and octet string, to name a few. (I discuss Active Directory syntaxes in more detail in Chapter 9.) Automation, on the other hand, supports a limited number of data types, such as binary string and variant, that can contain one of several other major data types, such as date, string, array, and so on.
Since Automation supports a limited number of data types and Active Directory supports a larger, and sometimes overlapping, set, ADSI must map and convert the two sets of data types. Additionally, since ADSI supports other directory services, each with their own unique data types, there are a lot of data types floating around. It's easy to get confused.
Adding to the confusion is the fact that both the IADsPropertyEntry and IADsPropertyValue interfaces have an ADsType property. When working with Active Directory, each value of a property must have the same type, so ADsType must be the same for both interfaces. In the future, it's possible that Active Directory or another directory service will support multiple values of different types, but that is not the case currently.
ADSI manages this data type problem using a generic set of data types for directory services. This set is defined in a series of constants specified in the ADSTYPEENUM enumeration shown in Table 7-5.
The ADSTYPEENUM enumeration contains many more types in addition to those listed in Table 7-5; only those types supported by the LDAP provider and Active Directory are listed.
ADSTYPEENUM Constant | Description |
---|---|
ADSTYPE_BOOLEAN | Boolean value |
ADSTYPE_CASE_EXACT_STRING | Case-sensitive string |
ADSTYPE_CASE_IGNORE_STRING | Case-insensitive string |
ADSTYPE_DN_STRING | String containing a distinguished name |
ADSTYPE_DN_WITH_BINARY | Structure (ADS_DN_WITH_BINARY) that associates a fixed GUID with a distinguished name of a directory object |
ADSTYPE_DN_WITH_STRING | Structure (ADS_DN_WITH_STRING) that associates a constant string with a distinguished name of a directory object |
ADSTYPE_INTEGER | Integer value |
ADSTYPE_INVALID | The data is of an invalid type |
ADSTYPE_LARGE_INTEGER | 64-bit (long) integer value |
ADSTYPE_NT_SECURITY_DESCRIPTOR | Windows NT/Windows 2000 security descriptor |
ADSTYPE_NUMERIC_STRING | String containing numerical characters |
ADSTYPE_OCTET_STRING | String of bytes representing binary data |
ADSTYPE_PRINTABLE_STRING | String containing characters that are safe to print and display (i.e. no control codes) |
ADSTYPE_PROV_SPECIFIC | The type is specific to the ADSI provider |
ADSTYPE_UNKNOWN | Unknown or undefined type |
ADSTYPE_UTC_TIME | Structure (SYSTEMTIME) containing a coordinated universal time (UTC) value |
Table 7-5 The ADSTYPEENUM constants supported by the LDAP provider and Active Directory.
In addition to the IADsPropertyValue interface, the PropertyValue object also supports the IADsPropertyValue2 interface, which can return values as variants of a particular subtype. Instead of having a fixed list of properties based on the data type of the value, the GetObjectProperty and PutObjectProperty methods of IADsPropertyValue2 accept the data type as a parameter. Since new data types might appear in the future, having this flexibility is the better way to go. This is already true of the ADSTYPE_DN_WITH_BINARY type, used by Active Directory to associate a GUID with a distinguished name to provide the well-known GUIDs functionality described in Chapter 4. The IADsPropertyValue2 methods are listed in Table 7-6.
IADsPropertyValue2 Method | Description |
---|---|
GetObjectProperty | Returns the value as a variant. The exact type is specified by an ADSTYPE value. For example, passing in ADS_UTC_TIME returns a VT_DATE variant. |
PutObjectProperty | Sets the value of the object using the ADSTYPE supplied. |
Table 7-6 IADsPropertyValue2 methods.
You can use the IADsPropertyValue2 interface to perform some data conversion between different types. For example, if a PropertyValue object contains a security descriptor (ADSTYPE_NT_SECURITY_DESCRIPTOR), you can ask ADSI to return the value as a different type instead of as a reference to an IADsSecurityDescriptor interface. To return the value as an octet string, call GetObjectProperty with ADSTYPE_OCTET_STRING. However, there are many limitations. I thought that by passing in ADSTYPE_UTC_TIME to the value of the lastLogon property, I could get ADSI to do the complex work of converting the LargeInteger data type to a date variant. Unfortunately, IADsPropertyValue2 won't do that.
Some ADSI types are contained in structures while others are represented using ADSI objects. Representations such as this are made when a single value cannot be easily expressed using the basic data types available or when the value contains multiple parts. A value defined as an ADS_LARGE_INTEGER data type, for example, actually returns a reference to an ADSI LargeInteger object from which you can use the IADsLargeInteger interface to manipulate the 64-bit number represented. The same is true for the ADSTYPE_NT_SECURITY_DESCRIPTOR type that returns a reference to a SecurityDescriptor object. A security descriptor contains the security information associated with an object. It is actually a structure defined by Win32 that contains arrays of other structures. The IADsSecurityDescriptor interface makes working with SecurityDescriptor objects easy, however.
Care must be taken when working with data types that are represented by objects. You cannot immediately use the value returned by the IADsPropertyValue or IADs-PropertyValue2 interfaces; they are object references, and the actual property value is retrieved using the properties and methods of the associated interface. Table 7-7 lists the data type objects supported by Active Directory.
ADSTYPE | Object and Interface | Description |
---|---|---|
ADSTYPE_LARGE_INTEGER | LargeInteger IADsLargeInteger | Represents a 64-bit integer value. Often used to contain values in the FILETIME Win32 structure. |
ADSTYPE_NT_SECURITY_DESCRI PTOR | SecurityDescriptor IADsSecurityDescriptor | Represents a security descriptor containing Access Control Lists (ACL) and Access Control Entries (ACE). |
ADSTYPE_DN_WITH_BINARY | DNWithBinary IADsDNWithBinary | Represents distinguished name and GUID pairing. Used by Active Directory for updating well-known GUIDs. |
ADSTYPE_DN_WITH_STRING | DNWithString IADsDNWithString | Represents a distinguished name with another, fixed string. Available but not used by the default Active Directory objects. |
Table 7-7 Active Directory data types represented by ADSI objects.