[英]Passing a vector/array from unmanaged C++ to C#
I want to pass around 100 - 10,000 Points from an unmanaged C++ to C#. 我想将100-10,000点从非托管C ++传递给C#。
The C++ side looks like this: C ++端看起来像这样:
__declspec(dllexport) void detect_targets( char * , int , /* More arguments */ )
{
std::vector<double> id_x_y_z;
// Now what's the best way to pass this vector to C#
}
Now my C# side looks like this: 现在我的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 */ );
}
}
How do I need to alter my code in order to pass the std::vector from unmanaged C++ with all it's content to C#? 为了将std :: vector及其所有内容从非托管C ++传递到C#,我该如何更改我的代码?
As long as the managed code does not resize the vector, you can access the buffer and pass it as a pointer with vector.data()
(for C++0x) or &vector[0]
. 只要托管代码不调整向量的大小,就可以使用
vector.data()
(对于C ++ 0x)或&vector[0]
来访问缓冲区并将其作为指针传递。 This results in a zero-copy system. 这导致零拷贝系统。
Example C++ API: 示例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<double>();
for (int i = 0; i < 500; i++)
{
items->push_back((double)i);
}
*hItems = reinterpret_cast<ItemListHandle>(items);
*itemsFound = items->data();
*itemCount = items->size();
return true;
}
EXPORT bool ReleaseItems(ItemListHandle hItems)
{
auto items = reinterpret_cast<std::vector<double>*>(hItems);
delete items;
return true;
}
Caller: 呼叫者:
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
I implemented this using C++ CLI wrapper. 我使用C ++ CLI包装器实现了这一点。 C++ CLI is one the three possible approaches for C++ C# interop.
C ++ CLI是C ++ C#互操作的三种可能方法之一。 The other two approaches are P/Invoke and COM.
其他两种方法是P / Invoke和COM。 (I have seen a few good people recommend using C++ CLI over the other approaches)
(我看到一些好人推荐使用C ++ CLI而不是其他方法)
In order to marshall information from native code to managed code, you need to first wrap the native code inside a C++ CLI managed class. 为了将信息从本机代码整理到托管代码,您需要首先将本机代码包装在C ++ CLI托管类中。 Create a new project to contain native code and its C++ CLI wrapper.
创建一个新项目以包含本机代码及其C ++ CLI包装器。 Make sure to enable the
/clr
compiler switch for this project. 确保为此项目启用
/clr
编译器开关。 Build this project to a dll. 将此项目生成为dll。 In order to use this library, simply add its reference inside C# and make calls against it.
为了使用该库,只需在C#中添加其引用并对其进行调用。 You can do this if both projects are in the same solution.
如果两个项目都在同一解决方案中,则可以执行此操作。
Here are my source files for a simple program to marshal a std::vector<double>
from native code into C# managed code. 这是我的一个简单程序的源文件,该程序可以将
std::vector<double>
从本机代码整理为C#托管代码。
1) Project EntityLib (C++ CLI dll) (Native Code with Wrapper) 1)项目EntityLib(C ++ CLI dll) (带有包装程序的本机代码)
File NativeEntity.h 文件NativeEntity.h
#pragma once
#include <vector>
class NativeEntity {
private:
std::vector<double> myVec;
public:
NativeEntity();
std::vector<double> GetVec() { return myVec; }
};
File NativeEntity.cpp 文件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 (Wrapper Class) 文件ManagedEntity.h (包装类)
#pragma once
#include "NativeEntity.h"
#include <vector>
namespace EntityLibrary {
using namespace System;
public ref class ManagedEntity {
public:
ManagedEntity();
~ManagedEntity();
array<double> ^GetVec();
private:
NativeEntity* nativeObj; // Our native object is thus being wrapped
};
}
File ManagedEntity.cpp 文件ManagedEntity.cpp
#include "stdafx.h"
#include "ManagedEntity.h"
using namespace EntityLibrary;
using namespace System;
ManagedEntity::ManagedEntity() {
nativeObj = new NativeEntity();
}
ManagedEntity::~ManagedEntity() {
delete nativeObj;
}
array<double>^ ManagedEntity::GetVec()
{
std::vector<double> tempVec = nativeObj->GetVec();
const int SIZE = tempVec.size();
array<double> ^tempArr = gcnew array<double> (SIZE);
for (int i = 0; i < SIZE; i++)
{
tempArr[i] = tempVec[i];
}
return tempArr;
}
2) Project SimpleClient (C# exe) 2)项目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]);
}
}
}
I could think of more than one option, but all include copying the data of the array anyways. 我可以想到的选项不止一个,但无论如何都包括复制数组的数据。 With [out] parameters you could try:
使用[out]参数,您可以尝试:
C++ code C ++代码
__declspec(dllexport) void __stdcall detect_targets(wchar_t * fn, double **data, long* len)
{
std::vector<double> id_x_y_z = { 1, 2, 3 };
*len = id_x_y_z.size();
auto size = (*len)*sizeof(double);
*data = static_cast<double*>(CoTaskMemAlloc(size));
memcpy(*data, id_x_y_z.data(), size);
}
C# code 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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.