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

时间:2016-12-01 21:43:33

标签: c++ c++-cli

场合

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

重要

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

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

结果

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

期望的结果

运行ClassB析构函数

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,并且当ClassB被破坏时也是'destruct'。

1 个答案:

答案 0 :(得分:0)

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

调用从非托管到托管的内容仍然需要特别注意,因为不这样做会导致异常:0xC0020001:字符串绑定无效!见this。我解决这个问题的方法是在ClassB范围内使用静态实例并使用ClassB :: GetInstance中的new运算符初始化它。否则它根本不会被初始化。然后我创建了一个函数ClassB :: CleanUp,我将其删除。然而,重要的是用#pragma managed(push,off)和#pragma managed(pop)标记整个类(头文件和源文件)不受管理。

要仍然能够调用托管方法/类,您必须在源文件中创建一个函数,该文件已被#pragma managed(push,on)和#pragma managed(pop)包围。然后,您可以从非托管类中调用此函数。这对我来说仍然很奇怪,因为这个功能也被管理了吗?