简体   繁体   English

从Windows DLL返回C ++对象

[英]Returning C++ objects from Windows DLL

Due to how Microsoft implements the heap in their non-DLL versions of the runtime, returning a C++ object from a DLL can cause problems: 由于Microsoft如何在其运行时的非DLL版本中实现堆,因此从DLL返回C ++对象可能会导致问题:

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

and: 和:

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

The above code runs fine provided both the DLL and the EXE are built with the Multi-threaded DLL runtime library. 如果DLL和EXE都是使用多线程DLL运行时库构建的,则上面的代码运行良好。

But if the DLL and EXE are built without the DLL runtime library (either the single or multi-threaded versions), the code above fails (with a debug runtime, the code aborts immediately due to the assertion _CrtIsValidHeapPointer(pUserData) failing; with a non-debug runtime the heap gets corrupted and the program eventually fails elsewhere). 但是,如果在没有DLL运行时库(单线程或多线程版本)的情况下构建DLL和EXE,则上面的代码将失败(使用调试运行时,由于断言_CrtIsValidHeapPointer(pUserData)失败,该代码将立即中止;并带有一个非调试运行时,堆会损坏,程序最终会在其他地方失败。

Two questions: 两个问题:

  1. Is there a way to solve this other then requiring that all code use the DLL runtime? 有没有办法解决这个问题,然后要求所有代码都使用DLL运行时?
  2. For people who distribute their libraries to third parties, how do you handle this? 对于将图书馆分发给第三方的人,您该如何处理? Do you not use C++ objects in your API? 您不在API中使用C ++对象吗? Do you require users of your library to use the DLL runtime? 是否要求您的库用户使用DLL运行时? Something else? 还有吗

Is there a way to solve this other then requiring that all code use the DLL runtime? 有没有办法解决这个问题,然后要求所有代码都使用DLL运行时?

Not that I know of. 从来没听说过。

For people who distribute their libraries to third parties, how do you handle this? 对于将图书馆分发给第三方的人,您该如何处理? Do you not use C++ objects in your API? 您不在API中使用C ++对象吗? Do you require users of your library to use the DLL runtime? 是否要求您的库用户使用DLL运行时? Something else? 还有吗

In the past I distributed an SDK w/ dlls but it was COM based. 过去我分发了一个带有dll的SDK,但它是基于COM的。 With COM all the marshalling of parameters and IPC is done for you automatically. 使用COM,所有参数和IPC的编组工作都会自动为您完成。 Users can also integrate in with any language that way. 用户还可以通过这种方式与任何语言集成。

Your code has two potential problems: you addressed the first one - CRT runtime. 您的代码有两个潜在问题:解决了第一个问题-CRT运行时。 You have another problem here: the std::string could change among VC++ versions. 您在这里还有另一个问题:std :: string在VC ++版本之间可能会更改。 In fact, it did change in the past. 实际上,它确实在过去发生了变化。

The safe way to deal with is to export only C basic types. 安全的处理方法是仅导出C基本类型。 And exports both create and release functions from the DLL. 并从DLL导出创建和释放函数。 Instead of export a std::string, export a pointer. 代替导出std :: string,而是导出指针。

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

__declspec(export)  void releasePSTRING(void* pObj)
{   
     delete ((std::string*)(pObj));
}

There is a way to deal with this, but it's somewhat non-trivial. 有一种方法可以解决这个问题,但这并不简单。 Like most of the rest of the library, std::string doesn't allocate memory directly with new -- instead, it uses an allocator ( std::allocator<char> , by default). 像该库的其余大部分内容一样, std::string不会直接使用new来直接分配内存,而是使用分配器(默认情况下为std::allocator<char> )。

You can provide your own allocator that uses your own heap allocation routines that are common to the DLL and the executable, such as by using HeapAlloc to obtain memory, and suballocate blocks from there. 您可以提供自己的分配器,该分配器使用DLL和可执行文件共有的自己的堆分配例程,例如使用HeapAlloc获取内存,然后从那里分配块。

If you have a DLL that you want to distribute and you don't want to bind your callers to a specific version to the C-Runtime, do any of the following: 如果您具有要分发的DLL,并且不想将调用者绑定到C运行时的特定版本,请执行以下任一操作:

I. Link the DLL to the static version of the C-Runtime library. I.将DLL链接到C运行时库的静态版本。 From the Visual Studio Project Properties page, select the tab for Configuration Properties-> C/C++ -> Code Generation. 从“ Visual Studio项目属性”页面,为“配置属性”->“ C / C ++”->“代码生成”选择选项卡。 These an option for selecting the "Runtime library". 这些选项用于选择“运行库”。 Select "Multithread" or "Multithreaded Debug" instead of the DLL versions. 选择“多线程”或“多线程调试”,而不是DLL版本。 (Command line equilvalent is /MT or /MTd) (等效于命令行为/ MT或/ MTd)

There are a couple of different drawbacks to this approach: 这种方法有几个不同的缺点:

a. 一种。 If Microsoft ever releases a security patch of the CRT, your shipped components may be vulnerable until your recompile and redist your binary. 如果Microsoft曾经发布过CRT的安全补丁,则在重新编译并重新分配二进制文件之前,出厂的组件可能很容易受到攻击。

b. b。 Heap pointers allocated by "malloc" or "new" in the DLL can not be "free"d or "delete"d by the EXE or other binary. DLL或其他二进制文件不能“释放”或“删除” DLL中由“ malloc”或“ new”分配的堆指针。 You'll crash otherwise. 否则,您将崩溃。 The same holds true for FILE handles created by fopen. fopen创建的FILE句柄也是如此。 You can't call fopen in teh DLL and expect the EXE to be able to fclose on it. 您不能在DLL中调用fopen并期望EXE能够对其进行fclose。 Again, crash if you do. 同样,如果发生崩溃。 You will need to build the interface to your DLL to be resilient to all of these issues. 您将需要构建DLL的接口,以应对所有这些问题。 For starters, a function that returns an instance to std::string is likely going to be an issue. 对于初学者来说,将实例返回到std :: string的函数可能会成为问题。 Provide functions exported by your DLL to handle the releasing of resources as needed. 提供由DLL导出的函数,以根据需要处理资源的释放。

Other options: 其他选项:

II. 二。 Ship with no c-runtime depedency. 出厂时没有c-runtime依赖。 This is a bit harder. 这有点难。 You first have to remove all calls to the CRT out of your code, provide some stub functions to get the DLL to link, and specify "no default libraries" linking option. 首先,您必须从代码中删除对CRT的所有调用,提供一些存根函数以获取要链接的DLL,并指定“无默认库”链接选项。 Can be done. 可以做到。

III. 三, C++ classes can be exported cleanly from a DLL by using COM interface pointers. 通过使用COM接口指针,可以从DLL干净地导出C ++类。 You'll still need to address the issues in 1a above, but ATL classes are a great way to get the overhead of COM out of the way. 您仍然需要解决上面1a中的问题,但是ATL类是消除COM开销的好方法。

The simple fact here is, Microsofts implementation aside, C++ is NOT an ABI. 简单的事实是,除了Microsoft的实现之外,C ++不是ABI。 You cannot export C++ objects, on any platform, from a dynamic module, and expect them to work with a different compiler or language. 您不能在任何平台上从动态模块导出C ++对象,并期望它们与其他编译器或语言一起使用。

Exporting c++ classes from Dlls is a largely pointless excercise - because of the name mangling, and lack of support in c++ for dynamically loaded classes - the dlls have to be loaded statically - so you loose the biggest benefit of splitting a project into dlls - the ability to only load functionality as needed. 从Dlls导出c ++类基本上是没有意义的-因为名称修改,并且c ++对动态加载的类缺乏支持-dll必须静态加载-因此,您失去了将项目拆分为dll的最大好处-仅根据需要加载功能的能力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM