ADO is an efficient way to query Active Directory for attribute values. However, it is often used in situations where you could also use the IADsNameTranslate interface. For example, converting the NT format name of one or more objects into the corresponding Distinguished Name. In these cases, the IADsNameTranslate interface is always faster.

To compare the relative performance of the IADsNameTranslate interface and ADO, an array of 25 NT format names was coded. The task was to convert these 25 names into Distinguished Names. Four methods were coded to perform this task and the amount of time required for each was compared.

Method 1 was to use ADO to search Active Directory for the object that has each NT format name and retrieve the Distinguished Name. Since there are 25 names in the array, Active Directory was queried 25 times and a recordset with one row, and one Distinguished Name, was returned 25 times. Ignoring the part of the code where the ADO objects were setup and the array of NT names was populated, the crucial loop was as follows:

lngStart = Timer()
For Each strNTName In arrNames
    ' Filter on object with specified NT name.
    strFilter = "(sAMAccountName=" & strNTName & ")"

    ' Construct the LDAP syntax query.
    strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
    adoCommand.CommandText = strQuery

    ' Run the query.
    Set adoRecordset = adoCommand.Execute

    ' Enumerate the resulting recordset.
    Do Until adoRecordset.EOF
        ' Retrieve values.
        strDN = adoRecordset.Fields("distinguishedName").Value
        ' Move to the next record in the recordset.
        adoRecordset.MoveNext
    Loop

    adoRecordset.Close
Next
lngEnd = Timer()

Wscript.Echo "Seconds: " & FormatNumber((lngEnd - lngStart), 7)

Method 2 was to create one ADO query for all 25 NT format names. This returned a recordset with 25 rows. Each row had the sAMAccountName and the corresponding Distinguished Name. In this case the crucial section of the code was:

' Construct filter for users specified by NT name.
strFilter = "(|"
For Each strNTName In arrNames
    strFilter = strFilter & "(sAMAccountName=" & strNTName & ")"
Next
strFilter = strFilter & ")"

' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery

lngStart = Timer()

' Run the query.
Set adoRecordset = adoCommand.Execute

' Enumerate the resulting recordset.
Do Until adoRecordset.EOF
    ' Retrieve values.
    strDN = adoRecordset.Fields("distinguishedName").Value
    strNTName = adoRecordset.Fields("sAMAccountName").Value
    ' Move to the next record in the recordset.
    adoRecordset.MoveNext
Loop
lngEnd = Timer()

Wscript.Echo "Seconds: " & FormatNumber((lngEnd - lngStart), 7)

Method 3 was to use the IADsNameTranslate interface to convert each of the 25 NT format names. The Put and Get method of the NameTranslate object were each called 25 times to retrieve the 25 Distinguished Names. Ignoring the part of the code where the NameTranslate object was setup and the array of NT names was populated, the code was:

lngStart = Timer()
For Each strNTName In arrNames
    ' Use the Set method to specify the NT format of the object name.
    On Error Resume Next
    objTrans.Set ADS_NAME_TYPE_NT4, strNetBIOSDomain & "\" & strNTName
    If (Err.Number <> 0) Then
        On Error GoTo 0
        strDN = "Unknown"
    Else
        On Error GoTo 0
        ' Use the Get method to retrieve the RFC 1779 Distinguished Name.
        strDN = objTrans.Get(ADS_NAME_TYPE_1779)
    End If
Next
lngEnd = Timer()

Wscript.Echo "Seconds: " & FormatNumber((lngEnd - lngStart), 7)

Method 4 was to use the PutEx and GetEx methods of the IADsNameTranslate interface to convert the 25 NT format names at once in an array. This resulted in one array of 25 Distinguished Names. In this case the relevant code was:

lngStart = Timer()
' Use the SetEx method to specify the NT format of the object names.
objTrans.SetEx ADS_NAME_TYPE_NT4, arrNames

' Use the GetEx method to retrieve the RFC 1779 Distinguished Names.
arrDNs = objTrans.GetEx(ADS_NAME_TYPE_1779)

lngEnd = Timer()

Wscript.Echo "Seconds: " & FormatNumber((lngEnd - lngStart), 7)

Each method was run five times on each of several computers. The time required to convert the 25 names was output. This time did not include the time required to populate the array of names, the time to setup either ADO or the NameTranslate object, or the time to output the results. The tests were run in a random order. The resulting average time in seconds (of 5 runs) to perform the task using each method on each computer is tabulated below.

Operating System RAM CPU CPU speed Method 1 Method 2 Method 3 Method 4
Windows Server 2003 768 MB Pentium III 550 MHz 0.2914 0.1891 0.1273 0.0438
Windows XP 256 MB Celeron 631 MHz 0.3875 0.1828 0.1484 0.0460
Windows 2000 Server 130 MB Pentium 635 MHz 0.4766 0.2211 0.1398 0.0469
Windows 2000 Pro 80 MB Pentium 100 MHz 2.2164 1.5727 0.2539 0.0609
Windows 95 32 MB Pentium 100 MHz 2.5695 2.2398 0.1750 0.0656

Clearly, the IADsNameTranslate interface is faster than using ADO to convert names in Active Directory, at least 2 to 3 times faster on a Domain Controller and 2.5 to 15 times faster on clients. The improvement is more dramatic on slower computers. In addition, it is faster to convert multiple names at once, whether you use ADO or IADsNameTranslate. Finally, it appears that converting names is also faster if the script is run on a Domain Controller, rather than on a client computer.

Note that method 4 of converting many names in bulk, while much faster, has one drawback compared to the other methods. If any of the NT format names in the array is invalid (the corresponding object is not found in Active Directory), the PutEx method raises an error so that none of the names are converted. With the other methods all other names are converted. When you use ADO, if the NT format name is invalid there is simply no row in the resulting recordset for that name. When you use the IADsNameTranslate object in method 3 you can trap the error, as was done in the code shown above, and continue if any name is invalid.