从Windows DLL返回C ++对象

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运行时库(单线程或多线程版本),则上述代码将失败(在调试运行时,由于断言_CrtIsValidHeapPointer(pUserData)失败,代码将立即中止;在非调试运行时,堆将损坏,程序最终在其他地方失败)。

两个问题:

  • 是否有一种方法可以解决这一问题,然后要求所有代码都使用dll运行时?
  • 对于将图书馆分发给第三方的人,您如何处理这个问题?在API中不使用C++对象吗?是否要求库的用户使用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的开销。


    有一种方法可以解决这个问题,但这有点不平凡。与库中的大多数其他部分一样,std::string不直接使用new分配内存,而是使用分配器(默认情况下为std::allocator)。

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