[Mono-list] Deep Marshaling

Jonathan Pryor jonpryor at vt.edu
Fri Oct 21 07:17:29 EDT 2005


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

-------------- next part --------------
A non-text attachment was scrubbed...
Name: sm.c
Type: text/x-csrc
Size: 309 bytes
Desc: not available
Url : http://lists.ximian.com/pipermail/mono-list/attachments/20051021/5bd6d742/sm.bin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sm.cs
Type: text/x-csharp
Size: 866 bytes
Desc: not available
Url : http://lists.ximian.com/pipermail/mono-list/attachments/20051021/5bd6d742/sm-0001.bin


More information about the Mono-list mailing list