C# 在运行时以编程方式编辑我的 .NET 程序集

C# programmatically edit my .NET assembly at runtime

我有一个由我构建的 .NET 程序集,但我希望能够在 运行时 上用一些次要但任意的属性更改文件重写 .DLL。具体来说,我希望能够更改 class 属性的 属性,以便我可以根据情况自定义二进制文件。

为了说明,我想实现编辑正在生成的程序集的效果

[SomeAttribute("Name")]
public class MyClass{
    ...

这样新程序集在功能上与

相同
[SomeAttribute("Custom Name")]
public class MyClass{
    ...

而这个 "Custom Name" 可以是任何东西(在运行时确定)。这可以在运行时执行吗?

之所以需要修改实际的 .DLL 是因为它将被一个 单独的 进程加载,该进程无法确定运行时信息(我不控制这个进程).

到目前为止的实验表明,如果新的 "Custom Name" 与原始长度相同,它似乎可以工作,但在其他情况下则不然(即使您编辑了指定长度的前面的字节;大概有偏移量存储在文件中的某处)。

编辑:忘了说,解决方案也需要在 .NET 2 框架下。

无需修改 DLL,您可以在 运行 时使用 TypeDescriptor class 添加属性;例如

TypeDescriptor.AddAttributes(typeof(MyClass), new SomeAttribute("Custom Name"));

此方法的唯一警告是,完全依赖反射的代码将无法读取添加的属性 - 您必须使用 TypeDescriptor.GetAttributes()。但是,大多数对属性进行操作的内置 .NET Framework 方法都知道在 运行 时间添加的元数据。

https://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor_methods(v=vs.110).aspx

不清楚你真正想做什么 (XY problem?)

不过,如果您想修改程序集,您通常会使用 Mono.Cecil 自述为: 您可以加​​载现有的托管程序集,浏览所有包含的类型,修改它们飞行并将修改后的程序集保存回磁盘。 .

请注意,属性 可以 在作为参数传递的数据之上包含额外的数据:

public class MyAttribute : Attribute
{
    public MyAttribute(string str)
    {
        argument = str;
    }

    private string argument;

    public string Argument { get; }

    public string AssemblyName
    {
        get
        {
            return Assembly.GetEntryAssembly().FullName;
        }
    }
}


[MyAttribute("Hello")]
class Program
{
    static void Main(string[] args)
    {
        var attr = typeof(Program).GetCustomAttribute<MyAttribute>();
        Console.WriteLine(attr.Argument);
        Console.WriteLine(attr.AssemblyName);
    }
}

根据@xanatos 的非常有用的建议,我制定了这个解决方案:

在 .NET 2 下,您可以安装包 Mono.Cecil 0.9.6.1.

那么代码如下:

AssemblyDefinition assbDef = AssemblyDefinition.ReadAssembly("x.dll");
TypeDefinition type = assbDef.MainModule.GetType("NameSpace.MyClass").Resolve();
foreach (CustomAttribute attr in type.CustomAttributes)
{
    TypeReference argTypeRef = null;
    int? index = null;
    for (int i = 0; i < attr.ConstructorArguments.Count; i++)
    {
        CustomAttributeArgument arg = attr.ConstructorArguments[i];

        string stringValue = arg.Value as string;
        if (stringValue == "Name")
        {
            argTypeRef = arg.Type;
            index = i;
        }
    }

    if (index != null)
    {
        attr.ConstructorArguments[(int)index] = new CustomAttributeArgument(argTypeRef, newName);
    }
}    
assbDef.Write("y.dll");

这将在程序集中搜索具有值 "Name" 的任何属性参数,并将其值替换为 newName