csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
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
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);
|
|
}
|
|
}
|
|
}
|
|
|