我将尝试通过一个例子来解释我的问题。
我有以下课程:
__declspec(dllexport) class myclass
{
public:
int a;
int b;
myclass() {};
virtual ~myclass() {};
// ~myclass() {};
};
myclass 用于 DLL 中,该DLL仅包含类 Test 的定义(实际上只是方法的接口) foo1 和 foo2 ):
class Test
{
public:
void foo1 (std::vector<myclass>& val);
std::vector<myclass> foo2 ();
};
extern "C"
{
__declspec(dllexport) bool foo1(std::vector<myclass>& val)
{
val.clear();
myclass tmp;
val.push_back(tmp);
val.push_back(tmp);
val.push_back(tmp); //just an example!
return true;
}
__declspec(dllexport) std::vector<myclass> foo2()
{
std::vector<myclass> val;
myclass tmp;
val.push_back(tmp);
val.push_back(tmp);
val.push_back(tmp); //just an example!
return val;
}
}
主应用程序打开DLL并调用接口方法以获得由方法 foo1 或 foo2 填充的向量。
typedef void (*FNPTR1)(std::vector<myclass>& val);
typedef std::vector<myclass> (*FNPTR2)();
int main()
{
if (0)
{
HINSTANCE hInst = Loadlibrary(L"C:\\software\\mydll.dll");
if (!hInst) { std::cout << "\nCould Not Load the library"; return EXIT_FAILURE; }
FNPTR1 fn = (FNPTR1)GetProcAddress(hInst,"foo1");
if (!fn) { std::cout << "\nCould not locate the function"; return EXIT_FAILURE; }
std::vector<myclass> tmp;
fn(tmp);
Freelibrary(hInst);
tmp.clear(); //crash here!
}
if (1)
{
HINSTANCE hInst = Loadlibrary(L"C:\\software\\mydll.dll");
if (!hInst) { std::cout << "\nCould Not Load the library"; return EXIT_FAILURE; }
FNPTR2 fn = (FNPTR2)GetProcAddress(hInst,"foo2");
if (!fn) { std::cout << "\nCould not locate the function"; return EXIT_FAILURE; }
std::vector<myclass> tmp;
tmp = fn();
Freelibrary(hInst);
tmp.clear(); //crash here!
}
return 1;
}
}
不幸的是,在两种情况下(使用 foo1 或 foo2 ),在以下情况下,我都会收到一个“读取XXXX的访问冲突” 错误卸载dll后,我尝试在 tmp 向量上清除(或执行其他任何操作)。
仅当 myclass 的析构函数是虚拟的时,才会发生此问题。
在我的应用程序中, myclass 是自动生成的,不允许我修改析构函数定义。 但是,如果在该级别存在某种解决方案,我可以自由修改dll接口方法(即foo1 / foo2)。
我还想知道是否可以通过某种方式告诉系统将 tmp 向量用于主程序的堆,而不是.dll使用的堆。
我当前正在使用Visual Studio c ++ 2017,并且可以使用C ++ 11标准(或更高版本)
谢谢您的帮助。
回答以下评论:
- 是的,exe和dll都是使用相同的工具链编译的。我正在使用/ MD选项。请告诉我是否应该检查其他内容。
-
关于关于 myclass 派生的评论,我还尝试了使用基类 myclassbase 和 myclass 的所有四种可能的组合>衍生自:
1-具有标准析构函数的myclassbase和具有标准析构函数的myclass->确定
2-具有标准析构函数的myclassbase和具有虚拟析构函数的myclass->崩溃
3-具有虚拟析构函数的myclassbase和具有标准析构函数的myclass->崩溃
4-具有虚拟析构函数的myclassbase和具有虚拟析构函数的myclass->崩溃
因此,我可以得出结论,仅存在一个虚拟析构函数就足以产生问题。