using System.Collections.Generic; using System.Linq; using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; using XamlX.TypeSystem; using MethodAttributes = Mono.Cecil.MethodAttributes; namespace Avalonia.Build.Tasks { static class ComInteropHelper { public static void PatchAssembly(AssemblyDefinition asm, CecilTypeSystem typeSystem) { var classToRemoveList = new List(); var initializers = new List(); foreach (var type in asm.MainModule.Types) { var i = type.Methods.FirstOrDefault(m => m.Name == "__MicroComModuleInit"); if (i != null) initializers.Add(i); PatchType(type, classToRemoveList); } // Remove All Interop classes foreach (var type in classToRemoveList) asm.MainModule.Types.Remove(type); // Patch automatic registrations if (initializers.Count != 0) { var moduleType = asm.MainModule.Types.First(x => x.Name == ""); // Needed for compatibility with upcoming .NET 5 feature, look for existing initializer first var staticCtor = moduleType.Methods.FirstOrDefault(m => m.Name == ".cctor"); if (staticCtor == null) { // Create a new static ctor if none exists staticCtor = new MethodDefinition(".cctor", MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void); staticCtor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); moduleType.Methods.Add(staticCtor); } foreach (var i in initializers) staticCtor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, i)); } } static void PatchMethod(MethodDefinition method) { if (method.HasBody) { var ilProcessor = method.Body.GetILProcessor(); var instructions = method.Body.Instructions; for (int i = 0; i < instructions.Count; i++) { Instruction instruction = instructions[i]; if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodReference) { var methodDescription = (MethodReference)instruction.Operand; if (methodDescription.Name.StartsWith("Calli") && methodDescription.DeclaringType.Name == "LocalInterop") { var callSite = new CallSite(methodDescription.ReturnType) { CallingConvention = MethodCallingConvention.StdCall }; if (methodDescription.Name.StartsWith("CalliCdecl")) { callSite.CallingConvention = MethodCallingConvention.C; } else if(methodDescription.Name.StartsWith("CalliThisCall")) { callSite.CallingConvention = MethodCallingConvention.ThisCall; } else if(methodDescription.Name.StartsWith("CalliStdCall")) { callSite.CallingConvention = MethodCallingConvention.StdCall; } else if(methodDescription.Name.StartsWith("CalliFastCall")) { callSite.CallingConvention = MethodCallingConvention.FastCall; } // Last parameter is the function ptr, so we don't add it as a parameter for calli // as it is already an implicit parameter for calli for (int j = 0; j < methodDescription.Parameters.Count - 1; j++) { var parameterDefinition = methodDescription.Parameters[j]; callSite.Parameters.Add(parameterDefinition); } // Create calli Instruction var callIInstruction = ilProcessor.Create(OpCodes.Calli, callSite); // Replace instruction ilProcessor.Replace(instruction, callIInstruction); } } } } } /// /// Patches the type. /// /// The type. static void PatchType(TypeDefinition type, List classToRemoveList) { // Patch methods foreach (var method in type.Methods) PatchMethod(method); if (type.Name == "LocalInterop") classToRemoveList.Add(type); // Patch nested types foreach (var typeDefinition in type.NestedTypes) PatchType(typeDefinition, classToRemoveList); } } }