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

时间:2012-02-03 22:03:05

标签: c++ windows visual-c++ dll g++

我使用和 Visual C ++ 编译器编译的应用程序。它可以以.dll的形式加载插件。究竟是什么确实无关紧要,事实是:

UML Diagram

这包括调用.dll中返回指向Applications API 对象的指针的函数等。

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

我不想使用Visual Studio IDE(不幸的是,这是使用Applications SDK的“首选”方式)。我可以将G ++配置为编译,如VC ++ 吗?

PS:我使用MINGW GNU G ++

6 个答案:

答案 0 :(得分:6)

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

你当然不能做的是分享任何类型的C ++构造。例如,您不能在主应用程序中new[]数组,而是让DLL delete[]。那是因为没有固定的C ++ ABI,因此任何给定的编译器都无法知道不同的编译器如何实现C ++数据结构。对于不与ABI兼容的不同版本的MSVC ++,情况甚至如此。

答案 1 :(得分:4)

我担心,所有C ++语言功能都将完全不兼容。从名称修改到内存分配到虚拟调用机制的所有内容都将完全不同,并且不可互操作。你可以希望的 best 是一个快速,仁慈的崩溃。

如果你的组件只使用extern "C"接口相互通信,你可以做这个工作,虽然在那里,你需要小心。两个C ++运行时都有启动和关闭代码,并且无法保证用于组装应用程序的链接器将知道如何为其他编译器包含此代码。你几乎必须必须用g ++链接g ++编译的代码。

如果您只使用一个编译器使用C ++功能,并使用该编译器的链接器,则它更有可能工作。

答案 2 :(得分:3)

如果你知道自己在做什么,这应该没问题。但有一些事情需要注意:

我假设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. 在EXE和DLL之间不太可能共享C-Runtimes,因此不要混合搭配。在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
    
  6. 这些是我所知道的问题。由于你没有解释DLL和EXE之间的接口,我无法告诉你更多信息。

答案 3 :(得分:1)

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

基本上所有其他内容的大小可以变化,变化的是模板。

结构的填充和对齐往往取决于编译器,并且通常依赖于编译器内的设置。存在如此松散的规则,例如通常位于平台位数边界上的指针以及在它们之后具有3个字节的bool,但是由编译器决定如何处理它。

模板,特别是来自STL的模板(每个编译器不同)可能有不同的成员,大小,填充,以及大多数东西。唯一的标准部分是API,后端留给STL实现(有一些规则,但编译器仍然可以不同地编译模板)。在一个版本的模块之间传递模板已经够糟糕了,但是在不同的编译器之间它往往是致命的。

非标准化(名称修改)或必要时(内存分配)高度具体的事物也将是不兼容的。您可以通过仅从创建的库(无论如何都是良好实践)中摧毁并使用带有删除,分配和使用未修饰名称和/或C样式(extern "C"导出的STL对象来解决这两个问题。 )用于导出方法。

我似乎还记得编译器如何处理vtable中的虚拟析构函数,但有一点不同。

如果你可以设法只传递你自己对象的引用,完全避免使用外部可见模板,主要使用指针和导出或虚拟方法,你可以避免绝大多数问题(COM正是如此,为了兼容性大多数编译器和语言)。写作可能会很痛苦,但如果你需要兼容性,那就有可能。

为了缓解一些但不是全部问题,使用STL的替代(如Qt的核心库)将删除该特定问题。将Qt扔进任何旧项目是一种可怕的浪费,并且会比“提升所有的东西!!!”造成更大的膨胀。哲学,它可以比使用STL库存更大程度地解耦库和编译器。

答案 4 :(得分:0)

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

我的经验是使用DirectX API。慢慢地,一个子集得到了由发烧友修改的二进制文件,但它从来没有像最新或可靠的所以在评估之后我切换到了一个合适的跨平台API,即SDL。

This wikipedia article描述了可以编译和链接库的不同方式。它比我在这里总结的更深入。

答案 5 :(得分:0)

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