与元帅无法合作的反思

时间:2015-07-22 11:01:00

标签: .net dynamic reflection struct marshalling

我在使用MarshalAs属性时遇到了反射问题。我想在运行时动态创建带反射的结构。结构可能包含需要编组的数组。自从.NET 2.0起,Field.SetMarshal()方法已经过时,我找不到替代方法。

我在运行时构建的struct的示例:

[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;
}

使用反射构建结构的代码:

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
    }
}

在过时的UnmanagedMarshal类的文档中,Microsoft说:“改为发送MarshalAs自定义属性”

好吧,我尝试制作一个CustomAttribute(“MarshalAs”),并用Field.SetCustomAttribute()添加它,但这也不起作用 - 结构的大小总是错误的。

    [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);
    }
}

1 个答案:

答案 0 :(得分:0)

如果有人对我的解决方案感兴趣:)

正如(废弃的)UnmanagedMarshal类的文档中所提到的,我必须使用CustomAttributeBuilder传递“MarshalAs”属性。

诀窍是使用MarshalAsAttribute类而不是自编的类(参见下面的代码)。由于关于这个主题的文档非常差,我在找到解决方案时遇到了一些麻烦。

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);
}