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

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to