我是Windows编程的新手,我只是"迷失"两个小时寻找一个每个人都知道的错误:你不能在DLL中创建一个对象并在另一个DLL(或主程序)中销毁它.
我几乎可以肯定,在Linux/Unix上,情况并非如此(如果是的话,请说出来,但我很确定我做了数千次而没有问题......).
在这一点上,我有几个问题:
1)静态链接的DLL使用与主程序不同的堆吗?
2)静态链接的DLL是否映射在主程序的同一进程空间中?(我很确定这里的答案是一个很大的问题,否则将主程序中的函数指针传递给DLL中的函数是没有意义的.)
我说的是普通/常规DLL,而不是COM/ATL服务
编辑:通过"静态链接"我的意思是我不使用LoadLibrary加载DLL但我链接到存根库
虽然它似乎是一个非常常见的问题,但我没有收集到太多信息:如何在DLL边界之间创建有关内存分配的安全接口?
众所周知
// in DLL a
DLLEXPORT MyObject* getObject() { return new MyObject(); }
// in DLL b
MyObject *o = getObject();
delete o;
Run Code Online (Sandbox Code Playgroud)
肯定会导致崩溃.但是,由于像上面那样的交互 - 我敢说 - 并不罕见,因此必须有一种方法来确保安全的内存分配.
当然,人们可以提供
// in DLL a
DLLEXPORT void deleteObject(MyObject* o) { delete o; }
Run Code Online (Sandbox Code Playgroud)
但也许有更好的方法(例如smart_ptr?).我也读到了在处理STL容器时使用自定义分配器的问题.
所以我的调查更多的是关于处理这个主题的文章和/或文献的一般指示.是否存在需要注意的特殊谬误(异常处理?)并且这个问题仅限于DLL还是"受到"的UNIX共享对象?
我知道在一个dll中进行的内存分配然后随后在另一个dll中释放可能会导致各种问题,特别是关于CRT.在导出STL容器时,这些问题尤其成问题.我们之前遇到过这些问题(编写与我们的库链接的自定义Adobe插件时),我们通过定义我们在所有容器中使用的自己的分配器来解决这些问题,例如:
typedef std::vector < SessionFields,
OurAllocator < SessionFields > >
VectorSessionFields;
typedef std::set < SessionFields,
std::less < SessionFields >,
OurAllocator < SessionFields > >
SetSessionFields;
Run Code Online (Sandbox Code Playgroud)
在向我们的代码传递类型时,这很有效,但是我们遇到了一个问题,因为我们现在不得不在Adobe的SDK中调用一个函数来返回一个填充的向量,当它超出范围时会导致崩溃.
显然,在我的代码中最终免费的Adobe的SDK属于不同的堆中分配内存是一个问题.所以我想也许我可以做一些聪明的事情,比如以某种方式覆盖或导出SDK中使用的分配器,这样我就可以用它来清理从它们的函数返回的容器.
我也在考虑编写一个包装器或某种类型的thunking层,从而可以在我的代码和SDK之间安全地编组STL容器(虽然这听起来非常混乱).
或者,我也在考虑使用GetProcessHeaps来识别SDK 中使用的堆,并尝试释放这个堆,而不是默认堆.
有没有人就如何解决这个问题提出任何建议?
在使用 Windows dll 时,我们应该限制 dll 边界内的内存分配/释放,因为 dll 可能使用自己的堆。所以我们有从 dll 导出分配器和自由函数。
IsomeInterface* getObject();
void freeObject(IsomeInterface *object);
Run Code Online (Sandbox Code Playgroud)
这样,对象的创建和删除将驻留在 dll 中。
linux上的共享库是否也存在这个问题?在处理共享库 (.so) 时,我们是否还需要注意在共享库中保持分配/解除分配。我在下面做了一些快速试用,它适用于 linux。相同的示例不适用于 windows dll(如果 exe 和 dll 都使用 /MD 编译以使用相同的堆,它将适用于 windows dll)。
std::vector<int> vec;
vec.push_back(42);
PassAVector(vec);
Run Code Online (Sandbox Code Playgroud)
其中PassAVector驻留在共享库
void PassAVector(std::vector<int> &vec)
{
vec.push_back(1); // These would cause reallocation
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
}
Run Code Online (Sandbox Code Playgroud)
这是否意味着 unix 上的共享库与可执行文件共享堆(相当于 Windows 上的 /MD 开关)?
是否可以在 linux 上编译(某些编译器标志)共享库 (.so) 或可执行文件,以便它们开始使用不同的堆(Windows 上的 /MT 开关)并且这个问题浮出水面?
编辑:发现这似乎表明只要编译器是 gcc,就可以跨边界传递 STL。