Bug 312786 (MONO48026) - C++ object return-by-value breaks P/Invoke argument passing
Summary: C++ object return-by-value breaks P/Invoke argument passing
Status: RESOLVED INVALID
Alias: MONO48026
Product: Mono: Runtime
Classification: Mono
Component: misc (show other bugs)
Version: unspecified
Hardware: Other Other
: P3 - Medium : Minor
Target Milestone: ---
Assignee: Mono Bugs
QA Contact: Mono Bugs
URL:
Whiteboard:
Keywords: interop
Depends on:
Blocks:
 
Reported: 2003-08-29 18:44 UTC by Patrick Hartling
Modified: 2007-09-15 21:24 UTC (History)
0 users

See Also:
Found By: ---
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas Wiest 2007-09-15 18:15:05 UTC


---- Reported by patrick@vrac.iastate.edu 2003-08-29 11:44:46 MST ----

Description of Problem:

Using the included C# and C++ code, the Mono 0.26 runtime fails to handle
argument passing with P/Invoke when a C++ object is returned by value.  The
custom marshaler included in the code is intended to provide the mapping
between the copied C++ object and the resultant C# object reference.  The
code does work with the .NET Framework 1.1.

It appears that the Mono runtime fails to pass the raw memory pointer into
the native function Marshal_getCopy() correctly when its return value is a
C++ object being returned by value.  Using by-reference return works just
fine.  Changing the C# code so that ReferenceData is declared as a struct
and the custom marshaler is removed also works fine.

Steps to reproduce the problem:
1. Compile return_copy.cpp to libreturn_copy.so
2. Compile return_copy.cs
3. Run 'mono return_copy.exe'

Actual Results:

Marshal::Marshal()
new_Marshal() -- m: 0x80f0980
new_Marshal() -- m->mRef.x: 51
DataUser.DataUser() -- mRawObject: 0x80f0980
DataUser.getReferenceCopy() -- mRawObject: 0x80f0980
Marshal_getCopy() -- self_: 0x80c2d20
Marshal_getCopy() -- self_->mRef.x: 135534788
MarshalNativeToManaged() -- obj's value: 135203200
Reference copy value: 135203200

Expected Results:

Marshal::Marshal()
new_Marshal() -- m: 0x80f0980
new_Marshal() -- m->mRef.x: 51
DataUser.DataUser() -- mRawObject: 0x80f0980
DataUser.getReferenceCopy() -- mRawObject: 0x80f0980
Marshal_getCopy() -- self_: 0x80f0980
Marshal_getCopy() -- self_->mRef.x: 51
MarshalNativeToManaged() -- obj's value: 51
Reference copy value: 51

How often does this happen? 

Every time.

Additional Information:

The following is the source used to expose this problem:

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

#ifdef _MSC_VER
#  include <windows.h>
#  define MY_API __declspec(dllexport)
#  define MY_CLASS_API __declspec(dllexport)
#else
#  define MY_API
#  define MY_CLASS_API
#endif

#include <iostream>
#include <iomanip>


class ReferenceData
{
public:
   unsigned int x;
};

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

   ReferenceData getCopy()
   {
      return mRef;
   }

private:
   ReferenceData mRef;
};

extern "C"
{

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

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

}

================

// 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) nativeObj;
      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_getCopy(IntPtr instPtr);

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

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

}



---- Additional Comments From vargaz@freemail.hu 2003-10-12 15:54:51 MST ----

I think the test program is not correct: according to the MSDN docs,
when the unmanaged function returns a structure by value , the 
corresponding managed type must be a structure:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconpassingstructures.asp

I think using a custom marshaller makes no difference here.

The fact that your example works under MS.NET is probably a
coincidance. Try adding a second field to the managed and unmanaged 
structures and it will no longer work. Also, the 
MarshalNativeToManaged function seems wrong. It should be:

obj.x = (uint) Marshal.ReadInt32 (nativeObj);




Unknown bug field "cf_op_sys_details" encountered while moving bug
   <cf_op_sys_details>Red Hat 8.0</cf_op_sys_details>
Unknown operating system unknown. Setting to default OS "Other".