【问题标题】:Is DllGetClassObject called before or after C++ static initialization是在 C++ 静态初始化之前还是之后调用 DllGetClassObject
【发布时间】:2019-07-09 09:12:29
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 你在找这个吗:docs.microsoft.com/en-us/windows/desktop/dlls/… 还有docs.microsoft.com/en-us/windows/desktop/dlls/dllmain 否则google 'dllmain raymond chen'(可以被认为是权威的)
  • 对这个问题使用恰当的术语,google "c++ static initialization order fiasco"。
  • @SimonMourier 虽然我还没有阅读全部内容,但该文档既没有提到 C++,也没有提到 DllGetClassObject。虽然它确实提出了关于何时初始化静态结构的建议,但我自己无法实现这些建议,因为 C++ 静态初始化的时间是由编译器/链接器控制的,而不是我。
  • @HansPassant 这不是关于多个静态变量的初始化顺序,而是关于DLL中所有静态符号的初始化顺序以及Microsoft定义的DLL中的回调。

标签: c++ dll com


【解决方案1】:

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

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

文档here描述了C/C++全局/静态初始化和DllMain之间的关系。

...当链接到 DLL 时,VCRuntime 代码提供了一个名为 _DllMainCRTStartup 的内部 DLL 入口点函数,该函数将 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 的调用者将收到DLL 的HMODULE。如果DllMain 返回错误代码,调用者将永远不会有一个用于DLL 的HMODULELoadLibrary 将返回NULL),因此调用者甚至找不到您的DllGetClassObject 函数,更不用说调用它了。

【讨论】:

    【解决方案2】:

    编辑:不要在DllGetClassObjectDllMain 中做任何“有趣”的事情。如果可以,请“按需”或在稍后的某个阶段调用您的初始化代码。在回调中做任何事情应该只是最后的手段(但它很可能会导致您在第一次测试期间可能没有发现的问题)。

    在阅读了一些人们在 cmets 中建议的材料后,我得出结论

    1. 没有权威信息。就微软而言,C++ 和 DLL 并不存在于同一个宇宙中。

    2. 任何非权威信息(例如https://blogs.msdn.microsoft.com/oldnewthing/20040127-00/?p=40873/)都表明整个 C++ 和 DLL 问题一团糟,很难推荐任何通用的最佳实践。

      李>

    在我的特定情况下(本机 DLL 作为 MMC 管理单元加载),将“有问题的”代码简单地移动到 DllMain 似乎有帮助。但是后来发现它在进程退出时导致了问题(死锁)。

    【讨论】:

    • 回答自己的问题并接受答案通常不是一个好习惯,尤其是当答案像这个一样糊涂时。对于用Visual C++编译的DLL,静态初始化保证在DllMain之前完成,DllMain必须在DllGetClassObject被调用之前完成。
    • @MichaelGunter 如果您将此作为答案发布,最好带有权威文档的链接,我很乐意将其标记为已接受的答案。但是,您确定 DllMain 在 DllGetClassObject 之前完成吗?在那种情况下,我想知道为什么我调用的初始化函数在 DllMain 中有效,但在 DllGetClassObject 中无效...
    • 虽然我的回答不是这个问题的最佳答案,但它可能是最实用的一个,因为 C++ 和 DLL 的组合存在问题(参见链接),并且在应该避免这些回调。如果您不同意,我们可以单独讨论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-02
    • 1970-01-01
    相关资源
    最近更新 更多