Converting data from C++ dll in C# - 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.

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?

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.

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];
}

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.

Categories

Resources