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

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?

Related

How to make this code safe when accessing DLL?

To return arrays in C pointers needed. To clarify my question, I created a simple .c function and compiled as DLL and accessing it by C#. The C function is:
#include <stdio.h>
__declspec(dllexport) int* ReturnArray(int size) {
int* arr = malloc(size * sizeof(int));
arr[0] = 19;
arr[1] = 18;
arr[2] = 17;
arr[3] = 16;
arr[4] = 15;
return arr;
}
On the C# side:
namespace ConsoleC2CSExample
{
class Program
{
[DllImport("array.dll", EntryPoint = "ReturnArray", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern short* ReturnArray(int size);
static unsafe void Main(string[] args)
{
int arraySize = 5;
short *ptr = ReturnArray(arraySize);
for(int i = 0; i < arraySize; i++)
Console.WriteLine(*(ptr + i * sizeof(short)));
Console.Read();
}
}
}
And the output is as expected as follows:
I have two questions here.
How can this be done by not using unsafe mode?
And what are the dangers if any of using unsafe?
C#/.NET is so-called managed code because it runs safely on the common language runtime (CLR). Whereas, C code is so-called unmanaged code.
The CLR ensures, that we will not suffer the typical problems known from C code (like buffer underflow, buffer overflow, ...) resulting in vulnerable code, crashing applications, unknown behavior, etc.
So the danger of using unsafe code is, that errors in your C library will probably terminate your entire C#/.NET application unexpectedly, causing you a lot of headache.
To get at least rid of the unsafe context within your C# code, you can try the following:
using System.Runtime.InteropServices;
namespace ConsoleC2CSExample
{
class Program
{
[DllImport("array.dll", EntryPoint = "ReturnArray", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ReturnArray(int size);
static void Main(string[] args)
{
int arraySize = 5;
IntPtr ptr = ReturnArray(arraySize);
for(int i = 0; i < arraySize; i++)
Console.WriteLine(Marshal.ReadInt32(ptr + i * sizeof(int)));
Console.Read();
}
}
}

passing an array to a function pointer from 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

Passing struct containing a predifined array from C# to C

I am trying to use a dll written in C in a C# program. I have defined a simple struct which contains an integer and an array. The array will not have always the same length. I have also written a simple code to better understand why it does not work.
The C# program initializes this struct and fills this array with data, then it passes to the dll. In order to check that this works correctly, the dll copies the content of the array of this struct to another array (initialized already in C#). Finally the C# program prints both arrays which should be the same, it is not case.
Execution shows no error, but it seems that the dll can not interpret correctly the content of the struct.
I tried to use some Marshal instructions but I am not so familiar with them, so it did not help. Here are both codes, C and C#, thank you very much for your help.
Thomas
#THE C# CODE
#region DeclareStruct
[StructLayout(LayoutKind.Sequential)]
public struct Test
{
public double[] theVector;
public int nElements;
}
#endregion DeclareStruct
#region DLLFunctions
[DllImport("TestLib.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "readStruct")]
public static extern void readStruct(ref Test theStruct, double[] a);
#endregion DLLFunctions
static void Main(string[] args)
{
//delcare struc
Test myStruct = new Test();
myStruct.nElements = 10;//will not be always 10
myStruct.theVector = new double[myStruct.nElements];
//fills myStruct.theVector
for (int i = 0; i < myStruct.nElements; i++) myStruct.theVector[i] = i * i;
//delcare the array a which should have the same content as myStruct.theVector
double[] a = new double[myStruct.nElements];
// Initialize unmanged memory to hold the struct.
//IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(myStruct)));
//Marshal.StructureToPtr(myStruct, pnt, false);
//executes dll
readStruct(ref myStruct, a);
//display myStruct.theVector and a
Console.WriteLine("This is theVector");
for (int i = 0; i < myStruct.nElements; i++) Console.WriteLine(myStruct.theVector[i].ToString());
Console.WriteLine("This is a");
for (int i = 0; i < myStruct.nElements; i++) Console.WriteLine(a[i].ToString());
}
#THE C CODE
typedef struct Test
{
double* theVector;
int nElements;
}
Test;
__declspec (dllexport) void readStruct(Test *myTest, double *a)
{
int i;
for (i = 0; i < myTest->nElements; i++) a[i] = myTest->theVector[i];
}

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.

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