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);
CListp_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)
{
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("SMARTMERGE.DLL Initializing!\n");
if (!AfxInitExtensionModule(SmartMergeDLL, hInstance))
return 0;
new CDynLinkLibrary(SmartMergeDLL);
AtInitInstance(initialiseSet);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("SMARTMERGE.DLL Terminating!\n");
AfxTermExtensionModule(SmartMergeDLL);
}
return 1;
}
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;
}
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
AtInitInstanceExecute();
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo))
return FALSE;
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!