【发布时间】:2011-07-20 13:41:19
【问题描述】:
在删除在 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 函数。
现在一切都恢复正常了。但是现在我的第一个问题来了:当myGrabberObj 被删除时,为什么我没有得到_CrtIsValidHeapPointer 断言?该对象是从 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 的两个实例而创建了两个不同的堆(那么这个解决方案根本无法解决我的问题)?还是这两个静态函数使用同一个堆?
我希望有人能解释一下这里发生了什么......
【问题讨论】: