The .NET 4.0 Framework introduces classes for reading and writing memory mapped files. The classes are centred around methods for reading and writing structures. These are not marshalled but copied from and to the file in the form in which they are laid out in managed memory.
Let's say I want to write two structures sequentially to a memory mapped file using these methods:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Foo
{
public char C;
public bool B;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bar
{
}
static void Write<T1, T2>(T1 item1, T2 item2)
where T1 : struct
where T2 : struct
{
using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32))
using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
{
accessor.Write<T1>(0L, ref item1); // <-- (1)
accessor.Write<T2>(??, ref item2); // <-- (2)
}
}
static void Main()
{
Foo foo = new Foo { C = 'α', B = true };
Bar bar = new Bar { };
Write(foo, bar);
}
How would I get the number of bytes written in (1) so I can write the next value adjacently in (2)?
Note: The number of bytes in the example is 3(=2+1), not 5(=1+4) as returned by Marshal.SizeOf.
Note 2: sizeof
cannot determine the size of generic type parameters.
It seems there is no documented/public way to access the internal SizeOfType
function used by the MemoryMappedViewAccessor
class, so the most practical way of getting the size of those structures would be to use reflection like this:
static readonly Func<Type, uint> SizeOfType = (Func<Type, uint>)Delegate.CreateDelegate(typeof(Func<Type, uint>), typeof(Marshal).GetMethod("SizeOfType", BindingFlags.NonPublic | BindingFlags.Static));
static void Write<T1, T2>(T1 item1, T2 item2)
where T1 : struct
where T2 : struct
{
using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32))
using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
{
accessor.Write(0, ref item1);
accessor.Write(SizeOfType(typeof(T1)), ref item2);
}
}
You can use Emit to access the Sizeof opcode and bypass compiler's restriction on getting sizeof(T):
var sizeOfMethod = new DynamicMethod(
"GetManagedSizeImpl"
, typeof(uint)
, null
, true);
var genSizeOf = sizeOfMethod.GetILGenerator();
genSizeOf.Emit(OpCodes.Sizeof, typeof(T));
genSizeOf.Emit(OpCodes.Ret);
var sizeOfFunction = (Func<uint>)sizeOfMethod.CreateDelegate(typeof(Func<uint>));
// ...
int size = checked((int)sizeOfFunction());
1. One answer on this page proposes using the internal function Marshal.SizeOf
, but this only works for structs which do not contain any managed references. On .NET 4.7 it throws an ArgumentException
when passed a reference (class
) type, or a struct
type which contains embedded references.
2. Another answer here suggests using the IL sizeof
opcode. This works correctly for all struct
value types--including generics and those with embedded references--but for reference types it always returns IntPtr.Size
(i.e., the value 4
or 8
), as opposed to the actual layout size of (an instance of) the managed class. This may be what you want, depending on your situation. Note that this sort of result gracefully degrades by conflating the case of a struct containing a single embedded reference (handle) with a single reference (handle) itself.
A simpler way to invoke the sizeof
IL instruction is via the System.Runtime.CompilerServices.Unsafe package:
int struct_layout_bytes = Unsafe.Sizeof<T>();
3. If you truly need the actual layout size of (an instance of) a managed class for some reason, then you're probably doing something wrong, but you can obtain it via the following, which only works for reference types, that is, when typeof(T).IsValueType
is false
.
int class_layout_bytes = Marshal.ReadInt32(typeof(T).TypeHandle.Value, 4)
4. Therefore--and still with the preceding caveat--to get the instance layout size for any reference- or value-type--including those containing embedded references--from its Type
handle, combine methods #2 and #3:
int instance_layout_bytes = typeof(T).IsValueType ?
Unsafe.Sizeof<T>() :
Marshal.ReadInt32(typeof(T).TypeHandle.Value, 4);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With