加载x86或x64汇编程序集

13

我有两个版本的System.Data.SQLite.DLL - 分别是x86和x64平台。其中x86版本保存在应用程序文件夹中,而x64版本保存在appFolder\x64文件夹中。 应用程序编译为AnyCPU。 如何根据Windows平台加载所需的SQLite版本?

7个回答

17
如果你使用的是来自http://system.data.sqlite.org的SQLite,那么System.Data.SQLite.DLL完全是托管的。存在一个底层的本地DLL,SQLite.Interop.DLL,需要根据进程(32位或64位)进行更改。
我在“.\Native\X64”中部署了64位的本地库,“.\Native\X86”中部署了32位的本地库。在运行时,使用P/Invoke SetDllDirectory设置DLL加载目录,指向进程所需的正确路径。 http://msdn.microsoft.com/en-us/library/ms686203(v=vs.85).aspx (请注意,我不熟悉来自http://sqlite.phxsoftware.com的旧版System.Data.SQLite.DLL的架构)
private static class NativeMethods
{
    [DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern bool SetDllDirectory(string pathName);
}

... 

    // Underlying SQLite libraries are native. 
    // Manually set the DLL load path depending on the process.
    var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Native");
    if(IntPtr.Size == 8) // or: if(Environment.Is64BitProcess) // .NET 4.0
    {
        path = Path.Combine(path, "X64");
    }
    else
    {
        // X32
        path = Path.Combine(path, "X86");
    }
    NativeMethods.SetDllDirectory(path);

3
我解决了这个问题。谢谢。我使用了 "IntPtr.Size == 8"。我使用了AppDomain.CurrentDomain.AssemblyResolve而不是SetDllDirectory。 - Rover

12

一些杀毒程序会阻止SetDllDirectory()函数的执行——我花了很长时间才意识到这一点。 我们正在使用

System.Reflection.Assembly myass = System.Reflection.Assembly.GetExecutingAssembly();
FileInfo fi = new FileInfo(myass.Location);
System.IntPtr moduleHandle = LoadLibraryEx(fi.Directory.FullName + "\\x64\\SQLite.Interop.DLL", IntPtr.Zero, 0);
加载x64 DLL需要使用显式路径。在这一点上加载它,.NET运行时将使用内存中的DLL,而不是在磁盘上搜索它。

在我的情况下没有起作用。尽管我已经通过调试器验证了本地库的正确加载,但SQLite仍然会抛出有关缺少本地DLL的异常。 - BartoszKP

5
1.0.80.0及以上版本已内置此功能支持。

如果开发和客户机器可能具有不同的处理器架构,则可能需要多个二进制包。 对于这种情况,使用本地库预加载功能是非常推荐的。它自1.0.80.0版本以来已可用,并默认启用。(来自下载页面)

但是,在我的插件中让它正常工作,我还必须在第一次引用SQLite之前添加以下内容:

// Make SQLite work... (loading dll from e.g. x64/SQLite.Interop.dll)
System.Environment.SetEnvironmentVariable("PreLoadSQLite_BaseDirectory", System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));

Sql.Data.SQLite...

参见此问题:新的混合SQLite程序集


3
我很惊讶这个方法可以工作。它应该先找到x86版本并失败。失败的程序集绑定不会通过AssemblyResolve产生另一个尝试。
显然,CLR实际上无法找到x86版本,否则在x64模式下也会失败。换句话说,当你解决了这个问题后,你将破坏64位代码。首先解决x86问题,使用Fuslogvw.exe查看为程序集探测哪些文件夹。
真正的解决方案应包括将x86程序集移动到单独的文件夹中,并相应地调整事件处理程序。您可以测试IntPtr.Size以查明是否在64位模式下运行(Size == 8)。还要确保生成完整的路径名,像现在一样使用相对路径可能会导致应用程序的工作目录未设置在你希望的位置时失败。Assembly.GetEntryAssembly().Location可以获取EXE的路径。

我删除了避免混淆的代码,只留下问题。 - Rover
嗯,我不认为这会改变我的答案。最后一段告诉你如何正确地做到这一点。 - Hans Passant
好的,我检查了平台,找到了所需的程序集。为了将程序集加载到AppDomain中,我该怎么做? - Rover
你的意思是想在非默认的AppDomain中加载它吗?最好为此新开一个问题。 - Hans Passant

1

@Rover:怎么回事?你确定已经安装了64位的.NET吗? - Paul Ruane
我使用的是没有这个变量的.NET 3.5。我认为我可以检查平台的版本,但它只能避免捕获一些异常,但需要的程序集未被加载。 - Rover
@Rover:啊哈,我现在明白了。你是说64位汇编在64位平台上无法加载,而不是你不知道如何检测本地平台? - Paul Ruane
问题是需要加载程序集 :) - Rover
@Rover:好的,不可能检查Environment.Is64BitProcess,然后根据结果加载32位库(Environment.Is64BitProcess = false)或64位库(Environment.Is64BitProcess = true)吗? - Paul Ruane
@paul-ruane:我该如何将程序集加载到AppDomain中? - Rover

0

你可不可以在你的解决方案中使用SQLite的源代码作为一个单独的项目,而不是预编译的程序集?使用AnyCPU,系统本身会处理所有事情,你不需要在代码中做任何操作...


这是我使用的其中一个dll,分为x86和x64版本。我并没有所有已使用dll的源代码。 - Rover
这并不能解决问题,因为开发机的架构并不总是与部署机相同。Any CPU 只适用于 .NET 程序集,而不适用于原生 DLL(SQLite 在内部使用),所以这并无助于解决问题。 - BigBoss

0
  • 在GAC中安装适当的DLL(例如,在64位平台上安装64位版本)
  • 在您的Web/App配置文件中使用程序集绑定(可能在机器配置文件中)
  • 在Web/App配置文件中完全限定任何部分程序集引用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接