Reading Attributes

ADSI makes getting the value of an object's attribute easy. The following code retrieves the cn (Common-Name) attribute using the Name property of the IADs interface.

 strCommonName = objADs.Name 

Using C++, you would do the same thing with this code:

 hResult = pobjIADs->get_Name ( &bstrName ); 

In both cases, I'm using the Name property of the IADs interface. IADs is the base interface that applies to all ADSI objects, so regardless of the type of object, you can always call the IADs properties and methods. Table 6-3 and Table 6-4 list the properties and methods for IADs. Note that all the properties of the IADs interface are read-only; you can't change their value.

IADs Property Returned Data Type Description

ADsPath

String

The ADsPath of the object

Class

String

The name of the object's class

GUID

String

The GUID of the object as a string of two-digit hexadecimal characters

Name

String

The relative distinguished name (RDN) of the object

Parent

String

The ADsPath for the parent of the object

Schema

String

The ADsPath of the class object for this object in the schema

Table 6-3 Properties of the IADs interface.

IADs Method Description

GetInfo

Retrieves all the attributes of the object from the directory and places them in the local property cache

SetInfo

Saves changes made to the object to the directory

Get

Retrieves the value of the property named

Put

Sets the value of the property named

GetEx

Retrieves the value or values of the property named and returns them as a variant array

PutEx

Adds, deletes, clears, or updates a value or values of the property named

GetInfoEx

Retrieves the values of the property named from the directory, overwriting the current cached values

Table 6-4 Methods of the IADs interface.

The following code is from a Visual Basic sample named IADsProperties that's included on the companion CD. This code binds to an object, reads the values of the IADs properties, and adds them to a list box.

 ` Bind to the object
Set objADsUser = GetObject(strADsPath)
` Gather the property values in a concatenated string
lstBox.AddItem "ADsPath:" & vbTab & objADsUser.ADsPath
lstBox.AddItem "Name:   " & vbTab & objADsUser.Name
lstBox.AddItem "Class:  " & vbTab & objADsUser.Class
lstBox.AddItem "GUID:   " & vbTab & objADsUser.Guid
lstBox.AddItem "Schema: " & vbTab & objADsUser.Schema
lstBox.AddItem "Parent: " & vbTab & objADsUser.Parent

When you run the code in the Visual Basic version of the IADsProperties sample, you should see something similar to Figure 6-1.

Figure 6-1 Output of the Visual Basic IADsProperties sample.

Accessing properties using C or C++ is the same in concept, but slightly different because of the nature of COM. You access interface properties using a method call that uses get_ and the property name, as shown in the following code from the C++ version of IADsProperties.

 // Get pointer to the user's object
IADs *pobjIADs;  // Pointer to object interface
hResult = ADsGetObject( 
    bstrUserADsPath,    // ADsPath of object
    IID_IADs,           // IID of interface requested
    (void**) &pobjIADs  // Pointer to interface
);
// Check for binding success
if ( SUCCEEDED ( hResult ) )
    {
    //------------------------------------------------
    // Retrieving IADs Properties
    //------------------------------------------------
    // ADSI allocates the storage for the binary string that
    // is returned for all IADs properties.  Declare variable 
    // to string, must free on exit using SysFreeString()
    BSTR bstrPropertyValue;
    // ADsPath property 
    hResult = pobjIADs->get_ADsPath ( &bstrPropertyValue );
    if ( SUCCEEDED ( hResult ) )
        // Display property value
        _tprintf( _T("ADsPath: %S\n"), bstrPropertyValue);
    // Name property
    hResult = pobjIADs->get_Name ( &bstrPropertyValue );
    if ( SUCCEEDED ( hResult ) )
        // Display property value
        _tprintf( _T("Name: %S\n"), bstrPropertyValue);
    // Class property
    hResult = pobjIADs->get_Class ( &bstrPropertyValue );
    if ( SUCCEEDED ( hResult ) )
         // Display property value
         _tprintf( _T("Class: %S\n"), bstrPropertyValue );
    // GUID property
    hResult = pobjIADs->get_GUID ( &bstrPropertyValue );
    if ( SUCCEEDED ( hResult ) )
        // Display property value
        _tprintf( _T("GUID: %S\n"), bstrPropertyValue );
    // Schema property
    hResult = pobjIADs->get_Schema ( &bstrPropertyValue );
    if ( SUCCEEDED ( hResult ) )
        // Display property value
        _tprintf( _T("Schema: %S\n"), bstrPropertyValue );
    // Parent property
    hResult = pobjIADs->get_Parent ( &bstrPropertyValue );
    if ( SUCCEEDED ( hResult ) )
        // Display property value
        _tprintf( _T("Parent: %S\n"), bstrPropertyValue );
    // Free string
    SysFreeString ( bstrPropertyValue );

When you run the code in the C++ version of the IADsProperties sample, you should see something similar to Figure 6-2.

Figure 6-2 Output of the C++ IADsProperties sample.

Comparing the Visual Basic and C++ versions of the IADsProperties sample, it's clear why Visual Basic has become such a popular application development platform. Visual Basic reduces some of the complexities of COM and takes care of some of the error checking, which allows for more intuitive programming.

The Get Method

As I mentioned earlier, not all the attributes that are stored with an object are easily accessible from a named property on an ADSI interface. How do you retrieve those attributes?

The IADs interface provides the Get method for retrieving any named attribute. The Get method takes a string attribute name as input and returns the value of the attribute as a variant.

Here is how you would use the IADs Get method in Visual Basic to retrieve the distinguishedName attribute of an object:

 strDN = objADs.Get("distinguishedName") 

In C and C++, the call to the Get method would be as follows:

 _variant_t var;
hResult = pobjIADs->Get( L"distinguishedName", &var );

You can use the Get method to retrieve any of the attributes associated with an object, as long as you know the name of the attribute. In Chapter 7, I'll show you ways to enumerate all the properties of an object.

The Get method is similar in appearance but different from the COM convention of using get_PropertyName.

Handling Errors in ADSI

ADSI is distinct among the various object-oriented technologies I've worked with in that accessing an attribute that has no value generates an Automation error. This error occurs because of the nature of the LDAP data model. To understand this point better, think of an Active Directory object simply as a collection of values, not attributes. Even if a particular attribute is allowed for a particular object, if the attribute has no value, the object has no knowledge of it. Thus, ADSI reports a "not found" error.

Specifically, if a call to the Get method (or to the GetEx method, which I'll describe in more detail later in the chapter) is asked to retrieve a property that contains no value, ADSI will return a COM result value of 0x8000500D (hexadecimal) or –2147463155 (decimal). The description supplied for this error is "The Active Directory property cannot be found in the cache." This simply means that ADSI checked the local property cache for the attribute value and did not find it. If the cache is empty, ADSI will automatically attempt to refresh the cache and check again.

This error is not a serious one; it just indicates that the particular attribute did not contain a value in the current object. It does not prevent you from assigning a value, which I'll discuss later in the chapter. However, since an error is generated by ADSI and Active Directory, you need to handle it in your code to prevent unexpected problems for a user.

Visual Basic developers can simply use the On Error Resume Next statement to bypass any errors. For example, if the Get method fails in the code shown here, code execution will resume at the next line:

 On Error Resume Next
strTelephone = objADsUser.Get("telephoneNumber")
Debug.Print strTelephone

In this code, if the user doesn't have a telephone number listed in the telephoneNumber attribute, Visual Basic will trap the error and proceed with the Debug.Print statement.

This approach is handy for dealing with errors such as the "not found" error. In the IADsProperties sample application on the companion CD, I use another method, which activates error handling and directs Visual Basic to go to the ErrorHandler line whenever an error occurs. The ErrorHandler block performs a check of the Err object and calls a display routine if an error occurred. By placing a handler like this at the end of the function, you can quickly exit the function whenever one occurs. As a final step, you can restore normal Visual Basic error handling by using the On Error GoTo 0 statement.

Here is a code from the Visual Basic IADsProperties sample that traps the error and calls a function named DisplayError to display more information:

 Public Function ...
On Error GoTo ErrorHandler
    ` (ADSI code)
ErrorHandler:
`================================================
` Check for errors
`================================================
If Err.Number <> 0 Then
    ` Display error message
    Call DisplayError
End If
` Turn off error checking
On Error GoTo 0
End Function

Here is the small but handy DisplayError function to display error information and get a response from the user. If the user chooses the Cancel button, the application exits. This function uses the Err object properties to gather the error information, including a description and source module.

 Public Function DisplayError()
`================================================
` Display Error message box
`================================================
Dim strError As String
Dim nAction As Long
` Build string with error information
strError = Err.Description & vbNewLine
strError = strError & "Number: " & vbTab & Hex(Err.Number) & _
    " (" & Err.Number & ")" & vbNewLine
strError = strError & "Source: " & vbTab & Err.Source & vbNewLine
` Display alert and get response
nAction = MsgBox(strError, vbExclamation + vbOKCancel, "Error")
If nAction = vbCancel Then
    ` Quit application
    Unload frmMain
End If
End Function

C and C++ programmers can use a COM-supplied macro to check the result code of a property or method call. The SUCCEEDED macro takes a COM result code, usually an HRESULT, and evaluates to either True or False depending on the result. In the C++ code sample, I used the SUCCEEDED macro as shown here:

 // Use Get method to retrieve attribute
hResult = pobjIADs->Get( L"distinguishedName",  &varDN );
if ( SUCCEEDED ( hResult ) )
    {
    // Extract the string from the variant
    BSTR bstrDN = _bstr_t(varDN);
    // Display attribute value
    _tprintf( _T("%S\n"), bstrDN );
    }
else
    // Error getting value, display
    _tprintf( _T("Error getting value: %S\n"), hResult );

To check for failure, you could use the FAILED macro, which evaluates to True if the method call resulted in an error.

All the ADSI errors are defined in the Adserr.h header file that is automatically included from the ActiveDS.h header file. In addition, other important error values, including the Win32 API result codes, are kept in the Winerror.h and Lmerr.h header files. Many LDAP-based errors are defined in the Winldap.h header file.

For simplicity and explanation purposes, many of the samples shown in this book do not include graceful handling of errors, both common and esoteric. All applications, and particularly networking applications, should be able to handle errors with ease, possibly offering the user information and choices about how to proceed when unexpected events occur.

Properties and Attributes Revisited

Once again I'd like to emphasize the distinction between LDAP attribute names and ADSI property names. In the following example, the first line of code works, but the second returns an error:

 varValue = objADs.Guid           ` Works
varValue = objADs.Get("GUID")    ` Does not work

The first line of code works because there is an IADs property named GUID. An error occurs in the second line because there is no attribute named GUID in the directory. There is an attribute named objectGUID and that is what the IADs GUID property maps to. Thus, the following two lines are nearly equivalent. They are "nearly" equivalent because the first line returns the GUID as a string and the second line returns the GUID as an octet string. (For more information on octet stings, see the section "GUID Binding" in Chapter 4.)

 varValue = objADs.Guid
varValue = objADs.Get("objectGUID")

Here is an interesting exception. If you try the following code, it does execute without an error, but each line produces different results:

 Debug.Print "Name property: " & objADs.Name
Debug.Print "Name attribute: " & objADs.Get("Name")

The output is something like the following:

 Name property:  CN=Administrator
Name attribute: Administrator

The previous code executes without an error because most objects in Active Directory have a name attribute, which is different from the Name property supplied by IADs. Code in ADSI determines what the RDN of an object is and returns that as the Name property. The name attribute is usually the same as the cn (Common-Name) attribute.

Table 6-5 shows the mapping between IADs properties and associated Active Directory LDAP attributes. To illustrate this, the table includes real-world values from an object of the domain class, which is the root of the domain's directory partition. For a code sample that outputs these property and attribute mappings, see the PropertyMapping folder on the companion CD.

IADs Property LDAP Attribute Description

AdsPath
"LDAP://DC=coppersoftware, DC=com" 

DistinguishedName
"DC=coppersoftware, DC=com"

The ADsPath property also includes the provider string,whereas the distinguished name of an object does not.

Class
"domainDNS"

ObjectClass
[ "top", "domain", "domainDNS" ]

The objectClass attribute actually contains multiple values (discussed later in this chapter). ADSI uses the last value for the Class property.

GUID
"adff8b88f6ed0a429b- 0d402ac63bf704"

ObjectGUID
[ ad, ff, 8b, 88, f6, ed, 0a, 42, 9b, 0d, 40, 2a, c6, 3b, f7, 04 ]

The objectGUID attribute returns an array of binary values, shown in hexadecimal. The GUID property returns a string.

Name
"DC=coppersoftware"

None

The Name property returns the RDN of the object, which is created dynamically by Active Directory.

Parent
"LDAP://DC=com"

None

The ADSI Parent property dynamically creates the ADsPath property of the parent object.

Schema
"LDAP://schema/domainDNS"

ObjectCategory
"CN=Domain-DNS, CN=Schema,CN=Co nfiguration,DC=coppersoft ware,DC=com"

The Schema property uses an ADsPath format, whereas the objectCategory attribute uses a distinguished name format.

Table 6-5 Mapping IADs properties to LDAP attributes. Values shown in quotation marks are binary strings; values enclosed in brackets are variant arrays.



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