ReflectionDude
ReflectionDude

Reputation: 51

Reflection in combination with marshal not working

I have a problem with reflection in combination with MarshalAs attributes. I would like to dynamically create structs with reflection during runtime. The structs may contain arrays which need to be marshaled. The Field.SetMarshal() Method is obsolete since .NET 2.0 and I'm failing to find an alternative.

Example of struct which I'm building during runtime:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
public struct Example
{
    public int   var1;
    public float var2;
    public byte  var3;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public int[] var4;
}

Code using reflection to build structs:

TypeBuilder tb = mb.DefineType(pT.name,                          // Name of struct type
                                TypeAttributes.Public |           // Public scope
                                TypeAttributes.SequentialLayout | // Sequential layout
                                TypeAttributes.AnsiClass,         // Ansi Charset
                                typeof(ValueType),                // Value type
                                PackingSize.Size1);               // Packing size is 1 byte

// Add public fields to new struct type
foreach (parsedField pF in pT.fields)
{
    FieldBuilder fb = tb.DefineField(pF.name, pF.type, FieldAttributes.Public);

    // Add Marshall information to arrays
    if (fb.FieldType.IsArray)
    {
        // ADD MARSHAL INFORMATION HERE
    }
}

In the documentation of the obsolete UnmanagedMarshal class Microsoft says: "Emit the MarshalAs custom attribute instead"

Well I tried to make an CustomAttribute ("MarshalAs") and added it with Field.SetCustomAttribute() but this does not work either - the size of the struct is always wrong.

    [AttributeUsage(AttributeTargets.Field)]
    public class MarshalAs : System.Attribute
    {
        public UnmanagedType type;
        public int SizeConst;

        public MarshalAs(UnmanagedType type, int SizeConst)
        {
            this.type = type;
            this.SizeConst = SizeConst;
        }
    }

...

TypeBuilder tb = mb.DefineType(pT.name,                          // Name of struct type
                                TypeAttributes.Public |           // Public scope
                                TypeAttributes.SequentialLayout | // Sequential layout
                                TypeAttributes.AnsiClass,         // Ansi Charset
                                typeof(ValueType),                // Value type
                                PackingSize.Size1);               // Packing size is 1 byte

// Add public fields to new struct type
foreach (parsedField pF in pT.fields)
{
    FieldBuilder fb = tb.DefineField(pF.name, pF.type, FieldAttributes.Public);

    // Add Marshall information to arrays
    if (fb.FieldType.IsArray)
    {
        ConstructorInfo ci = typeof(MarshalAs).GetConstructor(new Type[] { typeof(UnmanagedType), typeof(int) });

        CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(ci, new object[] { UnmanagedType.ByValArray, 8 });

        fb.SetCustomAttribute(customBuilder);
    }
}

Upvotes: 2

Views: 612

Answers (1)

ReflectionDude
ReflectionDude

Reputation: 51

In case if anyone is interested my solution solution :)

As mentioned in the documentation of the (obsolete) UnmanagedMarshal class I had to pass the "MarshalAs" attribute with a CustomAttributeBuilder.

The trick is using the MarshalAsAttribute class instead a self-written one (see code below). I had some trouble finding the solution due to the documentation on this topic is quite poor.

FieldBuilder fb = tb.DefineField(pF.name, pF.type, FieldAttributes.Public);

// Add Marshall information to arrays
// The "MarshallAs" attribute has to be passed with a CustomAttributeBuilder 
if (fb.FieldType.IsArray)
{
    // First we define a list of types of the arguments of the "MarshalAs" attribute constructor
    Type[] argTypes = new Type[] { typeof(UnmanagedType) };

    // Now we create a list of concrete constructor arguments for the "MarshalAs" attribute constructor
    object[] args = new object[] { UnmanagedType.ByValArray };

    // We need the field information of "SizeConst" of the MarshalAs attribute 
    FieldInfo[] sizeConstField = new FieldInfo[] { typeof(MarshalAsAttribute).GetField("SizeConst") };

    // We create the value for the field "SizeConst"
    object[] sizeConstValue = new object[] { pF.arrSize };

    // Now we retrieve the constructor of "MarshalAs" attribute that matches specified constructor argument types
    ConstructorInfo ci = typeof(MarshalAsAttribute).GetConstructor(argTypes);

    // Create builder for "MarshalAs" attribute
    CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(ci, args, sizeConstField, sizeConstValue);

    // Set builder for MarshalAs attribute                              
    fb.SetCustomAttribute(customBuilder);
}

Upvotes: 2

Related Questions