【问题标题】:C# programmatically edit my .NET assembly at runtimeC# 在运行时以编程方式编辑我的 .NET 程序集
【发布时间】:2018-12-31 16:05:23
【问题描述】:

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

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

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

使得新程序集在功能上与

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

而且这个“自定义名称”可以是任何东西(在运行时确定)。这可以在运行时执行吗?

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

到目前为止的实验表明,如果新的“自定义名称”的长度与原始名称相同,则它似乎可以工作,但不是其他情况(即使您编辑了指定长度的前一个字节;大概有偏移量存储在文件)。

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

【问题讨论】:

  • 我会推荐一种替代方法,您可以在其中创建新程序集并在该程序集中创建MyClass 的新子类以及属性名称的更改。您当前的方法可能不被允许,防病毒软件可能会将其检测为有害行为。
  • @AkashKava 我不是必须手动重建程序集,然后再使用里面的子类吗?我需要新名称是任意的;它不能预先制作。我目前并不担心防病毒,因为我可以完全控制原始程序集(它不是系统程序集或任何东西)
  • 我不记得确切的细节,但如果您使用签名的程序集 - 在生产环境中您应该这样做 - 那么您甚至无法在不使程序集失效的情况下更改一点。
  • @Enigmativity 是的,这是正确的,但现在这不是问题
  • 要修改它,Fody 和 Mono.Cecil 可能是你的工具。一般来说,您尝试做的是一个坏主意。可能是XY problem

标签: c# .net-assembly .net-2.0


【解决方案1】:

使用来自@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。

【讨论】:

    【解决方案2】:

    不清楚你真正想做什么 (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);
        }
    }
    

    【讨论】:

    • 对属性的好建议,不幸的是,有问题的属性是sealed 类,而且我要更改的值不是虚拟的。
    • 对不起,我之前忘了提这个,但是解决方案需要在 .NET 2 上运行,会更新问题
    • 我会在存档中查看旧版本的 Mono.Cecil,它们似乎曾经支持 .NET 2。
    【解决方案3】:

    您可以在运行时使用TypeDescriptor 类添加属性,而不是修改DLL;例如

    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

    【讨论】:

    • 不幸的是,这不是一个选项,可以在其他地方调用 dll(通过不同的进程)。它需要针对 dll 的所有未来可能用途进行更新,而不仅仅是我将其加载到内存中。我会稍微澄清一下这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-05
    • 2018-09-21
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 2012-03-20
    相关资源
    最近更新 更多