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);
}
}
Related
I need to pass an array from C++ to C#
The C++ header is the following
extern "C" GMSH_API void GMSH_Model_OCC_Fragments(int* arrayPtr);
The C++ cpp is the following
void GMSH_Model_OCC_Fragments(int* arrayPtr)
{
int array[] = {1,2};
arrayPtr = array;
}
The C# code is the following
[DllImport("GMSHCSHARP.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GMSH_Model_OCC_Fragments(out IntPtr arrayPtr);
public void Create()
{
GMSH_Model_OCC_Fragments(out IntPtr arrayPtr);
int[] ReturnArray = new int[2];
int size = Marshal.SizeOf(ReturnArray[0]) * ReturnArray.Length;
Marshal.Copy(arrayPtr, ReturnArray, 0, size);
}
Seems that arrayPtr is transmitted as null and this causes the Marshal.Copy to return an error.
I'd appreciate any help.
When you call C++ from C# you have to play by the rules of C++, so you cannot assign an array by
arrayPtr = array;
You can however fill an array given by C#
C++ Function
void someFunction(char* dest, size_t length){
char* someData = "helloWorld";
size_t copyLen = std::min(length, strlen(someData));
memcpy(dest, someData, copyLen);
//If it was a string you'd also want to make sure it's null terminated
}
C# Function call (Something like this sorry if it's not 100%)
[DllImport("MyDll.dll", CallingConvention = CallingConvention.CDecl)]
private static extern void someFunction(Byte[] dest, uint Length);
Byte[] array = new byte[10];
someFunction(array, 10);
I'm not sure if you can pass heap memory out of C++, but if you can you would do this:
void GMSH_Model_OCC_Fragments(int** dest){
*dest = new int[2];
*dest[0] = 1;
*dest[1] = 2;
}
Note you need to use a double pointer to pass back memory in this fashion. As you need the first pointer to reference the object or array, and the second pointer to keep track of that.
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();
}
}
}
I try to make a DLL out of a C/C++ Program that was written for Linux. I got it running in the Cygwin shell and made an interface for the DLL. I also used this to call the function but now the DLL hangs when it wants to allocate memory in a c file.
I broke the problem down to this simple program:
The malloctest.cpp locks like this
#include <stdlib.h>
#include "test.h"
extern "C" __declspec(dllexport) int malloctest();
int malloctest(){
int check = test();
return check;
}
test.h
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
int test();
#ifdef __cplusplus
}
#endif
test.c
#include "test.h"
int test(){
int * array = malloc(42 * sizeof(int));
free(array);
return 42;
}
I compiled it like this in the Cygwin shell
gcc -c malloctest.cpp test.c
gcc -shared -o malloctest.dll malloctest.o test.o
and I called it in C# like this:
class Program
{
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int test();
static void Main(string[] args)
{
IntPtr pcygwin = LoadLibrary("cygwin1.dll");
IntPtr pcyginit = GetProcAddress(pcygwin, "cygwin_dll_init");
Action init = (Action)Marshal.GetDelegateForFunctionPointer(pcyginit, typeof(Action));
init();
IntPtr libptr = LoadLibrary("malloctest.dll");
IntPtr funcptr = GetProcAddress(libptr, "malloctest");
test malloctest = (test)Marshal.GetDelegateForFunctionPointer(funcptr, typeof(test));
int check = malloctest(); //hold
Console.WriteLine(check);
}
}
Now it just hangs without an Error.
It works if I use malloc in malloctest.cpp but as soon as I call a C file that uses malloc the dll just hangs.
Edit:
this is my real interface.h
extern "C" {
typedef struct det det_t;
struct det{
int id;
int hamming;
float goodness;
float decision_margin;
double c[2];
double lu[2];
double ld[2];
double ru[2];
double rd[2];
};
__declspec(dllexport) int scanJPGfromBUF(det_t * dets, uint8_t *, char *);
__declspec(dllexport) int test ();
}
Try changing the delegate to:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int test();
The default for .NET should be Stdcall, but the default in C is Cdecl.
I would translate the C struct to:
struct det
{
int id;
int hamming;
float goodness;
float decision_margin;
double c1;
double c2;
double lu1;
double lu2;
double ld1;
double ld2;
double ru1;
double ru2;
double rd1;
double rd2;
}
So I would remove all the arrays and transform them to multiple elements. The signature of the C method can be translated in multiple ways in C#.
Each parameter with a * could be a ref, a out or a [] (but not a ref [])... So it could be:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int scanJPGfromBUF(ref det_t dets, byte[] bytes1, byte[] chars1);
I'm using Robert Giesecke's Unmanaged Exports package to be able to call from C++ to C#.
This has to use the C interface from within C++. I have managed to get most things working, by scouring the web and picking up bits here and there....
extern "C"
{
// Simple
__declspec(dllimport) int IntTest(int input);
__declspec(dllimport) double DoubleTest(double input);
// Array of simple types in
__declspec(dllimport) int passArray(int t[], int i, int xx);
// String in and out
__declspec(dllimport) int PassStringIn(wchar_t* str);
__declspec(dllimport) int PassStringOut(wchar_t** str);
__declspec(dllimport) wchar_t* PassStringInOut(wchar_t* str);
// Array of strings types in
//__declspec(dllimport) int passArrayStrings(char** t, int i);
}
....
// Int in and out
int aa = IntTest(4);
// Double in and out
double bb = DoubleTest(4.3);
// Pass array in
int arr[4] = { 1,2,3,4 };
int cc = passArray(arr, 4, 0);
// String in
wchar_t* a_str = L"input string from C++";
int dd = PassStringIn(a_str);
// String out
wchar_t* b_str = L"not used";
int ee = PassStringOut(&b_str);
// String in & out
wchar_t* d_str = L"bob";
wchar_t* result = PassStringInOut(d_str);
corresponding C#
[DllExport( CallingConvention = CallingConvention.Cdecl)]
static int IntTest(int input)
{
return input + 1;
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static double DoubleTest(double input)
{
return input + 1;
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int passArray([In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] tab, int i, int x)
{
return tab[x];
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int PassStringIn( [MarshalAs(UnmanagedType.LPWStr)] string inputString)
{
Console.WriteLine("Hi, the string passed in was :" + inputString);
return 1;
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static int PassStringOut([MarshalAs(UnmanagedType.BStr)] out string outputString)
{
Console.WriteLine("Hi, I will return the time from c#");
outputString = DateTime.Now.ToLongTimeString();
return 0; // indicates success
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPTStr)]
public static string PassStringInOut([MarshalAs(UnmanagedType.LPTStr)]string name)
{
return string.Format("Hello from .NET assembly, {0}!", name);
}
Which was nice! Anyway would anybody be able to help with passing arrays of strings in and out. I am pretty sure the C# section should look like this:
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int passArrayStrings( [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1)] string[] tab, int i)
{
return 1;
}
I need some help on the C++(C) side on how to structure the array of strings in, such that they can be marshaled correctly. The mixed mode assembly created has both C# and and a C interface. As it is C and not C++ the arguments types of the exposed functions are not visible.
Thanks
You can use an IntPtr parameter.
You'll have to allocate unmanaged memory and copy the array into that blob anyway. Otherwise the GC will eat your array at some point.
Unmanaged Exports with Arrays
ok so after a lot of messing about I came to a solution:
// Array of strings types in
__declspec(dllimport) int passArrayStrings(BSTR* bstrArray, int i);
BSTR bstrArray[10] = { 0 };
for (int i = 0; i < 10; i++)
{
bstrArray[i] = ::SysAllocString(L"My String.");
}
int ff = passArrayStrings(bstrArray, 10);
for (int i = 0; i < 10; i++)
{
::SysFreeString(bstrArray[i]);
}
and on the c# side:
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int passArrayStrings([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] string[] tab, int iSize)
{
return 1;
}
I'd like to call an unmanaged method that allocates memory, creates an array of LPWSTRs, and returns it to managed code. I'd like to avoid in/out parameters and writing code to manage memory and variable scopes as much as possible so I decided I would rely on using CoTaskMemAlloc and let the marshaller automagically clean up after me.
Here's what I have (a modified version of a p/invoke tutorial method on MSDN):
extern "C" DLL1_API LPWSTR *TestArrayOfStrings(_In_ int count)
{
STRSAFE_LPWSTR temp = NULL;
wchar_t * ppStrArray[10] = { NULL };
const size_t alloc_size = sizeof(wchar_t *) * 10;
for (int i = 0; i < 10; i++)
{
temp = (STRSAFE_LPWSTR)CoTaskMemAlloc(alloc_size);
if (i % 2 == 0)
StringCchCopy(temp, alloc_size, L"0123456789");
else
StringCchCopy(temp, alloc_size, L"9876543210");
CoTaskMemFree(ppStrArray[i]);
ppStrArray[i] = temp;
}
count = 10;
return ppStrArray;
}
and on the managed side:
[DllImport("Dll1.Windows.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.LPWStr)]
public static extern string[] TestArrayOfStrings(out int count);
As you can see I've tried to use additional attributes but the marshaller just doesn't seem to like it--I keep getting "Cannot marshal 'return value': Invalid managed/unmanaged type combination." I am trying to maintain typing as an array of LPWSTRs and would like to avoid SAFEARRAY, for which marshalling is marked obsolete.
Slightly modified code, but the signature was what was important. This method assigns a string value (the third parameter) to the first element of the uninitialized out array passed in.
[DllImport("Dll1.Windows.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void TestArrayOfStrings(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1)] [Out] out string[] test,
out int size, string someString);
extern "C" DLL1_API void TestArrayOfStrings(wchar_t ***strings, int *size, wchar_t *someString){
const size_t alloc_size = 64;
STRSAFE_LPWSTR temp = (STRSAFE_LPWSTR)CoTaskMemAlloc(alloc_size);
StringCchCopy(temp, alloc_size, someString);
*strings = (wchar_t **)CoTaskMemAlloc(sizeof(wchar_t));
*strings[0] = temp;
*size = 1;
}