static CcwMethodInfo CreateCcwMethod(ComMethodInfo comMethodInfo, TypeBuilder typeBuilder, Utils.UniqueNameGenerator methodNameUniqifier) { var methodInfo = comMethodInfo.MethodInfo; var methodNameTemplate = methodInfo.Name; var methodName = methodNameUniqifier.Uniquify(methodNameTemplate); var paramTypes = new List <Type>(); paramTypes.Add(typeof(IntPtr)); paramTypes.AddRange(comMethodInfo.ParameterInfos.Select((p) => p.IsOut ? p.ValueMarshaller.NativeParameterType.MakePointerType() : p.ValueMarshaller.NativeParameterType)); var methodBuilder = typeBuilder.DefineMethod(methodName, MethodAttributes.HideBySig | MethodAttributes.Static, CallingConventions.Standard, comMethodInfo.NativeReturnType, paramTypes.ToArray()); methodBuilder.DefineParameter(1, ParameterAttributes.In, "this"); // define a method foreach (var parameter in methodInfo.GetParameters()) { methodBuilder.DefineParameter(parameter.Position + 2, parameter.Attributes, parameter.Name); } CreateCcwMethodBody(comMethodInfo, methodBuilder); return(new CcwMethodInfo() { methodBuilder = methodBuilder, parameterTypes = paramTypes.ToArray() }); }
static void CreateFunctionPtrWrapperMethod(ComMethodInfo comMethodInfo, TypeBuilder typeBuilder) { var methodInfo = comMethodInfo.MethodInfo; var methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.Static | MethodAttributes.HideBySig, CallingConventions.Standard, methodInfo.ReturnType, methodInfo.GetParameters().Select((p) => p.ParameterType).ToArray()); // define a method foreach (var parameter in methodInfo.GetParameters()) { methodBuilder.DefineParameter(parameter.Position + 1, parameter.Attributes, parameter.Name); } CreateRCWMethodBody(comMethodInfo, null, methodBuilder); }
static void CreateRcwMethod(ComMethodInfo comMethodInfo, TypeBuilder typeBuilder, FieldInfo ptrFieldInfo, Utils.UniqueNameGenerator methodNameUniqifier) { var methodInfo = comMethodInfo.MethodInfo; var methodNameTemplate = comMethodInfo.MethodInfo.DeclaringType.Name + "." + methodInfo.Name; var methodName = methodNameUniqifier.Uniquify(methodNameTemplate); var methodBuilder = typeBuilder.DefineMethod(methodName, MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.Standard, methodInfo.ReturnType, methodInfo.GetParameters().Select((p) => p.ParameterType).ToArray()); typeBuilder.DefineMethodOverride(methodBuilder, methodInfo); // define a method foreach (var parameter in methodInfo.GetParameters()) { methodBuilder.DefineParameter(parameter.Position + 1, parameter.Attributes, parameter.Name); } CreateRCWMethodBody(comMethodInfo, ptrFieldInfo, methodBuilder); }
TypeInfo CreateRcfpwClass(Type delegateType) { if (delegateType.GetTypeInfo().BaseType != typeof(MulticastDelegate)) { throw new ArgumentException("Not a delegate.", nameof(delegateType)); } if (delegateType.IsConstructedGenericType) { throw new ArgumentException("Generic delegate type isn't supported yet.", nameof(delegateType)); } var typeBuilder = moduleBuilder.DefineType(delegateType.FullName + "<RCFPW>", TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.NotPublic); var invokeMethod = delegateType.GetRuntimeMethods().First((m) => m.Name == "Invoke"); var mi = new ComMethodInfo(invokeMethod, 0, ComMethodType.FunctionPtrThunk); CreateFunctionPtrWrapperMethod(mi, typeBuilder); return(typeBuilder.CreateTypeInfo()); }
public ComMethodParameterInfo(ComMethodInfo methodInfo, ParameterInfo paramInfo) { this.ComMethodInfo = methodInfo; this.ParameterInfo = paramInfo; }
static void CreateCcwMethodBody(ComMethodInfo comMethodInfo, MethodBuilder methodBuilder) { var methodInfo = comMethodInfo.MethodInfo; var gen = methodBuilder.GetILGenerator(); var returnTypeNative = comMethodInfo.ReturnValueMarshaller?.NativeParameterType.MakePointerType() ?? comMethodInfo.NativeReturnType; LocalBuilder returnValueNativeLocal = returnTypeNative != typeof(void) ? gen.DeclareLocal(returnTypeNative) : null; // create parameter marshallers var paramInfos = comMethodInfo.ParameterInfos .Select((p) => { var marshaller = p.ValueMarshaller; var runtimeLocal = gen.DeclareLocal(p.Type); var toNativeGenerator = p.IsOut ? marshaller.CreateToNativeGenerator(gen) : null; var toRuntimeGenerator = p.IsIn ? marshaller.CreateToRuntimeGenerator(gen) : null; var nativeType = p.IsOut ? marshaller.NativeParameterType.MakePointerType() : marshaller.NativeParameterType; Storage nativeStorage; if (p.IsReturnValue) { nativeStorage = new ParameterStorage(gen, nativeType, comMethodInfo.ParameterInfos.Count()); nativeStorage = new IndirectStorage(nativeStorage); } else if (p.IsOut) { nativeStorage = new ParameterStorage(gen, p.ParameterInfo.ParameterType, p.ParameterInfo.Position + 1); nativeStorage = new IndirectStorage(nativeStorage); } else { nativeStorage = new ParameterStorage(gen, p.Type, p.ParameterInfo.Position + 1); } return(new { ParameterInfo = p, RuntimeLocal = runtimeLocal, Marshaller = marshaller, ToNativeGenerator = toNativeGenerator, ToRuntimeGenerator = toRuntimeGenerator, RuntimeStorage = new LocalStorage(gen, runtimeLocal), NativeStorage = nativeStorage }); }).ToArray(); // start try/catch if (comMethodInfo.ReturnsHresult) { gen.BeginExceptionBlock(); } // marshal in parameters foreach (var paramInfo in paramInfos) { paramInfo.ToRuntimeGenerator?.EmitToRuntime(paramInfo.NativeStorage, paramInfo.RuntimeStorage, move: false); } // retrieve GCHandle (read ComClassHeader.handle) gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Sizeof, typeof(IntPtr)); // skip vtable gen.Emit(OpCodes.Add); // GCHandle.Target gen.Emit(OpCodes.Call, targetGetter); // convert to the target interface type gen.Emit(OpCodes.Castclass, comMethodInfo.MethodInfo.DeclaringType); // push each parameters foreach (var paramInfo in paramInfos) { if (paramInfo.ParameterInfo.IsReturnValue) { continue; } if (paramInfo.ParameterInfo.IsOut) { // out/inout parameter gen.Emit(OpCodes.Ldloca, paramInfo.RuntimeLocal); } else { paramInfo.RuntimeStorage.EmitLoad(); } } // call it! gen.Emit(OpCodes.Callvirt, comMethodInfo.MethodInfo); if (comMethodInfo.ReturnValueMarshaller != null) { if (comMethodInfo.ReturnsHresult) { throw new InvalidOperationException(); } // marshal real return value var runtimeLocal = gen.DeclareLocal(comMethodInfo.MethodInfo.ReturnType); gen.Emit(OpCodes.Stloc, runtimeLocal); comMethodInfo.ReturnValueMarshaller.CreateToNativeGenerator(gen) .EmitToNative(new LocalStorage(gen, runtimeLocal), new LocalStorage(gen, returnValueNativeLocal)); } foreach (var paramInfo in paramInfos) { // save [out, retval] if (paramInfo.ParameterInfo.IsReturnValue) { paramInfo.RuntimeStorage.EmitStore(); } } foreach (var paramInfo in paramInfos) { // marshal "out" parameters and optionally return value parameter if (paramInfo.ToNativeGenerator != null) { // drop existing values paramInfo.ToNativeGenerator.EmitDestructNativeValue(paramInfo.NativeStorage); // marshal paramInfo.ToNativeGenerator.EmitToNative(paramInfo.RuntimeStorage, paramInfo.NativeStorage); } } // handle exceptions if (comMethodInfo.ReturnsHresult) { gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Stloc, returnValueNativeLocal); gen.BeginCatchBlock(typeof(Exception)); // get hresult gen.Emit(OpCodes.Callvirt, hresultGetter); // store HRESULT gen.Emit(OpCodes.Stloc, returnValueNativeLocal); gen.EndExceptionBlock(); } // return if (returnValueNativeLocal != null) { gen.Emit(OpCodes.Ldloc, returnValueNativeLocal); } gen.Emit(OpCodes.Ret); }
static void CreateRCWMethodBody(ComMethodInfo comMethodInfo, FieldInfo ptrFieldInfo, MethodBuilder methodBuilder) { var methodInfo = comMethodInfo.MethodInfo; if ((ptrFieldInfo == null) != comMethodInfo.IsFirstParameterFunctionPtr) { throw new InvalidOperationException(); } // generate a method body var gen = methodBuilder.GetILGenerator(); LocalBuilder returnValueLocal = methodInfo.ReturnType != typeof(void) ? gen.DeclareLocal(methodInfo.ReturnType) : null; bool isInstanceMethod = (methodBuilder.Attributes & MethodAttributes.Static) == 0; // create parameter marshallers var paramInfos = comMethodInfo.ParameterInfos .Select((p) => { var marshaller = p.ValueMarshaller; var nativeLocal = gen.DeclareLocal(marshaller.NativeParameterType); var nativeLocalPin = p.IsOut ? gen.DeclareLocal(p.NativeType.MakePointerType(), true) : null; var toNativeGenerator = p.IsIn ? marshaller.CreateToNativeGenerator(gen) : null; var toRuntimeGenerator = p.IsOut ? marshaller.CreateToRuntimeGenerator(gen) : null; Storage storage; if (p.IsReturnValue) { storage = new LocalStorage(gen, returnValueLocal); } else if (p.IsOut) { storage = new ParameterStorage(gen, p.ParameterInfo.ParameterType, p.ParameterInfo.Position + (isInstanceMethod ? 1 : 0)); storage = new IndirectStorage(storage); } else { storage = new ParameterStorage(gen, p.Type, p.ParameterInfo.Position + (isInstanceMethod ? 1 : 0)); } return(new { ParameterInfo = p, NativeLocal = nativeLocal, NativeLocalPin = nativeLocalPin, Marshaller = marshaller, ToNativeGenerator = toNativeGenerator, ToRuntimeGenerator = toRuntimeGenerator, Storage = storage, NativeStorage = new LocalStorage(gen, nativeLocal), }); }).ToArray(); // marshal in parameters foreach (var paramInfo in paramInfos) { if (paramInfo.ToNativeGenerator != null) { paramInfo.ToNativeGenerator.EmitToNative(paramInfo.Storage, paramInfo.NativeStorage); } } // pin by-ref parameters foreach (var paramInfo in paramInfos) { if (paramInfo.ParameterInfo.IsOut) { paramInfo.NativeStorage.EmitLoadAddress(); gen.Emit(OpCodes.Stloc, paramInfo.NativeLocalPin); } } // push the interface pointer if (comMethodInfo.IsFirstNativeParameterInterfacePtr) { if (ptrFieldInfo == null) { throw new InvalidOperationException(); } gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldfld, ptrFieldInfo); } // push each parameters foreach (var paramInfo in paramInfos) { if (paramInfo.ParameterInfo.IsOut) { // out/inout parameter gen.Emit(OpCodes.Ldloc, paramInfo.NativeLocalPin); } else { paramInfo.NativeStorage.EmitLoad(); } } if (comMethodInfo.IsFirstParameterFunctionPtr) { // load the function ptr gen.Emit(OpCodes.Ldarg_0); } else { // get the interface pointer gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldfld, ptrFieldInfo); // get vtable gen.Emit(OpCodes.Ldind_I); // load the vtable element int vtableIndex = comMethodInfo.VTableOffset; if (vtableIndex > 0) { gen.Emit(OpCodes.Sizeof, typeof(IntPtr)); if (vtableIndex > 1) { gen.Emit(OpCodes.Ldc_I4, vtableIndex); gen.Emit(OpCodes.Mul); } gen.Emit(OpCodes.Conv_I); gen.Emit(OpCodes.Add); } gen.Emit(OpCodes.Ldind_I); } var nativeParamTypes = new List <Type>(); if (comMethodInfo.IsFirstNativeParameterInterfacePtr) { nativeParamTypes.Add(typeof(IntPtr)); } nativeParamTypes.AddRange(paramInfos.Select((p) => p.ParameterInfo.IsOut ? p.Marshaller.NativeParameterType.MakePointerType() : p.Marshaller.NativeParameterType)); // call! gen.EmitCalli(OpCodes.Calli, CallingConventions.Standard, comMethodInfo.NativeReturnType, nativeParamTypes.ToArray(), null); if (comMethodInfo.ReturnsHresult) { // handle COM error // ThrowExceptionForHR is extremely slow (60~ cycles) on some environments (namely, Mono) // check the HRESULT before passing it to ThrowExceptionForHR var resultLocal = gen.DeclareLocal(typeof(int)); gen.Emit(OpCodes.Dup); gen.Emit(OpCodes.Stloc, resultLocal); var noErrorLabel = gen.DefineLabel(); gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Bge, noErrorLabel); // clean-up foreach (var paramInfo in paramInfos) { // destruct "out" parameters and optionally return value parameter if (paramInfo.ToRuntimeGenerator != null) { paramInfo.ToRuntimeGenerator.EmitDestructNativeValue(paramInfo.NativeStorage); } // destruct native values for "in" parameters if (!paramInfo.ParameterInfo.IsOut && paramInfo.ToNativeGenerator != null) { paramInfo.ToNativeGenerator.EmitDestructNativeValue(paramInfo.NativeStorage); } } gen.Emit(OpCodes.Ldloc, resultLocal); gen.EmitCall(OpCodes.Call, throwExceptionForHRMethod, null); gen.MarkLabel(noErrorLabel); } if (comMethodInfo.ReturnValueMarshaller != null) { if (comMethodInfo.ReturnsHresult) { throw new InvalidOperationException(); } // marshal return value var nativeLocal = gen.DeclareLocal(comMethodInfo.NativeReturnType); gen.Emit(OpCodes.Stloc, nativeLocal); comMethodInfo.ReturnValueMarshaller.CreateToRuntimeGenerator(gen) .EmitToRuntime(new LocalStorage(gen, nativeLocal), new LocalStorage(gen, returnValueLocal), move: true); } // unpin by-ref parameters foreach (var paramInfo in paramInfos) { if (paramInfo.ParameterInfo.IsOut) { gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Conv_I); gen.Emit(OpCodes.Stloc, paramInfo.NativeLocalPin); } } foreach (var paramInfo in paramInfos) { // marshal "out" parameters and optionally return value parameter if (paramInfo.ToRuntimeGenerator != null) { paramInfo.ToRuntimeGenerator.EmitToRuntime(paramInfo.NativeStorage, paramInfo.Storage, move: true); } // destruct native values for "in" parameters if (!paramInfo.ParameterInfo.IsOut && paramInfo.ToNativeGenerator != null) { paramInfo.ToNativeGenerator.EmitDestructNativeValue(paramInfo.NativeStorage); } } // return if (methodInfo.ReturnType != typeof(void)) { gen.Emit(OpCodes.Ldloc, returnValueLocal); } gen.Emit(OpCodes.Ret); }