首页 > 解决方案 > 是在 C++ 静态初始化之前还是之后调用 DllGetClassObject

问题描述

我有一个加载到 Microsoft 管理控制台 (MMC) 中的 DLL。似乎它的DllGetClassObject函数正在调用第三方库的一些初始化函数,该函数已知依赖于静态初始化才能完成(因此main()在常规 C++ 程序中之前不能调用它)。此功能似乎失败,并且管理单元未显示在 MMC 中。奇怪的是,如果我从 MMC 中删除它并再次添加它,它会成功加载。

关于 C++ 静态初始化在 DLL 中发生的确切时间(在回调之前和之后)的信息似乎很少,微软似乎已经删除了它的“DLL 最佳实践”论文,该论文在处理这种类型的许多答案和文章中被引用问题。

在任何地方(最好在 MSDN 上)是否有关于 C++ 静态初始化和 DLL 回调顺序的一些权威信息?

(我已经尝试过https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-dllgetclassobject,我希望这会被记录在案)

编辑:将有问题的函数调用从DllGetClassObjectto移动DllMain似乎可以解决问题。不过,仍在寻找权威文档。

编辑:从这个结果和答案得出结论,我在使用第三方初始化函数时遇到的问题不可能是由静态初始化引起的,因为静态初始化应该在之前完成DllMain,因此也在之前完成DllGetClassObject

标签: c++dllcom

解决方案


在不宽恕或避免在 DLL 中使用 C++ 对象作为全局变量/静态变量的情况下,这里是您正在寻找的信息。操作顺序为:

  1. C/C++ 全局/静态初始化
  2. DllMain
  3. DllGetClassObject

这里的文档描述了 C/C++ 全局/静态初始化和DllMain.

...当链接到 DLL 时,VCRuntime 代码提供了一个内部 DLL 入口点函数,称为_DllMainCRTStartup处理 Windows 操作系统消息到 DLL 以附加到进程或线程或从进程或线程分离。该_DllMainCRTStartup函数执行基本任务,例如堆栈缓冲区安全设置、C 运行时库 (CRT) 初始化和终止,以及调用静态和全局对象的构造函数和析构函数_DllMainCRTStartup还调用其他库(如 WinRT、MFC 和 ATL)的挂钩函数来执行它们自己的初始化和终止。如果没有此初始化,CRT 和其他库以及您的静态变量将处于未初始化状态...

...在进程附加时,该_DllMainCRTStartup函数设置缓冲区安全检查,初始化 CRT 和其他库,初始化运行时类型信息,初始化和调用静态和非本地数据的构造函数,初始化线程本地存储,递增内部每个附加的静态计数器,然后调用用户或库提供的DllMain...

您可以通过在您的中设置断点DllMain然后查看导致DllMain被调用的堆栈帧来自己查看所有这些。你会发现很多有趣的事情在你DllMain被调用之前就已经完成了。

至于DllGetClassObject:这个导出的函数不是魔法调用的。该函数的调用者必须做 3 件事 - 加载您的 DLL(例如,LoadLibrary),查找从 DLL 加载的函数的地址(例如,GetProcAddress),然后使用已知的DllGetClassObject实现签名调用该地址。您可以手动完成所有这些操作,但像CoCreateInstance自动按照此处描述的顺序执行这三件事的功能。DllMain被调用时自动LoadLibrary调用,直到返回LoadLibrary后才DllMain返回。如果DllMain返回成功代码,则调用者LoadLibrary将收到HMODULEDLL 的。如果DllMain返回一个错误代码,调用者将永远不会有一个HMODULEDLL (LoadLibrary将返回NULL),因此调用者甚至找不到您的DllGetClassObject函数,更不用说调用它了。


推荐阅读