3

Devart.SQLite.DLLで使用するために、実行時に適切なx86/x64バージョンのSQLite3.DLLを動的にロードしようとしています。事前に適切なバージョンのDLLをアプリケーションルートにインストールすることを制御できないため、アプリケーションルートの/x86または/x64サブディレクトリから正しいバージョンを取得する必要があります。

これを達成する方法について何かアイデアはありますか?確かに、私はここで完全に迷子になっています。これまでの私のコードは次のとおりです。

Public Sub New()
    LoadAssembly("sqlite3.dll", True)
End Sub

Private Function GetAssemblyPath(ByVal assembly As String, ByVal version As String) As String
    Return Path.GetDirectoryName(Reflection.Assembly.GetExecutingAssembly().Location) & "\" & version & "\" & assembly
End Function ' GetAssemblyName

<Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint:="LoadLibraryW")> _
Public Shared Function LoadLibraryW(<Runtime.InteropServices.InAttribute()> <Runtime.InteropServices.MarshalAsAttribute(Runtime.InteropServices.UnmanagedType.LPWStr)> lpLibFileName As String) As IntPtr
End Function

Private Sub LoadAssembly(ByVal myAssembly As String, Optional ByVal doLoadLibrary As Boolean = False)
    Dim an As AssemblyName
    Dim filename As String
    Dim version As String

    If UIntPtr.Size = 8 Then version = "x64" Else version = "x86"
    filename = GetAssemblyPath(myAssembly, version)

    Try
        If doLoadLibrary Then
            HostLog.WriteEntry(filename, EventLogEntryType.Information)
            Dim ptr As IntPtr = LoadLibraryW(filename)
            HostLog.WriteEntry(ptr.ToString(), EventLogEntryType.Information)
        Else
            an = AssemblyName.GetAssemblyName(filename)
            AppDomain.CurrentDomain.Load(an)
        End If
    Catch ex As Exception
        HostLog.WriteEntry(ex.Message, EventLogEntryType.Error)
    End Try
End Sub ' LoadAssembly

編集 コメントで述べたように、sqlite3.dllをロードしようとしたときに受け取った実際のエラーを特定できませんでした。結局のところ、App.Configに次のものがありませんでした。

<system.data>
  <DbProviderFactories>
    <remove invariant="Devart.Data.SQLite" />
    <add name="dotConnect for SQLite" 
       invariant="Devart.Data.SQLite" 
       description="Devart dotConnect for SQLite" 
       type="Devart.Data.SQLite.SQLiteProviderFactory, Devart.Data.SQLite, Version=4.2.122.0, Culture=neutral, PublicKeyToken=09af7300eec23701" />
  </DbProviderFactories>
</system.data>

これをApp.Configに追加すると、以前のコードサンプルは期待どおりに機能しました。みんなの助けに感謝します。

4

3 に答える 3

1

2つの実装でインターフェースを作成できます。x86実装とx64実装。人は言うことができ[DllImport("x86version.dll")]Bob(string s);、人は言うことができる[DllImport("x64version.dll")]Bob(string s);

例:

public interface ISQLite
{
    public void Foo();
}

public class SQLite32 : ISQLite
{
   [DllImport("x86/SQLite3.dll")]
    private void foo();
   public void Foo()
   {
       foo();
   }
}

public class SQLite64 : ISQLite
{
   [DllImport("x64/SQLite3.dll")]
    private void foo();
   public void Foo()
   {
      foo();
   }
}

public static class SQLiteLoader
{
   public static ISQLite GetSQLite()
   {
       if(System.Environment.Is64BitOperatingSystem)
          return new SQLite64();
       else
          return new SQLite32();
   }
}
于 2012-12-01T08:32:43.167 に答える
0

実は、元のコードサンプルは機能していましたが、プログラムのApp.ConfigにSQLite用のDBProviderFactoryを正しく登録できませんでした。これで、実行時に適切なバージョンをロードして、アプリケーションルートからそれぞれの/x86および/x64ディレクトリにx86およびx64sqlite3.dllをバンドルできます。完全なソリューションをお探しの方は、以下を参照してください(コードの改善に感謝します。コードは組み込まれています)。

App.Config

<system.data>
  <DbProviderFactories>
    <remove invariant="Devart.Data.SQLite" />
    <add name="dotConnect for SQLite" 
       invariant="Devart.Data.SQLite" 
       description="Devart dotConnect for SQLite" 
       type="Devart.Data.SQLite.SQLiteProviderFactory, Devart.Data.SQLite, Version=4.2.122.0, Culture=neutral, PublicKeyToken=09af7300eec23701" />
  </DbProviderFactories>
</system.data>

AssemblyLoader.vb

Imports System.IO
Imports System.Reflection
Imports System.Security

''' <summary>
''' Handles dynamically loading managed and unmanaged assemblies.
''' </summary>
Public Class AssemblyLoader

  ''' <summary>
  ''' Loads the appropriate x86/x64 version of an assembly based on its filename.
  ''' </summary>
  ''' <param name="myAssembly">The filename of the assembly.</param>
  ''' <param name="isManaged">True if the assembly is managed, otherwise False.</param>
  ''' <exception cref="ArgumentException">If myAssembly is invalid, such as an assembly with an invalid culture.</exception>
  ''' <exception cref="SecurityException">The caller does not have path discovery permission.</exception>
  ''' <exception cref="BadImageFormatException">Thrown if myAssembly is not a valid assembly. -or-Version 2.0 or later of the common language runtime is currently loaded and assemblyRef was compiled with a later version.</exception>
  ''' <exception cref="FileLoadException">An assembly or module was loaded twice with two different evidences.</exception>
  ''' <exception cref="AppDomainUnloadedException">The operation is attempted on an unloaded application domain.</exception>
  Public Shared Sub LoadByVersion(ByVal myAssembly As String, Optional ByVal isManaged As Boolean = True)
    Dim an As AssemblyName
    Dim filename As String
    Dim version As String
    If UIntPtr.Size = 8 Then version = "x64" Else version = "x86"
    filename = GetAssemblyPath(myAssembly, version)

    Try
        If Not File.Exists(filename) Then Exit Sub
        If isManaged Then
            an = AssemblyName.GetAssemblyName(filename)
            If Not IsNothing(an) Then AppDomain.CurrentDomain.Load(an)
        Else
            LoadLibraryW(filename)
        End If
    Catch ex As ArgumentException
        Throw
    Catch ex As SecurityException
        Throw
    Catch ex As BadImageFormatException
        Throw
    Catch ex As FileLoadException
        Throw
    Catch ex As AppDomainUnloadedException
        Throw
    End Try
  End Sub ' LoadAssembly

  ''' <summary>
  ''' Gets the absolute path of the dependant assembly.
  ''' </summary>
  ''' <param name="assembly">The filename (without path) of the dependent assembly.</param>
  ''' <param name="version">The subfolder containing the version of the assembly needed (e.g. "x86", "x64").</param>
  ''' <returns>The absolute path of the specific version of the assembly.</returns>
  Private Shared Function GetAssemblyPath(ByVal assembly As String, ByVal version As String) As String
    Return Path.Combine(Path.GetDirectoryName(Reflection.Assembly.GetExecutingAssembly().Location), version, assembly)
  End Function ' GetAssemblyName

  ''' Return Type: HMODULE->HINSTANCE->HINSTANCE__*
  '''lpLibFileName: LPCWSTR->WCHAR*
  <Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint:="LoadLibraryW")> _
  Private Shared Function LoadLibraryW(<Runtime.InteropServices.InAttribute()> <Runtime.InteropServices.MarshalAsAttribute(Runtime.InteropServices.UnmanagedType.LPWStr)> lpLibFileName As String) As IntPtr
  End Function ' LoadLibraryW

End Class ' AssemblyLoader

** MyApp **

<snip>
Public Sub New()
    Try
        AssemblyLoader.LoadByVersion("sqlite3.dll", False)
    Catch ex As Exception
        ' Error logging done here
    End Try
End Sub
</snip>
于 2012-12-01T23:06:41.957 に答える
0

これに対する別の解決策は、適切に名前が付けられた各OSタイプのSQLite3 dll(たとえば、sqlite3-x86.dllとsqlite3-x64.dll)の両方をソリューションに含め、実行可能ファイルとともにアプリケーションの出力ディレクトリにコピーするように設定することです(つまり、[出力ディレクトリにコピー]を[常に]に設定します。次に、プログラムの起動時に、.dllが存在するかどうかを確認し、存在しない場合は使用されているOSを判別する機能があり、それに応じて必要な.dllの名前を変更します。これは、正しい.dllに名前を付けるために1回だけ実行されます。.dllを動的にロードする必要はありません。これは私が使用するコードです:

public static bool checkForSQLite()
{
      string sqliteFileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "sqlite3.dll");

            if (!File.Exists(sqliteFileName))
            {
                string version = "x86";

                if (IntPtr.Size == 8)
                {
                    version = "x64";
                }

                string resourceFileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "sqlite3-" + version + ".dll");

                File.Move(resourceFileName, sqliteFileName);

                return true;
            }
            else
            {
                return false;
            }
        }
于 2015-01-13T14:46:43.373 に答える