Writing Attributes

Up until now, of course, we've just been binding to objects, retrieving values, and enumerating multivalues, but what about actually modifying attribute values in Active Directory?

ADSI makes writing to Active Directory easy with the Put and PutEx methods. Conceptually, these methods are the same as Get and GetEx, but they work in reverse. Instead of retrieving values from the property cache, they write values to the cache. Once the changes have been made to the property cache, you use the SetInfo method to write the data to Active Directory.

The exact steps required to update a value depend on which language you're developing in and whether the attribute is a named ADSI property or an LDAP attribute.

ADSI Properties

Some properties, such as Name, TelephoneNumber, and others—which I've been referring to as named properties—are provided by the various ADSI interfaces. When using Visual Basic or scripting languages, COM and the IDispatch interface take care of figuring out whether to read the value from the property or to write a new value to the property. The following code, from the ReadWrite.bas sample on the companion CD, retrieves the TelephoneNumber property for the current user and then uses the InputBox function to prompt the user to change their telephone number. To determine the current user, as well as other system information, ADSI provides the ADSystemInfo object and the IADsADSystemInfo interface, which are supported only on Windows 2000.

 ` Create ADSystemInfo object
Set objSysInfo = CreateObject("ADSystemInfo")

` Get the distinguished name of the current user
strUserDN = objSysInfo.UserName

` Prefix the users DN with the ADSI provider string to form a ADsPath
strADsPath = "LDAP://" & strUserDN

` Bind to the object
Set objADs = GetObject(strADsPath)

` Read the telephone number property
strPhoneNumber = objADs.TelephoneNumber

` Prompt string
strPrompt = "The current phone number is shown in the edit box." & _
    "Type a new number and press Enter."

` Display the number as the default, and then ask to change it
strPhoneNumber = InputBox(strPrompt, objADs.Name, strPhoneNumber)

If strPhoneNumber <> "" Then
    ` Write the new value to the object
    objADs.TelephoneNumber = strPhoneNumber

    ` Commit the change to the server
    objADs.SetInfo
End If

Using the InputBox function from the ReadWrite.bas sample is shown in Figure 6-4.

Figure 6-4 The input box created by the ReadWrite.bas sample, prompting the user for a new telephone number.

Take a closer look at these two lines from the example:

 strPhoneNumber = objADs.TelephoneNumber
objADs.TelephoneNumber = strPhoneNumber

The first line reads the property, and the second line writes the property. Visual Basic, along with Automation, figures out the exact interface method to call based on the type of operation: a get (read) or a put (write).

C and C++ developers have to be more explicit with their code, calling get_TelephoneNumber to retrieve a property value and put_TelephoneNumber to set the value. Listing 6-3 shows code from the ReadWrite.cpp sample on the companion CD. The code reads the Name and TelephoneNumber properties of the current user. The program then prompts the user for a new phone number and writes that back to the directory.

 int _tmain(int /* argc */, _TCHAR /* **argv */, _TCHAR /* **envp */)
{
    // Initalize COM and result code
    HRESULT hResult = CoInitialize( NULL );

    // Create ADSystemInfo object
    IADsADSystemInfo *pobjADSysInfo;
    hResult = CoCreateInstance(
        CLSID_ADSystemInfo,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IADsADSystemInfo,
        (void**) &pobjADSysInfo);

    if ( SUCCEEDED(hResult) )
        {
        // Get the distinguished name of the current user
        BSTR bstrUserDN = NULL;
        hResult = pobjADSysInfo->get_UserName( &bstrUserDN );

        // ADSystemInfo object no longer needed
        if ( pobjADSysInfo )
            pobjADSysInfo->Release();

        // Prefix the users DN with the ADSI provider string to 
        // form a ADsPath
        _bstr_t bstrUserADsPath = 
            _bstr_t(L"LDAP://") + _bstr_t(bstrUserDN);

        // Free allocated BSTR
        SysFreeString( bstrUserDN );

        // Display information
        _tprintf( _T("Binding to object at %s\n"), 
            (const char *)bstrUserADsPath );         // Get a pointer to the object using the IADsUser interface
        IADsUser *pobjIADsUser;
        hResult = ADsGetObject( bstrUserADsPath, IID_IADsUser, 
            (void**) &pobjIADsUser );

        // Ensure binding success before dereferencing pointer
        if ( SUCCEEDED( hResult ) )
            {
            // Retrieve Name property into BSTR variable
            BSTR bstrPropertyValue;
            hResult = pobjIADsUser->get_Name( &bstrPropertyValue );

            if ( SUCCEEDED( hResult ) )
                {
                // Display BSTR variable - note %S to denote wide string
                _tprintf( _T("Name: %S \n"), bstrPropertyValue );
                }

            // Display current phone number
            _variant_t varPropertyValue;
            hResult = pobjIADsUser->get_TelephoneNumber( 
                &varPropertyValue );

            // Always check the result in case property doesn't exist
            if ( SUCCEEDED( hResult ) )
                {
                // Display a variant string, converting to 
                // null-terminated string
                _tprintf( _T("Current phone number is %s \n"), 
                    (const char *) _bstr_t( varPropertyValue ) );
                }
            else    // get_TelephoneNumber failed
                {
                // Possibly error 0x8000500D (E_ADS_PROPERTY_NOT_FOUND)
                _tprintf( 
                    _T("Could not access TelephoneNumber property.\n") );

                // Display the error code in decimal and hex forms
                _tprintf( _T("Error number %d (0x%X) \n"), 
                    hResult, hResult );
                }
            // Prompt for input
            _tprintf( _T("Type new phone number and press Enter: ") );

            // Get new number from console
            TCHAR  szPhoneNumber[81];
            _getts ( szPhoneNumber );

            // Copy the null-terminated string into the variant
            varPropertyValue = _variant_t( szPhoneNumber );

            // Write property back to the cache
            hResult = pobjIADsUser->put_TelephoneNumber( 
                varPropertyValue );

            if ( SUCCEEDED( hResult ) )
                {
                // Commit change to the directory using SetInfo
                hResult = pobjIADsUser->SetInfo();

                if ( SUCCEEDED( hResult ) )
                    {
                    _tprintf( _T("Change committed.\n") );
                    }
                }
            // Object no longer needed
            if ( pobjIADsUser )
                pobjIADsUser->Release();
            }
        }
    // Unload COM
    CoUninitialize();

    // Return the result of any failures
    return hResult;
}

Listing 6-3 ReadWrite.cpp shows how to write attribute values in C++.

Something interesting about this example is the different data types used for the Name and TelephoneNumber properties. Many interfaces use binary strings for property values, including all the IADs properties. However, the TelephoneNumber property uses a variant array. It does this because under other providers, the TelephoneNumber property might be an array of all the phone numbers for that particular user. In the case of the ADSI LDAP provider and Active Directory, all the IADsUser phone number properties (FaxNumber, TelephoneHome, TelephoneMobile, TelephoneNumber, and TelephonePager) return a variant array, but with a single string element.

You can't write to all the ADSI properties. As mentioned earlier in this chapter, all the IADs properties, such as Name, ADsPath, and so on are read-only and cannot be changed. They represent fixed information about the object.

The Put Method

Using the named properties of the ADSI interfaces is simple, really, and it's no different from using any other COM-based component, such as ADO or Collaboration Data Objects (CDO), that you might be familiar with.

Updating the values of the Active Directory attributes that aren't mapped to ADSI interfaces is just slightly different from the samples in the previous section. In this case you use the Put method to write new values, as shown in the following code, from the PutMethod.bas sample on the companion CD.

 ` Read the telephone number property
strPhoneNumber = objADs.Get("telephoneNumber")
...
` Write the new value to the object
objADs.Put "telephoneNumber", strPhoneNumber

Once again, updating values for Active Directory attributes that don't correspond to ADSI interfaces is more complex with C or C++, but conceptually it's the same. The following code, from the PutMethod.cpp sample on the companion CD, shows how to use the Put method in C++.

 // Get current telephone number for this user
_variant_t varPropertyValue;
hResult = pobjIADs->Get( L"telephoneNumber", &varPropertyValue );
...
// Write property back to the cache using the Put method
hResult = pobjIADs->Put( L"telephoneNumber", varPropertyValue );

Parentheses and Visual Basic

Visual Basic and thus VBScript have a quirk regarding the use of parentheses. Basically, parentheses are required when passing parameters to a function when the function is called as part of an expression or assignment. When you do not need to check or save the return value, parentheses are not allowed. If you want your code to be consistent with regard to parentheses, use the Call statement. For example:

 ` Parentheses not allowed
objADs.Put "telephoneNumber", strPhoneNumber
` Parentheses required
Call objADs.Put("telephoneNumber", strPhoneNumber)

The SetInfo Method

In the previous Visual Basic and C++ samples, you'll notice that the SetInfo method is called. After you've finished updating an object, you must call the SetInfo method to save your changes to the directory.

Instead of going to the directory with each update request, changes made using ADSI properties and the Put method are simply made to the local property cache. You can then make bulk changes to an object without incurring a round-trip performance penalty. ADSI marks each property entry in the cache as being changed or not, so only the values that have changed will be written to the directory when you call SetInfo.

Property value changes are not saved to the directory until there is a successful call to the SetInfo method.

If you explicitly call GetInfo, the current values of the object's properties will be fetched from the directory. This is by design, since you might want to refresh the cache with the latest values and flush any uncommitted changes. However, when the cache is refreshed, any changes not saved will be overwritten. The following code writes a new value to the telephoneNumber attribute, but the change is lost when GetInfo is called.

 Set objADs = GetObject(strADsPath)

` Write the new value to the object
objADs.Put "telephoneNumber", "800-555-1212"

` Explicit GetInfo
Call objADs.GetInfo()

` Display number – will show old number, not 800-555-1212
MsgBox objADs.TelephoneNumber


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