2009-08-23 8 views
8

Używam w moim rozwiązaniu DllImport.
Mój problem polega na tym, że mam dwie wersje tej samej biblioteki DLL, którą zbudowano dla wersji 32-bitowej, a drugą dla wersji 64-bitowej.Ustawianie programu dllimport programowo w C#

Oba prezentują te same funkcje o identycznych nazwach i identycznych podpisach. Moim problemem jest to, że muszę użyć dwóch statycznych metod, które je odsłaniają, a następnie w czasie wykonywania użyj rozmiaru IntPtr, aby określić właściwy, który należy wywołać.

private static class Ccf_32 
{ 
    [DllImport(myDllName32)] 
    public static extern int func1(); 
} 

private static class Ccf_64 
{ 
    [DllImport(myDllName64)] 
    public static extern int func1(); 
} 

muszę to zrobić bo myDllName32 i myDllName64 musi być stała i nie znaleźli sposób, aby ustawić go w czasie wykonywania.

Czy ktoś ma eleganckie rozwiązanie do tego, aby pozbyć się powielania kodu i stałej kontroli rozmiaru IntPtr.

Gdybym mógł ustawić nazwę pliku, musiałbym tylko raz sprawdzić i mógłbym pozbyć się tony powtarzającego się kodu.

+0

sensu wybierając go w czasie wykonywania Jeśli różnica jest to cała kompilacja . – Havenard

Odpowiedz

12

Prawdopodobnie można to osiągnąć za pomocą słowa kluczowego #if. Jeśli zdefiniujemy warunkową symbol kompilatora nazwie win32, następujący kod użyje win32-block, jeśli go usunąć będzie korzystał z innego bloku:

#if win32 
    private static class ccf_32 
    { 
     [DllImport(myDllName32)] 
     public static extern int func1(); 
    } 
#else  
    private static class ccf_64 
    { 
     [DllImport(myDllName64)] 
     public static extern int func1(); 
    } 
#endif 

prawdopodobnie oznacza to, że można zdjąć torebkę klasy, który cię mam teraz:

private static class ccf 
    { 
#if win32 
     [DllImport(myDllName32)] 
     public static extern int func1(); 
#else  
     [DllImport(myDllName64)] 
     public static extern int func1(); 
#endif 
    } 

Dla wygody, wydaje mi się, że można tworzyć konfiguracje kompilacji do kontrolowania symbolu kompilacji.

+3

Tak, ale chciałbym zachować opcję "Any CPU" w przeciwieństwie do wersji 32- i 64-bitowych. – Matt

+0

Podczas pracy to podejście nadal powoduje kopiowanie kodu, którego oryginalny plakat chce uniknąć, oraz ogranicza możliwość kompilowania oddzielnych wersji dla wersji 32 i 64-bitowej. Możesz usunąć duplikację kodu i kontynuować kierowanie zarówno na wersje 32-, jak i 64-bitowe z jedną skompilowaną biblioteką DLL/EXE, stosując metodę deanis do korzystania z SetDllDirectory lub kopalnię do korzystania z LoadLibrary. –

2

Dlaczego nie zawinąć ich w metodę?

private static class ccf_32_64 
{ 
    private static class ccf_32 
    { 
     [DllImport(myDllName32)] 
     private static extern int func1(); 
    } 

    private static class ccf_64 
    { 
     [DllImport(myDllName64)] 
     private static extern int func1(); 
    } 

    public static int func1() 
    { 
     if (32bit) 
     { 
      return ccf_32.func1(); 
     } 
     else 
     { 
      return ccf_64.func1(); 
     } 
    } 
} 
+0

Zasadniczo to, co mam teraz :-) – Matt

+0

Cóż, po zakończeniu tej czynności nie musisz się tym martwić. – ChaosPandion

1

Jedną z rozważanych opcji jest, aby obie wersje 32- i 64-bitowe niezarządzanego DLL mają taką samą nazwę, ale mieć je na żywo w oddzielnych folderach w produkcji budowlanej (powiedzmy, x64 x86 \ i \).

Następnie instalator lub inna dystrybucja jest aktualizowana, dzięki czemu wie, aby zainstalować odpowiednią bibliotekę DLL dla platformy, na której jest instalowany.

0

Nie możesz tego zrobić tak, jak chcesz. Musisz pomyśleć o atrybucie DllImport jako metadanych, które są używane podczas kompilacji. W rezultacie nie można zmienić biblioteki DLL importowanej dynamicznie.

Jeśli chcesz zachować kierowany kod kierowany na "Dowolny procesor", musisz zaimportować zarówno biblioteki 32-bitowe, jak i 64-bitowe, opakowane jako dwie różne funkcje, które możesz wywoływać w zależności od środowiska wykonawczego lub użycia kilka dodatkowych wywołań Win32 API do późnego załadowania poprawnej wersji niezarządzanego zespołu w czasie wykonywania i dodatkowych wywołań Win32 w celu wykonania wymaganych metod. Wadą jest to, że nie będziesz mieć wsparcia czasu kompilacji dla żadnego z tego typu kodu dla bezpieczeństwa typu, itp.

0

Hmm, zastanawiam się, czy można stworzyć interfejs, a następnie klasę z metodami opartymi na w 32-bitowych i 64-bitowych bibliotekach dll.

Nie jestem pewien, czy istnieje jawna metoda określania, czy używasz 64-bitowego, ale mogą działać następujące: zezwalaj na niebezpieczny kod i korzystaj z niebezpiecznej funkcji, która dostaje wskaźnik do jakiegoś adresu, a następnie określ, czy wskaźnik ma rozmiar 4 lub 8 bajtów. Na podstawie wyniku określ, która implementacja interfejsu ma zostać utworzona.

0

Możesz określić, czy korzystasz z 64 bitów, czy nie, sprawdzając rozmiar typu IntPtr (który tak czy inaczej nazywa się natywnym int). Następnie można załadować approriate DLL przy użyciu zaimportowanego połączenia LoadLibraryW, uzyskać wskaźnik funkcji używając GetProcAddress, a następnie sprawdzić Marshal.GetDelegateForFunctionPointer

to nie aż tak skomplikowane, jak mogłoby to wyglądać. Musisz DllImport zarówno LoadLibraryW i GetProcAddress.

16

Wolę to zrobić, korzystając z połączenia LoadLibrary z kernel32.dll, aby wymusić konkretną bibliotekę DLL do załadowania z określonej ścieżki.

Jeśli nazwy 32-bitowych i 64-bitowych plików DLL są takie same, ale umieszczone w różnych ścieżkach, można następnie użyć następującego kodu, aby załadować poprawne w oparciu o wersję systemu Windows, który używasz. Wszystko, co musisz zrobić to zadzwonić ExampleDllLoader.LoadDll() PRZED jakiegokolwiek kodu odwołującego się CCF klasa odwołuje:

private static class ccf 
{ 
    [DllImport("myDllName")] 
    public static extern int func1(); 
} 

public static class ExampleDllLoader 
{ 
    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] 
    private extern static IntPtr LoadLibrary(string librayName); 

    public static void LoadDll() 
    { 
     String path; 

     //IntPtr.Size will be 4 in 32-bit processes, 8 in 64-bit processes 
     if (IntPtr.Size == 4) 
      path = "c:/example32bitpath/myDllName.dll"; 
     else 
      path = "c:/example64bitpath/myDllName.dll"; 

     LoadLibrary(path); 
    } 
} 
+0

Wygląda na to, że to rozwiązanie wymaga, aby użytkownik miał prawa administratora. Czy ktoś może potwierdzić? –

+0

Nie powinno to wymagać uprawnień administratora, o ile użytkownik ma uprawnienia do odczytu biblioteki DLL, która jest ładowana. Używałem go w sytuacjach, w których zwykły użytkownik uruchamia aplikację. –

9

wiem, że to jest bardzo stare pytanie (jestem nowy - to jest złe odpowiedzieć na stare pytanie?), ale musiałem rozwiązać ten sam problem. Musiałem dynamicznie odwoływać się do 32-bitowej lub 64-bitowej biblioteki DLL opartej na systemie operacyjnym, podczas gdy mój .EXE jest kompilowany dla dowolnego procesora.

Możesz użyć DLLImport i nie musisz używać funkcji LoadLibrary().

Zrobiłem to przy użyciu SetDLLDirectory. W przeciwieństwie do nazwy, SetDLLDirectory dodaje do ścieżki wyszukiwania DLL i nie zastępuje całej ścieżki. To pozwoliło mi mieć bibliotekę DLL o tej samej nazwie ("TestDLL.dll" dla tej dyskusji) w podkatalogach Win32 i Win64 i odpowiednio nazwane.

public partial class frmTest : Form 
{ 
    static bool Win32 = Marshal.SizeOf(typeof(IntPtr)) == 4; 
    private string DLLPath = Win32 ? @"\Win32" : @"\Win64"; 

    [DllImport("kernel32.dll", SetLastError = true)] 
    public static extern bool SetDllDirectory(string lpPathName); 
    [DllImport("TestDLL.dll", SetLastError = true)] 
    static extern IntPtr CreateTestWindow(); 

    private void btnTest_Click(object sender, EventArgs e) 
    { 
     string dllDir = String.Concat(Directory.GetCurrentDirectory(), DLLPath); 
     SetDllDirectory(dllDir); 

     IntPtr newWindow = CreateTestWindow(); 
    } 
} 
+2

To świetne podejście, szczególnie jeśli masz wiele bibliotek DLL, które wymagają 32- i 64-bitowych wersji.Wierzę, że podoba mi się podejście SetDllDirectory jeszcze lepiej niż przy użyciu metody LoadDll, którą wyjaśniłem w mojej odpowiedzi. Jest to również o wiele mniej kodu niż zbliża się opakowanie, a my możemy skompilować dowolny procesor. –

1

można utworzyć na dwa sposoby i wybrać jeden w czasie wykonywania, dzięki czemu można zachować Any CPU

public static class Ccf 
{ 
    [DllImport(myDllName32)] 
    private static extern int func32(); 

    [DllImport(myDllName64)] 
    private static extern int func64(); 


    public static int func() 
    { 
     if(Environment.Is64BitProcess) 
     { 
      return func64(); 
     } 
     return func32(); 
    } 

}