我是 C# 新手,但广泛使用 C++。我有一个需要从 C# 调用的 C++ 函数。在阅读了 SO 的一些答案和一些谷歌搜索之后,我得出结论,我需要为该函数创建一个纯 C 接口(interface)。我已经这样做了,但仍然对如何从 C# 调用它感到困惑。
C++ 中的函数如下所示:
int processImages(
std::string& inputFilePath, // An input file
const std::vector<std::string>& inputListOfDirs, // Input list of dirs
std::vector<InternalStruct>& vecInternalStruct, // Input/Output struct
std::vector<std::vector< int > >& OutputIntsForEachFile,
std::vector< std::vector<SmallStruct> >& vecVecSmallStruct, // Output
int verboseLevel
);
相同的函数,用 C 语言转换后如下所示:
int processImagesC(
char* p_inputFilePath, // An input file
char** p_inputListOfDirs, // Input list of dirs
size_t* p_numInputDirs, // Indicating number of elements
InternalStruct* p_vecInternalStruct, // Input/Output struct
size_t* p_numInternalStructs,
int** p_OutputIntsForEachFile, // a 2d array each row ending with -1
size_t* p_numOutputIntsForEachFile //one number indicating its number of rows
SmallStruct** p_vecVecSmallStruct, // Output
size_t* p_numInVecSmallStruct,
int verboseLevel
);
这是基于this建议。
现在我需要从 C# 调用它,这就是困惑所在。我已尽力转换结构。
C# 代码如下所示:
[DllImport(
@"C:\path\to\cppdll.dll", CallingConvention=CallingConvention.Cdecl,
EntryPoint="processImagesC", SetLastError=true)]
[return: MarshalAs(UnmanagedType.I4)]
unsafe public static extern int processImagesC(
String inputFilePath,
String[] inputListOfDirs,
ref uint numInputListOfDirs,
// Should I use ref InternalStruct * vecInternalStruct?
ref InternalStruct[] vecInternalStruct,
ref uint numInternalStruct,
// Or ref int[] or ref int[][] or int[][]?
ref int[][] OutputIntsForEachFile,
ref uint numOutputIntsForEachFile,
// again, ref ..[], [][], or ref [][]?
ref SmallStruct[][] vecVecSmallStruct,
int verboseLevel
);
在 C/C++ 代码中完成了所有输出变量(指针)的内存分配。这可能意味着我们需要将代码声明为不安全,对吗?
我们如何处理内存释放?我是否应该编写另一个 API(函数)来重新分配由 C/C++ 分配的对象/数组?
C++ 代码需要符合标准且独立于平台,因此我不能在其中插入任何特定于 Windows 的东西。
我希望有人能理解这一点并提供答案或至少为我指明正确的方向。
最佳答案
由于似乎有人对将 It Just Works (IJW) 与 C++/CLI 结合使用感兴趣,因此我将发布一些相关信息,需要进行进一步的谷歌搜索和研究才能弄清楚这一切。可以使用单个编译器标志启用 C++/CLI(/CLI,通过属性页->常规->公共(public)语言运行时支持启用)。 C++/cli 不是 c++,而只是另一种托管语言。 C++/CLI 类可以编译成 dll 并直接从其他 .NET 项目(C#、VB.NET 等)调用。但是,与其他 .NET 语言不同,它可以直接与 C++ 代码交互。
This是学习 C++/CLI 的良好开端。要学习的最重要的事情是告诉你这个类是托管的(.NET 类)而不是 Vanila C++ 的装饰。 “ref”关键字将定义定义为 .NET 定义:
public ref class Foo{ public: void bar();};//Managed class, visible to C#
public ref struct Foo{};//Managed struct, visible to C#
所有引用类都是用句柄引用的,而不是指针或引用。句柄由 ^ 运算符表示。要创建一个新句柄,您可以使用 gcnew,而要访问该句柄的函数/成员,请使用 -> 运算符。
//in main
Foo^ a = gcnew Foo();
a->bar();
您经常需要将通用结构从 C# 移至 native 类型,然后再移回。 (例如托管 Array^ 或 String^ 到 void* 或 std::string)。此过程称为编码。 This handy table对于弄清楚这一点非常有用。
一个常见的任务是为本地类创建一个包装器,这样做是这样的:
//Foo.h
#include <string>
namespace nativeFoo
{
class Foo
{
private:
std::string fooHeader;
public:
Foo() {fooHeader = "asdf";}
std::string Bar(std::string& b) {return fooHeader+b;}
};
}
//ManagedFoo.h
#include "foo.h"
namespace managedFoo
{
public ref class Foo
{
private:
nativeFoo::Foo* _ptr;
public:
Foo(){_ptr = new nativeFoo::Foo();}
~Foo(){!Foo();}
!Foo(){if (_ptr){delete ptr;ptr = NULL;}}
String^ bar(String^ b)
{
return marshal_as<String^>(_ptr->bar(marshal_as<std::string>(b)));
}
};
}
警告:我完全遗漏了一堆#include 和#using 语句,这只是为了给出如何使用它的一般要点。
关于c# - 从 C# 调用 C++ 函数,具有大量复杂的输入和输出参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15672351/