2012-08-13 11 views
14

Powiel możliwe:
preprocessor directive…C#Zmień kod C# DllImport cel w zależności od x64/x86

Mam zewnętrznych dll C++ do importowania za pomocą dllimport. Jeśli moja aplikacja jest kompilowana w x64, muszę zaimportować wersję x64 tej biblioteki, jeśli jest to kompilacja x86, potrzebuję dll x86.

Jaki jest najlepszy sposób, aby to osiągnąć?

Idealnie, chciałbym jakąś dyrektywę preprocesora, ale rozumiem, to nie działa w C#?

Więcej informacji: biblioteka DLL jest importowana przez projekt ustawiony na AnyCPU. Projekt nadrzędny jest tym, który określa, czy aplikacja kompiluje się jako x64 lub x86. Kompilujemy obie wersje dla różnych klientów - i chcę udostępnić projekt potomny w obu wersjach.

+0

Co zrobić, aby zaimportować OBU wersje (prywatne metody), ale wystawiać na kod klienta prawo w zależności od środowiska? W .NET 4 po prostu sprawdź [Environment.Is64BitOperatingSystem] (http://msdn.microsoft.com/en-us/library/system.environment.is64bitoperatingsystem.aspx). Uwaga: Nie zachowałbym dwóch różnych wersji aplikacji C# ze względu na zależną natywną bibliotekę DLL (więc nie używałbym do tego preprocesora). –

+0

Michael - to prawie moje pytanie, ale mam jedną dodatkową komplikację, która oznacza, że ​​ich rozwiązanie nie zadziała. Moja biblioteka dll jest importowana przez projekt, który jest anycpu, a projekt nadrzędny decyduje, czy aplikacja jest x64 czy x86 – Sugrue

+0

@Sugrue Następnie będziesz musiał użyć rozwiązania runtime, a mianowicie zaimportuj oba i użyj 'Environment.Is64BitProcess' lub' sizeof (void *) 'lub' IntPtr.Size'. –

Odpowiedz

22

Jest to przede wszystkim problem rozmieszczenia, po prostu instalator skopiować właściwej DLL oparte na wersji systemu Windows na komputerze docelowym.

Ale nikt nigdy nie lubi tego robić. Dynamiczne przypinanie poprawnej funkcji DLL jest ogromnie bolesne, musisz napisać typy delegatów dla każdej wyeksportowanej funkcji i użyć LoadLibrary + GetProcAddress + Marshal.GetDelegateForFunctionPointer do utworzenia obiektu delegata.

Ale nikt nigdy nie lubi tego robić. Mniej bolesne podejście polega na dwukrotnym zadeklarowaniu funkcji, nadaniu jej różnych nazw i użyciu właściwości EntryPoint w atrybucie [DllImport] w celu określenia prawdziwej nazwy. Następnie przetestuj w czasie wykonywania, do którego chcesz zadzwonić.

Ale nikt nigdy nie lubi tego robić. Najskuteczniejszą sztuczką jest nakierowanie systemu Windows na ładowanie poprawnej biblioteki DLL dla ciebie. Pierwszą rzeczą, którą musisz zrobić, to skopiować bibliotekę DLL do katalogu, w którym system Windows jej nie szuka. Najlepszym sposobem jest utworzenie podkatalogu "x86" i "x64" w katalogu budowy i skopiowanie odpowiedniej biblioteki DLL do każdego z nich. Zrób to, pisząc zdarzenie post-build, które tworzy katalogi i kopie bibliotek DLL.

Następnie powiedz o tym systemowi Windows, wyszukując SetDllDirectory(). Podana ścieżka zostanie dodana do katalogów, w których system Windows szuka biblioteki DLL. Tak:

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(); 
     //etc.. 
    } 
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool SetDllDirectory(string path); 
} 

Czy rozważyć, czy mając bieg kodu w trybie 64-bitowym jest rzeczywiście przydatna. Rzadko zdarza się, że potrzebna jest gigantyczna przestrzeń adresowa pamięci wirtualnej, którą można uzyskać, jedyną realną korzyścią. Nadal musisz obsługiwać wersję 32-bitową, która musi działać poprawnie w przypadku 2 gigabajtów.

+0

Fajne rozwiązanie. PhonicUK i Michael Graczyk również mają dobre rozwiązania, ale ten nie wymaga powtarzania kodu, który mi się podoba. – Sugrue

+1

Osobiście wolę jawne wywołanie 'LoadLibrary', przekazując pełną ścieżkę, zamiast modyfikować ścieżkę wyszukiwania DLL, która zawsze wydaje mi się niezręczna. Zasadniczy pomysł jest oczywiście taki sam. –

+0

Funkcja LoadLibrary jest objęta drugim akapitem. Kiepski pomysł. –

5

Dodaj zarówno x86 i x86_64 import DLL z różnymi nazwami, a następnie można warunkowo wywołać je w zależności od architektury w czasie wykonywania przez sprawdzając wartość Environment.Is64BitProcess (lub IntPtr.size jeśli używasz < .Net 4). To zadziała niezależnie od tego, czy projekt jest zbudowany jako x86, x86_64, czy AnyCPU

Alternatywnie, skonfiguruj dwie różne konfiguracje kompilacji - taką, która wykonuje tylko x86 i taką, która obsługuje tylko x86_64, każdemu z nich symbol warunkowej kompilacji i użyj #ifdef na twoim niestandardowym symbolu.

+0

Myślę, że OP nie wie, jak użyć '# ifdef' czy możesz podać mu przykład kodu szybkiego i być może przykład kodu ładowania bibliotek? Nadal dał +1 bez przykładów. –

+0

'Environment.Is64BitProcess' Naprawdę pomógł mi dzięki – LuckyLikey