带有泛型委托的C#GetDelegateForFunctionPointer

时间:2014-11-02 12:41:15

标签: c#

我需要动态调用dll函数。

我使用标准的Windows API加载dll并获取proc地址。

在检索proc的IntPtr后,我尝试将其转换为委托:

Func<int> inv = (Func<int>)Marshal.GetDelegateForFunctionPointer(proc, typeof(Func<int>));

但它失败了因为typeof(Func)返回泛型类型。

有没有干净的方法来完成我试图做的事情,而不仅仅是宣布成员代表和 用它作为类型?

我的意思是我试图避免以下冗余:

//I need this member only for typeof operator
private delegate int RunDll();
RunDll inv = (RunDll)Marshal.GetDelegateForFunctionPointer(proc, typeof(RunDll));

3 个答案:

答案 0 :(得分:2)

如果委托类型是动态的并且您不知道参数,那会更糟。然后你可以使用.NET方法来生成它。

public static class DelegateCreator
{
    private static readonly Func<Type[],Type> MakeNewCustomDelegate = (Func<Type[],Type>)Delegate.CreateDelegate(typeof(Func<Type[],Type>), typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers").GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static));

    public static Type NewDelegateType(Type ret, params Type[] parameters)
    {
        Type[] args = new Type[parameters.Length];
        parameters.CopyTo(args, 0);
        args[args.Length-1] = ret;
        return MakeNewCustomDelegate(args);
    }
}

DelegateCreator.NewDelegateType(typeof(int)) //returns non-generic variant of Func<int>

答案 1 :(得分:1)

根据the documentation仿制药,此API并不支持。请注意,这不是一个很大的损失 - 毕竟,您需要只指定一次签名;唯一的缺点是你不能内联指定它 - 使用Func<int>并不要求间接,但是(由于演员)它在某种意义上实际上更加冗余。

顺便提一下,您可能希望查看普通的DllImport - 如果提前知道DLL和函数,则不需要执行此手动委托包装。

答案 2 :(得分:0)

首先:方法DelegateCreator.NewDelegateType的实现不正确!

public static Type NewDelegateType(Type ret, params Type[] parameters) {
  /*
  Type[] args = new Type[parameters.Length];   // Create "args" array of same length as "parameters" (must be length + 1)
  parameters.CopyTo(args, 0);                  // Copy all values of parameters to "args" array
  args[args.Length - 1] = ret;                 // Put "ret" value to last item of "args" array
  return MakeNewCustomDelegate(args);
  */
  var offset = parameters.Length;
  Array.Resize(ref parameters, offset + 1);
  parameters[offset] = ret;
  return MakeNewCustomDelegate(parameters);
}

如果我们无法调用类型代理,该助手的利润是多少?请参阅下面的测试示例。

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;

namespace GenericDelegates {
  static class DelegateCreator {
    public static readonly Func<Type[], Type> MakeNewCustomDelegate = (Func<Type[], Type>) Delegate.CreateDelegate(
      typeof(Func<Type[], Type>),
      typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers").GetMethod(
        "MakeNewCustomDelegate",
        BindingFlags.NonPublic | BindingFlags.Static
      )
    );
    public static Type NewDelegateType(Type ret, params Type[] parameters) {
      var offset = parameters.Length;
      Array.Resize(ref parameters, offset + 1);
      parameters[offset] = ret;
      return MakeNewCustomDelegate(parameters);
    }
  }
  static class Kernel {
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr GetModuleHandle(string name);
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr GetProcAddress(IntPtr module, string name);
  }
  static class InvokeHelper {
    public static Delegate MakeDelegate(this IntPtr address, Type ret, params Type[] parameters) {
      return Marshal.GetDelegateForFunctionPointer(address, DelegateCreator.NewDelegateType(ret, parameters));
    }
  }
  class Program {
    static void Main(string[] args) {
      var kernel = Kernel.GetModuleHandle("kernel32.dll");
      var address = Kernel.GetProcAddress(kernel, "GetModuleHandleW");
      Console.WriteLine(@"
Module base: 0x{0:X8}
Entry point: 0x{1:X8}
", (int) kernel, (int) address);
      var invoke = address.MakeDelegate(typeof(IntPtr), typeof(string));
      Console.WriteLine(@"
Untyped delegate: {0}
Cast to Invoke: {1}
", invoke, invoke as Func<string, IntPtr> == null ? "Error" : "Valid"); // invoke as Func<string, IntPtr> = NULL
      Console.ReadKey();
    }
  }
}