我有一个加载到Microsoft管理控制台(MMC)的DLL。似乎它的DllGetClassObject
函数正在调用已知依赖于静态初始化的第三方库的初始化函数(因此在常规C ++程序中不能在main()
之前调用它)。此功能似乎失败,并且管理单元未在MMC中显示。奇怪的是,如果我从MMC中删除它并再次添加它,它会成功加载。
有关何时在DLL(回调之前和之后)中发生C ++静态初始化的信息似乎很少,并且Microsoft似乎已经删除了其“DLL最佳实践”论文,该论文在许多答案和文章中引用了这种类型的的问题。
是否有关于C ++静态初始化和DLL回调的顺序的任何地方(最好是在MSDN上)有一些权威信息?
(我已经尝试过https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-dllgetclassobject,我希望这可以记录在案)
编辑:将有问题的函数调用从DllGetClassObject
移动到DllMain
似乎解决了这个问题。但仍在寻找权威文档。
编辑:从这个结果和答案得出结论,我对第三方初始化函数的问题不能由静态初始化引起,因为静态初始化应该在DllMain
之前完成,因此也在DllGetClassObject
之前完成。
如果没有纵容或避免在DLL中使用C ++对象作为全局/静态,这里是您正在寻找的信息。操作顺序是:
DllMain
DllGetClassObject
文档here描述了C / C ++全局/静态初始化和DllMain
之间的关系。
...当链接到DLL时,VCRuntime代码提供了一个名为
_DllMainCRTStartup
的内部DLL入口点函数,该函数处理到DLL的Windows操作系统消息,以附加到进程或线程或从进程或线程分离。_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的HMODULE
(LoadLibrary
将返回NULL
),因此调用者甚至无法找到你的DllGetClassObject
函数,更不用说它了。
编辑:不要在DllGetClassObject
或DllMain
做任何“有趣”的事情。如果可以,请“按需”或稍后阶段调用初始化代码。在回调中做任何事情应该只是最后的手段(但它很可能会导致你在第一次测试时可能没有遇到的问题)。
在阅读了评论中建议的一些材料后,我得出结论
在我的特定情况下(作为MMC管理单元加载的本机DLL),它似乎有助于简单地将“有问题”的代码移动到DllMain
。但是,后来证明它在进程退出时引起了问题(死锁)。