我在使用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);
}
}
答案 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);
}