If you are going to use the client cursor engine then often it's a good idea to disconnect your recordset...
Disconnected Recordsets
The normal procedure for using the Client Cursor Engine is to open your recordset with client side cursors and then disconnect it from the data source. This causes the CCE to pull all of your data out of the data source and effectively marshal it by value to your client. Though the code available so far allows the use of the CCE there's no way to disconnect the recordset from your simple data object. We'll address that issue now.
What happens when we disconnect?
When you disconnect a recordset by setting the active connection to nothing what happens inside your provider is that the Cancel method is called on your command object. In fact, the Cancel method is always called when the consumer is done with the underlying data source. Thus, we can intercept the call to Cancel and use it as a trigger to release our connection to the data object. To be able to do this we need to be able to communicate between the Command object and the rowset that it creates. To achieve this communication we can add a new interface to our proxy rowset:
interface IDisconnectableRowset : IUnknown
{
HRESULT DisconnectRowset();
};
Command object as the last thing we do inside of the Execute method.if (SUCCEEDED(hr))
{
hr = pGetAsRowset->GetAsRowset(
pOurUnknown,
pUnkOuter,
riid,
pcRowsAffected,
ppRowset);
pOurUnknown->Release();
}
if (SUCCEEDED(hr))
{
hr = (*ppRowset)->QueryInterface(
IID_IDisconnectableRowset,
(void**)&m_pRowset);
}
pGetAsRowset->Release();
Command object now has a way to tell the rowset that it has been disconnected, so we can add an implementation of ICommand::Cancel() that does that.HRESULT CConversionProviderCommand::Cancel()
{
HRESULT hr = ICommandTextImpl::Cancel();
ATLTRACE2(atlTraceDBProvider, 0, "CConversionProviderCommand::Cancel\n");
if (SUCCEEDED(hr))
{
if (m_pRowset)
{
hr = m_pRowset->DisconnectRowset();
m_pRowset->Release();
m_pRowset = 0;
}
}
return hr;
}
What does the Rowset do?
I chose to make all rowsets derived from CProxyRowsetImpl be disconnectable, so that's where the implementation of IDisconnectableRowset::DisconnectRowset() lives. It's fairly simple, it just causes the rowset to release the data object that it's a proxy rowset for. If nobody else has a reference to the data object at that point then it will cease to exist, that seems pretty disconnected to me...
template <
class DataClass,
class T,
class CreatorClass,
class Storage,
class ArrayType,
class RowClass,
class RowsetInterface>
HRESULT CProxyRowsetImpl<
DataClass,
T,
CreatorClass,
Storage,
ArrayType,
RowClass,
RowsetInterface>::DisconnectRowset()
{
ObjectLock lock(this);
if (m_pDataObject)
{
m_pDataObject = 0;
}
if (m_pUnkDataObject)
{
m_pUnkDataObject->Release();
m_pUnkDataObject = 0;
}
return S_OK;
}
Download
The following source built using Visual Studio 6.0 SP3. Using the July 2000 edition of the Platform SDK. If you don't have the Platform SDK installed then you may find that the compile will fail looking for "msado15.h". You can fix this problem by creating a file of that name that includes "adoint.h".
If your system drive isn't D:\ then you'll have to change the #import statements in IGetAsADORecordsetImpl.h and IGetAsADORecordset.cpp.