简体   繁体   English

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM