FIX: Handle Leak when Passing ADO Objects Between Processes (197426)



The information in this article applies to:

  • ActiveX Data Objects (ADO) 2.0

This article was previously published under Q197426

SYMPTOMS

When marshalling an ActiveX Data Objects (ADO) recordset between processes the handle count incrementally increases for each call made to the out-of- process component. This behavior only occurs if the rowset contains more than 8K of data.

CAUSE

This is a problem in ActiveX Data Objects Version 2.0.

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

This problem has been fixed in Microsoft ActiveX Data Objects versions 2.1 and later.

You can install the latest version of Microsoft Data Access Components from the following Web site:

MORE INFORMATION

Steps to Reproduce the Behavior

NOTE: The following steps assume that you are using Visual Basic 5.0 or later.
  1. Build an ActiveX .EXE that creates a disconnected recordset with ADO 2.0.
    1. In Visual Basic, create a new ActiveX EXE project named rsEXE.
    2. Rename the default Class module to rs.
    3. Add a reference to the Microsoft ActiveX Data Objects Library.
  2. Use the following SQL statement against the Pubs (SQL Server 6.5) database to produce a recordset that contains more than 8K of data:
    (SELECT authors.*, roysched.*, titleauthor.*, titles.*, publishers.*
    FROM titleauthor INNER JOIN authors ON titleauthor.au_id =
    authors.au_id INNER JOIN titles ON titleauthor.title_id =
    titles.title_id INNER JOIN roysched ON titles.title_id =
    roysched.title_id INNER JOIN publishers ON titles.pub_id =
    publishers.pub_id).
    					
  3. Pass the recordset as the return value of a function in the .exe.
    1. Here is sample code for a function that returns an ADODB recordset, using the SQL above. Paste this sample code into the rs Class Module's code window:
      Public Function GetRs() As ADODB.Recordset
         Dim cn As ADODB.Connection
         Dim Rs As ADODB.Recordset
         Dim strsql As String
      
         Set cn = New ADODB.Connection
         cn.Open "dsn=pubs"
         Set Rs = New ADODB.Recordset
         strsql = "(SELECT authors.*, roysched.*, titleauthor.*, titles.*, " & _
           "publishers.* FROM titleauthor INNER JOIN authors ON " & _
           "titleauthor.au_id = authors.au_id INNER JOIN titles ON " & _ 
           "titleauthor.title_id =  titles.title_id INNER JOIN roysched ON " & _
           "titles.title_id =  roysched.title_id INNER JOIN publishers ON " & _
           "titles.pub_id =  publishers.pub_id)"
      
         Rs.CursorLocation = adUseClient
         Rs.LockType = adLockBatchOptimistic
         Rs.Open strsql, cn
         Set Rs.ActiveConnection = Nothing
         Set GetRs = Rs
      
         cn.Close
         Set cn = Nothing
        
      End Function
      						
    2. Compile the ActiveX EXE to create rsEXE.EXE.
  4. Create a client application that creates an instance of the .exe and calls the recordset returning function from the .exe.
    1. In Visual Basic, create a new Standard EXE project.
    2. Set a reference to the Microsoft ActiveX Data Objects Library and to the rsEXE ActiveX EXE.
  5. It is important to maintain the scope of the .exe between calls.
  6. Repeat the call process to the .exe several times. Here is sample code that repeatedly calls a function that returns an ADO recordset from an ActiveX EXE named rsEXE, within the scope of a single procedure. Paste the sample code into the code window for the Form Load event of Form1:
    Dim rs1 As ADODB.Recordset
       Dim rs2 As ADODB.Recordset
       Dim rs3 As ADODB.Recordset
    
       Set rs1 = New ADODB.Recordset
       Set rs2 = New ADODB.Recordset
       Set rs3 = New ADODB.Recordset
    
       Dim gs As RsEXE.rs
       Set gs = New RsEXE.rs
     
      'Add rsEXE to Performance Monitor before executing the next line
    
       Set rs1 = gs.GetRs
       Set rs2 = gs.GetRs
       Set rs3 = gs.GetRs
    
       rs1.Close
       rs2.Close
       rs3.Close
    
       Set rs1 = Nothing
       Set rs2 = Nothing
       Set rs3 = Nothing
    
       Set gs = Nothing
    					
  7. Step through the client application:
    1. After creating the ActiveX EXE object, but before calling the function that returns a recordset, open Windows NT Performance Monitor.
    2. In Performance Monitor, choose Edit, Add to Chart.
    3. For Object, choose Process.
    4. For Counter, choose Handles.
    5. For Instance, choose the now running ActiveX EXE.
    6. Return to stepping through the client application. While stepping through the client, check the handle count for the ActiveX EXE in Performance Monitor. You will see the handle count increase by 2(+/-1) for each call you make to the component.

Modification Type:MinorLast Reviewed:9/22/2005
Keywords:kbHotfixServer kbQFE kbADO200fix kbADO210fix kbADO260fix kbbug kbDatabase kbfix kbMDAC210fix kbMDACNoSweep kbQFE KB197426