Search code examples
c#dynamicstructlayout

FatalExecutionEngineError when messing with StructLayouts


Im messing arround with structlayouts and I found some thing i find quite odd:

The following code is working as i thought it would:

using System;
using System.Runtime.InteropServices;
public class Program
{
    [STAThread]
    static void Main()
    {
        Magic m = new Magic 
        { 
            InstanceA = new ClassA(), 
            InstanceB = new ClassB {Value="47"} 
        };

        Console.WriteLine(m.InstanceA.Value);
        Console.ReadKey();
    }

    class ClassA
    {
        public dynamic Value;
    }

    class ClassB
    {
        public string Value; // Change to int and it will get messy
    }

    [StructLayout(LayoutKind.Explicit)]
    struct Magic
    {
        [FieldOffset(0)]
        public ClassA InstanceA;
        [FieldOffset(0)]
        public ClassB InstanceB;
    }
}

However, if you change classB.Value to int, this code will throw the mentioned FatalExecutionEngineError.

Can anyone explain why or maybe how to workarround? I know this is probably way too complicated and im just messing arround here but someone might want some challenge.


Solution

  • Basically, what you have done is completely undefined. You are tricking it in two very nasty ways:

    • by pretending that one non-null class reference is actually of a different type (but with this being done without a type-check)
    • by having it attempt to load an object reference (dynamic is just object with some fancy compiler tricks) from an int (note; in all cases this will most likely point to garbage; in the case of x64, it isn't even an entire width, so it will be reading garbage into the value to dereference

    Basically: don't do that. This is incidentally why explicit layout is treated as unverifiable code. As for how to do it "properly" (and I use that term generously):

    class Magic
    {
        private object val;
        public ClassA InstanceA { get { return (InstanceA)val;} set { val = value; } }
        public ClassB InstanceB { get { return (InstanceB)val;} set { val = value; } }
    }
    

    You could also use val as Foo instead of the (Foo)val if you want to see null when it is the other type.