代码之家  ›  专栏  ›  技术社区  ›  Cheetah

独立于CPU架构的P/Invoke:DllName或path可以是“动态”的吗?

  •  10
  • Cheetah  · 技术社区  · 15 年前

    有没有办法让P/Invoke(DllImport)签名引用的特定DLL依赖于CPU架构?

    我正在开发一个应用程序,它从第三方供应商的本机dll加载大量方法签名,在本例中是用户空间接口dll到一个硬件。该供应商现在已经开始提供x86和x64版本的DLL,我认为我的应用程序将从64位进程运行中受益。除了这一个DLL,一切都是.NET代码,所以构建为“任何CPU”都可以。

    本机DLL中的所有方法签名在64位上都是相同的,但是DLL的名称不同(Foo.dll与Foo_x64.dll进行比较)。有没有办法通过P/Invoke签名或应用程序配置条目我可以让它根据运行的CPU架构来选择加载哪个DLL?

    注意:因为这个用户空间DLL的版本必须与硬件安装的内核驱动程序相匹配,所以这个DLL没有与我们的应用程序捆绑在一起,而是依赖于供应商的安装程序将其放在%PATH%目录中。

    3 回复  |  直到 15 年前
        1
  •  12
  •   Gregory Pakosz    13 年前

    如果不是不同的DLL名称,而是在不同的文件夹中使用相同的名称,是否会打开其他选项

    也许这对你有用:

    public static class NativeMethods
    {
      // here we just use "Foo" and at runtime we load "Foo.dll" dynamically
      // from any path on disk depending on the logic you want to implement
      [DllImport("Foo", EntryPoint = "bar")]
      private void bar();
    
      [DllImport("kernel32")]
      private unsafe static extern void* LoadLibrary(string dllname);
    
      [DllImport("kernel32")]
      private unsafe static extern void FreeLibrary(void* handle);
    
      private sealed unsafe class LibraryUnloader
      {
        internal LibraryUnloader(void* handle)
        {
          this.handle = handle;
        }
    
        ~LibraryUnloader()
        {
          if (handle != null)
            FreeLibrary(handle);
        }
    
        private void* handle;
    
      } // LibraryUnloader
    
      private static readonly LibraryUnloader unloader;
    
      static NativeMethods()
      {
        string path;
    
        if (IntPtr.Size == 4)
          path = "path/to/the/32/bit/Foo.dll";
        else
          path = "path/to/the/64/bit/Foo.dll";
    
        unsafe
        {
          void* handle = LoadLibrary(path);
    
          if (handle == null)
            throw new DllNotFoundException("unable to find the native Foo library: " + path);
    
          unloader = new LibraryUnloader(handle);
        }
      }
    }
    

    它包括在P/Invoke本身尝试加载本机库之前显式地加载它的完整路径。

        2
  •  4
  •   JaredPar    15 年前

    不可能只有一个PInvoke签名就可以得到你想要的行为。属性已烧录到元数据中,并且必须具有常量值。不过,你可以做的一个方法是有多个方法。

    public static class NativeMethods32 {
      [DllImport("Foo.dll")]
      public static extern int SomeMethod();
    }
    
    public static class NativeMethods64 {
      [DllImport("Foo_x864.dll")]
      public static extern int SomeMethod();
    }
    
    public static class NativeMethods {
      public static bool Is32Bit { return 4 == IntPtr.Size; }
      public static SomeMethod() {
        return Is32Bit ? 
          NativeMethods32.SomeMethod(); 
          NativeMethods64.SomeMethod();
      }
    }
    

    然而,这不是首选方法。一种更简单的方法是使DLL在多个平台上具有相同的名称,并创建一个与平台无关的PInvoke签名。这是大多数/所有windows库所采用的方法。

        3
  •  1
  •   AndreyAkinshin    10 年前

    我为目标开发了一个特殊的库: InteropDotNet . 它引入了新的 RuntimeDllImport 动态库路径解析属性(动态)。默认情况下,您可以编写

    [RuntimeDllImport("NativeLib", 
       CallingConvention = CallingConvention.Cdecl, EntryPoint = "sum")]
    int Sum(int a, int b);
    

    而库的解决取决于环境。例如,Win/Linux、x86/x64的路径:

    x86/NativeLib.dll
    x86/libNativeLib.so
    x64/NativeLib.dll
    x64/libNativeLib.so