场合
我无法更改的外部库(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'。
答案 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)包围。然后,您可以从非托管类中调用此函数。这对我来说仍然很奇怪,因为这个功能也被管理了吗?