Mono.塞西尔——改变私有财产
本文关键字:改变 私有财产 Mono | 更新日期: 2023-09-27 18:03:42
我有第三方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();
}
我怎么能和莫诺。塞西尔一起做呢?我没找到太多关于"单核细胞增多症"的信息。所以我不知道如何获得属性(控件)和改变他们的属性(例如字体)。
应该可以了
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:
IL_0000: nop
IL_0001: ret
之后:
IL_0000: ldarg.0
IL_0001: ldfld class [System.Windows.Forms]System.Windows.Forms.Button ClassLibrary1.Test::_btn
IL_0006: ldc.i4.1
IL_0007: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Enabled(bool)
IL_000c: ldarg.0
IL_000d: ldfld class [System.Windows.Forms]System.Windows.Forms.Button ClassLibrary1.Test::_btn
IL_0012: call valuetype [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.Color::get_Red()
IL_0017: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_ForeColor(valuetype [System.Drawing]System.Drawing.Color)
IL_001c: ldarg.0
IL_001d: ldfld class [System.Windows.Forms]System.Windows.Forms.Button ClassLibrary1.Test::_btn
IL_0022: ldarg.0
IL_0023: ldfld class [System.Windows.Forms]System.Windows.Forms.Button ClassLibrary1.Test::_btn
IL_0028: callvirt instance class [System.Drawing]System.Drawing.Font [System.Windows.Forms]System.Windows.Forms.Control::get_Font()
IL_002d: callvirt instance class [System.Drawing]System.Drawing.FontFamily [System.Drawing]System.Drawing.Font::get_FontFamily()
IL_0032: ldarg.0
IL_0033: ldfld class [System.Windows.Forms]System.Windows.Forms.Button ClassLibrary1.Test::_btn
IL_0038: callvirt instance class [System.Drawing]System.Drawing.Font [System.Windows.Forms]System.Windows.Forms.Control::get_Font()
IL_003d: callvirt instance float32 [System.Drawing]System.Drawing.Font::get_Size()
IL_0042: ldc.i4.1
IL_0043: ldc.i4.2
IL_0044: newobj instance void [System.Drawing]System.Drawing.Font::.ctor(class [System.Drawing]System.Drawing.FontFamily, float32, valuetype [System.Drawing]System.Drawing.FontStyle, valuetype [System.Drawing]System.Drawing.GraphicsUnit)
IL_0049: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Font(class [System.Drawing]System.Drawing.Font)
IL_004e: ret