Copy / Reflection.Emit'ting静态数组

本文关键字:ting 静态 数组 Reflection Emit Copy | 更新日期: 2023-09-27 17:49:30

我正在尝试将静态初始化项从一个DLL复制到另一个DLL。

如果你在c#中有一个静态数组初始化器,你会得到这样的东西:

.class private auto ansi <PrivateImplementationDetails>{0D3E8B0E-F218-435F-989D-9D04F550A786}
    extends [mscorlib]System.Object
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
    .field assembly static valuetype <PrivateImplementationDetails>{0D3E8B0E-F218-435F-989D-9D04F550A786}/__StaticArrayInitTypeSize=120 $$method0x6000060-1 = ((binary data))

我发现读取这些数据最简单的方法是使用这个:

int size = field.FieldType.StructLayoutAttribute.Size;
byte[] data = new byte[size];
RuntimeHelpers.InitializeArray(data, field.FieldHandle);

基本上这会给你上面提到的"二进制数据"。

问题1: Dictionary会发生什么?这还管用吗?我很难弄清楚这里到底发生了什么(它似乎隐藏在反编译中)…

我已经发现在实现细节中使用的GUID是ModuleBuilder中的版本ID。使用其他显式字段信息意味着您应该能够复制数据。

问题2:如何使用反射将数据写回另一个ModuleBuilder/FieldBuilder。排放?

,

版本吗?.NET 4.5 (VS2013默认)

似乎对弹出的Dictionary 's感到困惑。我在我的代码中做了一些挖掘,它们似乎以[string->int]字典的形式出现,用于解析switch/case语句。

例如,可以在mscorlib.dll v4.0.30319.18444中找到它们。在Reflector中,它们看起来像这样:

.field assembly static class System.Collections.Generic.Dictionary`2<string, int32> $$method0x6003a20-1

至于为什么:我在执行一些测试之前改变了DLL。具有讽刺意味的是,我想要这样做的原因是因为我不想弄乱实现细节:-),因为多个实例可能会带来麻烦。

换句话说,我基本上只是想复制它们,如果它们有像这样的花哨数据,不管类型等,同时保留名称,就像它们是二进制blob一样。由于IL和数据在不同的DLL中以相同的方式处理,因此无论编译器是什么,这都应该是可能的,对吧?

Copy / Reflection.Emit'ting静态数组

经过几个小时的摆弄,它似乎是这样工作的:

<PrivateImplementationDetails>中的每个字段:

  • 可以使用值类型或引用类型。
  • 值类型可以携带数据,而引用类型不能。引用类型在使用之前被初始化(如volatile static,等等)。

从值类型(field.FieldType.IsValueType)获取数据可以通过RuntimeHelpers调用完成:

int size = GetManagedSize(field.FieldType);
byte[] data = new byte[size];
RuntimeHelpers.InitializeArray(data, field.FieldHandle);
FieldBuilder mappedField = myType.DefineInitializedData(
    field.Name, data, field.Attributes);

:

public static int GetManagedSize(Type type)
{
    var method = new DynamicMethod("GetManagedSizeImpl", typeof(uint), new Type[0], 
        GetType().Assembly, false);
    ILGenerator gen = method.GetILGenerator();
    gen.Emit(OpCodes.Sizeof, type);
    gen.Emit(OpCodes.Ret);
    var func = (Func<uint>)method.CreateDelegate(typeof(Func<uint>));
    return checked((int)func());
}

如果它是引用类型,则不需要做任何操作,因为它们在使用时被初始化为volatile static。

在此之后,您需要确保使用生成的fieldinfo's而不是原始字段;它们将包含相同的数据。对于引用,初始化为null

显然你不想在实现细节本身或它们的使用方式上搞砸…