我有第三方DLL我想直观地修改。 我想更改表单上可用的按钮外观。 使用dotPeek我发现按钮名称,现在在表单加载事件处理程序中我想使用下面的行修改该按钮:
this.cancelBtn.Enabled = true;
this.cancelBtn.ForeColor = Color.Red;
this.cancelBtn.Font = new Font(this.cancelBtn.Font.FontFamily, this.cancelBtn.Font.Size, FontStyle.Bold, GraphicsUnit.Pixel);
我可以使用Mono.Cecil加载和修改该程序集,如下所示:
var assembly = AssemblyDefinition.ReadAssembly(source);
var module = assembly.MainModule;
var name = assembly.Name;
TypeDefinition type = assembly.MainModule.Types.First(t => t.FullName == "SimpleEditor.MainDialog");
MethodDefinition method = type.Methods.First(m => m.Name == "Main_Load");
ILProcessor procesor = method.Body.GetILProcessor();
var firstInstruction = procesor.Body.Instructions.First();
MethodInfo writeLineMethod = typeof (Debug).GetMethod("WriteLine", new Type[] {typeof (string)});
var writeLine = assembly.MainModule.Import(writeLineMethod);
var callWriteLine = procesor.Create(Mono.Cecil.Cil.OpCodes.Call, writeLine);
const string sentence = @"MONO.CECIL rocks!";
var insertSentence = procesor.Create(Mono.Cecil.Cil.OpCodes.Ldstr, sentence);
procesor.InsertBefore(firstInstruction, insertSentence);
procesor.InsertAfter(insertSentence, callWriteLine);
assembly.Write(source);
但我现在所能做的就是在Main_Load方法的开头添加Debug.WriteLine("MONO.CECIL rocks!")
。
我想从以下方法更改该方法:
private void Main_Load(object sender, EventArgs e)
{
SetupEverything();
}
为:
private void Main_Load(object sender, EventArgs e)
{
this.cancelBtn.Enabled = true;
this.cancelBtn.ForeColor = Color.Red;
this.cancelBtn.Font = new Font(this.cancelBtn.Font.FontFamily, this.cancelBtn.Font.Size, FontStyle.Bold, GraphicsUnit.Pixel);
SetupEverything();
}
我怎么能用Mono.Cecil做到这一点? 我无法在线找到有关Mono.Cecil的更多信息,因此我不知道如何获取属性(控件)并更改其属性(例如Font)。
答案 0 :(得分:1)
这应该有效
.lock()
IL static void ChangeButtonProperties()
{
// Load the assembly and the main module
string assemblyPath = $"{Environment.CurrentDirectory}\\ClassLibrary1.dll";
var mainModule = AssemblyDefinition.ReadAssembly(assemblyPath).MainModule;
// Get the method to change
TypeDefinition type = mainModule.Types.First(t => t.Name == "Test");
MethodDefinition method = type.Methods.Single(m => m.Name == "Main_Load");
// Get the instance field of the button
FieldDefinition btnField = type.Fields.Single(f => f.Name == "_btn");
var controlType = mainModule.Import(typeof(Control));
// Import relevant types
var colorType = mainModule.Import(typeof(Color));
var fontType = mainModule.Import(typeof(Font));
var fonFamilyType = mainModule.Import(typeof(FontFamily));
// Get the setters of the requested properties
MethodDefinition setEnabled = controlType.Resolve().Methods.Single(m => m.Name == "set_Enabled");
MethodReference setEnabledRef = mainModule.Import(setEnabled);
MethodDefinition setForeColor = controlType.Resolve().Methods.Single(m => m.Name == "set_ForeColor");
MethodReference setForeColorRef = mainModule.Import(setForeColor);
MethodDefinition setFont = controlType.Resolve().Methods.Single(m => m.Name == "set_Font");
MethodReference setFontRef = mainModule.Import(setFont);
// Get the Font constructor. Maybe you can think of a better way
MethodDefinition fontCtor =
fontType.Resolve().Methods.Single(
m => m.IsConstructor &&
m.Parameters.Count == 4 &&
m.Parameters[0].ParameterType.Name == "FontFamily");
MethodReference fontCtorRef = mainModule.Import(fontCtor);
// Get the getters of the requested properties
var getRedColor = colorType.Resolve().Methods.Single(m => m.Name == "get_Red");
MethodReference getRedColorRef = mainModule.Import(getRedColor);
var getFont = controlType.Resolve().Methods.Single(m => m.Name == "get_Font");
MethodReference getFontRef = mainModule.Import(getFont);
var getFontSize = fontType.Resolve().Methods.Single(m => m.Name == "get_Size");
MethodReference getFontSizeRef = mainModule.Import(getFontSize);
var getFontFamily = fontType.Resolve().Methods.Single(m => m.Name == "get_FontFamily");
MethodReference getFontFamilyRef = mainModule.Import(getFontFamily);
// I clear just for the example.
// You can keep the instructions and enter the new one before\after
method.Body.Instructions.Clear();
ILProcessor processor = method.Body.GetILProcessor();
LoadInstanceField(processor, btnField);
// Set Enabled to true
processor.Emit(OpCodes.Ldc_I4_1);
processor.Emit(OpCodes.Callvirt, setEnabledRef);
LoadInstanceField(processor, btnField);
// Set color to red
processor.Emit(OpCodes.Call, getRedColorRef); // no need callvirt here because is static
processor.Emit(OpCodes.Callvirt, setForeColorRef);
LoadInstanceField(processor, btnField);
LoadInstanceField(processor, btnField);
// Get all parameters to create new Font object
processor.Emit(OpCodes.Callvirt, getFontRef);
processor.Emit(OpCodes.Callvirt, getFontFamilyRef);
LoadInstanceField(processor, btnField);
processor.Emit(OpCodes.Callvirt, getFontRef);
processor.Emit(OpCodes.Callvirt, getFontSizeRef);
processor.Emit(OpCodes.Ldc_I4_1); // Load 1. It's the enum value of FontStyle.Bold
processor.Emit(OpCodes.Ldc_I4_2); // Load 2. It's the enum value of GraphicsUnit.Pixel
// Call Font constructor
processor.Emit(OpCodes.Newobj, fontCtorRef);
// Set the font
processor.Emit(OpCodes.Callvirt, setFontRef);
processor.Emit(OpCodes.Ret); // Return from the method
method.Body.OptimizeMacros();
mainModule.Write(assemblyPath + ".new.dll"); // Save the new dll
}
static void LoadInstanceField(ILProcessor processor, FieldDefinition field)
{
processor.Emit(OpCodes.Ldarg_0); // Load this
processor.Emit(OpCodes.Ldfld, field); // Load button field
}
之前:
Main_Load
之后:
IL_0000: nop
IL_0001: ret