首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将向量/数组从非托管C++传递到C#

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

Stack Overflow用户
提问于 2015-07-15 05:25:50
回答 3查看 29.5K关注 0票数 20

我希望将非托管C++中的大约100 - 10,000点传递给C#。

C++端如下所示:

代码语言:javascript
运行
复制
__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#
}

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

代码语言:javascript
运行
复制
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#,我需要如何修改我的代码?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-07-15 07:09:39

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

C++接口示例:

代码语言:javascript
运行
复制
#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;
}

呼叫者:

代码语言:javascript
运行
复制
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
票数 28
EN

Stack Overflow用户

发布于 2015-07-15 07:08:34

我使用C++ CLI包装器实现了这一点。C++命令行界面是C++ C#互操作的三种可能方法之一。另外两种方法是P/Invoke和COM。(我已经看到一些很好的人推荐使用C++命令行界面而不是其他方法)

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

下面是我的一个简单程序的源文件,该程序用于将本机代码中的std::vector<double>编组为C#托管代码。

1)项目EntityLib (C++ CLI dll) (带包装的本机代码)

文件NativeEntity.h

代码语言:javascript
运行
复制
#pragma once

#include <vector>
class NativeEntity {
private:
    std::vector<double> myVec;
public:
    NativeEntity();
    std::vector<double> GetVec() { return myVec; }
};

文件NativeEntity.cpp

代码语言:javascript
运行
复制
#include "stdafx.h"
#include "NativeEntity.h"

NativeEntity::NativeEntity() {
    myVec = { 33.654, 44.654, 55.654 , 121.54, 1234.453}; // Populate vector your way
}

文件ManagedEntity.h (包装类)

代码语言:javascript
运行
复制
#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
    };

}

文件ManagedEntity.cpp

代码语言:javascript
运行
复制
#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)项目SimpleClient (C# exe)

代码语言:javascript
运行
复制
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]);
        }
    }
}
票数 12
EN

Stack Overflow用户

发布于 2015-07-15 06:54:13

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

C++代码

代码语言:javascript
运行
复制
__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#代码

代码语言:javascript
运行
复制
[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);
}
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31417688

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档