[Mono-list] Deep Marshaling

Giuseppe Greco giuseppe.greco at agamura.com
Sun Oct 23 09:21:18 EDT 2005


Jon,

that you said is really interesting... and I'm wondering how
can I solve another similar problem. Giving the following type:

[StructLayout(LayoutKind.Sequential)]
internal struct MyStruct
{
     public int Id;
     public byte[] Data;       // of course, that's wrong!!!
}

... I'd like to fill it like this:

public unsafe void FillMyStruct(string s)
{
     //
     // convert the given string into a byte array
     //
     byte[] data = Encoding.UTF8.GetBytes(s);

     //
     // create another byte array for storing
     // a new instance of MyStruct
     //
     byte[] buffer = new byte[
         sizeof(int)    // Size of MyStruct.Id
       + data.Length];  // Length of MyStruct.Data

     //
     // fill the buffer
     //
     fixed (byte* pBuffer = buffer) {
         MyStruct* pMyStruct = (MyStruct*) pBuffer;
         pMyStruct->Id = 100; // OK
         pMyStruct->Data = data; // ERROR
     }
}

Any help would be really appreciated,
j3d.

Jonathan Pryor wrote:
> On Thu, 2005-10-20 at 16:07 -0700, Shankari wrote:
> 
>>While marshalling class and structure members, Mono
>>doesnt do a deep marshal.
>>If the structure has a string member, a default of
>>charset.auto is set.
> 
> 
> Would you prefer some other default?
> 
> 
>>But if the structure has an array of integers, what
>>would be done in that case?
> 
> 
> You could always try it... :-)
> 
> And get this:
> 
>         ** ERROR **: Structure field of type Int16[] can't be marshalled
>         as LPArray
>         aborting...
>         Aborted
> 
> That was for marshaling this structure:
> 
>         struct Outer {
>                 public int size;
>                 public short[] inner;
>         }
> 
> For which the corresponding C structure would be:
> 
>         struct Outer {
>                 int size;
>                 short *inner;
>         };
> 
> However, *this* does marshal correctly:
> 
>         // C#
>         struct Outer {
>                 int size;
>                 [MarshalAs (UnmanagedType.ByValArray, SizeConst=100)]
>                 short[] inner;
>         }
>         
>         /* C */
>         struct Outer {
>                 int size;
>                 short inner [100];
>         };
>         
> But the above structure has very different semantics than the previous
> pair.
> 
>>From this we can see theorize that Mono's marshaler doesn't like nested
> pointers, unless those pointers are for strings.
> 
> There are workarounds.
> 
> 
>>In general, if an array of simple types is a member of
>>the structure , how would it be marshalled(if at all)?
> 
> 
> It depends on what the C layout is like.  If the array is "inline", e.g.
> 
>         struct Foo {
>                 char inline_array [100];
>         };
> 
> Then you can use [MarshalAs (UnmanagedType.ByValArray, SizeConst=100)]
> on the appropriate member:
> 
>         struct Foo {
>                 [MarshalAs (UnmanagedType.ByValArray, SizeConst=100)]
>                 public byte[] inline_array;
>         }
> 
> If the structure member is a *pointer* to the arrar, e.g.
> 
>         struct Foo {
>                 char *external_array;
>         };
> 
> Then you'll need to marshal it manually; see the next answer, as the
> solution applies to both built-in types and user-defined types.
> 
> 
>>For array of user defined structures, I assume no
>>marshalling will be done. (Am I correct?)
> 
> 
> No marshaling will be done by default -- you'll abort the program instead.
> 
> If the default marshaler won't handle it, you can marshal it yourself.
> It's not fun, but it's possible.
> 
> To marshal these structures:
> 
>         /* C */
>         struct Inner {
>                 char a, b;
>         };
>         
>         struct Outer {
>                 int size;
>                 struct Inner *inner;
>         };
>         
>         // C#
>         struct Inner {
>                 public byte a, b;
>         }
>         
>         struct Outer {
>                 public int size;
>                 public IntPtr inner; // [1]
>         }
> 
> The [1] comment is the first sign that we're doing things ourselves.  To
> actually invoke a C function defined as:
> 
>         void PrintOuter(struct Outer *outer);
> 
> we'd need the C# code:
> 
>         [DllImport ("test")]
>         private static extern void PrintOuter (ref Outer outer);
>         
>         public static void Main ()
>         {
>                 // Create inner array to marshal
>                 Inner[] inner = new Inner [26];
>                 for (int i = 0; i < inner.Length; ++i) {
>                         inner [i].a = (byte) ((int) 'a' + i);
>                         inner [i].b = (byte) ((int) 'z' - i)
>                 }
>                 
>                 Outer o = new Outer ();
>                 o.size = inner.Length;
>                 
>                 // Manually marshal the Inner[] to an IntPtr
>                 int inner_size = Marshal.SizeOf (typeof(Inner));
>                 o.inner = Marshal.AllocHGlobal
>                 (inner_size*inner.Length);
>                 for (int i = 0; i < inner.Length; ++i) {
>                         IntPtr p = (IntPtr) ((long) o.inner + (long)
>                         i*inner_size)
>                         Marshal.StructureToPtr (inner [i], p, false);
>                 }
>                 
>                 // We're done!
>                 PrintOuter (ref o);
>                 
>                 // Cleanup
>                 Marshal.FreeHGlobal (o.inner);
>         }
> 
> See the attached code:
> 
>         gcc -shared -o libsm.so sm.c
>         mcs sm.cs
>         LD_LIBRARY_PATH=`pwd` mono sm.exe
> 
>  - Jon
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> Mono-list maillist  -  Mono-list at lists.ximian.com
> http://lists.ximian.com/mailman/listinfo/mono-list


-- 
----------------------------------------
Giuseppe Greco
::agamura::

call giuseppe.greco via Skype
phone:  +41 (0)91 604 67 65
mobile: +41 (0)79 590 33 06
email:  giuseppe.greco at agamura.com
web:    www.agamura.com
----------------------------------------


More information about the Mono-list mailing list