public Delegate GetFunction(Type DelegateType, bool LoadLibrary = false)
        {
            RemoteImportAttribute RemoteImport = (RemoteImportAttribute)DelegateType
                                                 .GetCustomAttributes(typeof(RemoteImportAttribute), false)
                                                 .FirstOrDefault();

            if (RemoteImport == null)
            {
                throw new ArgumentException(string.Format("Type `{0}` must have RemoteImportAttribute", DelegateType));
            }

            if (string.IsNullOrEmpty(RemoteImport.DllName))
            {
                throw new ArgumentException("RemoteImportAttribute.DllName must be set to call this method");
            }

            if (string.IsNullOrEmpty(RemoteImport.EntryPoint))
            {
                throw new ArgumentException("RemoteImportAttribute.EntryPoint must be set to call this method");
            }

            IntPtr FunctionPtr = this.GetProcAddress(RemoteImport.DllName, RemoteImport.EntryPoint, LoadLibrary);

            return(this.GetFunction(FunctionPtr, DelegateType));
        }
        public Delegate GetFunction(IntPtr Function, Type DelegateType)
        {
            if (!DelegateType.IsSubclassOf(typeof(Delegate)))
            {
                throw new ArgumentException("DelegateType must be typeof Delegate");
            }

            RemoteImportAttribute RemoteImport = (RemoteImportAttribute)DelegateType
                                                 .GetCustomAttributes(typeof(RemoteImportAttribute), false)
                                                 .FirstOrDefault();

            string            FunctionName;
            CallingConvention callingConvention;
            CharSet           charSet;

            if (RemoteImport == null)
            {
                FunctionName      = DelegateType.Name;
                callingConvention = CallingConvention.Winapi;
                charSet           = CharSet.Ansi;
            }
            else
            {
                FunctionName      = RemoteImport.EntryPoint ?? DelegateType.Name;
                callingConvention = (RemoteImport.CallingConvention == 0)
                                        ? CallingConvention.Winapi
                                        : RemoteImport.CallingConvention;
                charSet = (RemoteImport.CharSet == 0)
                                        ? CharSet.Ansi
                                        : RemoteImport.CharSet;
            }

            MethodInfo DelegateInvoke = DelegateType.GetMethod("Invoke");

            ParameterInfo[] Paramaters = DelegateInvoke.GetParameters();

            Type[] ParamaterTypes = Paramaters
                                    .Select(param => param.ParameterType.IsByRef
                                        ? param.ParameterType.GetElementType()
                                        : param.ParameterType)
                                    .ToArray();

            MarshalAsAttribute[] MarshalAsAttributes = Paramaters
                                                       .Select(param => (MarshalAsAttribute)param
                                                               .GetCustomAttributes(typeof(MarshalAsAttribute), false)
                                                               .FirstOrDefault())
                                                       .ToArray();

            MarshalAsAttribute ReturnMarshalAs = (MarshalAsAttribute)DelegateInvoke.ReturnParameter.GetCustomAttributes(typeof(MarshalAsAttribute), false).FirstOrDefault();

            if (ReturnMarshalAs == null)
            {
                ReturnMarshalAs = DefaultMarshalling(DelegateInvoke.ReturnType);
            }

            ParameterExpression[] ExpressionParamaters = Paramaters
                                                         .Select(param => Expression.Parameter(param.ParameterType, param.Name))
                                                         .ToArray();

            bool[] IsByRef = ExpressionParamaters
                             .Select(param => param.IsByRef)
                             .ToArray();

            bool HasByRef = IsByRef.FirstOrDefault(byref => byref);

            Expression ParamaterArray = Expression.NewArrayInit(
                typeof(object),
                ExpressionParamaters.Select(param => Expression.Convert(param, typeof(object))));

            MethodInfo CallFunction = this.GetType()
                                      .GetMethod("CallFunction", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public, null, new Type[]
            {
                typeof(IntPtr),
                typeof(CallingConvention),
                typeof(CharSet),
                typeof(object[]),
                typeof(Type[]),
                typeof(MarshalAsAttribute[]),
                typeof(bool[]),
                typeof(Type),
                typeof(MarshalAsAttribute)
            }, null);

            Expression Body;

            if (HasByRef)
            {
                ParameterExpression ParamsVar = Expression.Variable(typeof(object[]), "params");
                ParameterExpression RetVar    = Expression.Variable(typeof(object), "ret");

                List <Expression> BodyBlock = new List <Expression>(3 + ExpressionParamaters.Length);
                BodyBlock.Add(Expression.Assign(ParamsVar, ParamaterArray));
                BodyBlock.Add(
                    Expression.Assign(RetVar,
                                      Expression.Call(
                                          Expression.Constant(this),
                                          CallFunction,
                                          /* Params */
                                          Expression.Constant(Function),
                                          Expression.Constant(callingConvention),
                                          Expression.Constant(charSet),
                                          ParamsVar,
                                          Expression.Constant(ParamaterTypes),
                                          Expression.Constant(MarshalAsAttributes),
                                          Expression.Constant(IsByRef),
                                          Expression.Constant(DelegateInvoke.ReturnType),
                                          Expression.Constant(ReturnMarshalAs, typeof(MarshalAsAttribute)))));

                for (int i = 0; i < ExpressionParamaters.Length; i++)
                {
                    if (ExpressionParamaters[i].IsByRef)
                    {
                        BodyBlock.Add(Expression.Assign(
                                          ExpressionParamaters[i],
                                          Expression.Convert(
                                              Expression.ArrayAccess(ParamsVar, Expression.Constant(i)),
                                              ExpressionParamaters[i].Type)
                                          ));
                    }
                }

                BodyBlock.Add(Expression.Convert(RetVar, DelegateInvoke.ReturnType));
                Body = Expression.Block(new ParameterExpression[] { ParamsVar, RetVar }, BodyBlock);
            }
            else
            {
                Body = Expression.Convert(
                    Expression.Call(
                        Expression.Constant(this),
                        CallFunction,
                        /* Params */
                        Expression.Constant(Function),
                        Expression.Constant(callingConvention),
                        Expression.Constant(charSet),
                        ParamaterArray,
                        Expression.Constant(ParamaterTypes),
                        Expression.Constant(MarshalAsAttributes),
                        Expression.Constant(IsByRef),
                        Expression.Constant(DelegateInvoke.ReturnType),
                        Expression.Constant(ReturnMarshalAs, typeof(MarshalAsAttribute))),
                    DelegateInvoke.ReturnType);
            }

            LambdaExpression Lambda = Expression.Lambda(
                DelegateType,
                Body,
                FunctionName,
                ExpressionParamaters);

            return(Lambda.Compile());
        }