Platform SDK: Active Directory, ADSI, and Directory Services |
To begin, let's look at a scenario with early binding support in place. Consider the following code snippet:
Dim x as IADsUser Dim y as IADsExt1 Dim z as IADsExt2 Set x = GetObject("LDAP://CN=JSmith, OU=Sales, DC=Microsoft,DC=COM") x.SetPassword("newPassword") Set y = x y.MyNewMethod( "\\srv\public") y.MyProperty = "Hello World" Set z = y z.OtherMethod() z.OtherProperty = 4362 Debug.Print x.LastName Set z = GetObject("LDAP://CN=Alice,OU=Engr, DC=Microsoft,DC=COM") z.OtherProperty = 5323
In this example, two extension components extend a user object. Each extension publishes its own interface. Each extension is not aware of the other extension's interface, only ADSI is aware of the existence of both extensions. Each extension will delegate its IUnknown to ADSI. ADSI will act as an aggregator for both extensions, and any other future extensions. Querying an interface from any extension, or from ADSI, will yield the same consistent result.
Let's look at the steps required to make an extension.
Follow the COM specification for adding aggregation to your component. In summary, you must honor the pUnknown requests to your component during CreateInstance, and delegate the pUnknown to the aggregator's IUnknown if the component is being aggregated.
Now you must decide which directory class you want to extend. You will not use the same interfaces to accomplish this that you would use for an ADSI interface (for example, IADsUser, IADsComputer). Directory objects are persisted in the directory, while your extension and ADSI will be running on the client's machine. Directory object examples are user, computer, printQueue, serviceConnectionPoint, and nTDSService. You can add a new class in Active Directory™ and create a new extension for this new class as well.
You will use registry keys to associate a directory class name with the ADSI extension components. The following figure represents the existing registry layout, as a well as new keys.
Note Extension objects are still required to register standard COM keys.
HKLM
|__Software
|___Microsoft
|____ADS
|__Providers
|__LDAP
<continue from above>
LDAP
|__Extensions
|___<ClassNameA>
|____<CLSID of ExtensionA1>
Interfaces(REG_BINARY
) {List of interfaces)
|____<CLSID of ExtensionA2>
Interfaces(REG_BINARY
) {List #1 of interfaces)
|____ <ClassNameB>
|____<CLSID of ExtensionB1>
Interfaces(REG_BINARY
) {List of interfaces)
|____<CLSID of ExtensionB2> {List of interfaces}
Interfaces(REG_BINARY
) {List #1 of interfaces)
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ADs\Providers\LDAP\Extensions\printQueue\{9f37f39c-6f49-11d1-8c18-00c04fd8d503}] "Interfaces"={466841B0-E531-11d1-8718-00C04FD44407} {466841B1-E531-11d1-8718-00C04FD44407}
The list of interfaces from Extension1 can be different from that of Extension2. The object, when it's alive, will support the interfaces of the aggregator (ADSI) and all the interfaces provided by the aggregate's Extension1 and Extension2. Resolution of conflicting interfaces (the same interface supported by both the aggregator and the aggregates or by multiple aggregates) is determined by priorities of the extensions.
You may also associate your CLSID extension with multiple object class names. For example, your extension can extend both the user and contact objects.
Note Extension behavior is added per object class, not per object instance.
It is best to register your extensions, as you would any other COM components, with a call to the DllRegisterSvr function. You should also provide a facility to unregister your extension with the DllUnregisterServer function.
Here is an example for how to register an extension:
/////////////////////////////////////////// // Register the class. /////////////////////////////////////////// hr = RegCreateKeyEx( HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\ADs\\Providers\\LDAP\\Extensions\\User\\{E1E3EDF8-48D1-11D2-B22B-0000F87A6B50}"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisposition ); /////////////////////////////////////////// // Register the Interface. /////////////////////////////////////////// const TCHAR szIf[] = _T("{E1E3EDF7-48D1-11D2-B22B-0000F87A6B50}"); hr = RegSetValueEx( hKey, _T("Interfaces"), 0, REG_BINARY, (const BYTE *) szIf, sizeof(szIf) ); RegCloseKey(hKey); return S_OK; }