예제 #1
0
        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()
            });
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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());
        }
예제 #5
0
 public ComMethodParameterInfo(ComMethodInfo methodInfo, ParameterInfo paramInfo)
 {
     this.ComMethodInfo = methodInfo;
     this.ParameterInfo = paramInfo;
 }
예제 #6
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);
        }
예제 #7
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);
        }