Put it another way: Unsafe knows nothing about .NET p/invoke feature, only Marshal does.
See the question and my original answer on StackOverflowThe Unsafe class has no knowledge of P/Invoke rules, only the Marshal class does.
For example, if you take this little program:
unsafe internal class Program
{
static void Main()
{
var info = new MyStruct();
Console.WriteLine(Marshal.SizeOf<MyStruct>()); // 8
Console.WriteLine(Unsafe.SizeOf<MyStruct>()); // 16
info.SomeValue = 0x12345678;
info.Name = "Test";
var p = Marshal.AllocCoTaskMem(100);
var ptr = p.ToPointer();
Unsafe.Write(ptr, info);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
public uint SomeValue;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] // includes terminating zero
public string Name;
}
}
And debug it with a breakpoint after the Unsafe.Write
method, open a memory window and use ptr as the start address, and you'll see this:
The SomeValue
member is visible, but the Name
is not, because ptr
points to the managed object (with a probable leading pointer to something internal), not to it's P/invoke "projection".
Also, note in this case Marshal.SizeOf
(P/Invoke size = 8) and Unsafe.SizeOf
(Managed size = 16) results are different.