I have run across what I think is a bug in Mono's lookup of custom marshalers associated with delegates. The problem occurs when a delegate defined in a base class (A.Base in assembly A.dll) has a custom marshaler for a parameter. If a class drived from A.Base in a different assembly (B.Derived in assembly B.dll) tries to pass the delegate as a function pointer to native code, Mono 0.29 hits an assertion failure on line 2069 of mono/metadata/marshal.c (line 2082 for today's snapshot).
At a high level, mono_reflection_type_from_name() is returning NULL for the custom marshaler type in question, though I have not nailed down exactly why this is happening. In mono_reflection_type_from_name(), info.assembly.name comes back NULL after the call to mono_reflection_parse_type(). I think that if info.assembly.name were not NULL, the correct assembly image would be found to get the custom marshaler. I have attached C# and C++ code with a makefile that can be used to repeat the problem I am running into. I have verified that this code works with the .NET Framework versions 1.0 and 1.1 (compiling the C++ code with Visual C++ 7.0 and 7.1 to match the .NET Framework version accordingly). The code is more complicated than I would like it to be, but to the best of my current understanding, it is the least amount of code I can come up with to demonstrate the crash. Note that in B.cs, a custom marshaler from the assembly A is used for a P/Invoke call, and it works fine. For that reason, I concluded that the lookup problem is related to delegates. Looking through Bugzilla, it looks as though the first C# attachment with Bug 43351 (http://bugzilla.ximian.com/showattachment.cgi?attach_id=4400) is related to what I am running into, but that code runs correctly with Mono 0.29. My guess is that the difference between the two cases is that I am trying to access a delegate in an external assembly whereas Marcus' example is contained within a single assembly. -Patrick -- Patrick L. Hartling | Research Assistant, VRAC [EMAIL PROTECTED] | 2274 Howe Hall Room 2624 http://www.vrac.iastate.edu/~patrick/ | http://www.vrac.iastate.edu/ PGP: http://wwwkeys.gpg.cz:11371/pks/lookup?op=get&search=0xEBF86398
using System; using System.Runtime.InteropServices; namespace A { public class Arg { protected internal IntPtr mRawObject = IntPtr.Zero; internal Arg(IntPtr obj) { mRawObject = obj; } [DllImport("a_native", CharSet = CharSet.Ansi)] private extern static IntPtr new_Arg(); public Arg() { mRawObject = new_Arg(); } [DllImport("a_native", CharSet = CharSet.Ansi)] private extern static IntPtr Arg_go(IntPtr obj); public void go() { Arg_go(mRawObject); } } // Custom marshaler for the type A.Arg which will be used as an attribute to // a delegate parameter in the class A.Base. public class ArgMarshaler : ICustomMarshaler { public void CleanUpManagedData(Object obj) { } public void CleanUpNativeData(IntPtr nativeData) { } public int GetNativeDataSize() { return -1; } public IntPtr MarshalManagedToNative(Object obj) { return ((A.Arg) obj).mRawObject; } public Object MarshalNativeToManaged(IntPtr nativeObj) { return new Arg(nativeObj); } public static ICustomMarshaler GetInstance(string cookie) { return mInstance; } private static ArgMarshaler mInstance = new ArgMarshaler(); } public abstract class Base { protected internal IntPtr mRawObject = IntPtr.Zero; internal Base(IntPtr obj) { mRawObject = obj; } [DllImport("a_native", CharSet = CharSet.Ansi)] private extern static IntPtr new_BaseAdapter( [MarshalAs(UnmanagedType.FunctionPtr)] f_delegate fCallback); protected Base() { m_f_delegate = new f_delegate(f); // Mono crashes when handling the following call when invoked from a // derived class constructor in a different assembly. mRawObject = new_BaseAdapter(m_f_delegate); } // Delegate with a custom marshaler for the parameter a. This causes Mono // to crash when m_f_delegate is passed to a native function as a function // pointer. public delegate void f_delegate( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgMarshaler))] Arg a); protected f_delegate m_f_delegate; public abstract void f(Arg a); } public class BaseMarshaler : ICustomMarshaler { private class DummyBase : Base { public DummyBase(IntPtr obj) { mRawObject = obj; } public override void f(Arg a) { } } public void CleanUpManagedData(Object obj) { } public void CleanUpNativeData(IntPtr nativeData) { } public int GetNativeDataSize() { return -1; } public IntPtr MarshalManagedToNative(Object obj) { return ((A.Base) obj).mRawObject; } public Object MarshalNativeToManaged(IntPtr nativeObj) { return new DummyBase(nativeObj); } public static ICustomMarshaler GetInstance(string cookie) { return mInstance; } private static BaseMarshaler mInstance = new BaseMarshaler(); } }
using System; using System.Runtime.InteropServices; namespace B { public class Derived : A.Base { public Derived() : base() { } public override void f(A.Arg a) { Console.WriteLine("[C#] B.Derived.f() calling A.Arg.go()"); a.go(); } } public class Run { // Looking up A.BaseMarshaler in this assembly works fine. [DllImport("b_native", CharSet = CharSet.Ansi)] private extern static void handleBase( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(A.BaseMarshaler))] A.Base obj); public static void Main(String[] args) { Derived obj = new Derived(); handleBase(obj); } } }
#include "a_native.h" #ifdef WIN32 #define DLL_API __declspec(dllexport) #else #define DLL_API #endif class BaseAdapter : public Base { public: typedef void (*f_callback_t)(Arg*); f_callback_t f_callback; void f(Arg* arg) { f_callback(arg); } }; extern "C" { DLL_API Arg* new_Arg() { return new Arg; } DLL_API void Arg_go(Arg* self_) { self_->go(); } DLL_API BaseAdapter* new_BaseAdapter(BaseAdapter::f_callback_t cb) { BaseAdapter* obj = new BaseAdapter; obj->f_callback = cb; return obj; } }
#ifndef _A_NATIVE_H_ #define _A_NATIVE_H_ #include <iostream> class Arg { public: void go() { std::cout << "[C++] Arg::go()" << std::endl; } }; class Base { public: virtual void f(Arg* arg) = 0; }; #endif
#include "a_native.h" #ifdef WIN32 #define DLL_API __declspec(dllexport) #else #define DLL_API #endif extern "C" { DLL_API void handleBase(Base* obj) { Arg arg; obj->f(&arg); } }
# Linux CSC= mcs /debug MAKE_DLL= c++ -shared -o $@ $< LDFLAGS= -L. -la_native NATIVE_DLL_A= liba_native.so NATIVE_DLL_B= libb_native.so # Windows #CSC= csc /nologo /debug #MAKE_DLL= cl /DWIN32 /nologo /EHsc $< /link /dll /out:$@ #LDFLAGS= /libpath:. a_native.lib #NATIVE_DLL_A= a_native.dll #NATIVE_DLL_B= b_native.dll all: A.dll B.exe $(NATIVE_DLL_A) $(NATIVE_DLL_B) A.dll: A.cs $(CSC) /target:library A.cs B.exe: B.cs $(CSC) B.cs /r:A.dll $(NATIVE_DLL_A): a_native.cpp $(MAKE_DLL) $(NATIVE_DLL_B): b_native.cpp $(MAKE_DLL) $(LDFLAGS) clean: rm -f *.exe *.lib *.exp *.dll *.so *.pdb *.obj
signature.asc
Description: This is a digitally signed message part