4

When using a 64-bit program System.Data.SQLite is throwing the following EntryPointNotFoundException:

Unable to find an entry point named 'sqlite3_changes_interop' in DLL 'SQLite.Interop.dll'.

Strangely it only occurs if Foreign Keys=True is specified in the connection string and more importantly only after I display a FolderBrowserDialog. I was browsing for a folder to display databases to load.

The following code displays the problem:

public Form1()
{
    InitializeComponent();

    // Exception below if this is displayed
    using (var diag = new FolderBrowserDialog())
    {
        diag.ShowDialog(this);
    }

    var conn = new SQLiteConnection("data source=':memory:'");
    conn.Open(); // Works fine
    conn.Close();

    // No exception below if displayed here instead
    //using (var diag = new FolderBrowserDialog())
    //{
    //    diag.ShowDialog(this);
    //}

    conn = new SQLiteConnection("data source=':memory:';foreign keys=True");
    conn.Open(); // EntryPointNotFoundException thrown here
    conn.Close();
}

Opening the connection with foreign keys=True works fine if the dialog isn't shown or if it is shown after any other SQLite connection is opened. The code also works fine if the program runs as a 32-bit process. The behaviour is also the same if I use either the single mixed mode x64 SQLite assembly or the MSIL + x64 interop assembly. I'm using v1.0.92.0 so this is not the problem.

So the question is why would showing the FolderBrowserDialog affect the System.Data.SQLite assembly finding the entry point in its own interop library and why would it only occur in a 64-bit process?

As a work around I can load an in-memory database before doing anything else in the program but I don't like this "solution" as I'm using EF6 and would like to be able to use different providers configured via a config file or even at runtime via user input. Hence all the sqlite specific code is in another assembly.

jww
  • 97,681
  • 90
  • 411
  • 885
kjbartel
  • 10,381
  • 7
  • 45
  • 66
  • Loading the `FolderBrowserDialog` on a 64bit system will initialize 64bit marshalling, creating problems for 32bit native code. Use a 64bit SQlite build for the native DLL in this scenario. – Eugen Rieck May 09 '14 at 01:14
  • @EugenRieck The SQLite.Interop.dll and the mixed mode System.Data.SQLite.dll are 64-bit builds. Using either the spilt (MSIL + interop) or single mixed mode assembly makes no difference to the behaviour. – kjbartel May 09 '14 at 01:18
  • I am not talking of the .NET assemblies, but of the native DLL, that these call. – Eugen Rieck May 09 '14 at 01:20
  • @EugenRieck The native dlls are included in the mixed mode assemblies and are 64-bit. – kjbartel May 09 '14 at 01:28
  • Are you sure about that? How could it run as a 32Bit process then? If you mean managed-and-32-and-64-bit-native mixed mode, I have seen this fail all the time. – Eugen Rieck May 09 '14 at 01:31
  • @EugenRieck Please read the section "Using Native Library Pre-Loading" near the top of the [System.Data.SQLite](https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki) download page. – kjbartel May 09 '14 at 01:42
  • Exactly what I ment: I have seen this fail more often than not – Eugen Rieck May 09 '14 at 01:46
  • @EugenRieck What is? You're also ignoring that I also used the x64 only build of System.Data.SQLite and it had the same behaviour. This build [here](https://system.data.sqlite.org/downloads/1.0.92.0/sqlite-netFx451-binary-bundle-x64-2013-1.0.92.0.zip) which only needs the single System.Data.SQLite.dll. – kjbartel May 09 '14 at 01:49
  • To elaborate: I have seen many instances, where the automatic preloading would fail. Often this can be traced back to a spurious old GAC installation somewhere, but sometimes the mystery stays unsolved. – Eugen Rieck May 09 '14 at 01:53
  • Last comment was in reply to the first version saying just "What is?" – Eugen Rieck May 09 '14 at 01:53
  • @EugenRieck Yep I made sure that it was not installed in the GAC and even tried loading the dlls dynamically by myself to make sure the correct ones were being loaded. Same problem. – kjbartel May 09 '14 at 02:00
  • As I said: I have seen this fail too often. I reverted to packaging for x64 and x86 separately, with the native DLLs separate from the assemblies. – Eugen Rieck May 09 '14 at 02:01

2 Answers2

6

An older version of System.Data.SQLite is being loaded when displaying the FolderBrowserDialog. If there are any shell / explorer extensions installed on the computer then displaying any of the common dialogs which includes Explorer will cause assemblies from those extensions to be loaded into the application's AppDomain.

In the case of System.Data.SQLite a native library is loaded (SQLite.Interop.dll) resulting in all loaded versions of the assembly to use that version of the native library. Loading the new version of the assembly first causes the new version of the native library to be loaded. This still results in multiple versions of the assembly being loaded in the AppDomain though and it means that shell extensions will be using a different version than they expect.

I tried opening the the FolderBrowserDialog in a different AppDomain but it still results in assemblies being loaded into the application's normal AppDomain. I've opened a bug on Microsoft connect regarding this but I'm not too hopeful that it will be fixed.

As a workaround I've added this to my app.config:

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="System.Data.SQLite"
                        version="1.0.92.0"
                        publicKeyToken="DB937BC2D44FF139"
                        language="neutral"
                        processorArchitecture="msil" />
      <bindingRedirect oldVersion="0.0.0.0-1.0.91.65535" newVersion="1.0.92.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

This results in only the single version of System.Data.SQLite being loaded. It does still mean that shell extensions will be using the wrong version and hence could potentially throw exceptions.

kjbartel
  • 10,381
  • 7
  • 45
  • 66
  • Just want to add that `version` is not a valid attribute anymore. For `bindingRedirect` you can just use ``. The `publicKeyToken` of the assembly can be obtained from the `csproj` file where your assembly is referenced. – emilaz Mar 21 '23 at 10:15
1

I had the same issue when I used GMAP.NET, which appeared to be opening an SQLite connection using an older version. Then, when I attempted to open a connection with the newer version, the error with SQLite.Interop.dll occurred.

By opening a dummy connection with the newer version before instantiating the GMAP.NET object using the older connection, the error went away. The connection doesn't have to do anything, it just has to be opened first.

using (SQLiteConnection con = new SQLiteConnection("Data Source=" + dat + ";Version=3;"))
{
    con.Open();
}
Alex Smith
  • 1,096
  • 8
  • 12