我在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;
当我尝试调用该方法时以及当我尝试删除指针时崩溃。尽管(第一行)正确创建了对象。创建对象时有一些调试输出,我可以在遇到访问冲突错误之前看到它们。
答案 0 :(得分:2)
永远不会工作,因为几乎可以保证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可能以完全不同的堆运行,因此指针未知。
如果您想依赖这种行为,您需要确保:
因此,最好的方法是将您的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中声明并导出一个函数,该函数将负责销毁指针。