【问题标题】:C++ Static objects in DLLs not having destructors calledDLL 中的 C++ 静态对象没有调用析构函数
【发布时间】:2014-03-19 08:35:51
【问题描述】:

我遇到了一个问题,即 DLL 中的静态析构函数没有被调用。正在调用构造函数,但没有调用析构函数。我的 DLL 中有一个这样的类

struct DLLDestructorTest
{
    int blah;
    DLLDestructorTest()
    {
        blah = 2;
    }
    ~DLLDestructorTest()
    {
        blah = 0;
    }
};
DLLDestructorTest DLLDestructorTestObj;

我可以在构造函数和析构函数中放置断点,并且可以验证构造函数断点是否命中而析构函数断点未命中。如果我在 main.cpp 文件中放入类似的代码块,我可以验证构造函数和析构函数都会被调用。

构造函数调用时的调用栈如下。

mydll.dll!mydll::DLLDestructorTest::DLLDestructorTest()  Line 1093  C++
mydll.dll!mydll::`dynamic initializer for 'DLLDestructorTestObj''()  Line 1100 + 0x26 bytes C++
msvcr90d.dll!_initterm(void (void)* * pfbegin=0x000007fee7f521a8, void (void)* * pfend=0x000007fee7f52bf8)  Line 903    C
mydll.dll!_CRT_INIT(void * hDllHandle=0x000007fee7920000, unsigned long dwReason=1, void * lpreserved=0x00000000002cf580)  Line 323 C
mydll.dll!__DllMainCRTStartup(void * hDllHandle=0x000007fee7920000, unsigned long dwReason=1, void * lpreserved=0x00000000002cf580)  Line 540 + 0x13 bytes  C
mydll.dll!_DllMainCRTStartup(void * hDllHandle=0x000007fee7920000, unsigned long dwReason=1, void * lpreserved=0x00000000002cf580)  Line 511    C

这基本上与此网页http://msdn.microsoft.com/en-us/library/988ye33t.aspx 上的描述相同,此页面提供了有关如何调用构造函数的详细信息,但没有关于如何调用析构函数的详细信息。在下面的段落中,它提到了有关调用哪些函数来调用构造函数的详细信息,我可以在我的调用堆栈中看到。

“C/C++ 运行时库代码执行 DLL 启动序列,无需像在 Windows 3.x 中那样与单独的模块链接。C/C++ 运行时库代码中包括DLL 入口点函数称为 _DllMainCRTStartup。_DllMainCRTStartup 函数做了几件事,包括调用 _CRT_INIT,它初始化 C/C++ 运行时库并在静态、非局部变量上调用 C++ 构造函数。没有这个函数,运行时库将处于未初始化状态。_CRT_INIT 可用于静态链接的 CRT 或从用户 DLL 链接到 CRT DLL Msvcr90.dll。"

我在这里找到了一篇帖子What happens to global variables declared in a DLL?,但这似乎只是说明将调用析构函数。

任何关于为什么不调用析构函数的想法将不胜感激。

谢谢, 约翰·劳里

更新

我已经看过这个并阅读了有关 DLL 的信息,但仍然没有解决方案。 DLL 与 __declspec(dllexport) 隐式链接。据我了解,卸载 DLL 不需要做任何特别的事情。它应该在进程终止时自动卸载。

我按照本页http://msdn.microsoft.com/en-us/library/ms235636(v=vs.90).aspx 上的说明制作了一个非常简单的DLL。我对从此项目调用析构函数没有任何问题。

我尝试比较这个简单项目和我当前的项目之间发生的情况。如果我将断点放在简单的测试项目DLL析构函数中,我可以查看调用堆栈并看到析构函数是从

crtdll.c line 447
crtdll.c line 560
crtdll.c line 510

在我当前的项目中,我可以看到它正在进入一个不同的 __DllMainCRTStartup 函数并在第 521 行开始执行。它最终到达了第 447 行的同一行,即 (*function_to_call)();但我的析构函数没有被调用。

如果有用的话,我调用的 DLL 是来自 juce.com 的 JUCE

谢谢

【问题讨论】:

  • 对不起,如果这是一个非常幼稚的评论,但是......你如何卸载你的图书馆?你的程序是如何终止的?如果进程退出,那么肯定会调用析构函数,如果终止则不会。
  • 尝试使用cout 等代替blah = 0 打印内容,看看是否打印/记录了任何内容。
  • 使用调试器和设置断点会掩盖正在发生的事情。除了使用调试器之外,您应该做的是调用诸如OutputDebugString("Destructor is called") 之类的函数。然后您检查该字符串是否显示在您的调试监视器/输出窗口(如果您正在使用 Visual Studio,或者如果没有,则类似于 www.sysinternals.com 中的 DebugView)。
  • 我已经使用 OutputDebugString 验证了析构函数不是为 DLL 中的析构函数调用的,而是为我的 main.cpp 文件中的析构函数调用的。我现在要研究这个 DLL 是如何被卸载的。据我所知,程序正常退出,但我将对此进行调查。
  • 所以我检查了我的程序流程,这就是我发现的。该程序似乎正常退出,没有异常或中止调用。它从 crtexe.c 中的 WinMain 返回,状态为 0。然后 WinMainCRTStartup 调用 exit(0)。它在我的 main.cpp 中的静态类的析构函数被调用。

标签: c++ dll destructor


【解决方案1】:

静态析构函数通常由 C++ 运行时注册以通过 atexit() 函数运行。但它们只为应用程序注册,而不是 DLL。原因是可以动态加载和卸载 DLL。如果 DLL 注册了 atexit 函数,则这些函数指针会在 DLL 卸载时悬空,并在程序实际退出时导致崩溃。

尽量避免 DLL 中的静态对象。如果您绝对需要它们,则在库中公开初始化和清理函数,以便用户处理。然后将这些变量转换为指针,并在 init 时 new 它们,在 deinit 时 delete 它们。

【讨论】:

    【解决方案2】:

    这是我的经验。

    我使用 LoadLibrary 显式绑定了我的 DLL。 当调用 FreeLibrary 时,在析构函数中发生了一些访问冲突。 立即卸载 DLL,并在从 (*function_to_call)(); 调用的析构函数之后卸载在 DLL 中已被取消。

    一个奇怪的事情是发生访问冲突时 Visual Studio 并没有中断。 然后从 FreeLibrary 继续执行,就好像程序没有错误地完成一样。 它看起来一切正常,除了一些没有调用析构函数的静态对象。

    我发现了以下设置的错误。当我的 DLL 的析构函数内部发生访问冲突时,这改变了 Visual Studio 的行为。

    Ctrl+Alt+E 显示此对话框。 当时我用的是Visual Studio 2010。

    【讨论】:

      猜你喜欢
      • 2012-12-26
      • 1970-01-01
      • 1970-01-01
      • 2020-12-25
      • 2014-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-29
      相关资源
      最近更新 更多