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();
}

我怎么能和莫诺。塞西尔一起做呢?我没找到太多关于"单核细胞增多症"的信息。所以我不知道如何获得属性(控件)和改变他们的属性(例如字体)。

Mono.塞西尔——改变私有财产

应该可以了

 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