这是一个典型的不安全结构声明,其中包含一个固定的缓冲区字段:
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public unsafe struct MyStruct
{
...
[FieldOffset(6)]
public fixed ushort MyFixedBuffer[28];
...
}
如何使用反射设置MyFixeBuffer的元素?
解决方法:
您不需要反思.您需要的是您要设置的结构的地址.那你可以用
byte* pStruct = xxxx
byte* pMyFixedBuffer = pStruct+6; // FieldOffset 6 tells me this
*(pMyFixedBuffer+i) = yourBytevalue; // set i-th element in MyFixedBuffer
我猜您在获取该结构的地址时遇到了问题,因为它很可能是按值传递给某些您要修补不同值的方法的.只要未将结构分配给类成员变量,您就可以从外部以某种方式访问该实例,那么您将无法在运行时修补任何内容.
由于您的课程是公开的,并且该领域也是公开的,因此完全没有理由使用反射或不安全的代码.但是我想这堂课不是您正在努力学习的实际课.
您可以使用反射来了解类的布局,但是在某些时候,您需要对实际要修补的字段进行硬编码.为此,您可以使用从反射获得的布局信息,然后在运行时确定地址.您需要获取字段的FieldOffset属性值,然后将其用作指针偏移量来获取它.
请注意,数组MyFixedBuffer在结构中不是真正的数组,因为它嵌入在结构中.普通对象指针GetType的东西将无法工作,因为不管理数组,而是固定大小的缓冲区,根本没有MT(方法表指针).在这一点上,您需要处理原始偏移量和要添加的字节中的补丁.
下面是一个使用反射的示例,如果您想构建使用值类型或诸如一个序列化器.
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public unsafe struct MyStruct
{
[FieldOffset(6)]
public fixed ushort MyFixedBuffer[28];
[FieldOffset(62)]
public byte Next;
}
class Program
{
unsafe static void Main(string[] args)
{
var field = typeof(MyStruct).GetField("MyFixedBuffer");
int offset = field.CustomAttributes.Where(x => x.AttributeType.Equals(typeof(FieldOffsetAttribute)))
.SelectMany(x => x.ConstructorArguments)
.Select(x => x.Value)
.Cast<int>()
.First();
KeyValuePair<Type,int> fixedBufferDescription = field.CustomAttributes.Where(x => x.AttributeType.Equals(typeof(FixedBufferAttribute)))
.Select(x => x.ConstructorArguments)
.Select(x => new KeyValuePair<Type, int>((Type)x[0].Value, (int)x[1].Value))
.First();
int fixedBuferLen = Marshal.SizeOf(fixedBufferDescription.Key) * fixedBufferDescription.Value;
MyStruct tmp = new MyStruct();
byte* raw = (byte*) &tmp;
short* pmyArray = (short *) (raw + offset);
for (int i = 0; i < fixedBuferLen/Marshal.SizeOf(fixedBufferDescription.Key); i++)
{
*(pmyArray + i) = (short) i;
}
}
}
这应该够了吧.