[RESOLVED] Interop of native C callback functions with void* parameter
Resuming an older topic (accessing the native library GSL) which I never managed to solve, I wrote a primitive example dll (with MingW/gcc on Windows XP) just to demonstrate the problem.
GSL (Gnu Scientific Library) contains a lot of functions (e.g.: integration routine) which require another (callback) function as argument with the prototype
double f (double x, void *params).
My library function example is
double average (double (*f) (double x, void *params),
void *par, double a, double b, int n)
{
int i;
double sum, x, dx;
sum = 0; x = a; dx = (b-a) / (n-1);
for (i=0; i<n; i++) { sum += f(x,par); x += dx; }
return sum / n;
}
I created a native dll from that and call it from a C test program which defines the actual callback function:
double quadratic (double x, void *par)
{ double *p;
p = par;
return (*p * x + *(p+1)) * x + *(p+2);
}
and calls it with
struct Par3 params = {1.0, 0.0, -5.0};
printf("Average in [0,2] (10 points): %f\n",
average (&quadratic, ¶ms, 0.0, 2.0, 10));
and everything works fine.
After several tentatives to call that from .NET/CSharp, which didn't work, I came up to the following, which doesn't work either, but which is the most complete one. Delegate and dllimport are
[StructLayout (LayoutKind.Sequential)]
class Parameters { public double a, b, c; }
public delegate double Func (double x, IntPtr p);
[DllImport ("generic.dll")] public static extern
double average (Func f, IntPtr p, double a, double b, int n);
par = new Parameters();
par.a = 1.0; par.b = 0.0; par.c = -5.0;
gch = GCHandle.Alloc (par, GCHandleType.Pinned);
func = new Func (Quad);
avg = average (func, (IntPtr)gch, 0.0, 2.0, 10);
which finally provokes the error message
FatalExecutionEngineError was detected
Message: The runtime has encountered a fatal error. The address of the error was at 0x79eb059e, on thread 0x56c. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
The complete source code with makefile and everything is attached.
What is wrong?
NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.
Re: Interop of native C callback functions with void* parameter
The following advice applies to specific situations, notably:
1) The person has a C++ bacckground...
2) The person is NOT an expert on "internals"...
3) Performance is not a 100% driving factor...
a) In native C++, write a COM compliant wrapper (if the target code is not already)
b) Use the COM interop functionallity to create a managed wrapper.
c) USe the managed wrapper.
Even if 1-3 do not "fully" apply, this approach (inconjunction with a tool like Reflector] is often the best way to "derive" the necessary mapping.
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!) 2008, 2009,2010 In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
Re: Interop of native C callback functions with void* parameter
Did as suggested. The complete C# program is now:
Code:
using System;
using System.Runtime.InteropServices;
namespace TestSharp
{
class MainClass
{
[StructLayout (LayoutKind.Sequential)]
struct Parameters { public double a, b, c; }
delegate double Func (double x, ref Parameters p);
[DllImport ("generic.dll")] static extern
double average (Func f, ref Parameters p, double a, double b, int n);
static double Quadratic (double x, ref Parameters p)
{ return (p.a*x + p.b)*x + p.c;}
public static void Main(string[] args)
{
Parameters par;
Func func;
double avg;
par = new Parameters(); { par.a = 1; par.b = 0; par.c=-5;}
func = new Func (Quadratic);
avg = average (func, ref par, 0.0, 2.0, 10);
Console.WriteLine("Average in [0,2] (10 points): " + avg.ToString() +"\r\n");
Console.WriteLine("RETURN to terminate ...");
Console.ReadLine();
}
}
}
The error message is now:
Exception System.ExecutionEngineException was thrown in debuggee:
<null reference>
Main() - c:\Documents and Settings\Frank\My Documents\Exercises\C\GSL\Generic\TestSharp\Main.cs:28,5
Line 28 is the one which goes avg = average (func, ref par, ...,
but neither func nor par is null when looking at it with the debugger.
The result is independent of 'Parameters' being a class or a struct.
Re: Interop of native C callback functions with void* parameter
Originally Posted by hreba
Did as suggested. The complete C# program is now:
I think I see now.
http://msdn.microsoft.com/en-us/library/aa288468.aspx
I dug just far enough to find examples of delegates / function pointers and PInvoke that cover the needed syntax. You can find the syntax down near the bottom of the article.
Re: Interop of native C callback functions with void* parameter
Originally Posted by ahoodin
I think I see now.
http://msdn.microsoft.com/en-us/library/aa288468.aspx
I dug just far enough to find examples of delegates / function pointers and PInvoke that cover the needed syntax. You can find the syntax down near the bottom of the article.
HTH,
Thanks for this link, I used this to fix a similar problem I was experiencing
Re: Interop of native C callback functions with void* parameter
To Quote from the MSDN:
Registering Callback Methods
To register a managed callback that calls an unmanaged function, declare a delegate with the same argument list and pass an instance of it via PInvoke. On the unmanaged side it will appear as a function pointer. For more information about PInvoke and callback, see A Closer Look at Platform Invoke.
For example, consider the following unmanaged function, MyFunction, which requires callback as one of the arguments:
Copy Code
typedef void (__stdcall *PFN_MYCALLBACK)();
int __stdcall MyFunction(PFN_ MYCALLBACK callback);
To call MyFunction from managed code, declare the delegate, attach DllImport to the function declaration, and optionally marshal any parameters or the return value:
Copy Code
Code:
public delegate void MyCallback();
[DllImport("MYDLL.DLL")]
public static extern void MyFunction(MyCallback callback);
Also, make sure the lifetime of the delegate instance covers the lifetime of the unmanaged code; otherwise, the delegate will not be available after it is garbage-collected.
Re: Interop of native C callback functions with void* parameter
Originally Posted by ahoodin
I think I see now.
http://msdn.microsoft.com/en-us/library/aa288468.aspx
I dug just far enough to find examples of delegates / function pointers and PInvoke that cover the needed syntax. You can find the syntax down near the bottom of the article.
HTH,
I cannot help myself, I think I have done everything as recommended there. Reinstalled MinGW and MSys and compiled everything anew. Nevertheless, I get the null reference error message. Attached eveything (native and C#).
Either the callback prototype in the native dll has to be specified as stdcall, or the calling convention of the C# delegate must be specified as cdecl:
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.