AtInitExitInstance - Register functions to be called during InitInstance and ExitInstance






3.50/5 (2 votes)
Jan 21, 2003
3 min read

50341

405
A technique to register functions to be called during InitInstance and ExitInstance
Introduction
If you want to initialize some variables before the main code has executed, using standard C/C++ you can create a static initializer like this:
int myVar = 0x1234; int main(void) { return myVar; }
If you want to run some code, you can create a class and use a static constructor to accomplish it.
class Init { public: Init() { printf(_T("Hello World")); } }; static Init _init; int main(void) { return 0; }
Also, if you want to execute some code after main has exited you can use atexit
void *pMemory; void deleteMemory(void) { delete pMemory; } int main(void) { atexit(deleteMemory); pMemory = new char[100]; return 0; }
So, after main has exited, deleteMemory
will be called before the process has been shut down
Now, all of this is OK for most cases, but recently I've been writing a large project that has multiple DLLs, using several plugins, loads of databases and good helpings of OLE.
Anyone who has used CRecordset
in DLLs, will tell you that if you want to have a statically created
CRecordset
then you need to create it dynamically.
CMyRecordset staticSet; CMyOtherRecordset *pDynamicSet;
If this code is in a DLL, then the first instance, will be created during DLL startup, and if the record-set is still in use during shutdown, you will get an exception thrown, because the database is trying to close itself, but some of the database machinery has probably been already closed down.
For the second instance, you will need to create the database during DLL startup manually, BUT you can't do it in
DllMain
because things might not be up and running when it is called, and also, you are limited to what you
can actually call during this function.
What we need is to be able to call this function later.
Also, we need to be able to close and delete the record-set later, but before
DllMain
is called with the shutdown
parameters.
The solution
It is for this purpose that I wrote the following functions :
typedef void (*AtExitInstanceFunc_t)(void); CListp_AtExitInstanceFuncList; void AtExitInstance(void (*func)(void)) { p_AtExitInstanceFuncList.AddTail (func); } void AtExitInstanceExecute() { while(p_AtExitInstanceFuncList.GetCount ()) { AtExitInstanceFunc_t func = p_AtExitInstanceFuncList.RemoveHead (); func(); } } typedef void (*AtInitInstanceFunc_t)(void); CList p_AtInitInstanceFuncList; void AtInitInstance(void (*func)(void)) { p_AtInitInstanceFuncList.AddTail (func); } void AtInitInstanceExecute() { while(p_AtInitInstanceFuncList.GetCount ()) { AtInitInstanceFunc_t func = p_AtInitInstanceFuncList.RemoveHead (); func(); } }
The downloadable source contains a MFC Extension DLL project that just contains these functions (with headers). The Release DLL is only 24kb, so it won't take up too much room on your disk :-). It only contains a .dsp file, as it is designed to be used as it within a bigger project, so you just include it in your current project.
Basically, all that happens is you register a function to be called later, and If you need to run a function nearer the start of program execution, do something like:
#include <InitExit.h> CMyOtherRecordset *pDynamicSet; void deleteSet(void) { delete pDynamicSet; } void initialiseSet(void) { pDynamicSet = new CMyOtherRecordset; AtExitInstance(deleteSet); } extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { // Remove this if you use lpReserved UNREFERENCED_PARAMETER(lpReserved); if (dwReason == DLL_PROCESS_ATTACH) { TRACE0("SMARTMERGE.DLL Initializing!\n"); // Extension DLL one-time initialization if (!AfxInitExtensionModule(SmartMergeDLL, hInstance)) return 0; new CDynLinkLibrary(SmartMergeDLL); AtInitInstance(initialiseSet); } else if (dwReason == DLL_PROCESS_DETACH) { TRACE0("SMARTMERGE.DLL Terminating!\n"); // Terminate the library before destructors are called AfxTermExtensionModule(SmartMergeDLL); } return 1; // ok }
This code will, during startup, queue initialiseSet
to be executed later. When initialiseSet
has been executed, deleteSet
will be queued for execution on program shutdown.
All that's left to do is to add some code to the InitInstance/ExitInstance
functions of the .EXE :
#include <InitExit.h> BOOL CFailsworthApp::InitInstance() { CWaitCursor cur; if (!AfxSocketInit()) { AfxMessageBox(IDP_SOCKETS_INIT_FAILED); return FALSE; } // Initialize OLE libraries if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif AtInitInstanceExecute(); // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line if (!ProcessShellCommand(cmdInfo)) return FALSE; // The main window has been initialized, so show and update it. pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow(); return TRUE; } int CFailsworthApp::ExitInstance() { AtExitInstanceExecute(); return CWinApp::ExitInstance(); }
I've added these calls after the majority of the initialization has taken place, but before the main window is shown. This allows as much of the machinery to be startup up as possible, before I try and actually use it.
The prototype for both types of functions are
void func(void);
so no parameters are passed and no return value is required.
Notes
You can also specify a function to be called using #pragma init_seg
, but the ordering of calls
is not really known.
Conclusion
Although standard C/C++ will give a version of this startup/shutdown process, sadly this doesn't work under all (windows) cases. The solution I have provided certainly works, and provides centralized routines to accomplish the goal under these special cases.
Thanks for reading. Hello mum!