C# & C++,调用 C++ 时出现运行时错误来自 C# 的 dll

发布于 2024-09-05 22:28:27 字数 1436 浏览 6 评论 0原文

我写了一个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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

素手挽清风 2024-09-12 22:28:27

使用 PInvoke 进行编组有很多规则。
供参考 managaed 与 managaed 之间的编组;非托管

首先关注 C# 端。
如果您预先知道消息的合理大小,则可以使用 StringBuilder 类型并定义该大小,例如。

[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
                                                    string parameterFileName,   
                                                    string baseNameToSaveData,
                                                    string logFileName, 
                                                    StringBuilder message
                                                    int messageLength );

从名称消息(和其他帖子)中得到的印象表明您事先不知道大小,并且您不会将部分消息传递给函数,因此也许

[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(in string fileNameToAnalyze, 
                                                    in string parameterFileName,   
                                                    in string baseNameToSaveData,
                                                    in string logFileName, 
                                                    out string message );

现在在 C/C++ 方面 - 以匹配第二个

extern "C" // if this is a C++ file to turn off name mangling for this function only
int WriteGenbenchDataWrapper( char * fileNameToAnalyze, 
                              char * parameterFileName,   
                              char * baseNameToSaveData,
                              char * logFileName, 
                              char ** message ) {
  string internalMessage;
  SomeFunc( internalMessage ); // these functions won't have extern "C" applied
  * message = (char *)::CoTaskMemAlloc(internalMessage.length()+1); 
  strcpy(* message, internalMessage.c_str());
}

定义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.

[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
                                                    string parameterFileName,   
                                                    string baseNameToSaveData,
                                                    string logFileName, 
                                                    StringBuilder message
                                                    int messageLength );

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

[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(in string fileNameToAnalyze, 
                                                    in string parameterFileName,   
                                                    in string baseNameToSaveData,
                                                    in string logFileName, 
                                                    out string message );

Now on the C/C++ side - to match the second definition

extern "C" // if this is a C++ file to turn off name mangling for this function only
int WriteGenbenchDataWrapper( char * fileNameToAnalyze, 
                              char * parameterFileName,   
                              char * baseNameToSaveData,
                              char * logFileName, 
                              char ** message ) {
  string internalMessage;
  SomeFunc( internalMessage ); // these functions won't have extern "C" applied
  * message = (char *)::CoTaskMemAlloc(internalMessage.length()+1); 
  strcpy(* message, internalMessage.c_str());
}

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"

烟花肆意 2024-09-12 22:28:27

在您的 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++ 代码中:

extern "C" __declspec(dllexport) const char* __stdcall GetCompany(const char *In) {
  return MyGetCompany(In); // Calls the real implementation
}

在我的 C# 代码中:

[DllImport("TheDLL.dll", EntryPoint = "GetCompany")]
private static extern IntPtr privGetCompany(String In);

// Call this one, not the one above:
public String GetProvince(String In)
{
  return Marshal.PtrToStringAnsi(privGetCompany(In));
}

最后一点,如果您在 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:

extern "C" __declspec(dllexport) const char* __stdcall GetCompany(const char *In) {
  return MyGetCompany(In); // Calls the real implementation
}

In my C# code:

[DllImport("TheDLL.dll", EntryPoint = "GetCompany")]
private static extern IntPtr privGetCompany(String In);

// Call this one, not the one above:
public String GetProvince(String In)
{
  return Marshal.PtrToStringAnsi(privGetCompany(In));
}

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文