【问题标题】:Using variable number of functions使用可变数量的函数
【发布时间】:2015-06-04 13:04:13
【问题描述】:

例如:在 app1 中,用户创建了 function_1() 和 function_2()。然后在 app2 中,用户想要调用 function_2()。我在谷歌上搜索,我唯一发现的就是写这段代码:

    class Program
{
    [DllImport("functions.dll")]
    public static extern void function_1();


    static void Main(string[] args)
    {
        function_1();
    }
}

【问题讨论】:

  • 所以这里的明显推断是这是一个非托管(非.NET DLL?)
  • 您可以在运行时选择性地加载 DLL 的唯一方法是使用 LoadLibrary,尽管我不确定这是否符合您的要求。在goo.gl/hm4NVz(必须发布缩短的链接)上有一篇很棒的文章,虽然有点过时了,但它详细介绍了这一点。

标签: c# dll unmanaged


【解决方案1】:

您必须使用一些 PInvoke 代码来加载 DLL (LoadLibrary) 并获取函数指针 (GetProcAddess) 和 Marshal.GetDelegateForFunctionPointer 以获得随后可以调用的委托。

【讨论】:

    【解决方案2】:

    您可以使用 dbghelp dll 中的 SymEnumerateSymbols64 来获取 DLL 中现有函数的列表。然后用户可以选择运行哪个函数。

    您可以在此处找到更详细的说明: C# get the list of unmanaged C dll exports

    【讨论】:

      【解决方案3】:

      Damien 所写内容的完整示例...请注意,它仅在方法具有所有相同签名时才有效(在此示例中为 void function_X())。此外,探索 dll 以发现导出了哪些方法是“困难的”,因此最好知道 dll 中应该包含哪些方法。

      [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
      public static extern IntPtr LoadLibrary(string dllToLoad);
      
      [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
      public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
      
      // Set the correct calling convention
      [UnmanagedFunctionPointer(CallingConvention.StdCall)]
      private delegate void DllMethodDelegate();
      
      IntPtr dll = LoadLibrary(@"PathToYourDll.DLL");
      
      if (dll == IntPtr.Zero)
      {
          throw new Exception();
      }
      
      string methodName = "function_1";
      
      IntPtr method = GetProcAddress(dll, methodName);
      
      if (method == IntPtr.Zero)
      {
          throw new Exception();
      }
      
      DllMethodDelegate method2 = (DllMethodDelegate)Marshal.GetDelegateForFunctionPointer(method, typeof(DllMethodDelegate));
      
      // Now you can do method2();
      

      请注意,您必须在DllMethodDelegate() 定义中设置正确的调用约定。通常dll方法应该是StdCall

      你写的方法的签名是:

      [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
      private delegate int DllMethodDelegate(char cmd, ref IntPtr out_address);
      

      请注意,“填充”out_address 非常复杂(头痛复杂)。

      {
          // I case:
          IntPtr ret = IntPtr.Zero;
          int result = method2('I', ref ret);
      }
      
      {
          // R case:
          IntPtr ptr = IntPtr.Zero;
      
          int result = method2('R', ref ptr);
      
          int value = Marshal.ReadInt32(ptr);
      }
      
      {
          // W case:
          int value = 100;
      
          GCHandle handle = default(GCHandle);
      
          try
          {
              int[] value2 = new int[] { value };
      
              handle = GCHandle.Alloc(value2, GCHandleType.Pinned);
              IntPtr ptr = handle.AddrOfPinnedObject();
      
              int result = method2('W', ref ptr);
          }
          finally
          {
              if (handle.IsAllocated)
              {
                  handle.Free();
              }
          }
      }
      

      有可能(但我不确定)对于第三个示例,您可以这样做

      object value2 = value;
      

      而不是

      int[] value2 = new int[] { value };
      

      boxing 和GCHandle 的交互并没有详细记录,但它似乎有效。 C# 规范 4.3 似乎还可以……但我不相信它,而且这种技术似乎在“.NET 和 COM:完整的互操作性指南”一书中有所描述,在“获取值类型的地址”一章中"(该章节可在 google 中搜索,使用我给出的确切章节标题。示例在 VB.NET 中,但读起来很清楚)

      【讨论】:

      • @ale93 它完全被 SO... 尝试将其粘贴到问题中。
      • @ale93 如果您有多个签名类型(在这种情况下,多个返回类型 == 多个签名),您将不得不通过多个委托类型手动处理它们......而void**,我不认为它可以在 C# 中轻松完成。
      • @ale93 增加了三种模式调用的例子。
      猜你喜欢
      • 2018-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      • 2012-03-03
      相关资源
      最近更新 更多