ID: Q137307
1.5x WINDOWS kbinterop kbcode
The information in this article applies to:
You cannot share CDatabase or CRecordset objects among multiple clients of a 16-bit _USRDLL DLL.
The CDatabase and CRecordset classes (and classes derived from them) use ODBC to gain access to data sources. Internal to ODBC, memory is allocated to hold state information on behalf of each client calling into ODBC. This memory is allocated task-specific (that is, not using GMEM_SHARE). This means that only the client that made the call that initially caused the internal memory to be allocated can reliably use that object.
One strategy for dealing with this situation is to have each _USRDLL DLL client call an initialization function in the DLL that returns a unique number that identifies the client. Then, in subsequent calls to the DLL, have the client pass this number back to identify which client is making a call.
On the DLL side, create separate database/recordset objects for each DLL client. Use the passed identifier to know which database/recordset to operate on.
The following "Sample Code" section shows the code for a simple _USRDLL DLL that allows for multiple clients and allocates a CDatabase object for each one. It provides a function that allows the client to retrieve the driver name for the CDatabase object created on its behalf. The client can pass in a data source name or NULL if it wants the user to select a registered ODBC data source. The following functions are provided by the DLL:
Init() - Initializes the client's database object and returns an
identifier to be used in subsequent calls.
DriverName() - returns the database's driver name in a caller-supplied
buffer.
Term() - Tells the DLL that the client will not be using its services
any more.
A typical sequence of calls would be:
char buffer[20];
int nID = Init(myHwnd,NULL);
DriverName(nID,buffer,20);
MessageBox(buffer,"Driver Name");
Term(nID);
The DLL maintains an array of CDatabase objects and passes the index of
the element of the array as the return value of the Init() function.
The caller then passes this array index as a parameter in any further
calls to the DLL. The DLL uses this index to determine which database
object should be used to process the call.
/* Compile options needed: /ALw /D "_USRDLL" /GD
*/
//----------------------------------------------------------------------
// mydll.h - header file
#ifdef __cplusplus
extern "C" {
#endif
int FAR PASCAL _export Init(HWND hwnd,char* szDSN);
void FAR PASCAL _export DriverName(int nID,char* szName,int cbName);
void FAR PASCAL _export Term(int nID);
#ifdef __cplusplus
}
#endif
//----------------------------------------------------------------------
// mydll.cpp - implementation file
#include <afxwin.h>
#include <afxext.h>
#include <afxcoll.h>
#include <afxdb.h>
// data members
//
CPtrArray* pDbArray = NULL;
int nClients = 0;
// exported functions
//
extern "C" int FAR PASCAL _export Init(HWND hwnd,char* szDSN)
{
int nReturn = -1;
AfxLockTempMaps();
TRY
{
// make sure m_pMainWnd is valid for CDatabase::Open()
AfxGetApp()->m_pMainWnd = CWnd::FromHandle(hwnd);
CDatabase* pDb = new CDatabase;
if (pDb->Open(szDSN))
{
// if database array is not allocated, do so
if (pDbArray == NULL)
pDbArray = new CPtrArray;
// first try to reuse existing elements
BOOL bFound = FALSE;
for (int i = 0; i <= pDbArray->GetUpperBound(); i++)
{
CDatabase* ptr = (CDatabase*)pDbArray->GetAt(i);
if (!ptr)
{
pDbArray->SetAt(i,(void*)pDb);
nReturn = i;
bFound = TRUE;
break;
}
}
// if you can't reuse existing elements, add one
if (!bFound)
nReturn = pDbArray->Add((void*)pDb);
nClients++;
}
}
CATCH(CException,e)
{
// handle exceptions here
}
END_CATCH
// free the temporary CWnd object
AfxUnlockTempMaps();
return nReturn;
}
extern "C" void FAR PASCAL _export DriverName(int nID,char* szName,
int cbName)
{
TRY
{
short cbReturned;
// get the database for this client
CDatabase* pDb = (CDatabase*)pDbArray->GetAt(nID));
// get the driver name for the database
::SQLGetInfo(pDb->m_hdbc,SQL_DRIVER_NAME,
szName,cbName,&cbReturned);
}
CATCH(CException,e)
{
// handle exceptions here
}
END_CATCH
}
extern "C" void FAR PASCAL _export Term(int nID) {
TRY
{
// get this client's database, close and delete it
CDatabase* pDb = (CDatabase*)pDbArray->GetAt(nID));
pDb->Close();
delete pDb;
// set the array element to NULL
pDbArray->SetAt(nID,NULL);
// if no more clients, empty and delete the array
if (!(--nClients))
{
pDbArray->RemoveAll();
delete pDbArray;
pDbArray = 0;
}
}
CATCH(CException,e)
{
// handle exceptions here
}
END_CATCH
}
// CWinApp-derived class
//
class CMyDLL : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
CMyDLL(const char* pszAppName)
: CWinApp(pszAppName) { }
};
BOOL CMyDLL::InitInstance() {
return TRUE;
}
int CMyDLL::ExitInstance()
{
return CWinApp::ExitInstance();
}
CMyDLL NEAR myDLL("mydll.dll");
// end source code
For more information, please see the following article in the Microsoft Knowledge Base:
ARTICLE-ID: Q110475
TITLE : INF: Sharing ODBC Handles Among Several Applications
Additional reference words: kbinf 1.50 1.51 1.52 1.52a 1.52b 2.50 2.51 2.52
2.52a 2.52b
KBCategory: kbinterop kbcode
KBSubcategory: MfcDatabase
Keywords : kb16bitonly
Last Reviewed: July 23, 1997