[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.
+
+
+
+