未调用第二个 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,因为该功能也被管理了?
情况
我无法更改的外部库 (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,因为该功能也被管理了?