PRB: Retrieving Properties with OLE DB May Cause Memory Leak (293349)



The information in this article applies to:

  • Microsoft OLE DB 2.0
  • Microsoft OLE DB 2.1
  • Microsoft OLE DB 2.5
  • Microsoft OLE DB 2.6
  • Microsoft Data Access Components 2.7

This article was previously published under Q293349

SYMPTOMS

Whenever an OLE DB consumer application retrieves properties from the provider, it is the responsibility of the consumer application to free the memory associated with the property structures that the provider returns.

In some cases, however, freeing the property structures alone, as described in the OLE DB documentation, is not sufficient. Retrieving and freeing the properties will cause a memory leak in this case.

CAUSE

The DBPROPSET structures returned to the client by the provider contain a pointer to an array of DBPROP structures. The DBPROP structures have a VARIANT member called vValue. Because the vValue member could contain a pointer to a BSTR or other allocated memory on output, the vValue member should be cleared by the consumer application by calling the VariantClear function. The OLE DB documentation does not specify this.

RESOLUTION

Call VariantClear on the vValue member of every DBPROP structure in every DBPROP array before freeing the DBPROP array.

STATUS

This behavior is by design.

MORE INFORMATION

The following OLE DB functions return property information to the consumer:
  • IDBProperties::GetProperties
  • IRowsetIndex::GetIndexInfo
  • ICommandProperties::GetProperties
  • IRowsetInfo::GetProperties
  • ISessionProperties::GetProperties

Steps to Reproduce Behavior

The following sample code demonstrates the code that will leak memory:
#include <oledb.h>
#include <oledberr.h>

#include <msdasc.h>
#define DBINITCONSTANTS
#include <msdasc.h>

int main(int argc, TCHAR * argv[])
{
	IDataInitialize*	pIDataInitialize = NULL;
	IDBInitialize*		pIDBInitialize = NULL;
	IDBProperties*		pIDBProperties = NULL;
	
	int					iLoopMax = 10000;
	int					iLoop;
	UINT				iSet;
	
	DBPROPSET*			pPropsets = NULL;
	unsigned long		cPropertyIDSets = 0 ;
	unsigned long		cPropertySets = 0 ;
	DBPROPIDSET         rgPropertyIDSets[1];
	
	HRESULT hr;
	//If needed, change the connection string here.
	OLECHAR* wszInitString = L"Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=pubs;Data Source=(local);";
	
	hr = CoInitialize(NULL);
	
	hr = CoCreateInstance( CLSID_MSDAINITIALIZE, 
		NULL, 
		CLSCTX_INPROC_SERVER, 
		IID_IDataInitialize, 
		( void ** ) & pIDataInitialize );
	
	hr = pIDataInitialize->GetDataSource(NULL, CLSCTX_INPROC_SERVER, wszInitString,
		IID_IDBInitialize, ( IUnknown ** ) & pIDBInitialize );
	
	hr = pIDBInitialize->Initialize();

	hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void**) &pIDBProperties);

	rgPropertyIDSets[0].rgPropertyIDs		= NULL ;
	rgPropertyIDSets[0].cPropertyIDs		= 0 ;
	rgPropertyIDSets[0].guidPropertySet		= DBPROPSET_DATASOURCE;
	
	for (iLoop = 0; iLoop < iLoopMax; iLoop++)
	{
		hr = pIDBProperties->GetProperties	( cPropertyIDSets,
												rgPropertyIDSets,
												&cPropertySets,
												&pPropsets) ;
		
		for (iSet = 0; iSet < cPropertySets;  iSet++ )
		{	
			DBPROP *  pr = pPropsets[iSet].rgProperties; 

			//NOTE :Uncomment the following lines to remove the memory leak
			//int iProp;
			//for (iProp = 0; iProp < pPropsets[iSet].cProperties;iProp++)
			//{
			//	VariantClear( &(pr[iProp].vValue) );
			//}

			CoTaskMemFree(pr);
		}
		CoTaskMemFree(pPropsets);		
		Sleep(1);
	}

	hr = pIDBInitialize->Uninitialize();
	
	if (pIDBProperties)
	{
		pIDBProperties->Release();
	}
	
	if (pIDBInitialize)
	{
		pIDBInitialize->Release();
	}

	if (pIDataInitialize)
	{
		pIDataInitialize->Release();
	}
	
	return 0 ;
}
				
To free the memory correctly, uncomment the loop in the code above to free each individual VARIANT member before freeing the DBPROP array.

Modification Type:MajorLast Reviewed:5/12/2003
Keywords:kbConsumer kbDatabase kbprb KB293349