Passing struct containing a predifined array from C# to C - 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];
}

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();
}
}
}

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?

Getting float array from C++ to C#

I have float array inside C++ function.
C++ function
void bleplugin_GetGolfResult(float* result)
{
float *array = new float[20];
for(int i=0; i < 20; i++)
array[i]= 25;
result = array;
//DEBUG PRINTING1
for(int i=0; i < 20; i++)
cout << result[i] << endl;//Here is correct
return;
}
Inside C#
[DllImport ("__Internal")]
private static unsafe extern void bleplugin_GetGolfResult (float* result);
public static unsafe float[] result = new float[20];
public unsafe static void GetGolfREsult(){
fixed (float* ptr_result = result) //or equivalently "... = &f2[0]" address of f2[0]
{
bleplugin_GetGolfResult( ptr_result );
//DEBUG PRINTING2
for(int i = 0; i < 20; i++)
Debug.Log("result data " + ptr_result[i]);
}
return;
}
I called GetGolfREsult() from another function to get result.
//DEBUG PRINTING1 has correct output.
But //DEBUG PRINTING2 produced 0 only.
What could be wrong?
As UnholySheep and nvoigt already stated,
result = array;
overrides the address of the passed pointer, making you lose the reference to the calling function.
Directly writing to your parameter should solve this.
result[i] = 25;
Further you dont actually have to use pointers in c#.
You can actually do the following:
Declare your Import like this:
private static extern void bleplugin_GetGolfResult (float arr[]);
Then you can call it like this:
float arr = new float[20];
bleplugin_GetGolfResult(arr);
This line in your C++ code:
float *array = new float[20];
creates a new array, which you operate on in C++. Then control returns to C#, who has it's own array and that's still unchanged. Why don't you write to the array you got?
The problem is that you use the assignment operator on the parameter result which prevents the data from being transferred to the C# array on return.
Using the following C++ example:
void z(int * x)
{
x = new int(4);
}
int main()
{
int * x = new int(-2);
z(x);
cout<<*x<<endl;
}
The output for this is -2 not 4 because you use the assignment operator on the parameter.

PInvoke function with pointer to pointer parameter

Hello I'm wrapping C++ library with C#. Next function in C++:
SCREENCAPTUREDLL_API wchar_t** getAudioDeviceList(int* listSize) {
static std::vector<wchar_t*> descriptionList;
AudioCaptureList::getInstance().Update();
AudioCaptureList::getInstance().getList(&descriptionList);
*listSize = descriptionList.size();
return &descriptionList[0];
}
Wrapping with next C# code:
[DllImport(screenCaptureDLLPath, CallingConvention = callConversion)]
private static extern IntPtr getAudioDeviceList(ref int arrayCount);
public static string[] GetAudioDeviceList()
{
IntPtr outputStr;
int length = 0;
outputStr = getAudioDeviceList(ref length);
string[] resultArray = new string[length];
for (int j = 0; j < length; j++)
{
resultArray[j] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(outputStr, 4 * j));
}
return resultArray;
}
That works perfect, exactly as I expected, but I was about to change the way I returning value from function itself to variable by reference, so I changing my code to:
C++
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** list, int* listSize) {
static std::vector<wchar_t*> descriptionList;
AudioCaptureList::getInstance().Update();
AudioCaptureList::getInstance().getList(&descriptionList);
*listSize = descriptionList.size();
list = &descriptionList[0];
}
C#
[DllImport(screenCaptureDLLPath, CallingConvention = callConversion)]
private static extern void getAudioDeviceList(out IntPtr listRef, ref int arrayCount);
public static string[] GetAudioDeviceList()
{
IntPtr outputStr;
int length = 0;
getAudioDeviceList(out outputStr, ref length);
string[] resultArray = new string[length];
for (int j = 0; j < length; j++)
{
resultArray[j] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(outputStr, 4 * j));
}
return resultArray;
}
But I got error, returned memory address is zero. What is the problem here? Please help me understood what cause the problem and how to fix that, thanks!
Why doesn't Pinvoke work? Because you are trying to interpret a pointer to a string as a pointer to a set of strings. But there is nothing wrong with PInvoke - it happens because there is actually a problem with new function signature and its internal code.
See:
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** listRef, int* listSize);
can't provide the same data like
DLL_API wchar_t** getAudioDeviceList(int* listSize)
Because original definition basically returned pointer to a set of pointers to strings(C style strings, I mean), while wchar_t** listRef can only allow to return a single pointer to a string.
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** listRef, int* listSize)
{
...
*listRef = "string";
I don't know what is going inside new version of the function(you didn't show the code), but listRef = &descriptionList[0]; will compile though won't do anything, and even if *listRef = &descriptionList[0]; somehow compiles it won't contain what you want.
So function signature should containt triple pointer to allow return of a set of strings.
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t*** listRef, int* listSize)
{
...
*listRef = &descriptionList[0];
}
Then your PInvoke would work correctly because it will have the same pointer to a set of string pointers.

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.

Categories

Resources