using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; namespace ScreenConnect; public static class ProxyBuilder { public static object GetPlatformInvokeProxy(Type type, string libraryNameOrPath) { Tuple tuple = Tuple.Create(type, libraryNameOrPath); return GetProxy(type, tuple, null, (IntPtr)0, CallingConvention.Winapi, delegate(TypeBuilder typeBuilder, ILGenerator generator, MethodInfo methodInfo, CallingConvention callingConvention, Type[] callParameterTypes) { MethodBuilder methodBuilder = typeBuilder.DefinePInvokeMethod(methodInfo.Name + "PlatformInvoke", libraryNameOrPath, methodInfo.Name, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.PinvokeImpl, CallingConventions.Standard, methodInfo.ReturnType, callParameterTypes, callingConvention, CharSet.Auto); methodBuilder.SetImplementationFlags(methodBuilder.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); generator.EmitCall(OpCodes.Call, methodBuilder, null); }); } public static object GetNativeLibraryProxy(Type type, INativeLibrary nativeLibrary) { Tuple tuple = Tuple.Create(type, nativeLibrary.GetHashCode()); return GetProxy(type, tuple, nativeLibrary, (IntPtr)0, CallingConvention.Winapi, delegate(TypeBuilder typeBuilder, ILGenerator generator, MethodInfo methodInfo, CallingConvention callingConvention, Type[] callParameterTypes) { IntPtr procAddress = nativeLibrary.TryGetProcedureAddress(methodInfo.Name); EmitCalliOrNotImplemented(generator, procAddress, callingConvention, methodInfo.ReturnType, callParameterTypes); }); } public static object GetComProxy(Type type, IntPtr innerObjectPointer) { IntPtr vtable = Marshal.ReadIntPtr(innerObjectPointer); Tuple tuple = Tuple.Create(type, vtable); return GetProxy(type, tuple, null, innerObjectPointer, CallingConvention.StdCall, delegate(TypeBuilder typeBuilder, ILGenerator generator, MethodInfo methodInfo, CallingConvention callingConvention, Type[] callParameterTypes) { if (methodInfo.GetCustomAttribute() == null) { throw new InvalidOperationException("Can only be used with PreserveSig methods"); } int comSlotForMethodInfo = Marshal.GetComSlotForMethodInfo((MemberInfo)methodInfo); IntPtr procAddress = Marshal.ReadIntPtr(new IntPtr(vtable.ToInt64() + comSlotForMethodInfo * IntPtr.Size)); EmitCalliOrNotImplemented(generator, procAddress, callingConvention, methodInfo.ReturnType, callParameterTypes); }); } private static object GetProxy(Type type, object cacheKey, object rootObject, IntPtr innerObjectPointer, CallingConvention defaultCallingConvention, Proc callInstructionEmitter) { return Activator.CreateInstance(EmitBuilder.GetOrBuild(cacheKey, (ModuleBuilder moduleBuilder) => BuildProxy(moduleBuilder, type, innerObjectPointer != (IntPtr)0, defaultCallingConvention, callInstructionEmitter)), rootObject, innerObjectPointer); } private static Type BuildProxy(ModuleBuilder moduleBuilder, Type type, bool useInnerObjectPointer, CallingConvention defaultCallingConvention, Proc callInstructionEmitter) { TypeBuilder typeBuilder = moduleBuilder.DefineType(type.FullName + "Proxy", TypeAttributes.NotPublic, type.IsInterface ? typeof(object) : type); MethodInfo methodInfo = null; CallingConvention arg = type.GetCustomAttribute()?.CallingConvention ?? defaultCallingConvention; if (type.IsInterface) { typeBuilder.AddInterfaceImplementation(type); } ILGenerator iLGenerator = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[2] { typeof(object), typeof(IntPtr) }).GetILGenerator(); FieldBuilder field = typeBuilder.DefineField("rootObject", typeof(object), FieldAttributes.Private); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Stfld, field); FieldBuilder field2 = typeBuilder.DefineField("innerObjectPointer", typeof(IntPtr), FieldAttributes.Private); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Ldarg_2); iLGenerator.Emit(OpCodes.Stfld, field2); iLGenerator.Emit(OpCodes.Ret); MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (!type.IsInterface && !methodInfo2.IsAbstract) { continue; } Type[] array = (from pi in methodInfo2.GetParameters() select pi.ParameterType).ToArray(); List list = new List(); ILGenerator iLGenerator2 = typeBuilder.DefineMethod(methodInfo2.Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig, methodInfo2.ReturnType, array).GetILGenerator(); if (useInnerObjectPointer) { list.Add(typeof(IntPtr)); iLGenerator2.Emit(OpCodes.Ldarg_0); iLGenerator2.Emit(OpCodes.Ldfld, field2); } for (int num = 0; num < array.Length; num++) { if (array[num].IsOfType(typeof(Delegate))) { list.Add(typeof(IntPtr)); EmitLoadArg(iLGenerator2, num); iLGenerator2.Emit(OpCodes.Call, methodInfo ?? (methodInfo = new Func(TryGetFunctionPointerForDelegate).Method)); } else if (array[num].IsArray) { Type elementType = array[num].GetElementType(); list.Add(elementType.Assembly.GetType(elementType.FullName + "*")); Label label = iLGenerator2.DefineLabel(); Label label2 = iLGenerator2.DefineLabel(); Label label3 = iLGenerator2.DefineLabel(); EmitLoadArg(iLGenerator2, num); iLGenerator2.Emit(OpCodes.Brfalse, label); EmitLoadArg(iLGenerator2, num); iLGenerator2.Emit(OpCodes.Ldlen); iLGenerator2.Emit(OpCodes.Conv_I4); iLGenerator2.Emit(OpCodes.Brfalse, label2); EmitLoadArg(iLGenerator2, num); iLGenerator2.Emit(OpCodes.Ldc_I4_0); iLGenerator2.Emit(OpCodes.Ldelema, elementType); iLGenerator2.Emit(OpCodes.Br, label3); iLGenerator2.MarkLabel(label); iLGenerator2.Emit(OpCodes.Ldc_I4_0); iLGenerator2.Emit(OpCodes.Conv_U); iLGenerator2.Emit(OpCodes.Br, label3); iLGenerator2.MarkLabel(label2); LocalBuilder local = iLGenerator2.DeclareLocal(elementType, pinned: true); iLGenerator2.Emit(OpCodes.Ldloca, local); iLGenerator2.MarkLabel(label3); } else { list.Add(array[num].IsByRef ? array[num].Assembly.GetType(array[num].FullName.Replace("&", "*")) : array[num]); EmitLoadArg(iLGenerator2, num); } } callInstructionEmitter(typeBuilder, iLGenerator2, methodInfo2, arg, list.ToArray()); iLGenerator2.Emit(OpCodes.Ret); } return typeBuilder.CreateType(); } private static void EmitCalliOrNotImplemented(ILGenerator generator, IntPtr procAddress, CallingConvention callingConvention, Type returnType, Type[] callParameterTypes) { if (procAddress == IntPtr.Zero) { generator.ThrowException(typeof(NotImplementedException)); return; } if (IntPtr.Size == 4) { generator.Emit(OpCodes.Ldc_I4, procAddress.ToInt32()); } else { generator.Emit(OpCodes.Ldc_I8, procAddress.ToInt64()); } generator.EmitCalli(OpCodes.Calli, callingConvention, returnType, callParameterTypes); } private static void EmitLoadArg(ILGenerator generator, int index) { switch (index) { case 0: generator.Emit(OpCodes.Ldarg_1); break; case 1: generator.Emit(OpCodes.Ldarg_2); break; case 2: generator.Emit(OpCodes.Ldarg_3); break; default: generator.Emit(OpCodes.Ldarg_S, index + 1); break; } } private static void EmitLoadLocal(ILGenerator generator, int index) { switch (index) { case 0: generator.Emit(OpCodes.Ldloc_0); break; case 1: generator.Emit(OpCodes.Ldloc_1); break; case 2: generator.Emit(OpCodes.Ldloc_2); break; default: generator.Emit(OpCodes.Ldloc_S, index); break; } } public static IntPtr TryGetFunctionPointerForDelegate(Delegate @delegate) { return @delegate?.Pipe(Marshal.GetFunctionPointerForDelegate) ?? ((IntPtr)0); } }