被称为
形势被称为
外部库(LibraryExternal),我不能改变LibraryA的调用LoadLibrary。在它成功加载后,它会调用一个导出的函数AExport,它返回一个指向ClassA的指针,该指针是一个静态实例。在AExport返回之前,它也通过称为LibraryB的LoadLibrary加载一个库。在成功加载后,它调用一个导出的函数BExport,该函数继而返回一个指向静态实例ClassB的指针。
重要
LibraryA的是一个C++ DLL与vs2012 XP工具编译和LibraryB是一个C++/CLI DLL也与vs2012 XP工具编译。
为了理解AExport和BExport返回的指针,所有库都共享一些只定义ClassA和ClassB需要派生的库。它们只不过是存根(stub)而已,在这个问题中并不重要(只有纯虚函数,没有字段,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的构造函数/析构函数调用,由于一些LibraryExternal功能,这给我一些文本确认。我似乎得到了生成test1.txt和test2.txt。但不是test3.txt。
在此之后,我仍然需要创建一个托管的引用LibraryC这是一个C#DLL和'破坏',当ClassB被破坏。
似乎无法在从非托管库管理的库上使用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)包围的函数。然后你可以从非托管类调用这个函数。这对我来说还是很奇怪,因为这个功能也是被管理的?
至少解释你为什么downvoted所以我可以做得更好下一次.. – Neijwiert
嗯,我可以看到如何发生。当你要求志愿者投入他们的空闲时间来帮助你时,最起码的礼貌是发布可以编译和运行的代码来重现问题。此代码不会编译,也不会运行。相当简单的照顾,只是先编译和运行它自己。 –