W rzeczywistości mam C ++ (działającą) bibliotekę DLL, którą chcę zaimportować do mojego projektu C #, aby wywołać jej funkcje.
Działa, gdy określę pełną ścieżkę do biblioteki DLL, na przykład:
string str = "C:\\Users\\userName\\AppData\\Local\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Problem polega na tym, że będzie to projekt do zainstalowania, więc folder użytkownika nie będzie taki sam (np. Pierre, paul, jack, mama, tata, ...) w zależności od komputera / sesji, na której zostanie uruchomiony.
Chciałbym więc, aby mój kod był trochę bardziej ogólny, na przykład:
/*
goes right to the temp folder of the user
"C:\\Users\\userName\\AppData\\Local\\temp"
then go to parent folder
"C:\\Users\\userName\\AppData\\Local"
and finally go to the DLL's folder
"C:\\Users\\userName\\AppData\\Local\\temp\\myLibFolder"
*/
string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Problem polega na tym, że „DllImport” wymaga parametru „const string” dla katalogu biblioteki DLL.
Więc moje pytanie brzmi: co można by zrobić w tym przypadku?
Odpowiedzi:
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 marshallerLoadLibrary
). Zasady te są tutaj szczegółowo wyliczone , ale najważniejsze z nich wyszczególniono tutaj: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:
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ą:
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:
źródło
.dll
a inne systemy dodadzą odpowiednie rozszerzenie w Mono (np. W.so
Linuksie). Może to pomóc, jeśli problemem jest przenośność.SetDllDirectory
. Możesz także po prostu zmienić,Environment.CurrentDirectory
a wszystkie względne ścieżki zostaną ocenione z tej ścieżki!AddDllDirectory
z drugiej strony ...DllImport
to coś więcej niż tylko opakowanieLoadLibrary
. Uwzględnia również katalog zestawu, w którymextern
metoda jest zdefiniowana . TeDllImport
ścieżki wyszukiwania mogą być dodatkowo ograniczone użyciuDefaultDllImportSearchPath
.Nawet lepiej niż sugestia Rana dotycząca użycia
GetProcAddress
, po prostu wykonaj wywołanie funkcjiLoadLibrary
przed jakimkolwiek wywołaniemDllImport
funkcji (tylko z nazwą pliku bez ścieżki), a automatycznie użyją załadowanego modułu.Użyłem tej metody, aby wybrać w czasie wykonywania, czy załadować 32-bitową, czy 64-bitową natywną bibliotekę DLL bez konieczności modyfikowania zestawu funkcji P / Invoke-d. Umieść kod ładujący w konstruktorze statycznym dla typu, który ma zaimportowane funkcje, a wszystko będzie działać poprawnie.
źródło
FunctionLoader
kodu.Jeśli potrzebujesz pliku .dll, który nie znajduje się na ścieżce ani w lokalizacji aplikacji, nie sądzę, że możesz to zrobić, ponieważ
DllImport
jest to atrybut, a atrybuty to tylko metadane, które są ustawione dla typów, członków i innych elementy językowe.Alternatywą, która może pomóc ci osiągnąć to, co myślę, że próbujesz, jest użycie natywnego
LoadLibrary
przez P / Invoke, w celu załadowania .dll ze ścieżki, której potrzebujesz, a następnie użyjGetProcAddress
aby uzyskać odniesienie do potrzebnej funkcji z tego .dll. Następnie użyj ich, aby utworzyć delegata, którego możesz wywołać.Aby ułatwić używanie, możesz następnie ustawić tego delegata na pole w swojej klasie, aby używanie go wyglądało jak wywołanie metody składowej.
EDYTOWAĆ
Oto fragment kodu, który działa i pokazuje, o co mi chodzi.
Uwaga: nie zawracałem sobie głowy używaniem
FreeLibrary
, więc ten kod nie jest kompletny. W prawdziwej aplikacji należy zadbać o zwolnienie załadowanych modułów, aby uniknąć wycieku pamięci.źródło
Tak długo, jak znasz katalog, w którym można znaleźć biblioteki C ++ w czasie wykonywania, powinno to być proste. Wyraźnie widzę, że tak jest w twoim kodzie. Twój
myDll.dll
byłby obecny wmyLibFolder
katalogu wewnątrz folderu tymczasowego bieżącego użytkownika.Teraz możesz kontynuować używanie instrukcji DllImport, używając ciągu const, jak pokazano poniżej:
Tuż w czasie wykonywania, zanim wywołasz
DLLFunction
funkcję (obecną w bibliotece C ++) dodaj następujący wiersz kodu w kodzie C #:To po prostu instruuje środowisko CLR, aby szukało niezarządzanych bibliotek C ++ w ścieżce katalogu, którą otrzymałeś w czasie wykonywania programu.
Directory.SetCurrentDirectory
call ustawia bieżący katalog roboczy aplikacji na określony katalog. Jeśli twojamyDLL.dll
jest obecna na ścieżce reprezentowanej przezassemblyProbeDirectory
ścieżkę, zostanie załadowana, a żądana funkcja zostanie wywołana przez p / invoke.źródło
ustaw ścieżkę dll w pliku konfiguracyjnym
przed wywołaniem biblioteki DLL w swojej aplikacji wykonaj następujące czynności
następnie wywołaj dll i możesz użyć jak poniżej
źródło
DllImport będzie działać poprawnie bez określonej ścieżki, o ile dll znajduje się gdzieś na ścieżce systemowej. Możesz tymczasowo dodać folder użytkownika do ścieżki.
źródło
Jeśli wszystko zawiedzie, po prostu umieść bibliotekę DLL w
windows\system32
folderze. Kompilator go znajdzie. Określ bibliotekę DLL do załadowania z:,DllImport("user32.dll"...
ustaw,EntryPoint = "my_unmanaged_function"
aby zaimportować żądaną niezarządzaną funkcję do aplikacji C #:Źródło i jeszcze więcej
DllImport
przykładów: http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspxźródło