访问冲突调用C ++ DLL

时间:2010-11-14 23:44:26

标签: c++ visual-studio dll mingw

我在linux(gcc)上编写的代码中创建了c ++ dll(使用mingw),但在VC ++中使用它时遇到了一些困难。 dll基本上只暴露了一个类,我为它创建了纯虚拟接口,还有工厂函数创建了对象(唯一的导出),如下所示:

extern "C" __declspec(dllexport) DeviceDriverApi* GetX5Driver(); 

我添加了extern“C”来防止名称损坏,dllexport在实际代码中被dllimport替换,我想使用dll,DeviceDriverApi是纯虚拟接口。

现在我在VC ++中编写了简单的代码,只调用工厂函数,然后尝试删除指针。它编译没有任何问题,但当我尝试运行它时,我得到访问冲突错误。如果我尝试调用该对象的任何方法,我会再次遇到访问冲突。

当我在MinGW(gcc)中编译相同的代码并使用相同的库时,它运行没有任何问题。因此,VC ++代码如何使用库和gcc代码之间必定存在一些东西(呵呵,我猜其实很多差异:)。

有什么想法吗?

干杯, 汤姆

修改 代码是:

    DeviceDriverApi* x5Driver = GetX5Driver();


if (x5Driver->isConnected())
    Console::WriteLine(L"Hello World");

delete x5Driver;

当我尝试调用该方法时以及当我尝试删除指针时崩溃。尽管(第一行)正确创建了对象。创建对象时有一些调试输出,我可以在遇到访问冲突错误之前看到它们。

4 个答案:

答案 0 :(得分:2)

  • 您使用一个编译器(mingw)作为DLL,另一个(VC ++)用于调用代码。
  • 您正在调用'C'函数,但返回指向C ++对象的指针。

永远不会工作,因为几乎可以保证VTable布局不兼容。而且,DLL和应用程序可能正在使用不同的内存管理器,因此您使用一个new()和另一个delete()。再一次,它不会起作用。

为此,两个编译器都需要支持标准ABI(应用程序二进制接口)。我认为Windows不存在这样的事情。

最好的选择是通过C函数公开所有DLL对象方法和属性(包括一个删除对象)。您可以在主叫端重新包装到C ++对象中。

答案 1 :(得分:1)

两个不同的编译器可能正在使用不同的调用约定。尝试将_cdecl放在客户端代码和DLL代码中的函数名之前,然后重新编译它们。

有关此处呼叫约定的更多信息:http://en.wikipedia.org/wiki/X86_calling_conventions

编辑:问题更新了更多细节,看起来问题可能是Adrien Plisson在他的答案结尾处描述的问题。你正在一个模块中创建一个对象并将其释放到另一个模块中,这是错误的。

答案 2 :(得分:1)

(1)我怀疑有一个呼唤的问题,尽管Leo的简单建议似乎没有帮助。

isConnected是虚拟的吗? MinGW和VC ++可能对VTable使用不同的实现,在这种情况下,运气不好。

试着看看你使用调试器有多远:它是在通话时崩溃还是返回?你到达无效代码吗? (如果你知道阅读汇编,这通常可以解决这些问题。)

或者,将trace语句添加到各种方法中,以查看您获得的程度。

(2)对于公共DLL接口,永远不要释放被调用者分配的调用者内存(反之亦然)。 DLL可能以完全不同的堆运行,因此指针未知。

如果您想依赖这种行为,您需要确保:

  • Caller和Callee(例如DLL和主程序,在您的情况下)使用相同版本的sam编译器进行编译
  • 对于所有受支持的编译器,您已配置了编译选项以确保调用者和被调用者使用相同的共享运行时库状态。

因此,最好的方法是将您的API更改为:

extern "C" __declspec(dllexport) DeviceDriverApi* GetX5Driver(); 
extern "C" __declspec(dllexport) void FreeDeviceDriver(DeviceDriverApi* driver); 

并且,在来电者网站,以某种方式包裹(例如在boost::intrusive_ptr中)。

答案 3 :(得分:0)

尝试从DLL和客户端可执行文件中查看导入的库。 (您可以使用依赖关系查看器 dumpbin 或您喜欢的任何其他工具)。验证DLL和客户端代码是否使用相同的C ++运行时。

如果不是这种情况,你确实遇到了一些问题,因为管理内存的方式在2之间可能不同,导致从一个运行时释放从另一个运行时分配的指针时发生崩溃。

如果这确实是你的问题,请尝试不破坏客户端可执行文件中的指针,而是在DLL中声明并导出一个函数,该函数将负责销毁指针。