[Mono-dev] managed code wrappers (exceptions & garbage collection across pinvoke barriers)
Sebastian Good
sebastian at palladiumconsulting.com
Sat Aug 25 15:40:59 EDT 2007
Thanks to help on this list I've come a long way in embedding mono into my
C++ application. During the transition of a very large application (25+ yrs
of C, Fortran, & C++) towards managed code, we will be adding new code in
.NET languages, but needing to access these objects from C++ fairly
intimately. Therefore I'm looking at writing wrappers which expose CLR
classes as C++ classes -- without resorting to (XP)COM or CORBA. I figure
most of these can be auto-generated. I believe it will make sense to emit
mono-embedding wrappers for Linux and Managed C++ wrappers for Windows. If
anyone else is interested (or has already undertaken!) such an effort, let
me know. However I'm still looking at some marshalling issues.
If I call .NET functions exclusively via the embedding reflection API, e.g.
mono_runtime_invoke, and carefully call g_free on returned copies of things
like strings, everything works fine, including managed exceptions. It seems
that by caching the reflected objects, e.g. MonoMethod*, performance is
good.
However I am having problems with delegates invoked across the barrier. They
execute properly, but appear to leak memory, and I am not sure how to catch
exceptions they might throw. For the majority of our interop, we can avoid
attempting this scenario, but we'd like to investigate it so that we can
provide managed callbacks for unmanaged code to call.
In our C++, we define (using MSVC syntax for this prototype)
// function: string->string
typedef char* (__stdcall * action_string)(char*);
// managed code will stash a delegate here for use by unmanaged code
action_string _f_string;
extern "C" _declspec(dllexport) void __stdcall init_string(action_string f)
{ _f_string =f ; }
// the unmanged code actually calls this code, e.g. do_string("hello")
extern "C" _declspec(dllexport) char* __stdcall do_string(char* s);
{ return _f_string(s); }
then in C# we write
// function: string->string, equivalent to action_string above
public delegate string ActionString(string _);
// the managed code we'll be calling from unmanaged code
public static string Echo(string s) { return s+s; }
// and the bootstrapper
[DllImport("libhost")] public static extern void init_string(ActionString
s);
public static void Boot { init_string(Echo); }
and again in C++, we can actually call the managed code like so
// find_method is just a shortcut using debug-helpers
MonoMethod *bootMethod = find_method("Hello.World:Boot", image);
mono_runtime_invoke(bootMethod, NULL, NULL, NULL);
// now we have a function pointer we can call
char *result = do_string("hello");
g_free(result);
Everything works. However, there appears to be a memory leak. I am not sure
whether it is the input that is leaking (i.e. a copy of char*"hello" turned
into utf16"hello"), or if I am improperly freeing the output (which I must
assume is a copied string) or something else in the internals. What is
encouraging is that all the marshalling is correct, just leaky. Also, if the
managed code throws an exception, the program prints an error message
("uncaught exception") and hangs. I am not sure what I would have expected
on the C++ side, perhaps a C++ exception, perhaps silence.
>From the fact that the function pointers work at all, I can tell a lot of
thought has already gone into this PInvoke stuff. What am I missing on the
garbage collection side? (And as soon as the strings work, I need to worry
about making sure that managed delegate doesn't move or get garbage
collected!)
Thanks
Sebastian
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.ximian.com/pipermail/mono-devel-list/attachments/20070825/3aa68e77/attachment.html
More information about the Mono-devel-list
mailing list