将矢量/数组从非托管C ++传递到C#

我想从非托管C ++传递大约100 – 10,000点到C#。

C ++端看起来像这样:

__declspec(dllexport) void detect_targets( char * , int , /* More arguments */ ) { std::vector id_x_y_z; // Now what's the best way to pass this vector to C# } 

现在我的C#端看起来像这样:

 using System; using System.Runtime.InteropServices; class HelloCpp { [DllImport("detector.dll")] public static unsafe extern void detect_targets( string fn , /* More arguments */ ); static void Main() { detect_targets("test.png" , /* More arguments */ ); } } 

我如何更改我的代码,以便将std :: vector从非托管C ++传递给C#?

只要托管代码不调整向量大小,就可以访问缓冲区并将其作为指针传递给vector.data() (对于C ++ 0x)或&vector[0] 。 这导致零拷贝系统。

示例C ++ API:

 #define EXPORT extern "C" __declspec(dllexport) typedef intptr_t ItemListHandle; EXPORT bool GenerateItems(ItemListHandle* hItems, double** itemsFound, int* itemCount) { auto items = new std::vector(); for (int i = 0; i < 500; i++) { items->push_back((double)i); } *hItems = reinterpret_cast(items); *itemsFound = items->data(); *itemCount = items->size(); return true; } EXPORT bool ReleaseItems(ItemListHandle hItems) { auto items = reinterpret_cast*>(hItems); delete items; return true; } 

呼叫者:

 static unsafe void Main() { double* items; int itemsCount; using (GenerateItemsWrapper(out items, out itemsCount)) { double sum = 0; for (int i = 0; i < itemsCount; i++) { sum += items[i]; } Console.WriteLine("Average is: {0}", sum / itemsCount); } Console.ReadLine(); } #region wrapper [DllImport("Win32Project1", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] static unsafe extern bool GenerateItems(out ItemsSafeHandle itemsHandle, out double* items, out int itemCount); [DllImport("Win32Project1", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] static unsafe extern bool ReleaseItems(IntPtr itemsHandle); static unsafe ItemsSafeHandle GenerateItemsWrapper(out double* items, out int itemsCount) { ItemsSafeHandle itemsHandle; if (!GenerateItems(out itemsHandle, out items, out itemsCount)) { throw new InvalidOperationException(); } return itemsHandle; } class ItemsSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { public ItemsSafeHandle() : base(true) { } protected override bool ReleaseHandle() { return ReleaseItems(handle); } } #endregion 

我使用C ++ CLI包装器实现了这一点。 C ++ CLI是C ++ C#interop的三种可能方法之一。 另外两种方法是P / Invoke和COM。 (我见过一些好人推荐使用C ++ CLI而不是其他方法)

为了将信息从本机代码编组到托管代码,您需要首先将本机代码包装在C ++ CLI托管类中。 创建一个新项目以包含本机代码及其C ++ CLI包装器。 确保为此项目启用/clr编译器开关。 将此项目构建到dll。 要使用此库,只需在C#中添加其引用并对其进行调用。 如果两个项目都在同一个解决方案中,您可以执行此操作。

这是我的源文件,用于将std::vector从本机代码封送到C#托管代码的简单程序。

1)Project EntityLib(C ++ CLI dll) (带有包装器的本机代码)

文件NativeEntity.h

 #pragma once #include  class NativeEntity { private: std::vector myVec; public: NativeEntity(); std::vector GetVec() { return myVec; } }; 

文件NativeEntity.cpp

 #include "stdafx.h" #include "NativeEntity.h" NativeEntity::NativeEntity() { myVec = { 33.654, 44.654, 55.654 , 121.54, 1234.453}; // Populate vector your way } 

File ManagedEntity.h (包装类)

 #pragma once #include "NativeEntity.h" #include  namespace EntityLibrary { using namespace System; public ref class ManagedEntity { public: ManagedEntity(); ~ManagedEntity(); array ^GetVec(); private: NativeEntity* nativeObj; // Our native object is thus being wrapped }; } 

文件ManagedEntity.cpp

 #include "stdafx.h" #include "ManagedEntity.h" using namespace EntityLibrary; using namespace System; ManagedEntity::ManagedEntity() { nativeObj = new NativeEntity(); } ManagedEntity::~ManagedEntity() { delete nativeObj; } array^ ManagedEntity::GetVec() { std::vector tempVec = nativeObj->GetVec(); const int SIZE = tempVec.size(); array ^tempArr = gcnew array (SIZE); for (int i = 0; i < SIZE; i++) { tempArr[i] = tempVec[i]; } return tempArr; } 

2)Project SimpleClient(C#exe)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EntityLibrary; namespace SimpleClient { class Program { static void Main(string[] args) { var entity = new ManagedEntity(); for (int i = 0; i < entity.GetVec().Length; i++ ) Console.WriteLine(entity.GetVec()[i]); } } } 

我可以想到多个选项,但所有选项都包括复制数组的数据。 使用[out]参数,您可以尝试:

C ++代码

 __declspec(dllexport) void __stdcall detect_targets(wchar_t * fn, double **data, long* len) { std::vector id_x_y_z = { 1, 2, 3 }; *len = id_x_y_z.size(); auto size = (*len)*sizeof(double); *data = static_cast(CoTaskMemAlloc(size)); memcpy(*data, id_x_y_z.data(), size); } 

C#代码

 [DllImport("detector.dll")] public static extern void detect_targets( string fn, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out double[] points, out int count); static void Main() { int len; double[] points; detect_targets("test.png", out points, out len); }