调用 C++ 时发生访问冲突动态链接库
我从我在 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;
当我尝试调用该方法以及尝试删除指针时,它会崩溃。不过,该对象已正确创建(第一行)。创建对象时有一些调试输出,我可以在出现访问冲突错误之前看到它们。
I created c++ dll (using mingw) from code I wrote on linux (gcc), but somehow have difficulties using it in VC++. The dll basically exposes just one class, I created pure virtual interface for it and also factory function which creates the object (the only export) which looks like this:
extern "C" __declspec(dllexport) DeviceDriverApi* GetX5Driver();
I added extern "C" to prevent name mangling, dllexport is replaced by dllimport in actual code where I want to use the dll, DeviceDriverApi is the pure virtual interface.
Now I wrote simple code in VC++ which just call the factory function and then just tries to delete the pointer. It compiles without any problems but when I try to run it I get access violation error. If I try to call any method of the object I get access violation again.
When I compile the same code in MinGW (gcc) and use the same library, it runs without any problems. So there must be something (hehe, I guess many differences actually :)) between how VC++ code uses the library and gcc code.
Any ideas what?
Cheers,
Tom
Edit:
The code is:
DeviceDriverApi* x5Driver = GetX5Driver();
if (x5Driver->isConnected())
Console::WriteLine(L"Hello World");
delete x5Driver;
It's crashing when I try to call the method and when I try to delete the pointer as well. The object is created correctly though (the first line). There are some debug outputs when the object is created and I can see them before I get the access violation error.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这永远不会起作用,因为VTable布局几乎肯定是不兼容的。而且,DLL 和应用程序可能使用不同的内存管理器,因此您对其中一个执行
new()
,对另一个执行delete()
。再说一遍,它根本行不通。为此,两个编译器都需要支持标准的 ABI(应用程序二进制接口)。我认为 Windows 不存在这样的东西。
最好的选择是通过 C 函数公开所有 DLL 对象方法和属性(包括删除对象的方法和属性)。您可以在调用端重新包装为 C++ 对象。
That will never work, because VTable layouts are almost guranteed to be incompatible. And, the DLL and app are probably using different memory managers, so you're doing
new()
with one anddelete()
with the other. Again, it just won't work.For this to work the two compilers need to both support a standard ABI (Application Binary Interface). I don't think such a thing exists for Windows.
The best option is to expose all you DLL object methods and properties via C functions (including one to delete the object). You can the re-wrap into a C++ object on the calling end.
两个不同的编译器可能使用不同的调用约定。尝试将 _cdecl 放在客户端代码和 DLL 代码中的函数名称之前,然后重新编译两者。
有关调用约定的更多信息:http://en.wikipedia.org/wiki/X86_calling_conventions
编辑:问题已更新为更多细节,看起来问题很可能是 Adrien Plisson 在其答案末尾所描述的内容。您在一个模块中创建一个对象并在另一个模块中释放它,这是错误的。
The two different compilers may be using different calling conventions. Try putting _cdecl before the function name in both the client code and the DLL code and recompiling both.
More info on calling conventions here: http://en.wikipedia.org/wiki/X86_calling_conventions
EDIT: The question was updated with more detail and it looks likely the problem is what Adrien Plisson describes at the end of his answer. You're creating an object in one module and freeing it in another, which is wrong.
(1) 我也怀疑调用约定问题,尽管 Leo 的简单建议似乎没有帮助。
isConnected
是虚拟的吗? MinGW 和 VC++ 可能对 VTable 使用不同的实现,在这种情况下,好吧,运气不好。尝试看看调试器能达到什么程度:它是在调用时崩溃还是在返回时崩溃?您是否得到无效代码? (如果您知道阅读汇编,这通常对解决这些问题有很大帮助。)
或者,向各种方法添加跟踪语句,看看您能走多远。
(2) 对于公共 DLL 接口,切勿释放调用方中由被调用方分配的内存(反之亦然)。该 DLL 可能在完全不同的堆上运行,因此该指针未知。
如果您想依赖该行为,则需要确保:
因此,最好的方法是将您的 API 更改为:
并且在调用方站点以某种方式包装(例如,在
boost::intrusive_ptr
中)。(1) I suspect a calling covnention problem as well, though the simple suggestion by Leo doesn't seem to have helped.
Is
isConnected
virtual? It is possible that MinGW and VC++ use different implementations for a VTable, in which case, well, tough luck.Try to see how far you get with the debugger: does it crash at the call, or the return? Do you arrive at invalid code? (If you know to read assembly, that usually helps a lot with these problems.)
Alternatively, add trace statements to the various methods, to see how far you get.
(2) For a public DLL interface, never free memory in the caller that was allocated by a callee (or vice versa). The DLL likely runs with a completely different heap, so the pointer is not known.
If you want to rely on that behavior, you need to make sure:
So the best way is to change your API to:
and, at caller site, wrap in some way (e.g. in a
boost::intrusive_ptr
).尝试查看 DLL 和客户端可执行文件中导入的库。 (您可以使用依赖关系查看器或dumpbin或您喜欢的任何其他工具)。验证 DLL 和客户端代码是否使用相同的 C++ 运行时。
如果不是这种情况,您确实可能会遇到一些问题,因为两者之间的内存管理方式可能不同,从而导致从一个运行时释放从另一个运行时分配的指针时发生崩溃。
如果这确实是您的问题,请尝试不要销毁客户端可执行文件中的指针,而是在 DLL 中声明并导出一个函数,该函数将负责销毁指针。
try looking at the imported libraries from both your DLL and your client executable. (you can use the Dependency Viewer or dumpbin or any other tool you like). verify that both the DLL and the client code are using the same C++ runtime.
if it is not the case, you can indeed run into some issues since the way the memory is managed may be different between the 2, leading to a crash when freeing from one runtime a pointer allocated from another runtime.
if this is really your problem, try not destroying the pointer in your client executable, but rather declare and export a function in your DLL which will take care of destroying the pointer.