예제 #1
0
        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);
        }
예제 #2
0
        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);
        }