跨DLL边界的内存分配和释放

9

我知道在一个动态链接库中分配的内存,后来在另一个动态链接库中释放可能会引起各种问题,特别是涉及到CRT时。当涉及到导出STL容器时,这些问题尤其棘手。我们以前曾经遇到过这些问题(在编写自定义Adobe插件并链接到我们的库时),我们通过定义自己的分配器并在所有容器中使用它来解决了这些问题,例如:

typedef std::vector < SessionFields, 
        OurAllocator < SessionFields > > 
        VectorSessionFields;

typedef std::set < SessionFields, 
        std::less < SessionFields >, 
        OurAllocator < SessionFields > > 
        SetSessionFields;

在向/从我们的代码传递类型时,这种方法很有效,但是现在我们遇到了一个问题,即我们现在必须调用Adobe的SDK中返回填充向量的函数,当它超出范围时会导致崩溃。

显然,这是一个内存问题,Adobe的SDK中分配的内存属于另一个堆栈,在我的代码中最终释放。因此,我想也许我可以做一些聪明的事情,比如某种方式覆盖或导出他们SDK中使用的分配器,以便我可以使用它来清理从他们的函数返回的容器。

我还在考虑编写包装器或某种缓冲层,从而可以安全地在我的代码和SDK之间传递STL容器(尽管这听起来非常混乱)。

或者,我还在考虑使用GetProcessHeaps来识别SDK中使用的堆栈,并尝试针对该堆栈进行释放,而不是默认堆栈。

有人能给出解决这个问题的建议吗?

3个回答

9
Ironically enough,Adobe Source Libraries有一个adobe::capture_allocator类,是专门为这种DLL安全性而编写的。它的工作方式是在实例化时捕获本地的newdelete,并将它们一起传递对象的生命周期。(详见adobe::new_delete_t,特别是此处的实现细节。)使用捕获的delete例程进行释放,确保无论您在何处都能正确删除。
您可以在Adobe源代码库中的version_1类型中看到capture_allocator的使用,例如adobe::any_regular_tadobe::copy_on_writecapture_allocator应与所有STL容器类型兼容。
更新:由于保留状态,capture_allocator不符合标准。这不会对其可用性造成太大阻碍,但这意味着它的使用不能保证与符合标准的容器一起工作。

这是一种非常常见的技术,我在 C 中也看到过,其中一个库要求其用户通过某种库 init() 点提供分配/释放回调。 - Justin
1
非常晚的更新:这应该在现代C++中工作;“非标准”适用于C++98。并且使用std::scoped_allocator_adaptor,即使您有一个std::vectorstd::string,这也可以工作。然后字符串将使用向量捕获的分配器。 - MSalters

2

目前我们正在开发一个dll,通过C接口暴露C++功能(为了让C#能够使用该dll)。

例如:该dll有一个结构体myStruct_s,接口公开以下函数:

interface.h

#ifndef INTERFACE_TYPES_H
# error Please include interace_types.h
#endif
    myStruct_s * CreateTheStruct() { return new myStruct_s(); }
    void DestroyTheStruct(myStruct_s * the_struct) { delete the_struct; }
    void DoSomethingToTheStruct(myStruct_s * the_struct);

interface_types.h

#define INTERFACE_TYPES_H
struct myStruct_s; // fwd declaration
#endif

interface.cpp

#if defined(__CPPPLUS) || defined(__cplusplus) || defined (__CPLUSPLUS)
#include<TheRealFileContainingTheRealMyStruct_s.h>
// handle the .h's functions here
#endif

comeOutsideCppFile.cpp

#include "interface_types.h"
#include "interface.h"

void main()
{
    myStuct_s * x = CreateTheStruct;
    DoSomethingToTheStruct(x);
    DestroyTheStruct(x);
}

上述是我们的东西如何工作的粗略概述,基本上: 无论dll暴露什么,都需要在dll端创建、处理和销毁 这段代码不是100%准确的!!!
此外,请记住,如果您使用纯C ++ dll,则可能需要与用于构建dll的相同编译器和相同设置。

1
@Maciek:interface.h 应该包含非内联函数。将函数设为内联会使它们编译在 comeOutsideCppFile.cpp 的编译单元中,这破坏了概念。 - shojtsy

0
你可以尝试查看一下是否有适用于 C++ 异常的正式规则,来了解当一个 DLL 中抛出异常,另一个 DLL 捕获并超出作用域时会发生什么 —— 这似乎非常相似。对于异常,我认为您需要提供一个具有特殊签名的复制构造函数,尽管现在我不确定确切的是什么。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接