2012-06-01 120 views
52

这里是情况,我在我的dot.net应用程序中使用基于C的dll。有2个DLL,一个是32位的叫MyDll32.dll,另一个是64位的叫MyDll64.dll的版本。在C#中使用32位或64位DLL DllImport

存在一个包含DLL文件名的静态变量:string DLL_FILE_NAME。

,它是在使用方式如下:

[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")] 
private static extern int is_Func1(int var1, int var2); 

简单为止。

您可以想象,该软件是在“Any CPU”打开的情况下编译的。

我还有以下代码来确定系统是否应该使用64位文件或32位文件。

#if WIN64 
     public const string DLL_FILE_NAME = "MyDll64.dll"; 
#else 
     public const string DLL_FILE_NAME = "MyDll32.dll";   
#endif 

现在,你应该看到.. DLL_FILE_NAME在编译时间定义,而不是在这样正确的dll是根据执行上下文不加载执行时间的问题。

什么是解决这个问题的正确方法?我不想要两个执行文件(一个用于32位,另一个用于64位)?如何在DllImport语句中使用之前设置DLL_FILE_NAME

+1

64位和32位dll的区别是什么?有没有什么32位不能在64?如果是这样,我只会使用32。 –

+0

在64位操作系统上,决定执行纯64位还是WOW64(32位仿真)时的代码。如果程序以32位模式执行,它应该使用基于C的dll,这些dll相应地编译为32位和64位。 – Gilad

+1

如果你真的想这样做,你需要完全绕过'DllImport'属性,并手动使用'LoadLibrary','GetProcAddess'和'FreeLibrary'函数来加载DLL。该技术在[这里]讨论(http://stackoverflow.com/questions/2818011/set-dllimport-attribute-dynamically)。尽管这是一个相当的工作,而且很容易出错。让P/Invoke机制为你做这件事更容易。正如其他人所指出的,如果你可以一直回到32位DLL作为最低公分母,那么可能不值得。 –

回答

47

我发现最简单的方法是使用不同的名称导入两个方法,并调用正确的方法。该DLL将不会被加载,直到呼叫被制成所以它的精细:

[DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] 
private static extern int Func1_32(int var1, int var2); 

[DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] 
private static extern int Func1_64(int var1, int var2); 

public static int Func1(int var1, int var2) { 
    return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2); 
} 

当然,如果你有很多的进口,这可以成为相当繁琐手工维护。

+25

为了获得最大的可读性,我建议使用['Environment.Is64BitProcess'属性](http://msdn.microsoft.com/en-us/library/system.environment.is64bitprocess)而不是检查IntPtr的大小'类型。当然,这个属性是在.NET 4.0中引入的,所以如果你定位的是旧版本,它将不可用。 –

+0

这正是我想要做的。尽管编码有一些开销,但它起作用并且完成了这项工作。 – Gilad

7

有一个静态变量持有的DLL文件名

它不是一个静态变量。在编译时这是一个常量。您不能在运行时更改编译时间常量。

什么是解决此问题的正确方法?

老实说,我会建议只针对x86和忘记所有64位版本,并让您的应用程序运行在WOW64上,除非您的应用程序有一个迫切需要以x64运行。

如果有需要的X64,你可以:

  • 更改DLL文件具有相同的名称,如MyDll.dll,并在安装/部署时间,到位正确的。 (如果操作系统是x64,请部署64位版本的DLL,否则为x86版本)。

  • 完全有两个单独的版本,一个用于x86,一个用于x64。

+1

同意,这绝对是解决问题的最简单方法。绝大多数应用程序,特别是业务线应用程序,从64位原生中获益不大。即使可能有一些边际性能改进,64位的增加的开销往往会取消这些。关于使用相同名称的DLL并根据系统架构部署正确版本的其他建议正是Microsoft对系统DLL所做的。这似乎也是一个很好的选择。 –

+0

+1在安装时添加一个dll! –

2

你所描述的被称为“并排装配”(同一装配的两个版本,一个32和另一个64位)......我想你会发现这些有用的:

Here你可以找到正是您的方案(.NET DLL环绕下的演练++/CLI DLL引用本地DLL)。

建议:

就打造为x86和用它做......或有2个建立(一个x86和一个64位)......作为上述技术相当复杂...

+1

我不想要两个应用程序。这是90年代。 – Gilad

+0

@Gilad然后按照提供的链接,他们显示了一些可能更像你想要的选项(注意:它变得非常复杂)... – Yahia

+0

@Gilad顺便说一句,你可以建立2(x86和x64),当安装程序运行它可以确定要安装哪一个 - 用户只能得到一个应用程序... ... – Yahia

0

我已经使用vcsjones meantioned的方法之一:

“更改DLL文件具有相同的名称,如将myDll.dll,并在安装/部署的时间,把正确的到位“。

这种方法需要维护两个构建平台,虽然看到这个链接查看更多细节:https://stackoverflow.com/a/6446638/38368

39

这里是另一种替代方案,要求两个DLL有同名,并放置在不同的文件夹中。例如:

  • win32/MyDll.dll
  • win64/MyDll.dll

诀窍是CLR确实它之前手动LoadLibrary加载DLL。然后它会看到一个MyDll.dll已经加载并使用它。

这可以在父类的静态构造函数中轻松完成。

static class MyDll 
{ 
    static MyDll() 
    {    
     var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath; 
     var myFolder = Path.GetDirectoryName(myPath); 

     var is64 = IntPtr.Size == 8; 
     var subfolder = is64 ? "\\win64\\" : "\\win32\\"; 

     LoadLibrary(myFolder + subfolder + "MyDll.dll"); 
    } 

    [DllImport("kernel32.dll")] 
    private static extern IntPtr LoadLibrary(string dllToLoad); 

    [DllImport("MyDll.dll")] 
    public static extern int MyFunction(int var1, int var2); 
} 

编辑2017年2月1日:使用Assembly.CodeBase使其作品即使Shadow Copying启用。

+1

这是最优雅的答案,最适合这个问题。我成功地将它用于FreeImage项目.NET Wrapper!真棒的想法。我已经使用了'Environment.Is64BitProcess',并且解决了像Kisdeds这样的程序集的路径。非常感谢你! – JanW

+0

您也可以将32位和64位DLL作为资源嵌入到.NET DLL中,如果要将所有内容打包,请提取正确的一个/调用LoadLibrary。 – Matt

14

在这种情况下,我应该做这样的(使2个文件夹,x64和x86 +把相应的DLL,具有相同的名称,在这两个文件夹):

using System; 
using System.Runtime.InteropServices; 
using System.Reflection; 
using System.IO; 

class Program { 
    static void Main(string[] args) { 
     var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); 
     path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86"); 
     bool ok = SetDllDirectory(path); 
     if (!ok) throw new System.ComponentModel.Win32Exception(); 
    } 
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool SetDllDirectory(string path); 
} 
1

一种替代方法可以是

public static class Sample 
{ 
    public Sample() 
    { 

     string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\"; 
     string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll"; 
     if (!File.Exists(ResolvedDomainTimeFileName)) 
     { 
      if (Environment.Is64BitProcess) 
      { 
       if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll")) 
        File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName); 
      } 
      else 
      { 
       if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll")) 
        File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName); 
      } 
     } 
    } 

    [DllImport("ABCLib__Resolved.dll")] 
    private static extern bool SomeFunctionName(ref int FT); 
} 
+0

它应该复制DLL让应用程序花费更多时间。 – lindexi