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.
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.
So from C# im passing in a double[] array into that function.
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?
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.
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.
}
double[] randomValues = new double[ARRAY_LENGTH]; //Array of random numbers
KernelWrapper.ImageNoiseFilter(ptr, image.Width, image.Height, randomValues);
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.
Pass by Value, means that a copy of the data is made and stored by way of the name of the parameter. Any changes to the parameter have NO affect on data in the calling function.
When you use pass-by-value, the compiler copies the value of an argument in a calling function to a corresponding non-pointer or non-reference parameter in the called function definition. The parameter in the called function is initialized with the value of the passed argument.
If you declare a formal parameter of a function as a pointer type, you are passing that parameter by its address. The pointer is copied, but not the data it points to. So, Pass By Address offers another method of allowing us to change the original argument of a function (like with Pass By Reference).
Pass by Result:This method uses out-mode semantics. Just before control is transferred back to the caller, the value of the formal parameter is transmitted back to the actual parameter. T his method is sometimes called call by result. In general, pass by result technique is implemented by copy.
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.
#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.
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Linq;
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);
static void ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, System::IntPtr randValPtr);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With