2012-06-01 8 views
52

Oto sytuacja, używam biblioteki dll opartej na C w mojej aplikacji dot.NET. Istnieją 2 biblioteki dll, jeden to 32-bitowy o nazwie MyDll32.dll, a drugi to wersja 64-bitowa o nazwie MyDll64.dll.Korzystanie z biblioteki dll 32-bitowej lub 64-bitowej w C# DllImport

Istnieje zmienna statyczna przechowująca nazwę pliku DLL: ciąg DLL_FILE_NAME.

i stosuje się go w następujący sposób:

[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")] 
private static extern int is_Func1(int var1, int var2); 

Proste tak daleko.

Jak można sobie wyobrazić, oprogramowanie zostało skompilowane z włączonym "dowolnym procesorem".

Mam również następujący kod, aby określić, czy system powinien używać pliku 64-bitowego czy 32-bitowego.

#if WIN64 
     public const string DLL_FILE_NAME = "MyDll64.dll"; 
#else 
     public const string DLL_FILE_NAME = "MyDll32.dll";   
#endif 

Teraz powinien pojawić się problemy .. DLL_FILE_NAME jest określona w czasie kompilacji, a nie w czasie wykonywania więc prawo dll nie jest załadowany w zależności od kontekstu wykonania.

Jaki byłby właściwy sposób rozwiązania tego problemu? Nie chcę dwóch plików wykonawczych (jednego dla 32-bitowego, a drugiego dla 64-bitowego)? Jak mogę ustawić plik DLL_FILE_NAME przed, który jest używany w instrukcji DllImport?

+1

Jaka jest różnica między 64 i 32 bitowymi bibliotekami DLL? Czy jest coś, czego 32-bit nie może zrobić na 64? Jeśli tak, po prostu użyłbym 32. –

+0

W 64-bitowym systemie operacyjnym decyzja o uruchomieniu kodu w wersji 64-bitowej lub WOW64 (emulacja 32-bitowa) jest podejmowana po uruchomieniu programu. Jeśli program jest uruchamiany w trybie 32-bitowym, powinien korzystać z bibliotek bazujących na C, które zostały skompilowane odpowiednio w wersji 32-bitowej i 64-bitowej. – Gilad

+1

Jeśli naprawdę chcesz to zrobić, będziesz musiał ominąć atrybut 'DllImport' i załadować bibliotekę DLL ręcznie, korzystając z funkcji' LoadLibrary', 'GetProcAddess' i' FreeLibrary'. Ta technika jest omawiana [tutaj] (http://stackoverflow.com/questions/2818011/set-dllimport-attribute-dynamicznie). To jednak spora praca i raczej łatwo się pomylić. Pozwalanie, by mechanizm P/Invoke zrobił to za Ciebie, jest o wiele łatwiejsze. Jak zauważyli inni, prawdopodobnie nie warto, jeśli można po prostu wrócić do 32-bitowej biblioteki DLL przez cały czas jako najniższy wspólny mianownik. –

Odpowiedz

47

Znalazłem najprostszy sposób, aby to zrobić, to zaimportować dwie metody o różnych nazwach i wywołać właściwą. DLL nie zostanie załadowany aż połączenie zostanie wykonane tak, to w porządku:

[DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] 
private static extern int Func1_32(int var1, int var2); 

[DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] 
private static extern int Func1_64(int var1, int var2); 

public static int Func1(int var1, int var2) { 
    return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2); 
} 

Oczywiście, jeśli masz wiele importu, to można się dość kłopotliwe w utrzymaniu ręcznie.

+25

Dla maksymalnej czytelności, proponuję użyć właściwości ['Environment.Is64BitProcess'] (http://msdn.microsoft.com/en-us/library/system.environment.is64bitprocess) zamiast sprawdzać rozmiar' IntPtr 'typ. Oczywiście ta właściwość została wprowadzona w .NET 4.0, więc jeśli kierujesz ją na starszą wersję, nie będzie ona dostępna. –

+0

To jest dokładnie to, co chciałem zrobić. Chociaż kodowanie ma pewien wpływ, to działa i spełnia swoją rolę. – Gilad

7

Jest statyczna zmienna przytrzymując nazwę pliku DLL

To nie jest zmienną statyczną. Jest stała, w czasie kompilacji. Nie można zmienić stałej czasu kompilacji w czasie wykonywania.

Jaki byłby właściwy sposób rozwiązania tego problemu?

Szczerze polecam tylko kierowanie na x86 i jednoczesne zapominanie wersji 64-bitowej oraz zezwalanie na uruchamianie aplikacji na WOW64, o ile aplikacja nie musi wyświetlać się jako x64.

Jeśli istnieje zapotrzebowanie na x64, można:

  • Zmiany DLLe mieć taką samą nazwę, jak MyDll.dll, a przy instalacji/wdrożyć czasie umieścić właściwy w miejscu. (Jeśli system operacyjny to x64, zainstaluj 64-bitową wersję biblioteki DLL, w przeciwnym razie wersję x86).

  • Posiadają dwie osobne wersje: jedną dla x86 i drugą dla x64.

+1

Uzgodnione, to zdecydowanie najprostsze rozwiązanie problemu. Zdecydowana większość aplikacji, zwłaszcza aplikacji biznesowych, nie czerpie korzyści z bycia 64-bitowym natywnym. Nawet jeśli może nastąpić nieznaczna poprawa wydajności, zwiększony narzut 64-bitowy ma tendencję do ich anulowania. Druga sugestia dotycząca używania tych samych nazw dla bibliotek DLL i wdrażania odpowiedniej wersji w zależności od architektury systemu jest dokładnie tym, co Microsoft robi z systemowymi bibliotekami DLL. To też wygląda na dobry zakład. –

+0

+1 za dodanie biblioteki dll w czasie instalacji! –

2

To, co opisujesz, jest znane jako "montaż side-by-side" (dwie wersje tego samego zestawu, jeden 32, a drugi 64 bit) ...Myślę, że znajdziesz to pomocne:

Here można znaleźć instruktażu dokładnie scenariusza (.NET owijania DLL C++/CLI DLL odwoływania się natywna biblioteka DLL).

POLECAMY:

Wystarczy zbudować go jako x86 i być z nim zrobić ... albo mieć 2 buduje (jeden i jedna x86 x64) ... jak powyższe techniki są dość skomplikowane ...

+1

Nie chcę dwóch aplikacji. To jest takie 90te. – Gilad

+0

@Gilad następnie postępuj zgodnie z podanymi linkami, pokazują niektóre opcje, które być może bardziej przypominają to, co chcesz (UWAGA: robi się naprawdę skomplikowane) ... – Yahia

+0

@Gilad BTW można zbudować 2 (x86 i x64) i gdy działa instalator może określić, które z obu instalacji - użytkownik otrzymuje dokładnie jedną aplikację ... – Yahia

0

Użyłem jednym z podejść meantioned przez vcsjones:

„Zmień DLL mieć taką samą nazwę, jak MyDll.dll, a przy instalacji/wdrożyć czasie umieścić prawo jeden w miejscu . "

Takie podejście wymaga utrzymywania dwóch platform kompilacji chociaż zobaczyć ten link po więcej szczegółów: https://stackoverflow.com/a/6446638/38368

39

Oto kolejna alternatywa, która wymaga, aby obie DLL mają samą nazwę i są umieszczone w różnych folderach. Na przykład:

  • win32/MyDll.dll
  • win64/MyDll.dll

Sztuką jest ręcznie załadować DLL z LoadLibrary przed CLR robi. Zobaczy wtedy, że MyDll.dll jest już załadowany i używa go.

Można to łatwo zrobić w statycznym konstruktorze klasy nadrzędnej.

static class MyDll 
{ 
    static MyDll() 
    {    
     var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath; 
     var myFolder = Path.GetDirectoryName(myPath); 

     var is64 = IntPtr.Size == 8; 
     var subfolder = is64 ? "\\win64\\" : "\\win32\\"; 

     LoadLibrary(myFolder + subfolder + "MyDll.dll"); 
    } 

    [DllImport("kernel32.dll")] 
    private static extern IntPtr LoadLibrary(string dllToLoad); 

    [DllImport("MyDll.dll")] 
    public static extern int MyFunction(int var1, int var2); 
} 

EDIT 01/02/2017 Use Assembly.CodeBase tak, że działa nawet jeśli Shadow Copying jest włączony.

+1

To jest przez farmę najbardziej elegancką odpowiedź, która najlepiej pasuje do pytania. Z powodzeniem zaadoptowałem to dla projektu FreeImage .NET Wrapper! Świetny pomysł. Użyłem zamiast tego 'Environment.Is64BitProcess' i rozwiązałem ścieżkę dla zestawu tak, jak sugeruje to odpowiedź Kisdeds. Dziękuję bardzo! – JanW

+0

Można również osadzić zarówno biblioteki DLL w wersji 32-bitowej, jak i 64-bitowej, jako zasoby w bibliotece .NET DLL i wyodrębnić właściwe/wywołać funkcję LoadLibrary, jeśli wszystko ma być spakowane. – Matt

14

W tym przypadku, należy zrobić tak (zrobić 2 foldery, x64 i x86 + umieścić odpowiedni dll z nazwą samo w obu folderach):

using System; 
using System.Runtime.InteropServices; 
using System.Reflection; 
using System.IO; 

class Program { 
    static void Main(string[] args) { 
     var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); 
     path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86"); 
     bool ok = SetDllDirectory(path); 
     if (!ok) throw new System.ComponentModel.Win32Exception(); 
    } 
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool SetDllDirectory(string path); 
} 
1

alternatywne podejście może być

public static class Sample 
{ 
    public Sample() 
    { 

     string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\"; 
     string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll"; 
     if (!File.Exists(ResolvedDomainTimeFileName)) 
     { 
      if (Environment.Is64BitProcess) 
      { 
       if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll")) 
        File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName); 
      } 
      else 
      { 
       if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll")) 
        File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName); 
      } 
     } 
    } 

    [DllImport("ABCLib__Resolved.dll")] 
    private static extern bool SomeFunctionName(ref int FT); 
} 
+0

Powinno to skopiować dll, aby aplikacja spędzała więcej czasu. – lindexi

Powiązane problemy