[Mono-list] Strange behavior with P/Invoke and return values

Patrick Hartling patrick@vrac.iastate.edu
Mon, 25 Aug 2003 16:40:12 -0500


This is a multi-part message in MIME format.
--------------050402000102010008060601
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

I am experimenting with mapping C++ to C# via P/Invoke and Mono 0.26 on 
Red Hat 8.0, and I have run into something odd when a native function 
returns a C++ object by value.  From the way it is behaving, it appears 
that something is going wrong in the allocation of memory on the stack 
for the return value.

I have attached some code that demonstrates the problem.  My intent is to 
use a custom marshaler to make a copy of the return value object in a 
freshly allocated C# object reference.  (The validity of this technique 
is what I am evaluating, so it's possible I am going down the wrong road 
with this.)

The output from running the C# application is the following:

Marshal::Marshal()
new_Marshal() -- mb: 0x80f0580
new_Marshal() -- mb->mRef.x: 51
DataUser.DataUser() -- mRawObject: 0x80f0580
DataUser.getReferenceCopy() -- mRawObject: 0x80f0580
Marshal_getReferenceCopy() -- self_: 0x80c3d20
Marshal_getReferenceCopy() -- self_->mRef.x: 135533516
MarshalNativeToManaged() -- nativeObj: 0x80f0580
MarshalNativeToManaged() -- obj's value: 135533516
Reference copy value: 135533516

mRawObject (C#) and self_ (C/C++) should always have the same value, but 
notice that when DataUser.getReferenceCopy() is called, the value of the 
pointer changes from 0x80f0580 to 0x80c3d20, but my custom marshaler's 
MarshalNativeToManaged() gets 0x80f0580 as the value of its argument, 
nativeObj.

By adding a second garbage argument to Marshal_getReferenceCopy(), I was 
able to determine that the first argument is what gets passed to my 
custom marshaler and the second is the value of mRawObject.  Am I 
misunderstanding something about how to marshal return values?  Is my 
approach to C++ object return value copying fubared?  Is there a specific 
place in the Mono source I could examine to try get a more in-depth 
understanding of Mono is trying to do here?

  -Patrick


-- 
Patrick L. Hartling                     | Research Assistant, VRAC
patrick@vrac.iastate.edu                | 2624 Howe Hall: 1.515.294.4916
http://www.137.org/patrick/             | http://www.vrac.iastate.edu/

--------------050402000102010008060601
Content-Type: text/plain;
 name="return_copy.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="return_copy.cpp"

// To compile:
//    c++ -o libreturn_copy.so return_copy.cpp

#include <iostream>
#include <iomanip>


class ReferenceData
{
public:
   ReferenceData() : x(0)
   {
   }

   unsigned int x;
};

class Marshal
{
public:
   Marshal()
   {
      std::cout << "Marshal::Marshal()" << std::endl;
      mRef.x = 51;
   }

   ReferenceData getReferenceCopy()
   {
      return mRef;
   }

private:
   ReferenceData mRef;
};

extern "C"
{

Marshal* new_Marshal()
{
   Marshal* mb = new Marshal;
   std::cout << "new_Marshal() -- mb: " << std::hex << mb << std::dec
             << std::endl;
   std::cout << "new_Marshal() -- mb->mRef.x: " << mb->getReferenceCopy().x
             << std::endl;
   return (Marshal*) mb;
}

ReferenceData Marshal_getReferenceCopy(Marshal* self_)
{
   std::cout << "Marshal_getReferenceCopy() -- self_: " << std::hex
             << self_ << std::dec << std::endl;
   std::cout << "Marshal_getReferenceCopy() -- self_->mRef.x: "
             << self_->getReferenceCopy().x << std::endl;
   return self_->getReferenceCopy();
}

}

--------------050402000102010008060601
Content-Type: text/plain;
 name="return_copy.cs"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="return_copy.cs"

// To compile:
//   mcs return_copy.cs

using System;
using System.Runtime.InteropServices;


namespace test
{

[StructLayout(LayoutKind.Sequential)]
class ReferenceData
{
   public override string ToString()
   {
      return x.ToString();
   }

   public uint x;
};

class ReferenceDataMarshaler : ICustomMarshaler
{
   public void CleanUpManagedData(Object obj)
   {
   }

   public void CleanUpNativeData(IntPtr nativeData)
   {
   }

   public int GetNativeDataSize()
   {
      return -1;
   }

   public IntPtr MarshalManagedToNative(Object obj)
   {
      return new IntPtr((int) ((ReferenceData) obj).x);
   }

   public Object MarshalNativeToManaged(IntPtr nativeObj)
   {
      ReferenceData obj = new ReferenceData();
      obj.x = (uint) Marshal.ReadInt32(nativeObj);
      Console.WriteLine("MarshalNativeToManaged() -- nativeObj: 0x{0:x}",
                        nativeObj.ToInt32());
      Console.WriteLine("MarshalNativeToManaged() -- obj's value: " + obj);
      return obj;
   }

   public static ICustomMarshaler GetInstance(string cookie)
   {
      return mInstance;
   }

   private static ReferenceDataMarshaler mInstance = new ReferenceDataMarshaler();
};

class DataUser
{
   protected internal IntPtr mRawObject;

   [DllImport("return_copy", CharSet=CharSet.Ansi)]
   private static extern IntPtr new_Marshal();

   public DataUser()
   {
      mRawObject = new_Marshal();
      Console.WriteLine("DataUser.DataUser() -- mRawObject: 0x{0:x}",
                        mRawObject.ToInt32());
   }

   [DllImport("return_copy")]
   [return : MarshalAs(UnmanagedType.CustomMarshaler,
                       MarshalTypeRef = typeof(ReferenceDataMarshaler))]
   private static extern ReferenceData Marshal_getReferenceCopy(IntPtr instPtr);

   public ReferenceData getReferenceCopy()
   {
      Console.WriteLine("DataUser.getReferenceCopy() -- mRawObject: 0x{0:x}",
                        mRawObject.ToInt32());
      return Marshal_getReferenceCopy(mRawObject);
   }
};

class Tester
{
   public static void Main()
   {
      DataUser u = new DataUser();
      Console.WriteLine("Reference copy value: " + u.getReferenceCopy());
   }
}

}

--------------050402000102010008060601--