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();
}
}
}
Related
i wanted to run a c++ function in c# via dll and pass few doubles.
The easiest way i found to pass multiple values is to predefine pointers and safe the values there.
However, here comes the question do i have to free the memory in c# and if yes, how can i delete the pointers?
Thanks!
[DllImport(CppPokerOddsCalculatorDLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static unsafe extern void CalculateOdds_2Hands_Test(string myHand1_param, string myHand2_param, double* res1, double* res2, double* res3);
static void Main(string[] args)
{
unsafe
{
double res1 = 0;
double res2 = 0;
double res3 = 0;
double* p_res1 = &res1;
double* p_res2 = &res2;
double* p_res3 = &res3;
CalculateOdds_2Hands_Test("AhAc", "3h3c", p_res1, p_res2, p_res3);
Console.WriteLine(res1);
Console.WriteLine(res2);
Console.WriteLine(res3);
}
}
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?
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];
}
I'm tyring to pass a C# string to C dll function which supposes to encrypt it. Unfortunately, it does simply nothing. After calling the function, the string is still the same.
C function:
#include <stdio.h>
#include <stdlib.h>
extern "C"{
__declspec(dllexport) void Encrypt( char *plainText,long height,long inputLength)
{
unsigned char *encryptedText=(unsigned char*)malloc(sizeof(plainText));
unsigned char **cipherArray;
cipherArray=(unsigned char**)malloc(height*sizeof(unsigned char *));
for(long i=0; i<height; i++)
{
cipherArray[i]=(unsigned char*)malloc(inputLength*sizeof(char));
for (long j=0; j<inputLength ; j++)
cipherArray[i][j]='#';
}
bool addRow=true;
long row=0;
long column = 0;
long arrayIterator = 0;
while(arrayIterator<inputLength){
cipherArray[row][column] = plainText[arrayIterator];
column++;
if(addRow)row++;
else row--;
if (row >= height)
{
row--;
row--;
addRow=false;
}
else if (row < 0)
{
row++;
row++;
addRow = true;
}
arrayIterator++;
}
long iterator=0;
for (long i=0; i< height; i++)
for(long j=0; j<inputLength;j++){
if(cipherArray[i][j]!='#'){
encryptedText[iterator]=cipherArray[i][j];
iterator++;
}
}
long j=0;
while(j<inputLength){
plainText[j]=encryptedText[j];
printf("%c",encryptedText[j]);
j++;
}
for(long i=0; i<height; i++)
free(cipherArray[i]);
free(cipherArray);
cipherArray = NULL;
}
}
Containing C# class:
namespace RailFenceCipher
{
public class CCipher
{
[DllImport("Win32Project3.dll", EntryPoint = "Encrypt", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void Encrypt([MarshalAs(UnmanagedType.LPStr)] string plainText, long height, long inputLength);
}
}
and calling for the function:
private void cipherC()
{
string plainText = this.fileInput;
Console.WriteLine("=== C# test, using IntPtr and Marshal ===");
CCipher.dllprint();
CCipher.Encrypt(plainText, this.height, this.fileInput.Length);
this.fileOutputC = plainText;
Console.WriteLine("=== END ===");
}
after the calling, plainText is not changed.
This is to be expected. You are marshalling the data in, but not out. That is the behaviour for a parameter of type string. You need to use StringBuilder in order to marshal text back from the native code.
In addition, C# long is 64 bit, but C++ long in 32 bit. Your pinvoke should be:
[DllImport("Win32Project3.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
public static extern void Encrypt(StringBuilder plainText,
int height, int inputLength);
And you need to make sure that the capacity of the StringBuilder instance that you pass is sufficient for the text to be returned.
Perhaps a bigger problem is that your C++ code is broken. At the least, you need to fix the malloc call which receives sizeof(plainText). That's the size of a pointer. You need to pass the length of the buffer, inputLength. You should debug that code first before attempting interop.
I am trying to create a Win32 DLL exposes some functions which are called in C# as follows
__declspec(dllexport) int GetData(unsigned char* *data, int* size)
{
try
{
int tlen = 3;
unsigned char* tchr = new unsigned char[5];
tchr[0] = 'a';
tchr[1] = 'b';
tchr[2] = 'c';
*size = tlen;
*data = tchr;
return 1;
}
catch (char *p)
{
return 0;
}
}
And on C# side
[DllImport("MyDll.dll")]
static extern int GetData(ref byte[] data, ref int size);
static void Main()
{
try
{
int hr = 0;
byte[] gData = null;
int gSize = 0;
hr = GetData(ref gData, ref gSize);
Console.WriteLine(gSize);
for (int i = 0; i < gSize; i++)
Console.WriteLine((char)gData[i]);
}
catch (Exception p)
{
Console.WriteLine(p.ToString());
}
}
When I run C# code, AccessViolationException happens on GetData function which is a sign of exception in C++ code however, following C++ code snippet works fine without any error.
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char* data = NULL;
int size = NULL;
GetData(&data, &size);
printf("%d", size);
for (int i = 0; i < size; i++)
printf("%c,", data[i]);
return 0;
}
If you compare C# main function and C++ _tmain, they are almost analoguous so where I may make a mistake?
You are returning an array allocated by a call to C++ new and hoping that the marshaler will turn it into a C# byte[]. That won't happen.
You'll need to pass a pointer by reference and then marshal it by hand. Your p/invoke should look like this:
[DllImport("MyDll.dll")]
static extern int GetData(out IntPtr data, out int size);
When the function returns data will point to the array and you can read the contents using the Marshal class. I guess you would copy it to a new byte array.
var arr = new byte[size];
Marshal.Copy(data, arr, 0, size);
Some other points:
The calling conventions do not match. The native side is cdecl and the managed is stdcall.
You'll need to export a deallocator to delete the memory returned by the native function. Consider a re-design where the caller allocates the buffer.