passing an array to a function pointer from C# - c#

I've implemented some functions in C program and wrap it into a dll, which will be called in another C# project. One of the function in the dll takes a function pointer as an input argument, i.e., in the source code generating the dll, one of the function looks like:
void functionA(
int* a,
int* b,
double *c,
double[] inputArr,
double[] outputtArr,
void(*fun)(int*, int*,double[],double[])
)
Inside the functionA, the inputArr will be used as input to the function pointer and outputArr will be used as the output. The inputArr should has a dimension of *a and the size of outputtArr should be *b.
In C#, I tried to import the dll and invoke the functionA by following:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace Test{
class Program
{
public delegate void costfun(ref int a, ref int b, double[] x, double[] fx);
static void Main(string[] args)
{
int a = 5, b = 3,
double c= 1e-14
double[] x = new double[5] { 1, 3, 5, 7, 9 };
double[] y= new double[3] { 100, 100, 100 };
costfun F = new costfun(fun);
functionA(ref a, ref b, ref c, x,y Marshal.GetFunctionPointerForDelegate(F));
}
public static void fun(ref int na, ref int nb, double[] inX, double[] outY)
{
int i = 0;
outY= new double[nb];
if (outY.Length != nb)
{
//Console.WriteLine("output incorrect");
return;
}
if (inX.Length != na)
{
// Console.WriteLine("input incorrect");
return;
}
for (i = 0; i < nb; i++)
{ outY[i] = inX[i] * inX[i + 1] + 1; }
}
// import the dll
[DllImport("cdll.dll", EntryPoint = "functionA", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void functionA(int* a, int* b, double*c,double[] inputArr, double[] outputtArr, IntPtr fcpointer);
}
}
The program is successfully compiled an can be executed. yet I found during the execution, whenever the function fun is invoked from the dll, the input array inX only has a single element, that is only inX[0] is passed. If I comment the two if statement inside the function fun, visual studio will break due to an index out of bound exception occurred in the statement:
outY[i] = inX[i] * inX[i + 1] + 1;
because of inX[i+1].Can anyone gives me some hints how to tackle this problem? Thanks

Related

Problem returning unsigned char value from C++ dll to C#

In the following example, I try to concatenate two unsigned chars(which is the requirement) by passing the values to a C++ dll and return a single string. The output I'm getting is not right.
C#:
using System;
using System.Runtime.InteropServices;
using System.Text;
class HelloWorld
{
[DllImport("cpp_func.dll")]
public static extern IntPtr concat_fun(byte[] a,byte[] b, int c, int d);
static void Main()
{
int x,y;
IntPtr return_value;
string hello = "hello", world = "world", final;
byte[] hel = Encoding.ASCII.GetBytes(hello);
byte[] wor = Encoding.ASCII.GetBytes(world);
x = hel.Length;
y = wor.Length;
return_value = concat_fun(hel, wor, hel.Length, wor.Length);
final = Marshal.PtrToStringAuto(return_value);
Console.WriteLine("Concatenated string:" +final);
Console.Read();
}
}
I've declared them as byte[], since that's how native type uint8_t is represented in .NET(https://learn.microsoft.com/en-us/dotnet/standard/native-interop/type-marshaling)
I've passed the two byte array along with their lengths as parameters.
C++:
_declspec(dllexport) unsigned char * concat_fun(unsigned char a[], unsigned char b[], int d, int e) {
int i, ind = 0;
unsigned char c[20];
for (i = 0; i < d; i++) {
c[ind] = a[i];
ind++;
}
for (i = 0; i < e; i++) {
c[ind] = b[i];
ind++;
}
return c;
}
The output I'm getting is this:
Concatenated string:????????????????
How do I get the concatenated string?
Note: Getting input as an unsigned char for the dll function parameter, is a requirement
I know I'm making some trivial mistake here, since I'm just a beginner.
The memory for c array is allocated in the concat_fun function and has a scope and lifetime of this function, so the memory is released when you leave the function body.
Try to allocate c array in the calling function Main or use dynamic memory allocation: new/delete or malloc/free in concat_fun.

C# P/Invoke | Inconsistent marshalling behaviour of array of blittable struct?

I was testing around with P/Invoke stuff today and ran into something that greatly confuses me.
I have a unmanaged library with functions taking array parameters, printing out their values and modifying them:
#include <cstdio>
#define export extern "C" void __cdecl __declspec(dllexport)
struct S
{
int x;
};
export PassIntArray(int* x, int size)
{
for (int i = 0; i < size; i++)
{
printf("x[%i] = %i\n", i, x[i]);
x[i] *= 10;
}
}
export PassSArray(S* s, int size)
{
for (int i = 0; i < size; i++)
{
printf("s[%i].x = %i\n", i, s[i].x);
s[i].x *= 10;
}
}
As well as a C# program accessing those function via P/Invoke:
using System.Runtime.InteropServices;
using static System.Console;
namespace Test
{
[StructLayout(LayoutKind.Sequential)]
struct S
{
public int X;
}
class Program
{
[DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PassIntArray(int[] x, int size);
[DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PassSArray(S[] s, int size);
static void Main(string[] args)
{
var z = new[] {1,2,3};
PassIntArray(z, z.Length);
foreach (var i in z)
WriteLine(i);
var u = new[] { new S { X = 1 }, new S { X = 2 }, new S { X = 3 } };
PassSArray(u, u.Length);
foreach (var i in u)
WriteLine(i.X);
}
}
}
Running this program, the output for the array functions is as follows:
// Unmanaged side:
x[0] = 1
x[1] = 2
x[2] = 3
// Managed side, after modification:
10
20
30
But, for PassSArray, it's this:
// Unmanaged side:
s[0].x = 1
s[1].x = 2
s[2].x = 3
// Managed side, after modification:
1
2
3
From this question:
"Happens when the value is blittable, an expensive word that means that the managed value or object layout is identical to the native layout. The pinvoke marshaller can then take a shortcut, pinning the object and passing a pointer to managed object storage. You'll inevitably see changes then since the native code is directly modifying the managed object."
From my understanding, S should be blittable (since it uses sequential layout and only contains fields of types which are blittable), and so should get pinned by the marshaller, causing modifications to 'carry over' as they do with PassIntArray. Where's the difference, and why?

Converting data from C++ dll in C#

I use C# and C++ dll. I want to send array from C++ to C#. I want to return from C++ array with 512 doubles. In C++ code it works perfect. I have results exactly what I expected in array of double.
Later I send data from C++ to C# and convert this data to array of double in C#. First 450 elements from C++ are moved to array in C# without any error. But left doubles are weird and they don't have anything common with input data.
I don't know why exactly at 450 element starts wronge doubles until end.
EDIT.
Also the same issue when I change arrays in C++ and C# to float and also when I parse data to integer.
C# code.
[DllImport(#"C:\Users\Jaryn\Documents\Visual Studio 2013\Projects\MathFuncDll\Debug\MathFuncDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr One(string address);
static void Main(string[] args)
{
var pointer = One(#"C:\Users\Jaryn\Documents\101_ObjectCategories\accordion\image_0002.jpg");
var result = new double[512];
Marshal.Copy(pointer, result, 0, 512);
foreach (var x in result)
{
Console.WriteLine(x + " ");
}
}
MathFuncsDll.h
#include <stdexcept>
using namespace std;
namespace MathFuncs
{
extern "C" { __declspec(dllexport) double* One(char* str); }
}
MathFuncsDll.cpp
double* One(char* adress)
{
IplImage* img = cvLoadImage(adress, CV_LOAD_IMAGE_COLOR);
double data[512];
int iteration = 0;
...
for (int h = 0; h <h_bins; h++)
{
for (int s = 0; s < s_bins; s++)
{
double bin_value = 0;
for (int v = 0; v < h_bins; v++)
{
bin_value += cvGetReal3D(hist->bins, h, s, v);
data[iteration] = bin_value;
iteration++;
}
}
}
...
return data;
}
Your problem is that you are returning the address of a local variable. As soon as the function returns, that local variable's life ends. And so the address you return is the address of an object whose life is over.
The clean way to do this is to let the caller allocate the array and have the callee populate it. Have the caller pass the address of the first element of the array, and the length of the array. Then the callee can be sure not to write beyond the end of the array.

Function for swapping 2 elements in an array doesn't work

I am new with C# and I can't understand why this code doesn't work.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
char[] sw = "ab".ToCharArray();
swap(sw[0], sw[1]);
string end = new string(sw);
Console.Write(end);
}
static void swap(char a, char b)
{
char temp = a;
a = b;
b = temp;
}
}
}
What I expect on console is "ba" but I get "ab". I was able to find different approach to solve this problem but what I would like to know is what is the mistake in this code.
Thanks for the help!
The problem is that the swap method is actually just manipulating local copies of a and b. You need to pass the arguments by reference. So you would define the swap method like this:
static void swap(ref char a, ref char b)
{
char temp = a;
a = b;
b = temp;
}
And call it like this:
swap(ref sw[0], ref sw[1]);
It should be modified like the following (Note: in this example, ref char[] arr is prefixed with ref mostly for didactic purpose: array will be passed by ref by default)
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
char[] sw = "ab".ToCharArray();
swap(0, 1, ref sw );
string end = new string(sw);
Console.Write(end);
}
static void swap(int indexA, int indexB, ref char[] arr)
{
char temp = arr[indexA];
arr[indexA] = arr[indexB];
arr[indexB] =temp;
}
}
}
A more generic array swap function:
public static void Swap<T>(this T[] array, int indexA, int indexB)
{
T temp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = temp;
}
Also, a generic function to swap several array elements:
public static void Swap<T>(this T[] array, int indexA, int indexB, int length)
{
while (length-- > 0)
Swap(array, indexA++, indexB++);
}
Your swap is taking two value types and swapping the values between the variables. There's nothing there that would modify the original array. You would need to modify your swap method to something like:
static void Swap(char[] array, int a, int b)
{
char temp = array[a];
array[a] = array[b];
array[b] = temp;
}
You could then call it from Main() like:
Swap(array, 0, 1);
You are passing your arguments a and b by value. See What's the difference between passing by reference vs. passing by value? for more information.
Here are two solutions to fix your issue.
//Pass by value and return the values
static Tuple<char, char> swap2(char a, char b)
{
char temp = a;
a = b;
b = temp;
return new Tuple<char, char>(a, b);
}
//Pass by reference
static void swap3(ref char a, ref char b)
{
char temp = a;
a = b;
b = temp;
}
public static void Main(string[] args)
{
char[] sw2 = "ab".ToCharArray();
var chars2 = swap2(sw2[0], sw2[1]);
sw2[0] = chars2.Item1;
sw2[1] = chars2.Item2;
//Will print "ba"
Console.WriteLine(sw2);
char[] sw3 = "ab".ToCharArray();
swap3(ref sw3[0], ref sw3[1]);
//Will print "ba"
Console.WriteLine(sw3);
}
Here's a question about whether you should use or try to avoid the ref keyword. Aside from the simplest uses, it's often advised to avoid ref when possible. Swap falls into the category of "simplest uses," but I suggest that you try to avoid using ref in most practical situations.
When is using the C# ref keyword ever a good idea?

Passing an C# array of structs with array of double member to C DLL by reference

We have the following code marshalling between c# and c dll. However, when print the value within the C dll function, the values associated with the double array properties are all 0.0000000. I have put some in line comments for the code having problems.
Did we miss any thing to configure the marshalling behaviour?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
MonteCarlo montecarlo = new MonteCarlo();
montecarlo.RunMonteCarlo();
}
}
class MonteCarlo
{
[DllImport("MonteCarloCUDA.dll")]
public static extern int MonteCarloPrint([In, Out]PurchaseOrder[] purchaseorders, int length);
public void RunMonteCarlo()
{
PurchaseOrder[] purchaseorders = new PurchaseOrder[3];
purchaseorders[0] = new PurchaseOrder();
purchaseorders[0].Value1 = "AAAAA";
purchaseorders[0].Value2 = 0.111;
purchaseorders[0].Value3 = new double[2]; // Assign the values to array of double
purchaseorders[0].Value3[0] = 0.11111;
purchaseorders[0].Value3[1] = 0.22222;
purchaseorders[1] = new PurchaseOrder();
purchaseorders[1].Value1 = "BBB";
purchaseorders[1].Value2 = 0.222;
purchaseorders[1].Value3 = new double[2];
purchaseorders[1].Value3[0] = 0.33333;
purchaseorders[1].Value3[1] = 0.44444;
purchaseorders[2] = new PurchaseOrder();
purchaseorders[2].Value1 = "CCC";
purchaseorders[2].Value2 = 0.333;
purchaseorders[2].Value3 = new double[2];
purchaseorders[2].Value3[0] = 0.55555;
purchaseorders[2].Value3[1] = 0.66666;
int result = MonteCarloPrint(purchaseorders, purchaseorders.Length);
Console.ReadKey();
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public unsafe struct PurchaseOrder
{
public string Value1;
public double Value2;
public double[] Value3; // Array of double member
}
}
}
// C Code
#include <stdio.h>
typedef struct PurchaseOrder
{
char* Value1;
double Value2;
double* Value3;
};
__declspec(dllexport) int __stdcall MonteCarloPrint(PurchaseOrder *hostPurchaseOrders, int length)
{
printf("\nSize of PurchaseOrder: %d",sizeof(PurchaseOrder));
for (int i = 0; i < length; i++)
{
printf("\n\nAddress: %u",hostPurchaseOrders+i);
printf("\nValue1: %s",(hostPurchaseOrders+i)->Value1);
printf("\nValue2: %f",(hostPurchaseOrders+i)->Value2);
printf("\nValue3[0]: %f",(hostPurchaseOrders+i)->Value3[0]);
printf("\nValue3[1]: %f",(hostPurchaseOrders+i)->Value3[1]);
}
}}
The print result from the C dll function
Size of PurchaseOrder: 24
Address: 13180880
Value1: AAAAA
Value2: 0.111000
Value3[0]: 0.000000 // No value are marshalled
Value3[1]: 0.000000
Address: 13180904
Value1: BBB
Value2: 0.222000
Value3[0]: 0.000000
Value3[1]: 0.000000
Address: 13180928
Value1: CCC
Value2: 0.333000
Value3[0]: 0.000000
Value3[1]: 0.000000
Remove the Pack property from the [DllImport] declaration, it is wrong. You are not using a #pragma pack directive in your C code, the default value for Pack is appropriate. If it would have been in effect then your C code would have reported 16.
You are seeing 24 because there's 4 bytes of padding to align the double and 4 bytes of padding at the end of the structure to make the double align when the structure is used in an array. 4 + 4 + 8 + 4 + 4 = 24. The packing in effect is 8, the default.
You can make it more efficient by swapping Value2 and Value3 to get a struct of 16 bytes, no padding necessary. In case it matters. That's what the JIT compiler does.
The next problem is tougher, the P/Invoke marshaller will marshal the embedded array as a SAFEARRAY. You can solve that on the unmanaged side by using this code:
#include "stdafx.h"
#include <stdio.h>
#include <objidl.h>
struct PurchaseOrder
{
char* Value1;
double Value2;
LPSAFEARRAY Value3;
int fence;
};
extern "C"
__declspec(dllexport) int __stdcall MonteCarloPrint(PurchaseOrder *hostPurchaseOrders, int length)
{
printf("\nSize of PurchaseOrder: %d",sizeof(PurchaseOrder));
for (int i = 0; i < length; i++)
{
printf("\n\nAddress: %u",hostPurchaseOrders+i);
printf("\nValue1: %s",(hostPurchaseOrders+i)->Value1);
printf("\nValue2: %f",(hostPurchaseOrders+i)->Value2);
double* arrayPtr;
if (S_OK == SafeArrayAccessData((hostPurchaseOrders+i)->Value3, (void**)&arrayPtr)) {
printf("\nValue3[0]: %f", arrayPtr[0]);
printf("\nValue3[1]: %f", arrayPtr[1]);
}
}
return 0;
}
I wrote the code using the C++ compiler, you may have to tweak.

Categories

Resources