For the project I am currently working on, I want to be able to load and
unload extension dlls explicitly. I have the loading and unloading working,
but when I run it in debug mode, I get hundreds of "first chance exception in
NTDLL.DLL at 0xXXXXXXXXX" messages in the debug windows.
The way my program is designed, the main exe creates objects from an extension
dll, uses them for a while, then deletes the objects and unloads the library.
I load the dll with AfxLoadLibrary(), use GetProcAddress() (passing it the
mangled name of the object's CreateObject method), then using that function to
create an object and return a pointer to it. I then can use the object just
fine through virtual functions because it is derived from a base class that in
another DLL, but is being explicitly loaded. Then the application deletes the
object and uses AfxFreeLibrary().
These objects created in the DLL also create CViews and CDocuments, but do not
register their templates with the DocManager. I am pretty sure I am not
referencing any objects I created from the DLL after I free it, but maybe MFC
has some pointers to the DLL data that is not getting freed right.
What do I need to do to get rid of those exceptions? The app runs just fine,
but I am sure those warnings will come back to bite me later because I am not
doing something right. If I comment out the AfxFreeLibrary(), I get no error
messages.
Or how can I catch those exceptions to see exactly what is going wrong? They
never stop my app so I can't figure out what is triggering them. They are
always at the same address tho, and are very repeatable.
Thanks
Rich
Pretty bizarre thing you've built. Read about AFX_MANAGE_STATE, which
is a call you should probably be making. MFC keeps separate lists of
window handles in the app and in the dll and you could be mixing it up.
The problem that you are having is because the CDynLinkList object
that is registered in the DLLMain of your extension library is not
being removed from the chain correctly when you unload the library. A
new one is being created and added to the chain, each time you load
the library, however. Then whenever a resource is looked up, it winds
through this CDynLinkList chain and hits the entries that are no
longer valid. The MFC library catches this error and displays the
message that you are seeing.
I ran into a similar problem when using extension dll's from within
COM objects (regular dll's). These dll's are repeatedly loaded and
unloaded as COM object come and go. With them, the extension dll's
are repeatedly loaded and unloaded as well.
You will have much better luck if you write a separate entry point in
which you will initialize the extension dll, similar to AfxDaoInit(),
for example. In this initialization function, you should create a
CDynLinkList object and add it to the extension chain. There is an
article in the knowledge base about the CDynLinkList objects and
extension dll's. You also need to ensure that you initialize all of
the other extension dll's that any dll uses.
The only problem that I ran into is that you have to be carefull only
create and insert one of these objects if it is not already in the
list. I wrote some code to handle this problem which I have included
as well as an example of my DLLMain.
xxxxxxxxxxxxxxx MY DLLMain for one of my extension dll's xxxxxxxxxxxx
// npbridgex.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include <afxdllx.h>
#include <InitExtDLL.h>
#include <nmsutxext.h>
#include <dbutxext.h>
#include <mfcutxext.h>
#include <nputilxext.h>
#include "NpBridgexext.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static AFX_EXTENSION_MODULE NpbridgexDLL = { NULL, NULL };
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("NPBRIDGEX.DLL Initializing!\n");
// Extension DLL one-time initialization
AfxInitExtensionModule(NpbridgexDLL, hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("NPBRIDGEX.DLL Terminating!\n");
}
return 1; // ok
}
int WINAPI InitNPBRIDGEXDLL()
{
InitExtDLL(NpbridgexDLL);
InitNMSUTXDLL();
InitDBUTXDLL();
InitMFCUTXDLL();
InitNPUTILXDLL();
return 1;
}
xxxxxxxxxxxxxxxxxxx End of DLLMain xxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxx InitExtDLL.h xxxxxxx
#ifndef InitExtDLL_h_DEFINED
#define InitExtDLL_h_DEFINED
extern void InitExtDLL(AFX_EXTENSION_MODULE& state);
#endif
xxxxxxxxxxxxxxxxx InitExtDLL.cpp xxxxxxxxxxx
#define VC_EXTRALEAN // Exclude rarely-used stuff from
Windows headers
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxole.h> // MFC OLE classes
#include <afxodlgs.h> // MFC OLE dialog classes
#include <afxdisp.h> // MFC OLE automation classes
#endif // _AFX_NO_OLE_SUPPORT
#ifndef _AFX_NO_DB_SUPPORT
#include <afxdb.h> // MFC ODBC database classes
#endif // _AFX_NO_DB_SUPPORT
#ifndef _AFX_NO_DAO_SUPPORT
#include <afxdao.h> // MFC DAO database classes
#endif // _AFX_NO_DAO_SUPPORT
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC support for Windows 95
Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT
#include <afximpl.h> // MFC implementation
#include "InitExtDLL.h"
void InitExtDLL(AFX_EXTENSION_MODULE& state)
{
ASSERT(AfxCheckMemory());
ASSERT(state.hModule != NULL);
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
AfxLockGlobals(CRIT_DYNLINKLIST);
for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL
!= NULL; )
{
CDynLinkLibrary* pNextDLL = pDLL->m_pNextDLL;
if (pDLL->m_hModule == state.hModule)
{
break;
}
pDLL = pNextDLL;
}
AfxUnlockGlobals(CRIT_DYNLINKLIST);
if (! pDLL)
{
new CDynLinkLibrary(state);
}
ASSERT(AfxCheckMemory());
}
Sho' nuff. I found this out late the other night, after much stepping through
all the MFC state inormation and MFC source code. As it turns out, some of
the Dlls (that I did not create) that my explicitly-loaded DLL was loading
did not have the AfxTermExtensionModule() in the 'process detach' section of
the DllMain (I also noticed that this func is missing from the code you
provided). I am guessing that earlier versions of the AppWizard did not put
this function in either by mistake or it was not necessary.
So I added that function call to all the DLLs and everything is happy. For
anyone having similar problems, just make sure that your DLLMain does
AfxInitExtensionModule() and AfxTermExtensionModule(), and Mfc should take
care of the rest. AfxInitExtensionModule() adds your library to the
linked-list of extension DLLs, and AfxTermExtensionModule() deletes the
CDynlinkLibrary object. That destructor removes the dll from the link-list.
thanks for the help,
-Rich