I have a third-party C DLL, that came only with its header file as API. I want to be able to call one of its functions. The C header file looks like this:
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __WATCOMC__
#define EXPORT extern int __declspec(dllexport) __stdcall
#define IMPORT extern int __declspec(dllimport) __stdcall
#endif
#ifdef _WINDOWS
#ifndef EXPORT
#define EXPORT extern int
#endif
#endif
#ifndef EXPORT
#define EXPORT int
#endif
#ifndef EXIMPORT
#define EXIMPORT EXPORT
#endif
.
.
.
EXIMPORT CFunction(
int callbackArraysLength,
int (*solutionCallbackFunction)(int*, double*, double)
);
solutionCallbackFunction
specification:
int *intArray
double *doubleArray
double doublefield
The C# code:
[DllImport(@"PathToDLL", EntryPoint = "CFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern int CFunction(int callbackArraysLength, [MarshalAs(UnmanagedType.FunctionPtr)] SolutionCallbackFuncDelegate solutionCallbackFunc);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int SolutionCallbackFuncDelegate ([In] int[] intArr, [In] double[] doubleArr,[In] double d);
public static int solutionFunction([In] int[] intArr, [In] double[] doubleArr,[In] double d) {
.
.
// Some code that checks the input...
.
.
return 0;
}
static SolutionCallbackFuncDelegate solCBDel;
public static void Main(string[] args)
{
solCBDel = new SolutionCallbackFuncDelegate(solutionFunction);
...
int arraysLength = 5; //I know the size of the arrays that will be returned from the callback function.
int result = CFunction(arraysLength, solCBDel);
...
}
Have I done something wrong? I'm getting this error:
System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
and I'm not sure this is the reason.
I get the exception when calling the function inside the Main:
int result = CFunction(arraysLength, solCBDel);
The documentation doesn't say if I need to allocate the memory for the arrays in the callback function.
If this is the reason, where exactly do I allocate the arrays in the code?
Edit:
@GSerg suggested this answer: Callback from Unmanaged code to managed
But in my callback function, no field has the size of the arrays: int (*solutionCallbackFunction)(int*, double*, double)
The main function that sends the callback function as a pointer is sent with the size of the arrays:
int result = CFunction(arraysLength, solCBDel);
On the C# side, I need to allocate the memory.
Is there another way of allocating the callback function arrays if the C++ and C# has the length when I'm sending it to C++ through arraysLength
??
After deep research, I found out how to call from C# to the C DLL.
The C# code should be:
[DllImport(@"PathToDLL", EntryPoint = "CFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern int CFunction(int callbackArraysLength, [MarshalAs(UnmanagedType.FunctionPtr)] SolutionCallbackFuncDelegate solutionCallbackFunc);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int SolutionCallbackFuncDelegate ([In] IntPtr intArrPtr, [In] IntPtr doubleArrPtr,[In] double d);
public static int solutionFunction([In] IntPtr intArrPtr, [In] IntPtr doubleArrPtr,[In] double d) {
int[] intArr = new int[arraysLength];
double[] doubleArr = new double[arraysLength];
Marshal.Copy(intArrPtr, intArr , 0, arraysLength);
Marshal.Copy(doubleArrPtr, doubleArr, 0, arraysLength);
// Some code that checks the input...
.
.
return 0;
}
public static int arraysLength = 5; //I know the size of the arrays that will be returned from the callback function. It should be global.
public static void Main(string[] args)
{
...
int result = CFunction(arraysLength, solutionFunction);
...
}
In the C# callback definition, we must use an IntPtr and then use Marshal.Copy(...) because the callback function doesn't have a parameter of length. In case there is a length parameter in the callback function we can use [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = "Index of the length parameter")]