SQLite

时间:2015-06-14 19:57:38

标签: c# .net sqlite dll

我们的一些用户遇到了在运行时加载的sqlite.interop.dll版本的问题,这真是令人头疼。

背景: 为AnyCPU构建的WPF应用程序,使用SQlite .NET和sqlite.interop.dll版本1.0.89进行部署。我们部署x86和x64 dll,并使用SQLite附带的延迟加载。 直到最近,当我们开始从通常最近购买新戴尔机器的用户那里获得一些支持问题时,情况一直很好。似乎有一个旧版本的sqlite.interop.dll(v.1.0.80),以某种方式,正在加载,而不是我们发运的那个。我们得到的错误是缺少入口点,' sqlite3_changes_interop'。

我们尝试了什么:

  1. 更改设置,只需将相应的dll(x86 / 64)复制到安装期间与可执行文件相同的目录(即没有单独的x86 / x64文件夹)。这意味着我们不再使用延迟加载,因为可执行目录中有正确的dll(尽管我们没有明确禁用sqlite.net中的延迟加载机制)。这并没有解决问题..

  2. 首次加载应用程序时显式加载sqlite.interop.dll。同样,这似乎无法解决问题。

  3. 近年来dll加载位置的排序似乎有所改变,我可能没有很好的处理。我总是假设可执行文件目录中的一个dll会得到第一个首选项,并且一个已经显式加载的dll会阻止在应用程序生命周期内重新加载相同的dll,所以对于我的生活,我无法理解这里发生了什么

    任何人都可以了解这里可能发生的事情吗?由于我无法在本地重现问题,因此问题进一步复杂化,例如将错误版本的dll放入我的系统路径等。这让我觉得GAC可能会发挥作用?

    真的坚持这个,所以任何帮助都会很棒。

    另外 - 作为最后的手段 - 我可能会考虑恢复到相同的1.0.80版本,这样我们就不会遇到这个问题。有谁知道我们可以在哪里找到旧版本的sqlite.net和sqlite.interop.dll?

    修改 - 其他一些信息:

    冲突是由随Dell Backup and Recovery一起安装的sqlite.interop.dll版本1.0.80的副本引起的。这安装在所有新戴尔计算机上,在这种计算机上安装我们软件的用户都遇到此问题。此Dell软件还使用System.Data.SQLite.dll。

    正确版本的sqlite.interop.dll与我们的可执行文件位于同一目录中,我对dll加载的所有了解都表明应该优先加载它。

    虽然我们尚未能在本地重现此问题,但似乎路径上的错误版本的interop.dll 不是。此外,Dell备份实用程序在启动时自动运行。有没有人知道任何可能的机制,这可能会挂钩到DLL加载请求和提供错误的文件?

    目前的思路是我们可以构建自己的System.Data.SQLite.dll并将互操作加载代码更改为特定命名的版本(例如sqlite.interop.1.0.89.dll)。不是一个很好的解决方案,但是..

3 个答案:

答案 0 :(得分:12)

我们的应用程序存在同样的问题。正如您所提到的,问题是Dell Backup and Recovery安装了一个shell扩展,它使用了几个流行dll的旧版本。他们使用任何启动文件对话框并使用这些库的应用程序,因为shell扩展将其dll加载到AppDomain中。到目前为止,我们唯一的解决方案是告诉用户卸载Dell Backup and Recovery。

如果你提到了force your app to load the correct library dymanoid,那么你的app会在显示文件对话框时崩溃(因为shell扩展会崩溃)。如果你不这样做,那么你的应用程序在尝试从其数据库中读取时会崩溃。

有趣的是,戴尔备份和恢复是一个屡犯;它也以同样的方式breaks QT5。来自QT人员的recommended solution是使用-qtnamespace [name]选项以不同的名称编译您的QT库。我们或许可以使用system.data.sqlite来安装类似的东西,但是我们必须编译自己的版本。

Microsoft已aware of the problem,但拒绝解决此问题。

我希望戴尔家伙实施他们的shell扩展like this

Portroit ProSONARAutoDesk解决此问题的方法也是卸载Dell备份和恢复。

问题的典型堆栈跟踪在我们的应用程序中看到了这一点:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. 
at System.Data.SQLite.UnsafeNativeMethods.sqlite3_open_interop(Byte[] utf8Filename, Int32 flags, IntPtr& db) 
at System.Data.SQLite.SQLite3.Open(String strFilename, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, Int32 maxPoolSize, Boolean usePool) 
at System.Data.SQLite.SQLiteConnection.Open() 
at STCommonShellIntegration.DataShellManagement.CreateNewConnection(SQLiteConnection& newConnection) 
at STCommonShellIntegration.DataShellManagement.InitConfiguration(Dictionary`2 targetSettings) 
at DBROverlayIcon.DBRBackupOverlayIcon.initComponent()

因此,在回答Track的评论时,如果您想检测这个特定问题并给予用户一些特别的通知,您可以这样做:

AppDomain.CurrentDomain.UnhandledException += UEHandler;
//...
static void UEHandler(object sender, UnhandledExceptionEventArgs e){
  var ex = e.ExceptionObject as Exception;
  if( ex.ToString().Contains( "DBROverlayIcon" ){
    //show some dialog here telling users to uninstall DBaR
  }
}

答案 1 :(得分:3)

SQLite.Interop.dll以棘手的方式加载 通过使用任何反射器,您可以自己检查 System.Data.SQLite.dll 中的UnsafeNativeMethods.Initialize()方法。
一些注释要证明,通过反射(1.0.89版本)可以获得一些有趣的东西:

  • 如果SQLite.Interop.dll放在基本目录中 - 它将被加载
  • PreLoadSQLite_BaseDirectory和PreLoadSQLite_UseAssemblyDirectory环境变量会影响加载过程
  • 可以在预定义的子文件夹(x86,x64,Win32,Itanium,W​​inCE)中搜索SQLite.Interop.dll
  • 调用Trace.WriteLine以通知所选路径(并非总是)

Source code is also available.

答案 2 :(得分:1)

我们正在处理这个问题,我们找到的解决方案是使用System.Data.SQlite网站中的捆绑软件包而不是nuget中的软件包: https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki

捆绑的dll同时合并了托管和非托管程序集,因此无需动态加载正确的Sqlite.Interop.dll,因此您不会在appdomain中出现冲突版本的问题。

使用捆绑程序集时,您需要在应用程序的安装程序中包含您自己的逻辑,以决定要复制哪个DLL(x86或x64)。

自从使用捆绑的程序集以来,我们在版本冲突方面没有任何问题。