Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

AtInitExitInstance - Register functions to be called during InitInstance and ExitInstance

0.00/5 (No votes)
20 Jan 2003 1  
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);

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)
{
    // 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!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here