我有一个加载到Microsoft管理控制台(MMC)中的DLL。看来它的DllGetClassObject
函数正在调用第三方库的一些初始化函数,该函数依赖于静态初始化的完成(因此,在常规C ++程序中,不得在main()
之前调用它) )。该功能似乎失败,并且该管理单元未在MMC中显示。奇怪的是,如果我从MMC中将其删除并再次添加,它会成功加载。
关于何时在DLL中准确进行C ++静态初始化的信息(在回调之前和之后)似乎很少,并且Microsoft似乎已经删除了其“ DLL最佳实践”论文,有关该问题的许多答案和文章都对此进行了引用。这类问题。
在任何地方(最好在MSDN上)是否存在有关C ++静态初始化和DLL回调的顺序的权威信息?
(我已经尝试过https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-dllgetclassobject,希望可以对此进行记录)
编辑:将有问题的函数调用从DllGetClassObject
移到DllMain
似乎可以解决问题。不过,仍在寻找权威文档。
编辑:从该结果和答案中得出结论,我与第三方初始化函数有关的问题不可能由静态初始化引起,因为静态初始化应该在DllMain
之前完成,因此也应该在之前完成。 DllGetClassObject
。
答案 0 :(得分:1)
在您的DLL中,没有纵容或避开使用C ++对象作为DLL中的全局变量/静态变量的情况。操作顺序为:
DllMain
DllGetClassObject
文档here描述了C / C ++全局/静态初始化与DllMain
之间的关系。
...当链接到DLL时,VCRuntime代码提供了一个称为
_DllMainCRTStartup
的内部DLL入口点函数,该函数处理Windows OS消息到DLL以便附加到进程或线程或从进程或线程分离。_DllMainCRTStartup
函数执行基本任务,例如堆栈缓冲区安全性设置,C运行时库(CRT)初始化和终止,以及对静态和全局对象的构造函数和析构函数的调用。_DllMainCRTStartup
还为其他库(如WinRT,MFC和ATL)调用挂钩函数,以执行其自身的初始化和终止。没有这种初始化,CRT和其他库以及您的静态变量将处于未初始化状态......在进程附加时,
_DllMainCRTStartup
函数设置缓冲区安全性检查,初始化CRT和其他库,初始化运行时类型信息,初始化并调用静态和非本地构造函数数据,初始化线程本地存储,为每个附加操作增加一个内部静态计数器,然后然后调用用户或库提供的DllMain
...
通过在DllMain
中设置一个断点,然后查看导致DllMain
被调用的堆栈帧,您可以自己看到所有这些。在您叫DllMain
之前,您会发现很多有趣的事情已经完成。
对于DllGetClassObject
:魔术没有调用此导出的函数。此函数的调用者必须做三件事-加载DLL(例如LoadLibrary
),查找从DLL加载的函数的地址(例如GetProcAddress
),然后调用该地址使用DllGetClassObject
实现已知的签名。您可以手动完成所有操作,但是CoCreateInstance
之类的功能会按照此处描述的顺序自动执行这三件事。 DllMain
在调用LoadLibrary
时自动被调用,并且LoadLibrary
直到DllMain
返回后才返回。如果DllMain
返回成功代码,则LoadLibrary
的调用者将收到DLL的HMODULE
。如果DllMain
返回错误代码,则调用者将永远没有DLL的HMODULE
(LoadLibrary
将返回NULL
),因此调用者甚至找不到您的{{ 1}}函数,更不用说了。
答案 1 :(得分:0)
编辑:不要对DllGetClassObject
或DllMain
做任何“有趣”的事情。如果可以,请按需或在以后的某个阶段调用初始化代码。在回调中做任何事情都应该是万不得已的方法(但这很可能会导致您在第一次测试中未发现的问题)。
阅读了评论中建议的一些实质性人物后,我得出结论
没有权威信息。就Microsoft而言,C ++和DLL不存在于同一个Universe中。
任何非权威性信息(例如https://blogs.msdn.microsoft.com/oldnewthing/20040127-00/?p=40873/)都表明,整个C ++和DLL都是一团糟,很难推荐任何通用的最佳做法。
在我的特定情况下(本机DLL作为MMC管理单元加载),似乎将简单地将“有问题的”代码移动到DllMain
似乎有所帮助。但是,后来发现它在进程退出时引起了问题(死锁)。