Pergunta

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.

Foi útil?

Solução

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.

Outras dicas

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();
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top