在VC ++编译的应用程序中使用G ++编译的DLL(插件)时会出现什么问题?

我使用和应用程序编译与Visual C ++编译器。 它可以以.dllforms加载插件。 事实上,它的作用并不重要,事实上是:

UML图

这包括从.dll调用返回指向应用程序API对象的指针的函数等。

我的问题是,当应用程序从.dll调用函数时,可能会出现什么问题,从中检索指针并使用它。 例如,我想到的是一个指针的大小。 VC ++和G ++有什么不同? 如果是的话,这可能会导致应用程序崩溃?

我不想使用Visual Studio IDE(这不幸是使用Applications SDK的“首选”方法)。 我可以configurationG ++来像VC ++一样编译吗?

PS:我使用MINGW GNU G ++

Solutions Collecting From Web of "在VC ++编译的应用程序中使用G ++编译的DLL(插件)时会出现什么问题?"

只要应用程序和DLL都在同一台机器上编译,并且只要它们只使用C ABI,就应该没问题。

你当然不能做的是共享任何类型的C ++构造。 例如,你不能在主应用程序中new[]一个数组,并让DLL delete[]它。 这是因为没有固定的C ++ ABI,因此任何给定的编译器都不知道不同的编译器如何实现C ++数据结构。 对于不同于ABI兼容的不同版本的MSVC ++来说,情况也是如此。

恐怕所有的C ++语言功能将完全不兼容。 从名称到内存分配到虚拟调用机制的所有内容都将完全不同且不可互操作。 最好的希望是一个快速,慈悲的崩溃。

如果你的组件只使用extern "C"接口相互交谈,你可以做这个工作,尽管即使在那里,你也需要小心。 这两个C ++运行时都会有启动和关闭代码,并且不能保证用于组装应用程序的链接器将知道如何将该代码包含在其他编译器中。 例如,你几乎必须链接g ++编译的代码和g ++。

如果只使用一个编译器来使用C ++特性,并使用该编译器的链接器,那么它就更有可能工作。

这应该是确定的,如果你知道你在做什么。 但有一些事情要注意:

我假设EXE和DLL之间的接口是一个“C”接口或COM类似的地方,只有暴露的C ++类是通过纯虚拟接口。 如果通过DLL导出具体类,则会变得更加混乱。

  1. 32位与64位。 32位应用程序不会加载64位DLL,反之亦然。 确保他们匹配。

  2. 调用约定。 __cdecl vs __stdcall。 通常情况下,Visual Studio应用程序会使用以__stdcall作为默认调用约定的标志进行编译(或者函数原型明确声明如此)。 因此,确保g ++编译器生成的代码与EXE预期的调用类型相匹配。 否则,导出的函数可能会运行,但堆栈可能会被回收。 如果通过这样的崩溃进行调试,那么很可能错误地指定了cdecl vs stdcall约定。 易于修复。

  3. C运行时不可能在EXE和DLL之间共享,所以不要混合和匹配。 在EXE中分配new或malloc的指针不应该在DLL中释放或释放(反之亦然)。 同样,由fopen()返回的FILE句柄不能在EXE和DLL之间共享。 如果发生这种情况,你可能会崩溃….这导致我的下一个点….

  4. 带有内联代码的C ++头文件引起了足够的头痛,并且是我在#3中调用的问题的来源。 如果DLL和EXE之间的接口是纯粹的“C”接口,那么你会好的。

  5. 命名修补问题。 如果遇到导出的函数名称由于名称变形或者由于前导下划线而不匹配的问题,则可以在.DEF文件中解决该问题。 至少这就是我以前用Visual Studio所做的。 不知道是否相当于g ++ / MinGW。 下面的例子。 学习使用“dumpbin.exe / exports”可以验证你的DLL是正确的导出函数的名字。 使用extern“C”也有助于解决这个问题。

      EXPORTS FooBar=_Foobar@12 BlahBlah=??BlahBlah@@QAE@XZ @236 NONAME 

那些是我所知道的问题。 我不能告诉你更多,因为你没有解释DLL和EXE之间的接口。

指针的大小不会改变; 这取决于平台和模块的位数,而不是编译器(32位和64位等)。

可以改变的是基本上所有的东西的大小,而不同的是模板。

填充和结构的对齐往往是依赖于编译器的,而且通常是依赖于编译器的设置。 有如此宽松的规则,比如指针通常位于一个平台的位边界上,后面跟着3个字节的布尔值,但是编译器应该如何处理。

模板,特别是来自STL(每个编译器都不同)的模板可能会有不同的成员,大小,填充以及大部分的东西。 唯一的标准部分是API,后端留给STL实现(有一些规则,但是编译器仍然可以编译模板)。 在一个模块之间传递模板是不够的,但是在不同的编译器之间,通常是致命的。

不规范(名称混乱)或高度特殊的(内存分配)也是不兼容的。 你可以通过仅仅从创建库(最佳实践)和使用带删除器的STL对象中摧毁这些问题来解决这两个问题,分配和使用未修饰名称和/或C风格( extern "C" )导出为导出的方法。

我似乎还记得编译器如何处理vtable中的虚拟析构函数,只是有一些细微的差别。

如果你可以管理只传递你自己的对象的引用,完全避免外部可见的模板,主要使用指针和导出或虚方法,你可以避免绝大多数的问题(COM正好这样做,为了兼容大多数编译器和语言)。 这可能是一个痛苦的写,但如果你需要这种兼容性,这是可能的。

为了减轻一些问题,但不是全部,使用STL的替代(如Qt的核心库)将会消除这个特定的问题。 把Qt扔进任何旧项目都是一个可怕的浪费,而且会比“提升所有的东西!!!”造成更多的膨胀。 哲学,与使用库存STL罐相比,将库和编译器解耦更有用。

你不能在它们之间传递C运行时对象。 例如,你不能打开一个FILE缓冲区,并将其传递给另一个。 你不能释放在另一边分配的内存。

主要的问题是函数签名和方法参数传递给库代码。 我很难让VC ++的dll在过去的gnu编译器中工作。 当VC ++总是花钱而mingw是免费的解决方案时,这就回来了。

我的经验是使用DirectX API。 慢慢地,一个子集获得了由爱好者修改的二进制文件,但从未像现在这样保持最新或可靠,所以在评估之后,我切换到了适当的跨平台API,即SDL。

这个维基百科文章描述了图书馆可以编译和链接的不同方式。 这是比我能够在这里总结更深入。