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






3.50/5 (2投票s)
2003 年 1 月 21 日
3分钟阅读

50911

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); 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(); } }
可下载的源代码包含一个 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)情况下都不起作用。 我提供的解决方案当然有效,并提供了集中式例程来在这些特殊情况下完成目标。
感谢您的阅读。 妈妈你好!