[Mono-list] Re: Calling back from unmanaged code to managed code.

Jonathan Pryor jonpryor@vt.edu
Thu, 16 Dec 2004 21:04:15 -0500


Just for "fun", I'll write the equivalent C# declarations inline with
the C code.  Note that I haven't tried to compile this, but the basic
idea should be seen...

On Thu, 2004-12-16 at 13:32 -0500, Nigel Benns wrote:
> Here is a C example to do a DeleteEvent:
> 
> void delete_cb(Ewl_Widget *w, void *event, void *data) {
>     ewl_widget_destroy(w);
>     ewl_main_quit();
> }

delegate void EwlDeleteCallback (IntPtr w, IntPtr event, IntPtr data);

class MyTest {

	private const string LIB = "ewl";

	[DllImport (LIB)] 
	private static extern void ewl_widget_destroy (IntPtr w);

	[DllImport (LIB)]
	private static extern void ewl_main_quit ();

	private void delete_cb (IntPtr w, IntPtr event, IintPtr data)
	{
		ewl_widget_destroy (w);
		ewl_main_quit ();
	}

> int main(int argc, char **argv) {
>    ewl_init();

	[DllImport (LIB)] private static extern void ewl_init ();
>    ...
>    win = ewl_window_new();

	[DllImport (LIB)] private static extern IntPtr ewl_window_new();

>    ...
>    /*Where this function appends the callback to a calback list read
>      by ewl_main().
>    */
>    ewl_callback_append(win, EWL_CALLBACK_DELETE_WINDOW, delete_cb, NULL);

	// Here's what you're interested in:
	[DllImport(LIB)]
	private static extern void ewl_callback_append (IntPtr win, 
		int callback_type, EwlDeleteCallback cb, IntPtr data);

	// I'd assume that ewl_callback_append can take a variety of 
	// function pointer types (the callback is probably void*),
	// so you could overload this function for each function 
	// pointer type that ewl_callback_append accepts.

	// obviously, this needs to be set to the correct value.
	private const int EWL_CALLBACK_DELETE_WINDOW = 0xdeadbeef;

>    /*The NULL is for the Data to pass */
>    ...
>    ewl_main();

	[DllImport(LIB)] private static extern void ewl_main ();
> }
> 
> Ok, so what I'm trying figure out is how to add the ewl_callback_append as
> a delegate in C#.

	public static void Main ()
	{
		ewl_init ();
		IntPtr win = ewl_window_new ();
		EwlDeleteCallback cb = new EwlDeleteCallback (delete_cb);
		ewl_callback_append (win, EWL_CALLBACK_DELETE_WINDOW, 
			cb, IntPtr.Zero);
		ewl_main ();
		System.GC.KeepAlive (cb);
	}
}

Note that the System.GC.KeepAlive is necessary so that the GC doesn't
collect your delegate before EWL is finished using the function pointer
it has (the delegate is marshaled as a function pointer).  It would be
"bad" to have the GC collect the delegate early.

> It doesn't seem write to me that I could pass a C# function to C, even if
> I could figure out how to do that?

You can pass a delegate referring to a C# method to C.

I would suggest reading my guide, "Everything you (n)ever wanted to know
about marshaling (and were afraid to ask!)":

	http://www.jprl.com/~jon/interop.html

It's also available through monodoc (though the above is more recent --
I've been lazy about committing):

	http://www.go-mono.com/docs/index.aspx?tlink=8@xhtml%3anew%2fen%2fprogramming%2finterop.html

You may also want to look into Gtk#, just to see how the wrapper code
can be used.

 - Jon