Reputation: 51
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
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