Bugzilla – Bug 312786
C++ object return-by-value breaks P/Invoke argument passing
Last modified: 2007-09-15 21:24:46 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".