返回C++;来自Windows DLL的对象 由于微软在运行时的非DLL版本中实现堆,所以从DLL返回C++对象会导致问题: // dll.h DLL_EXPORT std::string somefunc();
以及: 如果DLL和EXE都是使用多线程DLL运行库构建的,则上述代码运行良好 但是,如果DLL和EXE是在没有DLL运行时库(单线程或多线程版本)的情况下生成的,则上述代码会失败(在调试运行时,由于断言返回C++;来自Windows DLL的对象 由于微软在运行时的非DLL版本中实现堆,所以从DLL返回C++对象会导致问题: // dll.h DLL_EXPORT std::string somefunc();,c++,windows,dll,C++,Windows,Dll,以及: 如果DLL和EXE都是使用多线程DLL运行库构建的,则上述代码运行良好 但是,如果DLL和EXE是在没有DLL运行时库(单线程或多线程版本)的情况下生成的,则上述代码会失败(在调试运行时,由于断言\u CrtIsValidHeapPointer(pUserData))的缘故,代码会立即中止失败;在非调试运行时,堆会损坏,程序最终会在其他地方失败) 两个问题: 有没有办法解决这个问题,然后要求所有代码都使用DLL运行时 对于将其库分发给第三方的人,您如何处理?在API中不使用C++对象吗?
\u CrtIsValidHeapPointer(pUserData))的缘故,代码会立即中止
失败;在非调试运行时,堆会损坏,程序最终会在其他地方失败)
两个问题:
在过去,我发布了一个带有DLL的SDK,但它是基于COM的。使用COM,所有参数和IPC的编组都会自动完成。用户也可以通过这种方式与任何语言进行集成。有一种方法可以解决这一问题,但这并不简单。与库的大多数其余部分一样,
std::string
不直接使用new
分配内存,而是使用分配器(std::allocator
,默认情况下)
您可以提供自己的分配器,该分配器使用DLL和可执行文件共有的堆分配例程,例如使用
HeapAlloc
获取内存,并从那里子分配块。如果您有一个要分发的DLL,并且不想将调用者绑定到C运行时的特定版本,请执行以下任一操作:
I.将DLL链接到C运行时库的静态版本。从Visual Studio项目属性页中,选择配置属性->C/C++->代码生成的选项卡。这是一个选择“运行库”的选项。选择“多线程”或“多线程调试”而不是DLL版本。(命令行等效为/MT或/MTd)
这种方法有两个不同的缺点:
a。如果Microsoft发布了CRT的安全修补程序,则在重新编译和重新显示二进制文件之前,您附带的组件可能会受到攻击
b。DLL中由“malloc”或“new”分配的堆指针不能由EXE或其他二进制文件“free”d或“delete”d。否则你会崩溃的。fopen创建的文件句柄也是如此。您不能在DLL中调用fopen并期望EXE能够在其上关闭。再说一次,如果你这样做了,就崩溃。您需要构建DLL的接口,以适应所有这些问题。对于初学者来说,将实例返回到std::string的函数可能会成为一个问题。提供DLL导出的函数,以根据需要处理资源的释放
其他选择:
二,。发货时不依赖于c运行时。这有点难。您首先必须从代码中删除对CRT的所有调用,提供一些存根函数来链接DLL,并指定“无默认库”链接选项。这是可以做到的
使用COM接口指针可以从DLL中干净地导出C++类。您仍然需要解决上面1a中的问题,但是ATL类是消除COM开销的一个好方法。您的代码有两个潜在问题:您解决了第一个问题—CRT运行时。这里还有另一个问题:std::字符串可能会在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));
}
<> P>这里简单的事实是,微软的实施是一旁的,C++不是ABI。不能在任何平台上从动态模块导出C++对象,并期望它们与不同的编译器或语言一起工作。
从DLL导出C++类是一个很大的无意义的操作——因为名字的缩写,在C++中,对于动态加载类缺乏支持——必须静态加载DLL——因此,您将将项目拆分为DLL的最大好处是只需要按需加载功能。不幸的是,我们还需要用于各种Posix操作系统的库版本,因此我需要避免使用MS特定技术。@Jerry-接受您的回答,因为您提供了可行的替代解决方案。有点晚了,但是值得一提的是Jerry的答案:自定义分配器可以捕获当前翻译单元的分配函数
operator new
和operator delete
,从而将分配器“绑定”到原始DLL。看看我多年来成功使用的。在整个项目中,我使用了typedef std::basic_string mystring
而不是std::string
/std::wstring
“只根据需要加载功能的能力”确实是一个好处。但将代码放入DLL(即使它们一直都在加载)可以简化产品维护和支持,因此这是一种更广泛使用的模式。这是怎么回事?认真地在C++开发的上下文中,DLL的使用受到特别限制:-实际上,从第一类中的DLL导出C++类可以继承一种方式,要求DLL始终静态链接,并且所有DLL(和EXES)总是在一起响应于任何CL的改变而重新构建。
__declspec(export) void* createObject()
{
std::string* p = __impl_createObject();
return (void*)p;
}
__declspec(export) void releasePSTRING(void* pObj)
{
delete ((std::string*)(pObj));
}