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
