FIX: Aggregating ADO Recordsets or Commands May Cause Memory Leak (283106)



The information in this article applies to:

  • ActiveX Data Objects (ADO) 2.0
  • ActiveX Data Objects (ADO) 2.1
  • ActiveX Data Objects (ADO) 2.5
  • ActiveX Data Objects (ADO) 2.6
  • Microsoft Data Access Components 2.0
  • Microsoft Data Access Components 2.1
  • Microsoft Data Access Components 2.5
  • Microsoft Data Access Components 2.6
  • Microsoft Visual C++, 32-bit Enterprise Edition 6.0
  • Microsoft Visual C++, 32-bit Professional Edition 6.0
  • Microsoft Visual C++, 32-bit Learning Edition 6.0

This article was previously published under Q283106

SYMPTOMS

If a Visual C++ application or dynamic-link library (DLL) aggregates ActiveX Data Objects (ADO) Recordset or Command objects, a significant memory leak can occur due to resources that are not freed when the Release function is called on the object.

This occurs in all versions of ADO prior to and including version 2.6 RTM (2.60.6526).

CAUSE

Aggregation in Recordset and Command objects is implemented by using the CComAggObject classes in Active Template Library (ATL). CComAggObject deletes itself when its reference count goes to zero, and simply deletes the Recordset or Command object as well. The Recordset or Command object's Term function is never called, which is where the clean-up of allocated resources occurs.

RESOLUTION

MDAC

To resolve this problem, obtain the latest service pack for Microsoft MDAC 2.5. For additional information, click the following article number to view the article in the Microsoft Knowledge Base:

293312INFO: How to Obtain the Latest MDAC 2.5 Service Pack

This problem was corrected in Microsoft Data Access Components 2.6 Service Pack 1.
For additional information, click the article number below to view the article in the Microsoft Knowledge Base:

300635 INFO: How to Obtain the Latest MDAC 2.6 Service Pack

The English version of this fix should have the following file attributes or later:

MDAC 2.5
   Date          Version        Size             File name
   ------------------------------------------------------------
   12/14/2000    2.53.6013.0     20,480 bytes    Msader15.dll	
   12/14/2000    2.53.6013.0    491,792 bytes    Msado15.dll	
   12/14/2000    2.53.6013.0    172,304 bytes    Msadomd.dll	
   12/14/2000    2.53.6013.0     57,616 bytes    Msador15.dll	
   12/14/2000    2.53.6013.0    188,688 bytes    Msadox.dll	
   12/14/2000    2.53.6013.0     57,616 bytes    Msadrh15.dll	   	
   12/14/2000    2.53.6013.0     94,480 bytes    Msjro.dll	
   12/22/2000                   824,744 bytes    Q283106_w2k_sp3_x86_en.exe
				

STATUS

MDAC

Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article. This problem was first corrected in Microsoft MDAC 2.5 Service Pack 3. This problem was first corrected in Microsoft Data Access Components 2.6 Service Pack 1.

Windows 2000

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

MORE INFORMATION

This behavior is likely to be seen only when the application has been written in Visual C++, and when the developer deliberately aggregates Recordset or Command objects. This behavior does not occur in Microsoft Visual Basic applications, because aggregation is not possible from Visual Basic.

The MDAC 2.5 version of this hotfix can be applied over both 2.5 RTM (2.50.4403) and 2.5 SP1 (2.50.5303).

Steps to Reproduce Behavior

  1. Paste the following code into a new Visual C++ console application, and then compile and run the code.
    #include "objbase.h"
    #import "c:\Program Files\Common Files\System\ado\msado15.dll" no_namespace rename( "EOF", "adoEOF" )
    
    struct InitOle {
      InitOle()  { ::CoInitialize(NULL); }
      ~InitOle() { ::CoUninitialize();   }
    } _init_InitOle_; 
    
    
    
    class MyClass : public IUnknown
    {
    public:
    	MyClass()
    	{
    		m_refCount = 0;
    	}
    
    	STDMETHODIMP_(ULONG) AddRef()
      {
    		m_refCount++;
    		return m_refCount;
      }
    
    	STDMETHODIMP_(ULONG) Release()
    	{
    		m_refCount--;
    		return m_refCount;
      }
    
    	STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    	{
    		if (riid == IID_IUnknown) {
    			*ppv = static_cast<IUnknown *> (this);
    			AddRef();
    			return S_OK;
    		}
    
    		return E_NOINTERFACE;
    	}
    
    private:
    	ULONG m_refCount;	
    };
    
    
    int main(int argc, char* argv[])
      {
    	MyClass cls;
    	HRESULT hr;
    	IUnknown *pUnkInner;
    
    	CoInitialize(NULL);
    	cls.AddRef();
    
    	for (int i=0; i<10000; i++)
    	{
    		//while (true) {
    		pUnkInner = NULL;
    		hr = CoCreateInstance(__uuidof(Recordset), &cls, CLSCTX_INPROC_SERVER, IID_IUnknown,
    			(void **) &pUnkInner);
    		pUnkInner->Release();
    	}
    	return 0;
    }
    					
  2. Start Performance Monitor, and select the Private Bytes counter for the running executable. Note that there is an increasing amount of memory allocated that is never freed.

Modification Type:MinorLast Reviewed:9/26/2005
Keywords:kbHotfixServer kbQFE kbMDAC250SP3fix kbbug kbfix kbMDAC260sp1Fix KB283106