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
。
我有一个由我构建的 .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
。