Directory Services

Implementation Issues for ADSI Providers

The recommended way to implement ADSI interfaces is to first implement the COM interface IDirectoryObject. By providing this interface as a minimal overhead layer, you supply client applications the control required to access directory objects directly from the directory rather than through the ADSI cache, which optimizes network performance. Using this interface will also supply your own implementation with the most flexibility.

Next, implement the fundamental ADSI interfaces, IADs, IADsContainer, IADsCollection, and the IADsPropertyValue, IADsPropertyEntry, IADsPropertyList property cache interfaces. IADsGroup and IADsMembers are also interfaces in frequent demand by system administration software.

Implement the schema management interfaces if your directory service has an underlying schema: IADsClass, IADsProperty, IADsSyntax. If there is no underlying schema, you can use these interfaces to abstract the classes and properties used by the directory service. Schemas can be used to publish the features of your directory service to ADSI clients.

Collections

ADSI provider components can follow one of three models for caching collections during enumeration. The choice of a caching model determines the behavior of ADSI when an object in a collection is deleted from the underlying directory service external to ADSI.

The caching models include:

Regardless of the caching model, be aware that ADSI enumeration returns Active Directory service interfaces to the caller. To avoid the overhead of obtaining a new interface pointer, ADSI applications should cache the returned interface pointers for objects that they intend to manipulate. For example, a Visual Basic application that enumerates a container and populates a listbox with names can cache the interface pointers associated with the names for later use. This approach will provide greater performance than populating the list box during enumeration and obtaining a new interface pointer when the user makes a selection.

About Dispatch IDs

IDispatch is an Automation interface defined by COM for controllers that do not use COM interfaces directly. Accessing an object through IDispatch is called name-bound or late-bound access, because it occurs at run time ("late") and uses string names of properties and methods to resolve references ("name"). At run time, clients pass the string name of the property or method they wish to call into the IDispatch::GetIDsOfNames() method. If the property or method exists on the object, the dispatch identifier (dispID) of the corresponding function is retrieved. The dispID is then used to execute the function through IDispatch::Invoke(). Using IDispatch, properties and methods on the interfaces exposed by a single object appear as a flat list. Because name-bound access requires two function calls, it is less efficient than using a COM interface directly. Clients are encouraged to use the ADSI COM interfaces on the objects when performance is a consideration. Advanced Automation controllers such as Visual Basic 4.0 can call other COM interfaces as well as IDispatch, if the interfaces comply with the Automation constraints for data types and parameter passing.

ADSI providers generate dispIDs dynamically for each Active Directory object. The dispIDs retrieved through IDispatch::GetIDsOfNames for a given object are the generated values, but not the values that are in the IDL for the object. IDispatch users must call GetIDsOfNames to obtain valid dispIDs at run time.

Type Information and Type Libraries

The ADSI SDK supplies a type library, ActiveDs.tlb, that documents all the standard interfaces supported by ADSI. A provider must supply a similar type library for all interfaces found in ActiveDs.TLB, plus any additional type data for the interfaces implemented within the provider component.

The following is an IDL code example.

[ object, uuid(IID_IADsXYZ), oleautomation, dual ]
interface IADsXYZ: IDispatch
{
// Read-only properties.
[propget]
HRESULT AReadOnlyProp ([out, retval]BSTR *pbstrAReadOnlyProp);
 
// Read/write properties.
[propget]
HRESULT AReadWriteProp ([out, retval]long *plAReadWriteProp);
[propput]
HRESULT AReadWriteProp ([in]long lAReadWriteProp);
 
// Methods.
HRESULT AMethod ([in]DATE dateInParameter,
[out, retval]BSTR *pbstrReturnValue);
};

Thread Safety

Windows NT/Windows 2000 and Windows 9x support multiple threads in a single process. ADSI providers may therefore be used in multithreaded applications and must allow for this.

The Component Object Model (COM) describes the following three threading models. COM applications indicate which model is in use when initializing the COM library using the CoInitialize and CoInitializeEx functions:

ADSI does not assume any particular threading model. Writers of provider components should assume the free threading model and guarantee the consistency of their internal data structures by protecting them from thread-unsafe, that is, uncoordinated, updates through the use of synchronization objects such as critical sections or semaphores.

Object Locking

ADSI does not impose or define an object-locking scheme. Providers for namespaces that support access serialization using locking can expose the underlying locking scheme through provider-specific extensions to ADSI.

Property Names Within a Schema

ADSI represents properties as property objects within the ADSI schema container. This requires that property names be unique within each schema container. The provider must ensure that there are no name collisions.

Primary Interface

When a provider cannot identify which interface should be returned as the primary interface, IID_IADs should be returned. This provides name-bound access to all properties of an object through IDispatch and the IADs::Get, IADs::GetEx, IADs::Put, and IADs::PutEx methods.