删除在 DLL 中创建的对象

Posted

技术标签:

【中文标题】删除在 DLL 中创建的对象【英文标题】:Deleting an object which was created in a DLL 【发布时间】:2011-07-20 13:41:19 【问题描述】:

在时,我需要对运行时/堆问题进行一些说明。在我提出问题之前需要一些介绍......

在我的项目中,一个 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));

这也有效。但是我的第二个问题来了:静态函数createGrabberObjectdeleteGrabberObject 都加载了DLL。这是否意味着因为加载了 DLL 的两个实例而创建了两个不同的堆(那么这个解决方案根本无法解决我的问题)?还是这两个静态函数使用同一个堆?

我希望有人能解释一下这里发生了什么......

【问题讨论】:

【参考方案1】:

DLL 是引用计数的,不会加载两次,当您使用 LoadLibrary 时,无论如何它只会加载一次,它们将使用相同的堆。静态函数是这个问题的正常解决方案。

【讨论】:

谢谢!这真是太快了!【参考方案2】:

对于第二个问题,仅仅因为您两次加载 DLL 并不意味着有两个实例。操作系统足够智能,只加载一次。

编辑:对于第一个问题,这可能是因为共享指针实际上从未超出范围,或者因为 VC 运行时无法正确检测到这种情况(它没有致命地失败,但内存没有释放)。

【讨论】:

感谢您的快速回复!实际上共享指针超出范围(DLL 中~SomeSpecificGrabber 中的断点被命中)。所以看起来 VC 运行时没有检测到它。【参考方案3】:

嗯,这是因为(在您的情况下)有两个堆在工作。 DLL 有另一个堆管理器,而 EXE 有不同。这可能是因为:

调试/发布矛盾 使用的 VC 运行时(例如一个是 VC8,一个是 VC9)。上面也复合了! 用于构建 DLL/EXE 的不同模型(/MT[d] 标志,作为静态库链接等)。 您已针对有效的new 显示delete。但这也可能是 new 实际上是 malloc/HeapAlloc 的情况。

简而言之,X heap-manager 分配的内存不会被 Y heap-manager 找到,因此断言!

【讨论】:

以上是关于删除在 DLL 中创建的对象的主要内容,如果未能解决你的问题,请参考以下文章

如何删除在 AWS Elastic Beanstalk 中创建的 CNAME 映射

如何使用inotify自动删除linux中创建的文件?

在前块中创建的 Rspec 变量在功能规范到达 POSTed 控制器操作时被删除

如何使用 javascript 中的 mouseover 事件删除我在 javascript 中创建的 DOM 节点?

如何从Storyboard中创建的静态UITableView中删除单元格

如何释放在子对话框中创建的 CWin 对象以避免内存泄漏