2013-05-15 10 views
17

Mam aplikację zbudowaną jako "Dowolny procesor" i dwie biblioteki DLL innych firm z tej samej biblioteki kierowane na x86 i x64. Chciałbym dołączyć jedną z tych bibliotek w czasie wykonywania, w zależności od platformy, którą uruchomiłaby na komputerze klienta. Jaki byłby najlepszy sposób, aby to osiągnąć?Załaduj bibliotekę x64 lub x86 w zależności od platformy?

+1

Czy to zarządzane zespoły lub kod natywny? –

+0

Dlaczego nie chcesz po prostu tworzyć wersji x86 i x64 swojej aplikacji? – outcoldman

+0

Chciałbym powiedzieć, że są one mieszane. Odkompilowanie ich dało mi dużo kodu napisanego w C, ale wciąż nie jestem pewien. – Ammark

Odpowiedz

21

Jeśli mówimy o niezarządzanych DLL, deklaruje p/wywołuje tak:

[DllImport("DllName.dll")] 
static extern foo(); 

pamiętać, że nie są podając ścieżkę do biblioteki DLL, tylko jego nazwa, która przypuszczam jest taka sama dla zarówno wersje 32-, jak i 64-bitowe.

Następnie, zanim zadzwonisz do któregokolwiek z twoich p/invokes, załaduj bibliotekę do swojego procesu. Zrób to przez p/wywołanie funkcji API LoadLibrary. W tym momencie ustalisz, czy Twój proces jest 32- czy 64-bitowy i odpowiednio zbudujesz pełną ścieżkę do biblioteki DLL. Ta pełna ścieżka jest przekazywana do LoadLibrary.

Teraz, gdy wywołasz swoje p/invokes dla biblioteki, zostaną one rozwiązane przez moduł, który właśnie załadowałeś.

Dla zarządzanych złożeń można użyć Assembly.LoadFile, aby określić ścieżkę zespołu. Może to być trochę trudne do orkiestracji, ale ten znakomity artykuł pokazuje, jak: Automatically Choose 32 or 64 Bit Mixed Mode DLLs. Jest wiele szczegółów dotyczących trybu mieszanego i rodzimych zależności DLL, które prawdopodobnie nie są dla ciebie istotne. Kluczem jest obsługa zdarzeń AppDomain.CurrentDomain.AssemblyResolve.

+0

Bardzo fajnie. Nie wiedziałem tego. – Ani

+0

Dzięki David, Czy muszę utworzyć wrapper wokół istniejących bibliotek dll, ponieważ metody, które muszę uzyskać dostęp nie są statyczne?. – Ammark

+0

Czy używasz 'DllImport' teraz, gdy wywołujesz swój kod podczas kompilacji tak jak x86 lub tylko x64? –

1

Jestem właściwie doświadczony w tym temacie, więc pomyślałem, że opublikuję odpowiedź zgodnie ze sposobem, w jaki używałem Pencil.Gaming. Po pierwsze musisz "DllImport" "dwie funkcje, jedna z 32-bitowej biblioteki dll i jedna z 64-bitowej biblioteki DLL (lub tak, lub dylib, niezależnie od używanej platformy).

static class Foo32 { 
    [DllImport("32bitdll.dll")] 
    internal static extern void Foo(); 
} 
static class Foo64 { 
    [DllImport("64bitdll.dll")] 
    internal static extern void Foo(); 
} 

Następnie trzeba klasę pośrednią zawierającą delegatów i importowanie ich z 32 lub 64-bitowej współdziałanie w zależności od rozmiaru IntPtr (nie używam Environment.Is64BitProcess, ponieważ jest to funkcja .NET 4) :

internal delegate void FooDelegate(); 
static class FooDelegates { 
    internal static FooDelegate Foo; 

    static FooDelegates() { 
     Type interop = (IntPtr.Size == 8) ? typeof(Foo64) : typeof(Foo32); 
     FieldInfo[] fields = typeof(FooDelegates).GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); 
     foreach (FieldInfo fi in fields) { 
      MethodInfo mi = glfwInterop.GetMethod(fi.Name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); 
      Delegate function = Delegate.CreateDelegate(fi.FieldType, mi); 
      fi.SetValue(null, function); 
     } 
    } 
} 

I wtedy zazwyczaj używają "prawdziwe" klasy, zawierający funkcję importowane (choć nie jest to technicznie wymagane):

public static class FooApi { 
    public static void Foo() { 
     FooDelegates.Foo(); 
    } 
} 

To jest prawdziwy problem, jeśli potrzebujesz tylko jednej lub dwóch funkcji, ale sposób importowania delegatów jest naprawdę skuteczny w przypadku większych bibliotek/aplikacji. Możesz sprawdzić Pencil.Gaming na github, ponieważ używa tej metody dość szeroko (here jest przykładem tego, że jest często używany).

Inną zaletą tej metody jest to, że jest w 100% wieloplatformowy i nie polega na żadnych funkcjach WinAPI.

+0

Nie potrzebujesz żadnych delegatów. Możesz zrobić to wszystko za pomocą zwykłego "DllImport" dla wszystkich importowanych funkcji. –

+0

@DavidHeffernan Ale wtedy nie możesz użyć "Any CPU". Będziesz wówczas potrzebował redystrybuować oddzielne wersje aplikacji dla systemów 32- i 64-bitowych. – antonijn

+0

Pewnie możesz użyć AnyCPU. Moja odpowiedź wyjaśnia, w jaki sposób. Potrzebujesz bibliotek DLL, które mają taką samą nazwę, ale pamiętaj o różnych folderach. Ale jest to normalne w przypadku bibliotek DLL z 32/64 bitowością. –

2

Moim kompletnym rozwiązaniem mojego problemu było użycie drugiego linku dostarczonego przez Davida Heffernana. To, co zrobiłem, to 1. Przywoływano podręczną bibliotekę DLL w projekcie. 2. Określone dwa zdarzenia pre-build

xcopy /y "$(SolutionDir)\Assemblies\Lib\x86\(Assembly name)*" "$(TargetDir)" 
xcopy /y "$(SolutionDir)\Assemblies\Lib\x64\(Assemble name)*" "$(TargetDir)" 

3. i uruchamiania aplikacji w przypadku resolve zespół zmienił odpowiedni montaż w zależności od platformy.

 var currentDomain = AppDomain.CurrentDomain; 
     var location = Assembly.GetExecutingAssembly().Location; 
     var assemblyDir = Path.GetDirectoryName(location); 

     if (assemblyDir != null && (File.Exists(Path.Combine(assemblyDir, "(Assembly name).proxy.dll")) 
            || !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x86.dll")) 
            || !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x64.dll")))) 
     { 
      throw new InvalidOperationException("Found (Assembly name).proxy.dll which cannot exist. " 
       + "Must instead have (Assembly name).x86.dll and (Assembly name).x64.dll. Check your build settings."); 
     } 

     currentDomain.AssemblyResolve += (sender, arg) => 
     { 
      if (arg.Name.StartsWith("(Assembly name),", StringComparison.OrdinalIgnoreCase)) 
      { 
       string fileName = Path.Combine(assemblyDir, 
        string.Format("(Assembly).{0}.dll", (IntPtr.Size == 4) ? "x86" : "x64")); 
       return Assembly.LoadFile(fileName); 
      } 
      return null; 
     }; 
+1

To jednak działa tylko dla bibliotek .net, nie dla żadnego dll – Tyron

Powiązane problemy