System.AccessViolationException when calling from C# to unsafe C++ - c#

I'm programming C# code that imports a C++ function similar to this:
//C++ code
extern "C" _declspec(dllexport) int foo(double *var1, double *var2){
double dubs[10];
RandomFunction(dubs);
*var1 = dubs[5];
*var2 = dubs[7];
return 0;
}
To import this function to C# I've tried 2 methods and both have resulted in a System.AccessViolationException. That means I'm trying to access protected memory...
//C# import code
//method 1
[DllImport(example.dll,CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int foo(ref double var1, ref double var2);
//method 2
[DllImport(example.dll,CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int foo(double *var1, double *var2);
//C# calling code
//method 1
public unsafe int call_foo(){
....Stuff
double var1 = 0;
double var2 = 0;
foo(ref var1, ref var2);
....More Stuff
}
//method 2
public unsafe int call_foo(){
....Stuff
double var1 = 0;
double var2 = 0;
double *v1 = &var1;
double *v2 = &var2;
foo(v1, v2);
....More Stuff
}
Up until now, I've thought it was a problem with my C# code, but would the C++ code's use of an array cause a problem? Am I missing something really obvious here?

This isn't a problem with your C# p/invokes or the signature of the native function foo. Aside from your C++ array syntax and what exactly RandomFunction does, everything works fine. I've just tested with my own little demo:
C++:
void RandomFunction( double dubs[] );
extern "C" __declspec(dllexport) int foo ( double* var1, double* var2 )
{
double dubs[10];
RandomFunction( dubs );
*var1 = dubs[5];
*var2 = dubs[7];
return 0;
}
void RandomFunction( double dubs[] ) {
for ( int i = 0; i < 10; i++ ) {
dubs[i] = i;
}
}
C#:
[DllImport( "Native.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern int foo( ref double var1, ref double var2 );
public static void Main( string[] args )
{
FooTest();
}
private static void FooTest()
{
double var1 = 0;
double var2 = 0;
foo( ref var1, ref var2 );
Console.Out.WriteLine( "Var1: {0:0.0}; Var2: {1:0.0}", var1, var2 );
// Prints "Var1: 5.0; Var2: 7.0"
}
I only have a few differences from what you have:
My C++ arrays are declared using the standard syntax double dubs [10].
My compiler requires two underscores in the __declspec syntax
I'm compiling everything using Visual Studio 2012, in 64-bit, on Windows 7 SP 1.

Related

deleting pointer in c#

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

How to use the catboost C API in dotnet?

I am trying to use catboost C API in C#. Below is the working code in C:
#include <stdio.h>
#include "c_api.h"
int main(int argc, char** argv){
float floatFeatures[3] = {96.215, 1.595655e+09, 3000};
char* catFeatures[0];
double result[1];
ModelCalcerHandle* modelHandle = ModelCalcerCreate();
// LoadFullModelFromFile is time consuming
if (!LoadFullModelFromFile(modelHandle, "../../test_catboost_model.cbm")) {
printf("LoadFullModelFromFile error message: %s\n", GetErrorString());
}
// CalcModelPredictionSingle is fast
if (!CalcModelPredictionSingle(modelHandle,
&floatFeatures, 3,
&catFeatures, 0,
&result, 1
)) {
printf("CalcModelPrediction error message: %s\n", GetErrorString());
}
ModelCalcerDelete(modelHandle);
printf("model score is %.20f", result[0]);
return 0;
}
And below is my attempt to do the same thing in C# (dotnet core on Linux), but it does not work... When I run "dotnet run" just no output no error message.
class Program
{
static void Main(string[] args)
{
var floatFeatures = new float[] { 96.215f, 1.595655e+09f, 3000 };
var catFeatures = new string[0];
var results = new double[1];
var modelHandle = ModelCalcerCreate();
if (!LoadFullModelFromFile(modelHandle, "{absolute path to the same model}/test_catboost_model.cbm"))
{
Console.WriteLine($"Load model error: {GetErrorString()}");
}
if (!CalcModelPredictionSingle(modelHandle, floatFeatures, 3, catFeatures, 0, out results, 1))
{
Console.WriteLine($"Predict error : {GetErrorString()}");
}
Console.WriteLine($"Model score is {results[0]}");
}
[DllImport("catboostmodel", EntryPoint = "ModelCalcerCreate")]
private static extern IntPtr ModelCalcerCreate();
[DllImport("catboostmodel", EntryPoint = "GetErrorString")]
private static extern string GetErrorString();
[DllImport("catboostmodel", EntryPoint = "LoadFullModelFromFile")]
private static extern bool LoadFullModelFromFile(IntPtr modelHandle, string fileName);
[DllImport("catboostmodel", EntryPoint = "CalcModelPredictionSingle")]
private static extern bool CalcModelPredictionSingle(
IntPtr modelHandle,
float[] floatFeatures, ulong floatFeaturesSize,
string[] catFeatures, ulong catFeaturesSize,
out double[] result, ulong resultSize
);
}
The relevant C header file is like below. The entire file is available on github.
#if defined(_WIN32) && !defined(CATBOOST_API_STATIC_LIB)
#ifdef _WINDLL
#define CATBOOST_API __declspec(dllexport)
#else
#define CATBOOST_API __declspec(dllimport)
#endif
#else
#define
CATBOOST_API
#endif
typedef void ModelCalcerHandle;
CATBOOST_API ModelCalcerHandle* ModelCalcerCreate();
CATBOOST_API const char* GetErrorString();
CATBOOST_API bool LoadFullModelFromFile(
ModelCalcerHandle* modelHandle,
const char* filename);
CATBOOST_API bool CalcModelPredictionSingle(
ModelCalcerHandle* modelHandle,
const float* floatFeatures, size_t floatFeaturesSize,
const char** catFeatures, size_t catFeaturesSize,
double* result, size_t resultSize);
Any suggestions are appreciated. Thank you!
It turns out I should not use the "out" keyword before "double[] result" in CalcModelPredictionSingle's signature, removing that fix the problem.
Below works.
[DllImport("catboostmodel")]
private static extern bool CalcModelPredictionSingle(
IntPtr modelHandle,
float[] floatFeatures, ulong floatFeaturesSize,
string[] catFeatures, ulong catFeaturesSize,
double[] result, ulong resultSize
);

Call Cygwin GCC DLL in C# hangs on malloc

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

Unmanaged Exports, passing an array of strings from C++ to C#

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

PInvoke - Method's type signature is not PInvoke compatible

This is the header file signature and C code I am trying to use in C#:
__declspec(dllexport) emxArray_real_T *emxCreateWrapper_real_T(real_T *data, int32_T rows, int32_T cols);
struct emxArray_real_T
{
real_T *data;
int32_T *size;
int32_T allocatedSize;
int32_T numDimensions;
boolean_T canFreeData;
};
emxArray_real_T *emxCreateWrapper_real_T(real_T *data, int32_T rows, int32_T
cols)
{
emxArray_real_T *emx;
int32_T size[2];
int32_T numEl;
int32_T i;
size[0] = rows;
size[1] = cols;
emxInit_real_T(&emx, 2);
numEl = 1;
for (i = 0; i < 2; i++) {
numEl *= size[i];
emx->size[i] = size[i];
}
emx->data = data;
emx->numDimensions = 2;
emx->allocatedSize = numEl;
emx->canFreeData = FALSE;
return emx;
}
I am currently trying to invoke it as follows in C#:
[DllImport(#"C:\bla\bla.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern emxArray_real_T emxCreateWrapper_real_T(double[,] data, int rows, int cols);
double[,] array2D = new double[,] { { 1 }, { 3 }, { 5 }, { 7 } };
var x = emxCreateWrapper_real_T(array2D, 1, 4);
but get:
Method's type signature is not PInvoke compatible.
emxArray_real_T currently looks like this:
[StructLayout(LayoutKind.Sequential)]
public struct emxArray_real_T
{
//public IntPtr data;
//public IntPtr size;
double[] data;
int[] size;
public int allocatedSize;
public int numDimensions;
[MarshalAs(UnmanagedType.U1)]
public bool canFreeData;
}
There are multiple problems. First of all your C++ function returns a pointer (emxArray_real_T *) but your import declaration returns a struct. That cannot work. Also, you declare data as double[,] in the import declaration, but as double[] in the structure. Suggestions:
Replace the struct by a class
Determine if data should be a double[] or a double[,]
Also check the final size of real_T. I believe it's a platform dependent variable that could be float (32 bit) or double (64 bit).

Categories

Resources