W przeciwieństwie do sugestii niektórych innych odpowiedzi, używanie DllImport
atrybutu jest nadal właściwym podejściem.
Naprawdę nie rozumiem, dlaczego nie możesz zrobić tak jak wszyscy na świecie i określić krewnego ścieżki do swojej biblioteki DLL. Tak, ścieżka, w której aplikacja zostanie zainstalowana, różni się na komputerach różnych osób, ale w zasadzie jest to uniwersalna zasada dotycząca wdrażania. DllImport
Mechanizm został zaprojektowany z tą myślą.
W rzeczywistości nawet to nie DllImport
rozwiązuje problemu. To natywne reguły ładowania Win32 DLL, które rządzą, niezależnie od tego, czy używasz poręcznych zarządzanych opakowań (po prostu wywołuje P / Invoke marshaller LoadLibrary
). Zasady te są tutaj szczegółowo wyliczone , ale najważniejsze z nich wyszczególniono tutaj:
Zanim system wyszuka bibliotekę DLL, sprawdza następujące elementy:
- Jeśli biblioteka DLL o tej samej nazwie modułu jest już załadowana do pamięci, system używa załadowanej biblioteki DLL, bez względu na katalog, w którym się ona znajduje. System nie wyszukuje biblioteki DLL.
- Jeśli biblioteka DLL znajduje się na liście znanych bibliotek DLL dla wersji systemu Windows, w którym działa aplikacja, system używa swojej kopii znanej biblioteki DLL (i zależnych od niej bibliotek DLL, jeśli istnieją). System nie wyszukuje biblioteki DLL.
Jeśli SafeDllSearchMode
jest włączona (domyślnie), kolejność wyszukiwania jest następująca:
- Katalog, z którego aplikacja została załadowana.
- Katalog systemowy. Użyj
GetSystemDirectory
funkcji, aby uzyskać ścieżkę do tego katalogu.
- 16-bitowy katalog systemowy. Nie ma funkcji, która uzyskuje ścieżkę do tego katalogu, ale jest ona przeszukiwana.
- Katalog systemu Windows. Użyj
GetWindowsDirectory
funkcji, aby uzyskać ścieżkę do tego katalogu.
- Bieżący katalog.
- Katalogi wymienione w
PATH
zmiennej środowiskowej. Należy zauważyć, że nie obejmuje to ścieżki dla aplikacji określonej przez klucz rejestru App Paths. Klucz App Paths nie jest używany podczas obliczania ścieżki wyszukiwania DLL.
Tak więc, jeśli nie nazwiesz swojej biblioteki DLL tym samym, co systemowa biblioteka DLL (czego oczywiście nie powinieneś robić, nigdy, w żadnych okolicznościach), domyślna kolejność wyszukiwania rozpocznie wyszukiwanie w katalogu, z którego została załadowana aplikacja. Jeśli umieścisz tam bibliotekę DLL podczas instalacji, zostanie ona znaleziona. Wszystkie skomplikowane problemy znikną, jeśli użyjesz tylko ścieżek względnych.
Tylko napisz:
[DllImport("MyAppDll.dll")] // relative path; just give the DLL's name
static extern bool MyGreatFunction(int myFirstParam, int mySecondParam);
Ale jeśli to nie zadziała z jakiegokolwiek powodu i musisz zmusić aplikację do wyszukania biblioteki DLL w innym katalogu, możesz zmodyfikować domyślną ścieżkę wyszukiwania za pomocą SetDllDirectory
funkcji .
Zauważ, że zgodnie z dokumentacją:
Po wywołaniu SetDllDirectory
standardowa ścieżka wyszukiwania DLL to:
- Katalog, z którego aplikacja została załadowana.
- Katalog określony przez
lpPathName
parametr.
- Katalog systemowy. Użyj
GetSystemDirectory
funkcji, aby uzyskać ścieżkę do tego katalogu.
- 16-bitowy katalog systemowy. Nie ma funkcji, która uzyskuje ścieżkę do tego katalogu, ale jest ona przeszukiwana.
- Katalog systemu Windows. Użyj
GetWindowsDirectory
funkcji, aby uzyskać ścieżkę do tego katalogu.
- Katalogi wymienione w
PATH
zmiennej środowiskowej.
Tak długo, jak wywołujesz tę funkcję przed wywołaniem funkcji zaimportowanej z biblioteki DLL po raz pierwszy, możesz zmodyfikować domyślną ścieżkę wyszukiwania używaną do lokalizowania bibliotek DLL. Zaletą jest oczywiście to, że do tej funkcji można przekazać wartość dynamiczną, która jest obliczana w czasie wykonywania. Nie jest to możliwe w przypadku DllImport
atrybutu, więc nadal będziesz tam używać ścieżki względnej (tylko nazwa biblioteki DLL) i polegać na nowej kolejności wyszukiwania, aby ją znaleźć.
Będziesz musiał P / Wywołać tę funkcję. Deklaracja wygląda następująco:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);