[Mono-bugs] [Bug 58646][Wis] Changed - custom marshaling not working
bugzilla-daemon@bugzilla.ximian.com
bugzilla-daemon@bugzilla.ximian.com
Tue, 18 May 2004 15:37:23 -0400 (EDT)
Please do not reply to this email- if you want to comment on the bug, go to the
URL shown below and enter your comments there.
Changed by mtraudt@quantifisolutions.com.
http://bugzilla.ximian.com/show_bug.cgi?id=58646
--- shadow/58646 2004-05-18 15:21:43.000000000 -0400
+++ shadow/58646.tmp.28668 2004-05-18 15:37:23.000000000 -0400
@@ -52,6 +52,188 @@
------- Additional Comments From vargaz@freemail.hu 2004-05-18 15:21 -------
Shouldn't MarshalManagedToNative return ptrBlock instead of
pDst ? AFAIK, the first four bytes are used by MS for some unknown
purpose.
+
+------- Additional Comments From mtraudt@quantifisolutions.com 2004-05-18 15:37 -------
+See example below from MSDN web site. This is what I used as a
+guide. Unless I'm mistaken, MarshalManagedToNative returns the data
+pointer, not the buffer pointer. As I mentioned in the original
+post, this code has been working fine for us using the MS CLR).
+
+__gc public class MCity
+{
+public:
+ MCity(String* name,int x,int y)
+ {
+ this->name = name;
+ this->x = x;
+ this->y = y;
+ }
+ String* name;
+ int x;
+ int y;
+};
+__value public struct CustomMarshalAPI
+{
+ [DllImport("TraditionalDLL.dll")]
+ static public void MDrawCity(IntPtr hdc, MCity* aCity);
+ [DllImport("TraditionalDLL.dll")]
+ static public void MDrawCities(IntPtr hdc,
+ MCity* aCity [], int n);
+};
+To translate the MCity* objects to CITY structures, implement a
+custom marshaler class named CITY_CustomMarshaler to do the
+marshaling:
+
+__gc public class CITY_CustomMarshaler :
+ public System::Runtime::InteropServices::ICustomMarshaler
+{
+public:
+ CITY_CustomMarshaler(void);
+ ~CITY_CustomMarshaler(void);
+ Object* MarshalNativeToManaged(IntPtr pNativeData);
+ IntPtr MarshalManagedToNative(Object* ManagedObj);
+ void CleanUpNativeData(IntPtr pNativeData);
+ void CleanUpManagedData(Object* ManagedObj);
+ static ICustomMarshaler* GetInstance(String* cookie);
+ int GetNativeDataSize();
+private:
+ IntPtr MarshalManagedArrayToNative(MCity * pCity []);
+ IntPtr MarshalManagedObjectToNative(MCity* pCity);
+ static CITY_CustomMarshaler* marshaler = 0;
+};
+This definition is a generic custom marshaler definition. It will be
+able to marshal single objects and arrays of objects.
+MarshalManagedArrayToNative and MarshalManagedObjectToNative are
+helper methods that apply to the different cases.
+
+The following code implements the MarshalManagedToNative,
+MarshalManagedArrayToNative, MarshalManagedObjectToNative, and
+CleanUpNativeData methods.
+
+IntPtr CITY_CustomMarshaler::MarshalManagedToNative(Object*
+ManagedObj)
+{
+ if (ManagedObj == 0)
+ throw new System::ArgumentNullException(L"ManagedObj");
+ MCity* pCity = dynamic_cast<MCity*>(ManagedObj);
+ if (pCity != 0)
+ {
+ return MarshalManagedObjectToNative(pCity);
+ }
+ else
+ {
+ MCity * pCityArr [] = dynamic_cast<MCity* []>(ManagedObj);
+ if (pCityArr != 0)
+ {
+ return MarshalManagedArrayToNative(pCityArr);
+ }
+ }
+ throw new System::ArgumentException("Cannot marshal this type");
+};
+IntPtr CITY_CustomMarshaler::MarshalManagedArrayToNative(MCity*
+pCity [])
+{
+ int size = sizeof(CITY)*pCity->Length+sizeof(int);
+ IntPtr ptrBlock = (Marshal::AllocCoTaskMem(size)).ToPointer();
+ int offset = ptrBlock.ToInt32()+sizeof(int);
+ IntPtr ptrCity(offset);
+ int __nogc * pi = (int __nogc *)ptrBlock.ToPointer();
+ *pi = pCity->Length;
+ CITY* city = static_cast<CITY*>(ptrCity.ToPointer());
+ for(int i=0;i<pCity->Length;i++)
+ {
+ IntPtr p = Marshal::StringToHGlobalAnsi(pCity[i]->name);
+ city->name = static_cast<char*>(p.ToPointer());
+ city->location.x = pCity[i]->x;
+ city->location.y = pCity[i]->y;
+ city++;
+ }
+ return ptrCity;
+}
+IntPtr CITY_CustomMarshaler::MarshalManagedObjectToNative(MCity*
+pCity)
+{
+ int size = sizeof(CITY)+sizeof(int);
+ IntPtr ptrBlock = (Marshal::AllocCoTaskMem(size)).ToPointer();
+ int offset = ptrBlock.ToInt32()+sizeof(int);
+ IntPtr ptrCity(offset);
+ int __nogc * pi = (int __nogc *)ptrBlock.ToPointer();
+ *pi = 1;
+ CITY* city = static_cast<CITY*>(ptrCity.ToPointer());
+ IntPtr p = Marshal::StringToHGlobalAnsi(pCity->name);
+ city->name = static_cast<char*>(p.ToPointer());
+ city->location.x = pCity->x;
+ city->location.y = pCity->y;
+ return ptrCity;
+}
+void CITY_CustomMarshaler::CleanUpNativeData(IntPtr pNativeData)
+{
+ CITY * pCity = static_cast<CITY*>(pNativeData.ToPointer());
+ CITY* pTemp = pCity;
+ int offset = pNativeData.ToInt32()-sizeof(int);
+ IntPtr pBlock(offset);
+ int __nogc * pi= static_cast<int __nogc*>(pBlock.ToPointer());
+ for(int i=0;i<*pi;i++)
+ {
+ if (pTemp->name != 0)
+ Marshal::FreeCoTaskMem(pTemp->name);
+ pTemp++;
+ }
+ Marshal::FreeCoTaskMem(pBlock);
+};
+The last step is to modify the PInvoke signatures to indicate that
+the CITY_CustomMarshaler class must be used to marshal the
+attributed parameters.
+
+__value public struct CustomMarshalAPI
+{
+ [DllImport("TraditionalDLL.dll")]
+ static public void DrawCity(IntPtr hdc,
+ [In,MarshalAs(UnmanagedType::CustomMarshaler,
+ MarshalTypeRef=__typeof(CITY_CustomMarshaler))]
+ MCity* aCity);
+ [DllImport("TraditionalDLL.dll")]
+ static public void DrawCities(IntPtr hdc,
+ [In,MarshalAs(UnmanagedType::CustomMarshaler,
+ MarshalTypeRef=__typeof(CITY_CustomMarshaler))]
+ MCity* aCity [],
+ int n);
+};
+
+The code demonstrates the following concepts:
+
+The MarshalManagedToNative uses dynamic_cast to determine which
+method (MarshalManagedArrayToNative or MarshalManagedObjectToNative)
+to call.
+The MarshalManagedArrayToNative or MarshalManagedObjectToNative
+methods both allocate a block of unmanaged memory that is larger
+than is required for the data by the size of an int. The number of
+marshaled objects is stored in this extra space.
+The MarshalManagedArrayToNative or MarshalManagedObjectToNative
+methods do not return a System::IntPtr pointing to the start of the
+unmanaged block. Instead, the methods return the start address of
+the actual marshaled data.
+The CleanUpNativeData method uses object count stored at the
+beginning of the unmanaged block to correctly clean up embedded
+pointers and then free the entire block.
+The following code shows how you can call the attributed methods.
+
+Graphics* g = this->CreateGraphics();
+IntPtr hdc = g->GetHdc();
+MCity* mc = new MCity("Bloemfontein",80,380);
+CustomMarshalAPI::MDrawCity(hdc,mc);
+MCity* arrMC [] = new MCity*[2];
+arrMC[0] = new MCity("Jeffries Bay",80,420);
+arrMC[1] = new MCity("Richards Bay",80,460);
+CustomMarshalAPI::MDrawCities(hdc,arrMC,arrMC->Length);
+g->ReleaseHdc(hdc);
+For the client, there are no special coding techniques required, and
+it does not have to be exposed to the underlying unmanaged
+representation for the marshaled data.
+
+
+
+