IDirectoryObject

Developers using Visual Basic or a scripting language are insulated from having to specifically request the IADs interface since COM and ADSI provide access to all the properties and methods of an object dynamically using the IDispatch mechanism in Automation. However, Microsoft giveth, and Microsoft taketh away. When you're given simplicity, reduced performance is usually the cost. Since ADSI is an in-process COM component, calling interface methods can be very fast if the application uses early binding. Still, the code supporting both early binding and late binding using the IDispatch mechanism incurs a slight performance penalty. In addition, the ADSI interfaces mold LDAP attribute types into COM data types such as binary strings and variants.

Realizing that C and C++ developers generally want the fastest possible performance, ADSI provides the IDirectoryObject interface for more direct access to directory objects. In this section I'll focus on how to use IDirectoryObject with C and C++.

Using IDirectoryObject from C and C++

IDirectoryObject is the best interface to use when a program requires low-level, low-overhead access to directory objects. Similar in purpose and functionality to IADs, IDirectoryObject complements the IDirectorySearch interface described in Chapter 5.

The IDirectoryObject interface is not a dual interface; in other words, it does not support IDispatch and thus cannot be used from the scripting languages. Although the later versions of Visual Basic can read type libraries and use them for early binding, getting Visual Basic to understand C/C++ data structures is prohibitive.

In addition to trimming away the overhead of supporting dual interfaces, IDirectoryObject also does away with the local property cache. While the property cache is a performance enhancement in most cases, IDirectoryObject provides direct access to the information in the directory and gives the developer control over exactly how often the directory is accessed. This is an "on-the-wire" access protocol, meaning that each method call results in an LDAP operation over the network from the client to the server. Table 7-8 lists the methods for the IDirectoryObject interface.

IdirectoryObject Method Description

GetObjectInformation

Returns an ADS_OBJECT_INFO structure with members that contain fixed information about the object itself. Similar to the IADs properties such as Name and Class.

GetObjectAttributes

Returns an array of ADS_ATTR_INFO structures with members that contain information about the requested attributes of the object. Similar to the IADs GetEx method.

SetObjectAttributes

Accepts an array of ADS_ATTR_INFO structures with which to update the object. Similar to the PutEx and SetInfo methods.

CreateDSObject

Creates a new directory object as a child of the current object. Accepts a distinguished name string and an array of ADS_ATTR_INFO structures with the attributes that make up the new object. Similar to the IADsContainer Create method.

DeleteDSObject

Deletes a directory object. Accepts a relative distinguished name string of a leaf object to delete. Similar to the IADsContainer Delete method.

Table 7-8 IDirectoryObject methods.

Although IDirectoryObject doesn't use the property cache or the property cache interfaces, you'll see that much of what follows is similar to the discussion earlier in this chapter. For example, both the property cache interfaces and IDirectoryObject are designed to be low-level and to work more directly with the data. Also, IDirectoryObject uses structures (ADS_ATTR_INFO and ADSVALUE) that have similarities to the PropertyEntry and PropertyValue objects.

IDirectoryObject might be friendlier to C and C++ developers because it uses structures rather than COM interface properties to work with data. Instead of using binary strings and variants, IDirectoryObject uses its own data type, known as an ADSTYPE, that, like a variant, stores various types of data. However, ADSTYPE is very specific and is optimized for directory service data.

In some ways, it's surprising that IDirectoryObject is a COM interface at all. It feels like a group of Win32 API functions. It you are comfortable working with the LDAP API but want some of the benefits that ADSI can provide, the IDirectoryObject interface is a natural stepping stone.

Listing 7-3, from IDirectoryObject.cpp on the companion CD, is a sample program that shows how the GetObjectInformation and GetObjectAttributes methods of the IDirectoryObject interface can be used to retrieve information about a specified object.

 #include <stdio.h>      // Standard I/O
#include <comdef.h>     // COM definitions
#include <activeds.h>   // ADSI definitions

// The following must be updated to point to your own Active Directory domain
#define ADSPATH L"LDAP://CN=Administrator,CN=Users,DC=coppersoftware,DC=com"
int main(int argc, char **argv , char  **envp )
{
    // Initalize COM
    HRESULT hResult = CoInitialize ( NULL );
    // Get pointer to object's IDirectoryObject interface
    IDirectoryObject   *pdsoUser = NULL;
    hResult = ADsGetObject( ADSPATH , IID_IDirectoryObject, 
        (void**) &pdsoUser );
    // Check for binding success
    if ( SUCCEEDED ( hResult ) )
        {
        // Retrieve and display object information
        ADS_OBJECT_INFO *padsObjInfo;
        hResult = pdsoUser->GetObjectInformation( &padsObjInfo );
        if ( SUCCEEDED( hResult ) )
            {
            printf("Relative Distinguished Name: %S\n", 
                padsObjInfo->pszRDN);
            printf("Distinguished Name: %S\n", padsObjInfo->pszObjectDN);
            printf("Parent: %S\n", padsObjInfo->pszParentDN);
            printf("Class: %S\n", padsObjInfo->pszClassName);
            printf("Schema: %S\n", padsObjInfo->pszSchemaDN);
 
            // ADSI allocates the info structure and the app must 
            // free it
            FreeADsMem( padsObjInfo );
            }
 
        // Get specified values 
        ADS_ATTR_INFO   *pdsoAttributeInfo;
        DWORD   dwReturn;
        LPWSTR   pdsoAttributeNames[] =             {
            L"sn",
            L"otherTelephone",
            L"whenChanged"
            };
        DWORD   dwNumAttr = sizeof( pdsoAttributeNames ) / 
            sizeof( LPWSTR );
        hResult = pdsoUser->GetObjectAttributes( pdsoAttributeNames, 
            dwNumAttr, 
            &pdsoAttributeInfo, 
            &dwReturn );
 
        if ( SUCCEEDED( hResult ) )
            {
            // Loop through all the returned attributes
            for ( DWORD curAttribute = 0; 
                curAttribute < dwReturn;
                curAttribute++, pdsoAttributeInfo++ )
                {
                // Display attribute name
                printf( "%S: ", pdsoAttributeInfo->pszAttrName );
                // Loop through all values of current attribute
                for ( DWORD curValue = 0;
                    curValue < pdsoAttributeInfo->dwNumValues; 
                    curValue++, pdsoAttributeInfo->pADsValues++ ) 
                    {
                    // Retrieve attribute value based on ADSTYPE
                    switch (pdsoAttributeInfo->dwADsType)
                        {
                        case ADSTYPE_CASE_IGNORE_STRING:
                            printf ( "%S\n", pdsoAttributeInfo->
                                pADsValues->CaseIgnoreString );
                            break;
                        case ADSTYPE_UTC_TIME:
                            printf ( "%02d/%02d/%04d %02d:%02d:%02d\n", 
                                pdsoAttributeInfo->pADsValues->
                                    UTCTime.wMonth,
                                pdsoAttributeInfo->pADsValues->
                                    UTCTime.wDay, 
                                pdsoAttributeInfo->pADsValues->
                                    UTCTime.wYear,                                  pdsoAttributeInfo->pADsValues->
                                    UTCTime.wHour, 
                                pdsoAttributeInfo->pADsValues->
                                    UTCTime.wMinute, 
                                pdsoAttributeInfo->pADsValues->
                                    UTCTime.wSecond );
                            break;
                        default:
                            printf ( "Unsupported data type (%#0.2x)\n", 
                                pdsoAttributeInfo->dwADsType );
                            break;
                        }
                    }
                }
            }
        // Allocated by ADSI, must free 
        FreeADsMem( pdsoAttributeInfo );
        }
    // Release interface if allocated
    if (pdsoUser)
        pdsoUser->Release ();
    // Unload COM
    CoUninitialize ();
    // Return the result of any failures
    return hResult;
}

Listing 7-3 IDirectoryObject.cpp demonstrates accessing directory attributes using the IDirectoryObject interface.

When you run this code, you should see something similar to Figure 7-2.

Figure 7-2 Output of IDirectoryObject.cpp sample.

GetObjectInformation

The GetObjectInformation method of IDirectoryObject is defined using the following prototype:

 HRESULT GetObjectInformation (
    PADS_OBJECT_INFO *ppObjInfo );

GetObjectInformation uses the ADS_OBJECT_INFO structure to store several pieces of fixed information about the directory object. The members of the ADS_OBJECT_INFO structure are listed in Table 7-9. The members are very similar to the IADs read-only properties, which are Name, ADsPath, Parent, Schema, and Class.

ADS_OBJECT_INFO Members C/C++ Data Type Description

pszRDN

LPWSTR

Relative distinguished name of the object

pszObjectDN

LPWSTR

Distinguished name (DN) of the object

pszParentDN

LPWSTR

DN of the object's parent

pszSchemaDN

LPWSTR

DN of the object representing the schema for the current object

pszClassName

LPWSTR

Name of the class of which this object is an instance

Table 7-9 Members of the ADS_OBJECT_INFO structure.

To use GetObjectInformation you must create a pointer variable of type ADS_OBJECT_INFO. You then pass the address of the pointer (double indirection) as the parameter to GetObjectInformation. The function will allocate memory and adjust the passed pointer variable to point to the object information. When the function returns, use the pointer to dereference the ADS_OBJECT_INFO members for the object's information.

 // Retrieve and display object information
ADS_OBJECT_INFO *padsObjInfo;
hResult = pdsoUser->GetObjectInformation( &padsObjInfo );
if ( SUCCEEDED( hResult ) )
    {
    printf("Relative Distinguished Name: %S\n", padsObjInfo->pszRDN);
    printf("Distinguished Name: %S\n", padsObjInfo->pszObjectDN);
    printf("Parent: %S\n", padsObjInfo->pszParentDN);
    printf("Class: %S\n", padsObjInfo->pszClassName);
    printf("Schema: %S\n", padsObjInfo->pszSchemaDN);
 
    // ADSI allocates the info structure 
    // and the app must free it
    FreeADsMem( padsObjInfo );
    }

All the ADS_OBJECT_INFO members are pointers to null-terminated, wide-character strings. With the exception of pszClassName, they all use some form of an LDAP distinguished name, not an ADsPath however.

Since ADSI allocates memory automatically on your application's behalf; you must manually free the memory when you are finished with it. Use the FreeADsMem function to accomplish this.

GetObjectAttributes

The GetObjectAttributes method is used to gather the object's attributes and values into an array. Similar to the property cache but without the COM-style interfaces, GetObjectAttributes is defined using the following prototype:

 HRESULT GetObjectAttributes (
    LPWSTR         *pAttributeNames,
    DWORD          dwNumberAttributes,
    PADS_ATTR_INFO *ppAttributeEntries,
    DWORD          *pdwNumAttributesReturned );

GetObjectAttributes accepts an array of attribute names to be retrieved from the server. A pointer to the array of names is passed using the pAttributeNames parameter along with the number of names in the dwNumberAttributes parameter. When called, ADSI queries the server for the attributes requested. The function returns a pointer to the array of ADS_ATTR_INFO structures containing the attribute information in the ppAttributeEntries parameter. The value of the DWORD variable pointed to by pdwNumAttributesReturned is updated with the number of attributes collected. If no attributes are found, E_ADS_PROPERTY_NOT_FOUND is returned as the result code.

The ADS_ATTR_INFO structure is the IDirectoryObject version of the PropertyEntry object. It contains the same information, including the data type of the property and the series of values for that property. The members of the ADS_ATTR_INFO structure are listed in Table 7-10.

ADS_ATTR_INFO Member Win32 Type Description

pszAttrName

LPWSTR

Pointer to a string with the name of the attribute.

dwControlCode

DWORD

Indicates how the attribute should be modified. Value corresponds to one of the following update control code constants:

ADS_ATTR_APPEND

ADS_ATTR_CLEAR

ADS_ATTR_DELETE

ADS_ATTR_UPDATE

These are equivalent to the similarly named ADS_PROPERTY_OPERATION_ENUM enumeration.

dwADsType

ADSTYPE

Indicates the data type of the attribute. Corresponds to constants defined in the ADSTYPEENUM enumeration (Table 7-5).

pADsValues

PADSVALUE

Pointer to an array of ADSVALUE structures that contain values for the attribute. (See Table 7-11.)

dwNumValues

DWORD

Number of ADSVALUE structures in the array.

Table 7-10 Members of the ADS_ATTR_INFO structure.

Setting up a call to GetObjectAttributes involves creating an array of attribute names. You also need to pass the number of attributes you're looking for.

 // Get specified values 
ADS_ATTR_INFO   *pdsoAttributeInfo;
DWORD   dwReturn;
LPWSTR   pdsoAttributeNames[] =
    {
    L"sn",
    L"otherTelephone",
    L"whenChanged"
    };
DWORD   dwNumAttr = sizeof( pdsoAttributeNames ) / sizeof( LPWSTR );
hResult = pdsoUser->GetObjectAttributes(
    pdsoAttributeNames, 
    dwNumAttr, 
    &pdsoAttributeInfo, 
    &dwReturn );

In this example, I've requested that the sn (Surname), otherTelephone, and whenChanged attributes be returned. Because of the lack of a local property cache, you must specify the attributes by name. There is no provision for enumerating unknown attributes of an object when it is accessed through IDirectoryObject.

If GetObjectAttributes returns successfully (S_OK), pdsoAttributeInfo will point to three ADS_ATTR_INFO structures. By dereferencing this pointer, you can access the value of the attribute directly.

 printf( "%S: ", pdsoAttributeInfo->pszAttrName );
// Loop through all values of current attribute
for ( DWORD curValue = 0;
    curValue < pdsoAttributeInfo->dwNumValues; 
    curValue++, pdsoAttributeInfo->pADsValues++) 
    {
    ...
    }

The dwNumValue member contains the number of values that a particular attribute contains. Before accessing the value, however, you must check the data type and choose the ADSVALUE union member that corresponds to the correct data type. The easiest way to do this is with a switch statement.

 // Retrieve attribute value based on ADSTYPE
switch (pdsoAttributeInfo->dwADsType)
    {
    case ADSTYPE_CASE_IGNORE_STRING:
        ...
        break;
    }

IDirectoryObject uses the same set of data types as the IADsPropertyEntry interface, which are defined in the ADSTYPEENUM enumeration. See Table 7-5 for a list of these data types.

After you know what data type the particular value is, you can use the ADSVALUE union member to access the value, just like the IADsPropertyValue interface.

 printf ( "%S\n", pdsoAttributeInfo->pADsValues->CaseIgnoreString ); 

Table 7-11 lists members of the ADSVALUE structure that are used by Active Directory. Table 7-12 lists the corresponding Win32 data type for each ADSI data type.

ADSVALUE Member ADSI Data Type Description

dwType

ADSTYPE

The data type of the value. One of the constants defined by ADSTYPEENUM (Table 7-5).

Boolean

ADS_BOOLEAN

Boolean value.

CaseExactString

ADS_CASE_EXACT_STRING

Case-sensitive string.

CaseIgnoreString

ADS_CASE_IGNORE_STRING

Case-insensitive string.

DNString

ADS_DN_STRING

String containing a distinguished name.

Integer

ADS_INTEGER

Integer value.

LargeInteger

ADS_LARGE_INTEGER

Long integer value.

NumericString

ADS_NUMERIC_STRING

String containing numerical characters.

OctetString

ADS_OCTET_STRING

String of bytes representing binary data.

pDNWithBinary

PADS_DN_WITH_BINARY

Pointer to an ADS_DN_WITH_BINARY structure that associates a fixed GUID with a distinguished name of a directory object.

pDNWithString

PADS_DN_WITH_STRING

Pointer to an ADS_DN_WITH_STRING structure that associates a constant string with a distinguished name of a directory object.

PrintableString

ADS_PRINTABLE_STRING

String containing characters that are safe to print and display (i.e. no control codes).

ProviderSpecific

ADS_PROV_SPECIFIC

Provider-specific structure.

SecurityDescriptor

ADS_NT_SECURITY_DESCRIP TOR

Windows NT/Windows 2000 security descriptor.

UTCTime

ADS_UTC_TIME

Time interval value expressed in coordinated universal time (UTC).

Table 7-11 Members of the ADSVALUE structure that are used by Active Directory.

ADSI Data Type Win32 Data Type

ADS_BOOLEAN

DWORD

ADS_CASE_EXACT_STRING

LPWSTR

ADS_CASE_IGNORE_STRING

LPWSTR

ADS_DN_STRING

LPWSTR

ADS_DN_WITH_BINARY

typedef struct {

PADS_DN_WITH_BINARY

DWORD dwLength;LPBYTE lpBinaryValue;LPWSTR pszDNString;};

ADS_DN_WITH_STRING

typedef struct {

PADS_DN_WITH_STRING

LPWSTR pszStringValue;LPWSTR pszDNString;};

ADS_INTEGER

DWORD

ADS_LARGE_INTEGER

LARGE_INTEGER

ADS_NT_SECURITY_DESCRIPTOR

typedef struct {DWORD dwLength;LPBYTE lpValue;};

ADS_NUMERIC_STRING

LPWSTR

ADS_OCTET_STRING

typedef struct {DWORD dwLength;LPBYTE lpValue;};

ADS_PRINTABLE_STRING

LPWSTR

ADS_PROV_SPECIFIC

typedef struct {DWORD dwLength;LPBYTE lpValue;};

ADS_UTC_TIME

SYSTEMTIME

ADSTYPE

DWORD

Table 7-12 ADSI data types and corresponding Win32 data types.

Writing Attributes with SetObjectAttributes

The SetObjectAttributes method is used to add, delete, or modify an attribute or value of an object. SetObjectAttributes is defined using the following prototype:

 HRESULT SetObjectAttributes (
    PADS_ATTR_INFO pAttributeEntries,
    DWORD          dwNumAttributes,
    DWORD          *pdwNumAttributesModified ) ;

To update the directory attributes, simply change the values contained in the ADSVALUE structure and update the dwControlCode member of the ADS_ATTR_INFO structure with the type of operation requested: update, append, delete, or clear.

Pass the ADS_ATTR_INFO pointer in the pAttributeEntries parameter along with the number of attributes you'd like to have updated. When the update is finished, the DWORD variable pointed to by the pdwNumAttributesModified parameter will contain the actual number of attributes modified.

Two methods not shown in the sample code above are the CreateDSObject and DeleteDSObject methods, which are used to create and delete objects within containers. The functionality of these methods is the same as provided with the IADsContainer interface, which I described in Chapter 6.



MicrosoftR WindowsR 2000 Active DirectoryT Programming
MicrosoftR WindowsR 2000 Active DirectoryT Programming
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net