加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Windows > 正文

exe和dll之间的接口与不同的C/C++运行时库

发布时间:2020-12-14 01:52:59 所属栏目:Windows 来源:网络整理
导读:给定:Executable使用dll.它们具有不同的c / c运行时.它们之间的接口有哪些限制? 除了他们使用相同的编译器,相同的Boost版本(但不同的预编译的升压库). 我知道不同的运行时可以有不同的堆.因此,delete必须与同一堆中的new对应. 最重要的是我们无法通过接口S
给定:Executable使用dll.它们具有不同的c / c运行时.它们之间的接口有哪些限制?
除了他们使用相同的编译器,相同的Boost版本(但不同的预编译的升压库).

我知道不同的运行时可以有不同的堆.因此,delete必须与同一堆中的new对应.

最重要的是我们无法通过接口STL对象传递,因为当我们构建exe时,STL对象与一个运行时链接
当构建dll时,同一个对象(如果我们通过引用传递它或通过接口复制)将与另一个运行时链接.
另一个运行时可以具有该对象的不同实现.

让我们考虑一下案例:

>我认为以下是安全的. Dll导出函数,该函数具有参数:对包含私有STL类作为成员的导出用户定义类的引用.
Dll为此对象分配内存.当想要删除它时,Exe调用此对象的Release方法.
>我认为以下是不安全的.用户定义的类在exe中实例化并通过exe / dll接口传递.
此类包含私有STL类作为成员. exe和dll共享此用户类的头文件/实现文件.
当此类在单独的项目中构建时,将使用不同的STL实现.例如,不同的实现
string :: size()(来自不同的运行时)将应用于内存中的同一对象.
>我认为以下是安全的.用户定义的类在exe中实例化并通过exe / dll接口传递.
此类不依赖于标准库,它仅使用原始C类型. exe和dll共享此用户类的头文件/实现文件.
此外,我们必须控制new和delete对应于同一个堆.例如,我们可以重载new / delete,因此它们使用:: GetProcessHeap.
>我认为以下是不安全的:通过exe / dll接口传递boost对象,因为它们可以依赖于标准库类.删除也可能与新堆不对应.
>我认为以下是不安全的:即使我们通过exe / dll接口传递boost对象并且它们不依赖于标准库类但不是仅作为头文件实现 – 而是可以使用一个boost lib创建对象(对于一个运行时)并与另一个boost lib一起使用(用于另一个运行时).删除也可能与新堆不对应.

此外,我想使用一些智能指针来传递对象(在第3项中提到)从exe到dll以及从dll到exe的引用.
我认为这个智能指针也应该重载new / delete以从默认进程堆分配引用计数器.
当它试图删除指向对象时,它将调用由该对象重载的删除(如第3项)

对于第1项中的对象,我想使用自定义智能指针,它将调用指向对象的释放方法
(作为自定义版本的boost :: shared_ptr)

没有提到哪些问题?请纠正我.

解决方法

您的问题的一般答案是:如果实际执行的操作不依赖于运行时,对从EXE / DLL接收的对象执行某些操作是安全的.例如. vtable调用,函数指针调用,DLL的显式函数调用.

依赖于运行时的事情(来自头文件的内联方法,任何对STL对象布局做出任何假设的事情等)都是不安全的.

回到你的例子:

>如果调用Release()方法,则应该小心并确保从DLL调用Release()的实现,而不是编译器生成EXE文件所创建的另一个实现.确保它的最简单方法是使Release()成为虚拟的,这样调用总是使用vtable中的方法指针(由DLL提供)调用.
>对,STL的内联方法如string :: size()会在这里引起问题.
>对.如果重载new / delete以使用GetProcessHeap(),则代码将起作用.
>总的来说,对.特别是,请参阅所需的boost类的实现.
>如果他们不依赖于STL并且您确定两个编译器的内存占用量相同并且您提供了自定义新/删除(),则通常不应该存在问题(请参阅下面的备注).

备注:

有一些低概率的东西可能导致上面没有提到的问题:

>如果使用不同的编译器,默认情况下它们可能会使用不同的调用约定(cdecl / stdcall).
>默认对齐选项也可能不同,从而导致不同的内存布局.
>如果从DLL中抛出异常,则具有不同运行时的exe可能无法捕获它们,而是会崩溃.
>使用DLL导入/导出属性标记方法并确保它们不被内联可能非常麻烦.此外,如果您使用的是不同的编译器,则C名称修改算法可能会有所不同.

总结一下,建议查看Microsoft COM中的实现方式并设计类似的东西.即将EXE / DLL交互限制为:

>接口.即仅使用纯虚方法的C类.使用虚拟Release()删除对象.
>简单明确的结构.确保所有编译器的对齐选项匹配.如果你想使用非平凡的结构而你正在使用不同的编译器,最好是偏执并放置这样的检查:

struct MyStruct
{
    ...
};
C_ASSERT(sizeof(MyStruct) == ...);
C_ASSERT(FIELD_OFFSET(MyStruct,MyMember) = ... );

>类C函数传递和/或返回指向接口的指针.>不要从EXE调用的方法中抛出异常

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读