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); }