从Windows DLL返回C ++对象

由于Microsoft如何在运行时的非DLL版本中实现堆,从DLL返回一个C ++对象可能会导致问题:

// dll.h DLL_EXPORT std::string somefunc(); 

和:

 // app.c - not part of DLL but in the main executable void doit() { std::string str(somefunc()); } 

上述代码运行正常,只要DLL和EXE都是使用multithreadingDLL运行时库构build的。

但是,如果DLL和EXE是在没有DLL运行时库(单线程或multithreading版本)的情况下生成的,则上面的代码会失败(使用debugging运行时,由于断言_CrtIsValidHeapPointer(pUserData)失败,代码会立即中止;非debugging运行时,堆被损坏,程序最终在别处失败)。

两个问题:

  1. 有没有办法解决这个问题,然后要求所有的代码使用DLL运行时?
  2. 对于将图书馆分发给第三方的人,你如何处理这个问题? 你不在你的API中使用C ++对象吗? 你是否需要库的用户使用DLL运行时? 别的东西?

有办法解决这个问题,但它有点不重要。 像大多数库的其余部分一样, std::string不直接使用new分配内存 – 相反,它使用分配器(默认情况下是std::allocator<char> )。

您可以提供自己的分配器,使用您自己的堆分配例程,这些例程是DLL和可执行文件共有的,例如使用HeapAlloc获取内存,并从那里分配块。

有没有办法解决这个问题,然后要求所有的代码使用DLL运行时?

从来没听说过。

对于将图书馆分发给第三方的人,你如何处理这个问题? 你不在你的API中使用C ++对象吗? 你是否需要库的用户使用DLL运行时? 别的东西?

在过去,我分发了一个SDK的W / DLL,但它是基于COM。 使用COM,所有参数和IPC的编组都会自动完成。 用户也可以用这种方式与任何语言进行整合。

你的代码有两个潜在的问题:你解决了第一个问题 – CRT运行时。 你还有另外一个问题:std :: string可能会在VC ++版本中改变。 事实上,过去确实发生了变化。

处理的安全方法是仅导出C基本类型。 并导出从DLL创建和释放函数。 而不是导出一个std :: string,导出一个指针。

 __declspec(export) void* createObject() { std::string* p = __impl_createObject(); return (void*)p; } __declspec(export) void releasePSTRING(void* pObj) { delete ((std::string*)(pObj)); } 

如果您有要分发的DLL,并且不希望将调用程序绑定到C运行时的特定版本,请执行以下任一操作:

I.将DLL链接到C运行时库的静态版本。 从Visual Studio项目属性页面,选择配置属性 – > C / C ++ – >代码生成选项卡。 这些用于选择“运行时库”的选项。 选择“多线程”或“多线程调试”而不是DLL版本。 (命令行等于/ MT或/ MTd)

这种方法有几个不同的缺点:

一个。 如果微软发布了CRT的安全补丁,那么在你重新编译并重新编译你的二进制文件之前,你的组件可能会受到攻击。

湾 在DLL中由“malloc”或“new”分配的堆指针不能由EXE或其他二进制“免费”或“删除”d。 否则你会崩溃。 对于由fopen创建的FILE句柄也是如此。 您不能在DLL中调用fopen,并期望EXE能够在它上面调用。 再次,如果你这样做,会崩溃。 您将需要构建到您的DLL的接口,以适应所有这些问题。 对于初学者来说,将一个实例返回给std :: string的函数可能会成为一个问题。 提供您的DLL导出的函数,以根据需要处理资源释放。

其他选项:

II。 没有c运行时依赖。 这有点困难。 您首先必须从代码中移除所有对CRT的调用,提供一些存根函数来获取DLL链接,并指定“无默认库”链接选项。 可以做到。

III。 C ++类可以使用COM接口指针从DLL中完整地导出。 你仍然需要解决上面1a中的问题,但是ATL类是避免COM开销的好方法。

这里简单的事实是,抛开微软的实现,C ++不是ABI。 您不能在任何平台上从动态模块中导出C ++对象,并期望它们使用不同的编译器或语言。

从Dll中导出c ++类是一个毫无意义的练习 – 由于名称的改变,以及c ++中缺少对动态加载类的支持 – dll必须静态加载 – 所以你放弃了将项目拆分为dll的最大好处 – 只能根据需要加载功能的能力。