C# & C++,调用 C++ 时出现运行时错误来自 C# 的 dll
我写了一个C++包装DLL供C#调用。该 DLL 经过测试,在我的 C++ 测试程序中运行良好。
现在与 C# 集成,我遇到运行时错误并崩溃了。无法使用调试器查看更多详细信息。
C++端只有一个方法:
#ifdef DLLWRAPPERWIN32_EXPORTS
#define DLLWRAPPERWIN32_API __declspec(dllexport)
#else
#define DLLWRAPPERWIN32_API __declspec(dllimport)
#endif
#include "NB_DPSM.h"
extern "C" {
DLLWRAPPERWIN32_API int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
string& message) ;
}
在C#端,有定义,
[DllImport("..\\..\\thirdParty\\cogs\\DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
ref string message);
有调用:
string msg = "";
int returnVal = WriteGenbenchDataWrapper(rawDataFileName,
parameterFileName, outputBaseName, logFileName, ref msg);
我猜函数的最后一个参数肯定有问题。 C++ 中的 string&
应该是 C# 中的 ref string
吗?
EDIT:
我们真的需要extern "C"
吗?
EDIT 2:
从 dll 中删除 extern "C
后,我得到了 EntryPointNotFoundException。当我使用 DLL Export Viewer 查看 dll 时,我发现函数名称是 "int __cdecl WriteGenbenchDataWrapper(class std:: ...”我需要包含“__cdecl”吗?
I have written a C++ wrapper DLL for C# to call. The DLL was tested and worked fine with my C++ test program.
now integrated with C#, I got runtime error and crashed. Cannot use debugger to see more details.
The C++ side has only one method:
#ifdef DLLWRAPPERWIN32_EXPORTS
#define DLLWRAPPERWIN32_API __declspec(dllexport)
#else
#define DLLWRAPPERWIN32_API __declspec(dllimport)
#endif
#include "NB_DPSM.h"
extern "C" {
DLLWRAPPERWIN32_API int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
string& message) ;
}
in the C# side, there is a definition,
[DllImport("..\\..\\thirdParty\\cogs\\DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
ref string message);
and a call:
string msg = "";
int returnVal = WriteGenbenchDataWrapper(rawDataFileName,
parameterFileName, outputBaseName, logFileName, ref msg);
I guess there must be something wrong with the last parameter of the function. string&
in C++ should be ref string
in C#?
EDIT:
Do we really need the extern "C"
?
EDIT 2:
after I remove the extern "C
from the dll, I got the EntryPointNotFoundException. When I look at the dll by using DLL Export Viewer, I found the function name is "int __cdecl WriteGenbenchDataWrapper(class std:: ..." Do I need to include the " __cdecl"?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用 PInvoke 进行编组有很多规则。
供参考 managaed 与 managaed 之间的编组;非托管
首先关注 C# 端。
如果您预先知道消息的合理大小,则可以使用 StringBuilder 类型并定义该大小,例如。
从名称消息(和其他帖子)中得到的印象表明您事先不知道大小,并且您不会将部分消息传递给函数,因此也许
现在在 C/C++ 方面 - 以匹配第二个
定义unicode/ansi 字符串的值也很重要,请参阅 [MarshalAsAttribute(UnmanagedType.LPWSTR)]
对于发布模式,您需要删除开发路径设置“..\..\thirdParty\cogs”
There are a bunch of rules for marsheling with PInvoke.
For reference Marsheling between managaed & unmanaged
Focusing on the C# side first.
If you knew a reasonable size of the message up front you could use StringBuilder type and define that size, something like.
Impression from the name message (and other posts) indiciates you don't know the size up front, and you won't be passing a partial message to the function so maybe
Now on the C/C++ side - to match the second definition
Consideration of unicode/ansi strings is also important, refer to [MarshalAsAttribute(UnmanagedType.LPWSTR)]
For release mode you will want to remove your development path settings "..\..\thirdParty\cogs"
在您的 C++ 代码中:
我一直需要 extern“C”。如果不这样做的话,C++ 会修改函数名称(需要修改以支持函数重载)。 extern“C”告诉它不要这样做。
我还将函数声明为 __stdcall。我相信您可以告诉 C# 使用哪种类型的调用约定,但我认为 __stdcall 是默认值。
至于传递字符串对象,我对此不确定,我坚持只使用基元进行参数传递,因此我将使用 const char * 并在我的 C++ 代码中进行相应调整。
另外,我尽量避免通过引用传递。相反,如果我需要返回多个值,我将设置一系列 getter 来处理这个问题(const char * 作为 IntPtr 返回)。
在 C# 代码中:
我使用 String 表示 const char *,使用 int 表示 int,等等。我相信微软有一个图表可以告诉你什么应该代替什么。
处理返回的字符串时,需要将其转换为 ANSI。这可以通过调用 Marshal.PtrToStringAnsi() 来完成。
例如:
在我的 C++ 代码中:
在我的 C# 代码中:
最后一点,如果您在 64 位计算机上运行,“任何 CPU”配置将生成 64 位 C# 可执行文件,这将需要 64 位 DLL。如果您只有 32 位 DLL,则需要添加配置 (x86)。
您收到的错误消息表明您的 C# 程序可能正确找到了 DLL 和函数,因此名称修改不太可能是问题所在。听起来像是调用约定问题或参数传递问题。
In your C++ code:
I've always needed the extern "C". C++ mangles function names if you don't (the mangling is needed to support function overloading). The extern "C" tells it not to do this.
I also will declare the functions as __stdcall. I believe you can tell C# which type of calling convention to use, but I think __stdcall is the default.
As far as passing a string object, I'm not sure about that, I stick to only using primitives for parameter passing, so I would use const char * and adjust accordingly in my C++ code.
Also, I try to avoid passing by reference. Rather, if I need to return several values, I'll set up a series of getters to handle this (a const char * returns as an IntPtr).
In your C# code:
I use String for the const char *, int for int, and so on. I believe Microsoft has a chart somewhere to tell you what should sub in for what.
When dealing with a returned string, you need to convert it to ANSI. This can be done with a call to Marshal.PtrToStringAnsi().
For Example:
In my C++ code:
In my C# code:
One final note, if you're running on a 64-bit machine, the 'Any CPU' configuration will make a 64-bit C# executable, which will need a 64-bit DLL. If you only have a 32-bit DLL, you'll need to add a configuration (x86).
The error message you got indicates that your C# program is probably finding the DLL correctly and the function as well, so name mangling is not likely the problem. It sounds like calling convention issue or a problem with the parameter passing.