DllGetClassObject是在C++静态初始化之前还是之后调用
Is DllGetClassObject called before or after C++ static initialization
我有一个加载到 Microsoft 管理控制台 (MMC) 中的 DLL。它的 DllGetClassObject
函数似乎正在调用已知依赖于静态初始化完成的第三方库的一些初始化函数(因此在常规 C++ 程序中不得在 main()
之前调用它).此功能似乎失败,管理单元未显示在 MMC 中。奇怪的是,如果我从 MMC 中删除它并重新添加它,它会成功加载。
关于 DLL 中 C++ 静态初始化发生的确切时间(在回调之前和之后)的信息似乎很少,Microsoft 似乎已经删除了它的 "DLL Best Practices" 论文,许多答案和文章中都引用了该论文处理这类问题。
是否有关于 C++ 静态初始化和 DLL 回调顺序的权威信息(最好在 MSDN 上)?
(我已经尝试过 https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-dllgetclassobject,我希望这会被记录在案)
编辑:将有问题的函数调用从 DllGetClassObject
移动到 DllMain
似乎可以解决问题。不过,仍在寻找权威文档。
编辑:从这个结果和答案得出结论,我在第三方初始化函数中遇到的问题不可能是由静态初始化引起的,因为静态初始化应该在 DllMain
之前完成,因此也在 DllGetClassObject
.
之前
编辑:不要在 DllGetClassObject
或 DllMain
中做 任何事情 "interesting"。如果可以,请调用您的初始化代码 "on-demand" 或在稍后阶段调用。在回调中做任何事情都应该只是最后的手段(但它很可能会导致您在第一次测试期间可能没有发现的问题)。
在阅读了一些 material 人在评论中提出的建议后,我得出的结论是
暂无权威信息。就微软而言,C++ 和 DLL 不存在于同一个宇宙中。
任何非权威信息(例如https://blogs.msdn.microsoft.com/oldnewthing/20040127-00/?p=40873/)都表明整个C++和DLL问题一团糟,很难推荐任何通用的最佳实践。
在我的特定情况下(作为 MMC 管理单元加载的本机 DLL),将 "problematic" 代码简单地移动到 DllMain
似乎有所帮助。但是后来发现是在进程退出时出现问题(死锁)。
在不宽恕或避免在 DLL 中使用 C++ 对象作为 globals/statics 的情况下,这里是您正在寻找的信息。运算顺序为:
- C/C++ global/static初始化
DllMain
DllGetClassObject
文档here描述了C/C++ global/static初始化和DllMain
之间的关系。
...When linked into a DLL, the VCRuntime code provides an internal DLL entry-point function called _DllMainCRTStartup
that handles Windows OS messages to the DLL to attach to or detach from a process or thread. The _DllMainCRTStartup
function performs essential tasks such as stack buffer security set up, C run-time library (CRT) initialization and termination, and calls to constructors and destructors for static and global objects. _DllMainCRTStartup
also calls hook functions for other libraries such as WinRT, MFC, and ATL to perform their own initialization and termination. Without this initialization, the CRT and other libraries, as well as your static variables, would be left in an uninitialized state...
...On process attach, the _DllMainCRTStartup
function sets up buffer security checks, initializes the CRT and other libraries, initializes run-time type information, initializes and calls constructors for static and non-local data, initializes thread-local storage, increments an internal static counter for each attach, and then calls a user- or library-supplied DllMain
...
您可以通过在 DllMain
中设置断点然后查看导致调用 DllMain
的堆栈帧来自己查看所有这些内容。你会发现很多有趣的事情在你的 DllMain
被调用之前完成。
至于DllGetClassObject
:这个导出的函数不是被魔法调用的。该函数的调用者必须做 3 件事 - 加载您的 DLL(例如 LoadLibrary
),查找从 DLL 加载的函数的地址(例如 GetProcAddress
),然后调用该地址使用以 DllGetClassObject
实现而闻名的签名。您可以手动完成所有这些操作,但是像 CoCreateInstance
这样的函数会按照此处描述的顺序自动执行这三件事。 DllMain
会在 LoadLibrary
被调用时自动调用,而 LoadLibrary
不会 return 直到 DllMain
return 之后。如果 DllMain
return 是一个成功代码,那么 LoadLibrary
的调用者将收到 DLL 的 HMODULE
。如果 DllMain
return 是错误代码,调用者将永远不会有 DLL 的 HMODULE
(LoadLibrary
将 return NULL
),并且因此调用者甚至无法找到您的 DllGetClassObject
函数,更不用说调用它了。
我有一个加载到 Microsoft 管理控制台 (MMC) 中的 DLL。它的 DllGetClassObject
函数似乎正在调用已知依赖于静态初始化完成的第三方库的一些初始化函数(因此在常规 C++ 程序中不得在 main()
之前调用它).此功能似乎失败,管理单元未显示在 MMC 中。奇怪的是,如果我从 MMC 中删除它并重新添加它,它会成功加载。
关于 DLL 中 C++ 静态初始化发生的确切时间(在回调之前和之后)的信息似乎很少,Microsoft 似乎已经删除了它的 "DLL Best Practices" 论文,许多答案和文章中都引用了该论文处理这类问题。
是否有关于 C++ 静态初始化和 DLL 回调顺序的权威信息(最好在 MSDN 上)?
(我已经尝试过 https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-dllgetclassobject,我希望这会被记录在案)
编辑:将有问题的函数调用从 DllGetClassObject
移动到 DllMain
似乎可以解决问题。不过,仍在寻找权威文档。
编辑:从这个结果和答案得出结论,我在第三方初始化函数中遇到的问题不可能是由静态初始化引起的,因为静态初始化应该在 DllMain
之前完成,因此也在 DllGetClassObject
.
编辑:不要在 DllGetClassObject
或 DllMain
中做 任何事情 "interesting"。如果可以,请调用您的初始化代码 "on-demand" 或在稍后阶段调用。在回调中做任何事情都应该只是最后的手段(但它很可能会导致您在第一次测试期间可能没有发现的问题)。
在阅读了一些 material 人在评论中提出的建议后,我得出的结论是
暂无权威信息。就微软而言,C++ 和 DLL 不存在于同一个宇宙中。
任何非权威信息(例如https://blogs.msdn.microsoft.com/oldnewthing/20040127-00/?p=40873/)都表明整个C++和DLL问题一团糟,很难推荐任何通用的最佳实践。
在我的特定情况下(作为 MMC 管理单元加载的本机 DLL),将 "problematic" 代码简单地移动到 DllMain
似乎有所帮助。但是后来发现是在进程退出时出现问题(死锁)。
在不宽恕或避免在 DLL 中使用 C++ 对象作为 globals/statics 的情况下,这里是您正在寻找的信息。运算顺序为:
- C/C++ global/static初始化
DllMain
DllGetClassObject
文档here描述了C/C++ global/static初始化和DllMain
之间的关系。
...When linked into a DLL, the VCRuntime code provides an internal DLL entry-point function called
_DllMainCRTStartup
that handles Windows OS messages to the DLL to attach to or detach from a process or thread. The_DllMainCRTStartup
function performs essential tasks such as stack buffer security set up, C run-time library (CRT) initialization and termination, and calls to constructors and destructors for static and global objects._DllMainCRTStartup
also calls hook functions for other libraries such as WinRT, MFC, and ATL to perform their own initialization and termination. Without this initialization, the CRT and other libraries, as well as your static variables, would be left in an uninitialized state......On process attach, the
_DllMainCRTStartup
function sets up buffer security checks, initializes the CRT and other libraries, initializes run-time type information, initializes and calls constructors for static and non-local data, initializes thread-local storage, increments an internal static counter for each attach, and then calls a user- or library-suppliedDllMain
...
您可以通过在 DllMain
中设置断点然后查看导致调用 DllMain
的堆栈帧来自己查看所有这些内容。你会发现很多有趣的事情在你的 DllMain
被调用之前完成。
至于DllGetClassObject
:这个导出的函数不是被魔法调用的。该函数的调用者必须做 3 件事 - 加载您的 DLL(例如 LoadLibrary
),查找从 DLL 加载的函数的地址(例如 GetProcAddress
),然后调用该地址使用以 DllGetClassObject
实现而闻名的签名。您可以手动完成所有这些操作,但是像 CoCreateInstance
这样的函数会按照此处描述的顺序自动执行这三件事。 DllMain
会在 LoadLibrary
被调用时自动调用,而 LoadLibrary
不会 return 直到 DllMain
return 之后。如果 DllMain
return 是一个成功代码,那么 LoadLibrary
的调用者将收到 DLL 的 HMODULE
。如果 DllMain
return 是错误代码,调用者将永远不会有 DLL 的 HMODULE
(LoadLibrary
将 return NULL
),并且因此调用者甚至无法找到您的 DllGetClassObject
函数,更不用说调用它了。