Class/struct and Marshal.SizeOf

S

SB

I feel dumb to ask because I bet this is a simple question...

Looking at the code below, can someone please explain why I get two
different values in my Marshal.SizeOf calls (see the commented lines)?

TIA!
sb


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
public short a;
public short b;
public short c;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyClass
{
public short a;
public short b;
public short c;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyStructTestClass
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray,
SizeConst = 256)]
public MyStruct[] dummy;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyClassTestClass
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray,
SizeConst = 256)]
public MyClass[] dummy;
}

// test code...
private void testButton_Click(object sender, EventArgs e)
{
MyStruct myStructTest = new MyStruct();
MessageBox.Show("myStructTest size: " +
Marshal.SizeOf(myStructTest).ToString());//will show size of 6

MyClass myClassTest = new MyClass();
MessageBox.Show("myClassTest size: " +
Marshal.SizeOf(myClassTest).ToString());//will show size of 6

MyStructTestClass myStructClass = new MyStructTestClass();
MessageBox.Show("myStructClass size: " +
Marshal.SizeOf(myStructClass).ToString());//will show size of 1536

MyClassTestClass myClassClass = new MyClassTestClass();
MessageBox.Show("myClass2 size: " +
Marshal.SizeOf(myClassClass).ToString());//will show size of 1024???!!
}
 
B

Bob Calvanese

When I run the code I get the first 2 sizes (6 and 6), and then the
following exception...
An unhandled exception of type 'System.ArgumentException' occurred in
SizeOf.exe

Additional information: Type MyStructTestClass can not be marshaled as an
unmanaged structure; no meaningful size or offset can be computed.

Any idea's?
bobc
 
S

SB

First, thanks for looking at the code. I'm not sure why you are getting
that error...but I will post the entire MainForm.cs file here which may
help. This compiles and runs fine on my end. Note that some changes were
made today by me but nothing substantial. One thing that may be different
on your end is that I'm using VC# Express and the .NET Framework 2.0b (link
below)...I should have mentioned that before.

http://lab.msdn.microsoft.com/express/vcsharp/default.aspx

Anyway, I've been googling this problem ALL DAY...and I'm beginning to
believe this is a bug in the new framework. However, I'm still hoping that
someone will show me otherwise.

-sb

#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
#endregion

namespace MyTestCenter
{
partial class mainForm : Form
{
public mainForm()
{
InitializeComponent();

richTextBox.AppendText("MyStruct size: " +
Marshal.SizeOf(typeof(MyStruct)).ToString());//will show size of 6
richTextBox.AppendText("\nMyClass size: " +
Marshal.SizeOf(typeof(MyClass)).ToString());//will show size of 6
richTextBox.AppendText("\nMyStructTestClass size: " +
Marshal.SizeOf(typeof(MyStructTestClass)).ToString());//will show size of
1536
richTextBox.AppendText("\nMyClassTestClass size: " +
Marshal.SizeOf(typeof(MyClassTestClass)).ToString());//will show size of
1024
}

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
public struct MyStruct
{
public short a;
public short b;
public short c;
}

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
public class MyClass
{
public short a;
public short b;
public short c;
}

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
public class MyStructTestClass
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray,
SizeConst = 256)]
public MyStruct[] dummy;
}

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
public class MyClassTestClass
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray,
SizeConst = 256)]
public MyClass[] dummy;
}
}
}
 
W

Willy Denoyette [MVP]

SB said:
I feel dumb to ask because I bet this is a simple question...

Looking at the code below, can someone please explain why I get two
different values in my Marshal.SizeOf calls (see the commented lines)?

TIA!
sb


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
public short a;
public short b;
public short c;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyClass
{
public short a;
public short b;
public short c;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyStructTestClass
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray,
SizeConst = 256)]
public MyStruct[] dummy;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyClassTestClass
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray,
SizeConst = 256)]
public MyClass[] dummy;
}

// test code...
private void testButton_Click(object sender, EventArgs e)
{
MyStruct myStructTest = new MyStruct();
MessageBox.Show("myStructTest size: " +
Marshal.SizeOf(myStructTest).ToString());//will show size of 6

MyClass myClassTest = new MyClass();
MessageBox.Show("myClassTest size: " +
Marshal.SizeOf(myClassTest).ToString());//will show size of 6

MyStructTestClass myStructClass = new MyStructTestClass();
MessageBox.Show("myStructClass size: " +
Marshal.SizeOf(myStructClass).ToString());//will show size of 1536

MyClassTestClass myClassClass = new MyClassTestClass();
MessageBox.Show("myClass2 size: " +
Marshal.SizeOf(myClassClass).ToString());//will show size of 1024???!!
}

This is not a bug, structs are value types and classes are reference types,
it is fundamental to understand the difference in layout and contents of
both.
1. A struct or class that holds an array of x elements of a value type has a
(marshaled) size of :
x * sizeof the value type, in your case: 256 * 6 = 1536.
2. A struct or a class that holds an array of x elements of a reference type
has a (marshaled) size of:
x * sizeof the reference, in your case: 256 * 4 = 1024.

It's clear that passing #1 to unmanaged code will not result in what you
expected.

This all assumes you are running the 32 bit version of the framework with
the default packing of structs.

Willy.


Note that this only works in v2.0 of the framework, and such topics should
not be posted here, better post to the whidbey NG.
 
S

SB

Willy,
See my notes inline:
This is not a bug, structs are value types and classes are reference
types, it is fundamental to understand the difference in layout and
contents of both.
1. A struct or class that holds an array of x elements of a value type has
a (marshaled) size of :
x * sizeof the value type, in your case: 256 * 6 = 1536.
2. A struct or a class that holds an array of x elements of a reference
type has a (marshaled) size of:
x * sizeof the reference, in your case: 256 * 4 = 1024.

I knew I was missing something obvious...now I feel dumb for not figuring
that out on my own! It makes perfect sense now.
It's clear that passing #1 to unmanaged code will not result in what you
expected.

#1 or #2? Maybe I'm misreading the above. If I understand things
correctly: it is safe for me to pass a struct which contains elements of
value types to a C dll right (assuming the correct packing)?
This all assumes you are running the 32 bit version of the framework with
the default packing of structs.

Yes, I'm using the default packing (8) so I can pass this on to a C dll.
Willy.
Note that this only works in v2.0 of the framework, and such topics should
not be posted here, better post to the whidbey NG.

Noted :)


After digesting the above, why was it necessary to have StructLayout
available for classes? In what situations would you use it for a class?

Thanks
sb
 
W

Willy Denoyette [MVP]

SB,

See inline [Willy]
Willy.

SB said:
Willy,
See my notes inline:


I knew I was missing something obvious...now I feel dumb for not figuring
that out on my own! It makes perfect sense now.


#1 or #2? Maybe I'm misreading the above. If I understand things
correctly: it is safe for me to pass a struct which contains elements of
value types to a C dll right (assuming the correct packing)?

[Willy]
#1, You can pass both a class (a) or a struct (b) containing an array of
value type (struct).

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public short a;
public short b;
public short c;
}

a)
[StructLayout(LayoutKind.Sequential)]
public class MyStructTestClass
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 256)]
public MyStruct[] dummy;
}

b)
[StructLayout(LayoutKind.Sequential)]
public struct MyStructTestStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public MyStruct[] dummy;
}

Note however that for(b) you must pass a reference to the struct!

function(ref myStructContainsAnArrayOfStruct);
 
S

SB

Ok, I think I have it now. Thanks for the help :)

sb

Willy Denoyette said:
SB,

See inline [Willy]
Willy.

SB said:
Willy,
See my notes inline:


I knew I was missing something obvious...now I feel dumb for not figuring
that out on my own! It makes perfect sense now.


#1 or #2? Maybe I'm misreading the above. If I understand things
correctly: it is safe for me to pass a struct which contains elements of
value types to a C dll right (assuming the correct packing)?

[Willy]
#1, You can pass both a class (a) or a struct (b) containing an array of
value type (struct).

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public short a;
public short b;
public short c;
}

a)
[StructLayout(LayoutKind.Sequential)]
public class MyStructTestClass
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 256)]
public MyStruct[] dummy;
}

b)
[StructLayout(LayoutKind.Sequential)]
public struct MyStructTestStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public MyStruct[] dummy;
}

Note however that for(b) you must pass a reference to the struct!

function(ref myStructContainsAnArrayOfStruct);

Yes, I'm using the default packing (8) so I can pass this on to a C dll.


Noted :)


After digesting the above, why was it necessary to have StructLayout
available for classes? In what situations would you use it for a class?

Thanks
sb
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top