C# 和 C++,从 C# 调用 C++ dll 时出现运行时错误

标签 c# .net c++ pinvoke

我已经编写了一个 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”吗?

最佳答案

使用 PInvoke 进行编码有很多规则。 供引用Marsheling between managaed & unmanaged

首先关注 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)]

对于 Release模式,您需要删除开发路径设置“..\..\thirdParty\cogs”

关于C# 和 C++,从 C# 调用 C++ dll 时出现运行时错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3064062/

相关文章:

c# 按联系人状态和名称对列表中的对象进行排序

.net - 指定 Azure 工具版本

c++ - std::ratio<> 背后的设计原则

c++ - 随机高斯函数的时间复杂度

c++ - push_back std::pair 进入 std::vector 错误

c# - 如何将参数从 C# 程序传递给 powershell 脚本?

c# - 为什么要手动双缓冲?

c# - .NET Standard 和 .NET Core 3.x 或 ASP.NET Core 3.x

c# - 合并从多个实例触发的事件的可观察序列的正确方法

c# - 在 SQL Server Compact Edition 中将 Entity Framework 代码中的种子主键首先设置为 0