ADO provides a great deal of flexibility. There are several ways to accomplish the same task, which is generally to return a recordset with information from Active Directory. The recordset contains attribute values for objects satisfying the search filter.

In the example on the previous page an ADO Connection object is used to establish a connection to Active Directory. This Connection object is assigned to the ActiveConnection property of an ADO Command object. Then the LDAP syntax query is assigned to the CommandText property of the Command object. Finally, the Execute method of the Command object returns the ADO Recordset object.

The ADO Command object is used because we can assign values to several useful properties. In particular, we can assign a value to the "Page Size" property. Any value up to a maximum of 1000 can be assigned to this property. The value assigned is less important than the fact that a value is assigned, since this turns on "paging". With paging turned on, ADO returns rows in pages until all rows satisfying the query are retrieved. Without paging, ADO will stop after 1000 rows.

Tests were performed using different values for "Page Size" to see if any difference in performance could be detected. The tests involved a query for all users in the domain. The test domain has over 2100 user objects. The test was repeated with "Page Size" values of 100, 200, 400, 600, 800, and 1000. The differences were very small, but perhaps the optimal value in this case was 200. The tests were performed using both VBScript and PowerShell.

Another ADO Command object property often assigned is the "Timeout" property. This specifies the timeout value for the query in seconds. Also, queries will be more efficient if we assign "False" to the "Cache Results" property.

An alternative approach is to assign the query to the Source property of an ADO Recordset object. The Open method of the Recordset object is used to run the query. No Command object is used. The Connection object is assigned to the ActiveConnection property of the Recordset object. This approach is bit more straightforward. For example, the same program given earlier can be coded as follows:

Option Explicit
Dim adoConnection, strBase, strFilter, strAttributes
Dim objRootDSE, strDNSDomain, strQuery, adoRecordset, strName, strCN

' Setup ADO objects.
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
Set adoRecordset = CreateObject("ADODB.Recordset")
Set adoRecordset.ActiveConnection = adoConnection

' Search entire Active Directory domain.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strBase = "<LDAP://" & strDNSDomain & ">"

' Filter on user objects.
strFilter = "(&(objectCategory=person)(objectClass=user))"

' Comma delimited list of attribute values to retrieve.
strAttributes = "sAMAccountName,cn"

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

' Run the query.
adoRecordset.Source = strQuery
adoRecordset.Open

' Enumerate the resulting recordset.
Do Until adoRecordset.EOF
    ' Retrieve values and display.
    strName = adoRecordset.Fields("sAMAccountName").Value
    strCN = adoRecordset.Fields("cn").value
    Wscript.Echo "NT Name: " & strName & ", Common Name: " & strCN
    ' Move to the next record in the recordset.
    adoRecordset.MoveNext
Loop

' Clean up.
adoRecordset.Close
adoConnection.Close

Because the Recordset object is created before the recordset is opened, you can assign a value to the cursorType property of the Recordset object. This can be useful if you want to use a cursor other than the default forward only cursor. This would be necessary, for example, if you want to retrieve the value of the RecordCount property of the Recordset object, and then enumerate the recordset. The RecordCount property is the number of rows in the recordset. Retrieving the value of this property requires reading the entire recordset, which leaves the cursor at the end of the recordset. You must move the cursor back to the beginning with the MoveFirst method before enumerating the recordset, but this cannot be done with the default forward only cursor. The following code snippet demonstrates how this is done by assigning a value to the cursorType property of the Recordset object before it is opened.

Const adOpenStatic = 3

Set adoRecordset = CreateObject("ADODB.Recordset")
Set adoRecordset.ActiveConnection = adoConnection
' Assign cursorType that allows forward and backward movement.
adoRecordset.cursorType = adOpenStatic

' Run the query.
adoRecordset.Source = strQuery
adoRecordset.Open

' Display number of records.
' This positions the cursor at the end of the recordset.
Wscript.Echo adoRecordset.RecordCount

' Move the cursor back to the beginning.
' The cursorType assignment allows this.
adoRecordset.MoveFirst

' Enumerate the resulting recordset.
Do Until adoRecordset.EOF
    ' Retrieve values and display.
    strName = adoRecordset.Fields("sAMAccountName").Value
    strCN = adoRecordset.Fields("cn").value
    Wscript.Echo "NT Name: " & strName & ", Common Name: " & strCN
    ' Move to the next record in the recordset.
    adoRecordset.MoveNext
Loop

' Clean up.
adoRecordset.Close

The disadvantage is that you cannot assign values to the Command object properties. In particular, you cannot turn on paging. This is a problem if the recordset has more than 1000 rows. To handle this situation, you can instead assign a value to the cursorLocation property of the Connection object. If the cursorLocation property of the Connection object is adUseClient, then the default cursorType of any Recordset object using this Connection object will be adOpenStatic. This allows us to declare an ADO Command object, assign values to the Command object properties like "Page Size", and use the Execute method of the Command object to create the recordset. This is necessary if you need a cursor that allows movement forward and backward and you need to retrieve more than 1000 rows. The following demonstrates how this is done.

Option Explicit
Dim adoCommand, adoConnection, strBase, strFilter, strAttributes
Dim objRootDSE, strDNSDomain, strQuery, adoRecordset, strName, strCN

Const adUseClient = 3

' Setup ADO objects.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.cursorLocation = adUseClient
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection

' Search entire Active Directory domain.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strBase = "<LDAP://" & strDNSDomain & ">"

' Filter on user objects.
strFilter = "(&(objectCategory=person)(objectClass=user))"
' Comma delimited list of attribute values to retrieve.
strAttributes = "sAMAccountName,cn"

' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 100
adoCommand.Properties("Timeout") = 30
adoCommand.Properties("Cache Results") = False

' Run the query.
Set adoRecordset = adoCommand.Execute

' Display number of records.
' This positions the cursor at the end of the recordset.
Wscript.Echo adoRecordset.RecordCount

' Move the cursor back to the beginning.
adoRecordset.MoveFirst

' Enumerate the resulting recordset.
Do Until adoRecordset.EOF
    ' Retrieve values and display.
    strName = adoRecordset.Fields("sAMAccountName").Value
    strCN = adoRecordset.Fields("cn").value
    Wscript.Echo "NT Name: " & strName & ", Common Name: " & strCN
    ' Move to the next record in the recordset.
    adoRecordset.MoveNext
Loop

' Clean up.
adoRecordset.Close
adoConnection.Close