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