Returning C++ objects from Windows DLL
由于微软是如何在运行库的非DLL版本中实现堆的,所以从DLL返回C++对象可能会导致问题:
1 2 | // dll.h DLL_EXPORT std::string somefunc(); |
还有:
1 2 3 4 5 | // app.c - not part of DLL but in the main executable void doit() { std::string str(somefunc()); } |
如果dll和exe都是用多线程dll运行库构建的,则上述代码运行良好。
但是,如果构建的dll和exe没有dll运行时库(单线程或多线程版本),则上述代码将失败(在调试运行时,由于断言
两个问题:
您的代码有两个潜在的问题:您解决了第一个单CRT运行时。这里还有另一个问题:std::string可能在VC++版本之间发生更改。事实上,它确实改变了过去。
处理的安全方法是只导出C基本类型。并从DLL中导出创建和释放函数。不导出std::string,而是导出指针。
1 2 3 4 5 6 7 8 9 10 | __declspec(export) void* createObject() { std::string* p = __impl_createObject(); return (void*)p; } __declspec(export) void releasePSTRING(void* pObj) { delete ((std::string*)(pObj)); } |
Is there a way to solve this other then requiring that all code use the DLL runtime?
我不知道。
For people who distribute their libraries to third parties, how do you handle this? Do you not use C++ objects in your API? Do you require users of your library to use the DLL runtime? Something else?
在过去,我发布了一个带有DLL的SDK,但它是基于COM的。通过COM,所有参数和IPC的编组都会自动为您完成。用户也可以通过这种方式与任何语言集成。
这里的一个简单的事实是,微软的实现不在旁边,C++不是ABI。不能在任何平台上从动态模块导出C++对象,并期望它们与不同的编译器或语言一起使用。
从DLL中导出C++类是一个很大的无意义的操作——因为名称的限制,以及C++对动态加载类的支持不足——DLL必须静态加载——因此,您将将一个项目分割成DLL的最大好处是只需要按需加载功能。
如果您有一个要分发的DLL,并且不想将调用方绑定到C-runtime的特定版本,请执行以下任一操作:
i.将DLL链接到C运行库的静态版本。从VisualStudio项目属性页中,选择配置属性的选项卡--> C/C++>代码生成。这是一个选择"运行库"的选项。选择"多线程"或"多线程调试"而不是dll版本。(命令行等效为/mt或/mtd)
这种方法有几个不同的缺点:
A.如果微软曾经发布过CRT的安全补丁,那么在您重新编译和重新测试二进制文件之前,您附带的组件可能会受到攻击。
b.由dll中的"malloc"或"new"分配的堆指针不能由exe或其他二进制文件"free"d或"delete"d。否则你会撞车的。对于fopen创建的文件句柄也是如此。您不能调用teh dll中的fopen并期望exe能够在其上强制关闭。再次,如果你这样做的话,撞车。您将需要构建到您的DLL的接口,以适应所有这些问题。对于初学者来说,将实例返回到std::string的函数可能是一个问题。提供由DLL导出的函数,以根据需要处理资源释放。
其他选项:
二。不带C运行时深度的发货。这有点难。首先必须从代码中删除对CRT的所有调用,提供一些存根函数以使DLL链接,并指定"无默认库"链接选项。可以做到。
通过使用COM接口指针,可以从DLL干净地导出C++类。您仍然需要在上面的1a中解决这些问题,但是ATL类是一个很好的方法来避免COM的开销。
有一种方法可以解决这个问题,但这有点不平凡。与库中的大多数其他部分一样,
您可以提供自己的分配器,该分配器使用自己的堆分配例程,这些例程是DLL和可执行文件所共有的,例如使用