Passing a C# double array to a C++ function using CLI

时间:2022-09-01 23:51:25

Right, what is needed sounds simple, but it is proving to be a real pain.

I have some GUI code in C# (note i've never used C# before but familiar with the syntax) and I have C++ code which interacts with it using CLI.

我在C#中有一些GUI代码(注意我以前从未使用过C#但熟悉语法)并且我有使用CLI与之交互的C ++代码。

In C#, I want to create an array of doubles, and send it to my C++ code. I'm using the code below as the means of passing an array, and this complies in isolation.

在C#中,我想创建一个双精度数组,并将其发送到我的C ++代码。我使用下面的代码作为传递数组的方法,这是孤立的。

So from C# im passing in a double[] array into that function.

所以从C#im传入double []数组到该函数。

public ref class KernelWrapper
{
public: 
    static void ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, array<double>^ values); 

What parameter type should I use to retrieve this array from the C++ side?

我应该使用什么参数类型从C ++端检索此数组?

I've tried:

 MyFunction(double values[]){}
 MyFunction(double* values){}
 MyFunction(array<double>^ values){} 

but none compile, usually with the message of "array is not a template" for the last one, and

但没有编译,通常使用“数组不是模板”的消息,最后一个,和

Error   1   error C2664: 'RunImageNoiseFilterKernel' : cannot convert parameter 4 from 'cli::array<Type> ^' to 'double *'

Any tips on how this achieve this will be greatly appreciated.

任何有关如何实现这一目标的提示将不胜感激。


For readability, I'm updating with code up here

.cpp file:

void Bangor::KernelWrapper::ImageNoiseFilter(System::IntPtr imageData, int imageWidth,    int imageHeight, pin_ptr<double> pval){
    RunImageNoiseFilterKernel((Format24bppRgb*)((int)imageData), imageWidth, imageHeight); //If parameter would work, 4th argument would also be passed into this.
}

C# code:

    double[] randomValues = new double[ARRAY_LENGTH]; //Array of random numbers
    KernelWrapper.ImageNoiseFilter(ptr, image.Width, image.Height, randomValues);

The errors are:

Error   1   error C3824: 'cli::pin_ptr<Type>': this type cannot appear in this context (function parameter, return type, or a static member)
Error   3   The best overloaded method match for 'Bangor.KernelWrapper.ImageNoiseFilter(System.IntPtr, int, int, double*)' has some invalid arguments   
Error   4   Argument 4: cannot convert from 'double[]' to 'double*' 

Hope this clarifies a little.

希望这澄清一点。

2 个解决方案

#1


3  

You should use the pin_ptr<> class to convert the managed array to a double*. It pins the array so the garbage collector cannot move it while your native function is executing and accessing the array data.

您应该使用pin_ptr <>类将托管数组转换为double *。它固定数组,因此当您的本机函数执行并访问数组数据时,垃圾收集器无法移动它。

#include <vcclr.h>                 // Declares cli::pin_ptr<>
using namespace cli;
#pragma managed(push, off)
#  include "unmanagedstuff.h"      // Declaration of MyFunction here
#pragma managed(pop)

...

        pin_ptr<double> pvalues = &values[0];
        MyFunction(pvalues, values->Length);

With the assumption that surely MyFunction() also needs to know the size of the array so I added an extra argument that passes the size of the array. Not doing this is lethal, your native code is liable to destroy the managed heap integrity. Note that the array will only stay pinned while pvalues is in scope so it is important that MyFunction() does not store the passed pointer. If that's the case then you must copy the array.

假设MyFunction()肯定也需要知道数组的大小,所以我添加了一个额外的参数来传递数组的大小。不这样做是致命的,您的本机代码可能会破坏托管堆的完整性。请注意,只有当pvalues在范围内时,数组才会保持固定状态,因此MyFunction()不存储传递的指针很重要。如果是这种情况,那么你必须复制数组。

#2


2  

Okay, so most attempts have failed for me; likely because I'm doing it wrong :P, but here is a solution which does work.

Imports:

using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Linq;

In the C# file, I create an array as so:

    float[] randomValues = new float[ARRAY_LENGTH]; //Array of random numbers
    GCHandle handle = GCHandle.Alloc(randomValues, GCHandleType.Pinned);
    var randPtr = handle.AddrOfPinnedObject();
    KernelWrapper.ImageNoiseFilter(ptr, image.Width, image.Height, randPtr);

The definition of ImageNoiseFilter is:

static void ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, System::IntPtr randValPtr);

To obtain in my C++ function which expects a float pointer, I do this:

void KernelWrapper::ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, System::IntPtr randValues){
    RunImageNoiseFilterKernel((Format24bppRgb*)((int)imageData), imageWidth, imageHeight, (float*) ((int) randValues));
}

All this seems to work fine :), how good or safe this approach is I do not know, so anyone reading this should not assume its the "correct" approach, it just works.

所有这些看起来工作得很好:),这种方法有多么好或不安全我不知道,所以读这篇文章的人不应该假设它是“正确的”方法,它只是有效。

#1


3  

You should use the pin_ptr<> class to convert the managed array to a double*. It pins the array so the garbage collector cannot move it while your native function is executing and accessing the array data.

您应该使用pin_ptr <>类将托管数组转换为double *。它固定数组,因此当您的本机函数执行并访问数组数据时,垃圾收集器无法移动它。

#include <vcclr.h>                 // Declares cli::pin_ptr<>
using namespace cli;
#pragma managed(push, off)
#  include "unmanagedstuff.h"      // Declaration of MyFunction here
#pragma managed(pop)

...

        pin_ptr<double> pvalues = &values[0];
        MyFunction(pvalues, values->Length);

With the assumption that surely MyFunction() also needs to know the size of the array so I added an extra argument that passes the size of the array. Not doing this is lethal, your native code is liable to destroy the managed heap integrity. Note that the array will only stay pinned while pvalues is in scope so it is important that MyFunction() does not store the passed pointer. If that's the case then you must copy the array.

假设MyFunction()肯定也需要知道数组的大小,所以我添加了一个额外的参数来传递数组的大小。不这样做是致命的,您的本机代码可能会破坏托管堆的完整性。请注意,只有当pvalues在范围内时,数组才会保持固定状态,因此MyFunction()不存储传递的指针很重要。如果是这种情况,那么你必须复制数组。

#2


2  

Okay, so most attempts have failed for me; likely because I'm doing it wrong :P, but here is a solution which does work.

Imports:

using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Linq;

In the C# file, I create an array as so:

    float[] randomValues = new float[ARRAY_LENGTH]; //Array of random numbers
    GCHandle handle = GCHandle.Alloc(randomValues, GCHandleType.Pinned);
    var randPtr = handle.AddrOfPinnedObject();
    KernelWrapper.ImageNoiseFilter(ptr, image.Width, image.Height, randPtr);

The definition of ImageNoiseFilter is:

static void ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, System::IntPtr randValPtr);

To obtain in my C++ function which expects a float pointer, I do this:

void KernelWrapper::ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, System::IntPtr randValues){
    RunImageNoiseFilterKernel((Format24bppRgb*)((int)imageData), imageWidth, imageHeight, (float*) ((int) randValues));
}

All this seems to work fine :), how good or safe this approach is I do not know, so anyone reading this should not assume its the "correct" approach, it just works.

所有这些看起来工作得很好:),这种方法有多么好或不安全我不知道,所以读这篇文章的人不应该假设它是“正确的”方法,它只是有效。