删除在DLL中创建的对象时,我需要对运行时/堆问题做一些澄清。在我提出问题之前需要一些介绍...
在我的项目中,DLL(由其名称指定)返回一个新的Grabber对象。在我的代码的早期版本中,DLL导出了这样的函数:
extern "C"
__declspec(dllexport) Grabber* CreateGrabber(string settings)
{
return new SomeSpecificGrabber(settings);
}
在EXE中,我使用这样的静态函数来创建一个新的Grabber对象:
static Grabber* createGrabberObject(const std::string& grabberType, const std::string& grabberSettings)
{
FARPROC hProc = 0;
// load dll with the name of grabberType
HMODULE hDLL = LoadLibrary(grabberType.c_str());
// get address for CreateGrabber function
hProc = GetProcAddress(hDLL, "CreateGrabber");
// instantiate a function pointer of our type and typecast the address
// of the CreateGrabber function to this type
CreateGrabberFunctionType CreateGrabberFunction = (CreateGrabberFunctionType)hProc;
// call CreateGrabber in DLL to get a Grabber object
return CreateGrabberFunction(grabberSettings);
}
在EXE中,Grabber对象的生命周期由智能指针管理:
shared_ptr<Grabber> myGrabberObj = shared_ptr<Grabber>(createGrabberObject("SomeGrabber.DLL", "Settings"));
只要我使用/MDd
设置(VC ++ 2010)编译EXE和DLL,这一切都运行正常,这意味着EXE和DLL使用相同的堆。
现在我想用/MTd
设置编译我的解决方案。有了这个,我得到了一个类型_CrtIsValidHeapPointer
的运行时断言,用于我传递给DLL的设置字符串对象。这是有道理的,因为DLL尝试删除在EXE中创建的字符串对象。而且他们不再使用相同的堆了。
我通过稍微更改导出的DLL函数(const char*
而不是string
)解决了这个问题:
extern "C"
__declspec(dllexport) Grabber* CreateGrabber(const char* settings)
{
return new SomeSpecificGrabber(settings);
}
在createGrabberObject
中,我将grabberSettings.c_str()
而不是grabberSettings
传递给DLL函数。
现在一切正常了。但是现在出现了第一个问题:为什么在删除_CrtIsValidHeapPointer
时我没有得到myGrabberObj
断言?该对象是从DLL中创建的,但是从EXE中删除(通过智能指针)。为什么我在这里遇到与上面的字符串对象相同的问题?
我想一个干净的解决方案就是DLL也会导出这样的函数:
extern "C"
__declspec(dllexport) void DeleteGrabber(Grabber* grabber)
{
delete grabber;
}
然后我的EXE中也会有一个静态函数,它在DLL中调用DeleteGrabber:
static void deleteGrabberObject(const std::string& grabberType, Grabber* grabber)
{
FARPROC hProc = 0;
// load dll with the name of grabberType
HMODULE hDLL = LoadLibrary(grabberType.c_str());
// get address for DeleteGrabber function
hProc = GetProcAddress(hDLL, "DeleteGrabber");
// instantiate a function pointer of our type and typecast the address
// of the DeleteGrabber function to this type
DeleteGrabberFunctionType DeleteGrabberFunction = (DeleteGrabberFunctionType)hProc;
// call DeleteGrabber in DLL
DeleteGrabberFunction(grabber);
}
然后,智能指针可以自动调用此静态函数:
shared_ptr<Grabber> myGrabberObj = shared_ptr<Grabber>(createGrabberObject("SomeGrabber.DLL", "Settings"),
boost::bind(deleteGrabberObject, "SomeGrabber.DLL", _1));
这也有效。但是我的第二个问题:静态函数createGrabberObject
和deleteGrabberObject
都加载了DLL。是否意味着创建了两个不同的堆,因为加载了DLL的两个实例(那么这个解决方案根本不能解决我的问题)?或者这两个静态函数是否使用相同的堆?
我希望有人能解释这里发生的事情......
答案 0 :(得分:5)
DLL是引用计数,未加载两次,当您使用LoadLibrary时,它只会加载一次,并且它们将使用相同的堆。静态函数是解决此问题的正常方法。
答案 1 :(得分:3)
对于您的第二个问题,仅仅因为您加载DLL两次并不意味着有两个实例。操作系统非常智能,只能加载一次。
编辑:对于第一个问题,可能是因为共享指针实际上从未超出范围OR,因为VC运行时无法正确检测到案例(它没有致命地失败但是内存不是释放)。
答案 2 :(得分:3)
嗯,这是因为(在你的情况下),两堆正在工作。 DLL有另一个堆管理器,EXE有不同。这可能是因为:
/MT[d]
标志的不同模型,链接为static-lib等)。delete
展示了new
这是有效的。但也可能是new
实际上是malloc / HeapAlloc。简而言之, Y 堆管理器找不到 X 堆管理器分配的内存,因此断言!