65.9K
CodeProject 正在变化。 阅读更多。
Home

AtInitExitInstance - 注册要在 InitInstance 和 ExitInstance 期间调用的函数

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (2投票s)

2003 年 1 月 21 日

3分钟阅读

viewsIcon

50911

downloadIcon

405

一种在 InitInstance 和 ExitInstance 期间注册要调用的函数的技巧

引言

如果你想在主代码执行之前初始化一些变量,使用标准的 C/C++,你可以创建一个静态初始化器,就像这样

int myVar = 0x1234;

int main(void)
{
    return myVar;
}

如果你想运行一些代码,你可以创建一个类并使用静态构造函数来实现它。

class Init
{
public:
    Init() { printf(_T("Hello World")); }
};

static Init _init;

int main(void)
{
    return 0;
}

此外,如果你想在 main 函数退出后执行一些代码,你可以使用 atexit

void *pMemory;

void deleteMemory(void)
{
    delete pMemory;
}

int main(void)
{
    atexit(deleteMemory);

    pMemory = new char[100];

    return 0;
}

因此,在 main 函数退出后,deleteMemory 将在进程关闭之前被调用

现在,对于大多数情况,这一切都很好,但最近我一直在编写一个大型项目,该项目有多个 DLL,使用几个插件,大量的数据库和大量的 OLE。

任何在 DLL 中使用过 CRecordset 的人都会告诉你,如果你想拥有一个静态创建的 CRecordset,那么你需要动态地创建它。

CMyRecordset staticSet;
CMyOtherRecordset *pDynamicSet;

如果此代码在 DLL 中,那么第一个实例将在 DLL 启动期间创建,并且如果在关闭期间仍然使用记录集,您将收到一个异常,因为数据库正尝试关闭自己,但是某些数据库机制可能已经被关闭了。

对于第二个实例,您将需要在 DLL 启动期间手动创建数据库,但是您不能在 DllMain 中执行此操作,因为在调用它时可能还没有启动,并且您也仅限于在此函数期间实际可以调用的内容。

我们需要的是能够稍后调用此函数。

此外,我们需要能够稍后关闭和删除记录集,但在使用关闭参数调用 DllMain 之前。

解决方案

出于这个目的,我编写了以下函数

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();
    }
}

可下载的源代码包含一个 MFC 扩展 DLL 项目,该项目仅包含这些函数(带标头)。 Release DLL 只有 24kb,因此它不会占用您磁盘上太多的空间 :-)。它只包含一个 .dsp 文件,因为它被设计为在更大的项目中使用,因此您只需将其包含在您当前的项目中即可。

基本上,发生的一切是您注册一个稍后调用的函数,如果您需要在程序执行开始时运行一个函数,可以执行以下操作

#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
}

此代码将在启动期间将 initialiseSet 放入队列中,以便稍后执行。当 initialiseSet 执行完毕后,deleteSet 将被加入队列,以便在程序关闭时执行。

剩下的就是将一些代码添加到 .EXE 的 InitInstance/ExitInstance 函数中

#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();
}

我已在大多数初始化完成后添加了这些调用,但在显示主窗口之前。这允许尽可能多地启动机制,然后我尝试实际使用它。

这两种类型的函数的原型都是 void func(void);,因此不需要传递参数,也不需要返回值。

注释

您还可以使用 #pragma init_seg 指定要调用的函数,但是调用的顺序实际上是未知的。

结论

虽然标准的 C/C++ 将提供此启动/关闭过程的一个版本,但遗憾的是,这在所有(Windows)情况下都不起作用。 我提供的解决方案当然有效,并提供了集中式例程来在这些特殊情况下完成目标。

感谢您的阅读。 妈妈你好!

© . All rights reserved.