A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

130 lines
5.4 KiB

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<TypeDefinition>();
var initializers = new List<MethodDefinition>();
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 == "<Module>");
// 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);
}
}
}
}
}
/// <summary>
/// Patches the type.
/// </summary>
/// <param name="type">The type.</param>
static void PatchType(TypeDefinition type, List<TypeDefinition> 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);
}
}
}