未调用第二个 dll 中静态实例的析构函数

Destructor of static instance in second dll not being called

情况

我无法更改的外部库 (LibraryExternal) 在 LibraryA 上调用 LoadLibrary。成功加载后,它调用导出函数 AExport,其中 returns 指向 ClassA 的指针,这是一个静态实例。在 AExport returns 之前,它也通过 LoadLibrary 加载一个名为 LibraryB 的库。成功加载后,它调用导出函数 BExport,后者又 returns 指向静态实例 ClassB 的指针。

重要

LibraryA 是用 vs2012 xp 工具编译的 c++ dll,LibraryB 是用 vs2012 xp 工具编译的 c++/cli dll。

所有库都共享一些其他库,这些库只定义了 ClassA 和 ClassB 需要派生的内容,以便理解 AExport 和 BExport 返回的指针。它们只不过是存根,在这个问题中无关紧要(只有纯虚函数,没有字段,ctor/dtor 中什么也没做)。

结果

当 LibraryExternal 通过程序退出被卸载时,它调用 LibraryA 上的 FreeLibrary。这成功调用了 ClassA 的析构函数,后者又释放了库 LibraryB。但是 ClassB 的析构函数永远不会 运行 不知何故。

想要的结果

有B类析构函数运行

ClassA.h

#include <StubA.h>

class StubB;

class ClassA: public StubA
{
    public:
        ClassA();
        ~ClassA();

        bool Initialize();

        static ClassA &GetInstance()
        {
            static ClassA INSTANCE;

            return INSTANCE;
        }

    private:
        ClassA(ClassA const &);
        void operator=(ClassA const&);

        HMODULE wrapperModule;
        StubB *wrapperPlugin;
};

ClassA.cpp

#include "ClassA.h"

#include <Windows.h>

// typedef WrapperPlugin *(*WrapperPluginInitType) (); This is normally in shared library

static const wchar_t *WRAPPER_MODULE_NAME = L"LibraryB.dll";
static const char *WRAPPER_MODULE_INIT_FUNCTION_NAME = "BExport";

ClassA::ClassA() :
    wrapperModule(NULL),
    wrapperPlugin(NULL)
{

}

ClassA::~ClassA()
{
    if (this->wrapperModule != NULL)
    {
        FreeLibrary(this->wrapperModule);
    }
}

bool CSharpBridge::Initialize()
{
    this->wrapperModule = LoadLibraryW(WRAPPER_MODULE_NAME);
    if (this->wrapperModule == NULL)
    {
        return false;
    }

    WrapperPluginInitType wrapperPluginInit = reinterpret_cast<WrapperPluginInitType>(GetProcAddress(this->wrapperModule, WRAPPER_MODULE_INIT_FUNCTION_NAME));
    if (wrapperPluginInit == NULL)
    {
        return false;
    }

    this->wrapperPlugin = wrapperPluginInit();
    if (this->wrapperPlugin == NULL)
    {
        return false;
    }

    return true;
}

extern "C"
{
    __declspec(ddlexport) StubA *AExport()
    {
        if (!ClassA::GetInstance().Initialize())
        {
            return NULL;
        }

        return &ClassA::GetInstance();
    }
}

ClassB.h

#include <StubB.h>

class ClassB : public StubB
{
    public:
        ClassB ();
        ~ClassB ();

        static ClassB &GetInstance()
        {
            static ClassB INSTANCE;

            return INSTANCE;
        }

    private:
        ClassB (ClassB const &);
        void operator=(ClassB const&);
};

ClassB.cpp

#include "ClassB.h"

#include <Windows.h>
#include <iostream>
#include <fstream>

ClassB::ClassB()
{
    std::ofstream myfile;
    myfile.open("C:\Users\USERNAME\Desktop\test1.txt");
    myfile << "ClassB::ClassB\r\n";
    myfile.close();
}

ClassB::~ClassB()
{
    std::ofstream myfile;
    myfile.open("C:\Users\USERNAME\Desktop\test3.txt");
    myfile << "ClassB::~ClassB\r\n";
    myfile.close();
}

extern "C"
{
    __declspec(dllexport) StubB *WrapperInit()
    {
        std::ofstream myfile;
        myfile.open("C:\Users\USERNAME\Desktop\test2.txt");
        myfile << "WrapperInit\r\n";
        myfile.close();

        return &ClassB::GetInstance();
    }
}

现在我 100% 确定 ClassA ctor/dtor 被调用是由于一些 LibraryExternal 函数给了我一些文本确认。而且我似乎正在生成 test1.txt 和 test2.txt。但不是 test3.txt.

在此之后我仍然需要创建一个对 LibraryC 的托管引用,它是一个 C# dll 并且 'destruct' 在 ClassB 被破坏时也是如此。

您似乎无法在由非托管库管理的库上使用 FreeLibrary。由于托管库将启动非托管库对其一无所知的 AppDomain。 AppDomain 使库保持活动状态,因此析构函数从未 运行。请参阅 this 答案。

从非托管到托管的调用仍然需要特别注意,因为不这样做会导致异常:0xC0020001:字符串绑定无效!参见 this。我为解决此问题所做的工作是在 ClassB 范围内拥有一个静态实例,并使用 ClassB::GetInstance 中的新运算符对其进行初始化。否则它根本不会被初始化。然后我创建了一个函数 ClassB::CleanUp 并在其中删除了它。然而,使用#pragma managed(push, off) 和#pragma managed(pop).

将整个 class(头文件和源文件)标记为非托管很重要

为了仍然能够调用 managed methods/classes,您必须在源文件中创建一个函数,该函数已被 #pragma managed(push, on) 和 #pragma managed(pop) 包围。然后您可以从非托管 class 调用此函数。对我来说,这仍然是 st运行ge,因为该功能也被管理了?