Bahkan, saya mendapat DLL C ++ (berfungsi) yang ingin saya impor ke proyek C # untuk memanggil fungsinya.
Itu berfungsi ketika saya menentukan path lengkap ke DLL, seperti ini:
string str = "C:\\Users\\userName\\AppData\\Local\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Masalahnya adalah bahwa itu akan menjadi proyek yang dapat diinstal, sehingga folder pengguna tidak akan sama (mis: pierre, paul, jack, ibu, ayah, ...) tergantung komputer / sesi di mana ia akan dijalankan.
Jadi saya ingin kode saya menjadi sedikit lebih umum, seperti ini:
/*
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);
Masalahnya adalah "DllImport" menginginkan parameter "const string" untuk direktori DLL.
Jadi pertanyaan saya adalah :: Apa yang bisa dilakukan dalam kasus ini?
Jawaban:
Bertentangan dengan saran dari beberapa jawaban lain, penggunaan
DllImport
atribut masih merupakan pendekatan yang benar.Jujur saya tidak mengerti mengapa Anda tidak bisa melakukan seperti orang lain di dunia dan menentukan jalur relatif ke DLL Anda. Ya, jalur di mana aplikasi Anda akan diinstal berbeda pada komputer orang yang berbeda, tapi itu pada dasarnya aturan universal ketika datang ke penyebaran. The
DllImport
Mekanisme dirancang dengan pikiran ini.Bahkan, itu bahkan tidak
DllImport
menanganinya. Ini adalah aturan pemuatan asli Win32 DLL yang mengatur berbagai hal, terlepas dari apakah Anda menggunakan pembungkus yang mudah ditangani (marshaller P / Invoke hanya meneleponLoadLibrary
). Aturan-aturan itu disebutkan dengan sangat rinci di sini , tetapi yang penting dikutip di sini:Jadi, kecuali jika Anda menamai DLL Anda dengan hal yang sama dengan DLL sistem (yang seharusnya tidak Anda lakukan, dalam keadaan apa pun), urutan pencarian default akan mulai mencari di direktori tempat aplikasi Anda dimuat. Jika Anda menempatkan DLL di sana selama instalasi, itu akan ditemukan. Semua masalah rumit hilang jika Anda hanya menggunakan jalur relatif.
Tulis saja:
Tetapi jika itu tidak berhasil karena alasan apa pun, dan Anda perlu memaksa aplikasi untuk mencari di direktori lain untuk DLL, Anda dapat memodifikasi jalur pencarian default menggunakan
SetDllDirectory
fungsi .Perhatikan bahwa, sesuai dokumentasi:
Jadi, selama Anda memanggil fungsi ini sebelum Anda memanggil fungsi yang diimpor dari DLL untuk pertama kalinya, Anda dapat memodifikasi jalur pencarian default yang digunakan untuk menemukan DLL. Manfaatnya, tentu saja, adalah Anda dapat memberikan nilai dinamis ke fungsi ini yang dihitung pada saat dijalankan. Itu tidak mungkin dengan
DllImport
atribut, jadi Anda masih akan menggunakan jalur relatif (hanya nama DLL) di sana, dan bergantung pada urutan pencarian baru untuk menemukannya untuk Anda.Anda harus P / Aktifkan fungsi ini. Deklarasi terlihat seperti ini:
sumber
.dll
dan sistem lain akan menambahkan ekstensi yang sesuai di bawah Mono (misalnya.so
di Linux). Ini dapat membantu jika portabilitas menjadi perhatian.SetDllDirectory
. Anda juga dapat mengubahEnvironment.CurrentDirectory
dan semua jalur relatif akan dievaluasi dari jalur itu!AddDllDirectory
di sisi lain ...DllImport
ini lebih dari sekadar bungkusLoadLibrary
. Ini juga mempertimbangkan direktori perakitanextern
metode didefinisikan . JalurDllImport
pencarian juga dapat dibatasi menggunakanDefaultDllImportSearchPath
.Bahkan lebih baik daripada saran Ran untuk menggunakan
GetProcAddress
, cukup buat panggilanLoadLibrary
sebelum panggilan keDllImport
fungsi (hanya dengan nama file tanpa jalur) dan mereka akan menggunakan modul yang dimuat secara otomatis.Saya telah menggunakan metode ini untuk memilih pada saat runtime apakah akan memuat DLL asli 32-bit atau 64-bit tanpa harus memodifikasi banyak fungsi P / Invoke-d. Masukkan kode pemuatan ke konstruktor statis untuk tipe yang memiliki fungsi yang diimpor dan semuanya akan berfungsi dengan baik.
sumber
FunctionLoader
kode saya .Jika Anda memerlukan file .dll yang tidak ada di jalur atau di lokasi aplikasi, maka saya tidak berpikir Anda bisa melakukan itu, karena
DllImport
merupakan atribut, dan atribut hanya metadata yang ditetapkan pada tipe, anggota dan lainnya elemen bahasa.Alternatif yang dapat membantu Anda mencapai apa yang saya pikir Anda coba, adalah menggunakan asli
LoadLibrary
melalui P / Invoke, untuk memuat .dll dari jalur yang Anda butuhkan, dan kemudian gunakanGetProcAddress
untuk mendapatkan referensi ke fungsi yang Anda butuhkan. dari itu. Kemudian gunakan ini untuk membuat delegasi yang dapat Anda panggil.Untuk membuatnya lebih mudah digunakan, Anda dapat mengatur delegasi ini ke bidang di kelas Anda, sehingga menggunakannya tampak seperti memanggil metode anggota.
EDIT
Berikut ini cuplikan kode yang berfungsi, dan menunjukkan apa yang saya maksud.
Catatan: Saya tidak repot menggunakan
FreeLibrary
, jadi kode ini tidak lengkap. Dalam aplikasi nyata, Anda harus berhati-hati untuk melepaskan modul yang dimuat untuk menghindari kebocoran memori.sumber
Selama Anda tahu direktori tempat pustaka C ++ Anda dapat ditemukan pada saat run time, ini harus sederhana. Saya dapat dengan jelas melihat bahwa ini adalah kasus dalam kode Anda. Anda
myDll.dll
akan berada di dalammyLibFolder
direktori di dalam folder sementara pengguna saat ini.Sekarang Anda dapat terus menggunakan pernyataan DllImport menggunakan string const seperti yang ditunjukkan di bawah ini:
Tepat pada saat dijalankan sebelum Anda memanggil
DLLFunction
fungsi (ada di perpustakaan C ++) tambahkan baris kode ini dalam kode C #:Ini hanya menginstruksikan CLR untuk mencari pustaka C ++ yang tidak dikelola di jalur direktori yang Anda peroleh saat menjalankan program Anda.
Directory.SetCurrentDirectory
panggilan mengatur direktori kerja aplikasi saat ini ke direktori yang ditentukan. Jika AndamyDLL.dll
hadir di path yang diwakili olehassemblyProbeDirectory
path maka akan dimuat dan fungsi yang diinginkan akan dipanggil melalui p / invoke.sumber
atur jalur dll dalam file konfigurasi
sebelum memanggil dll di aplikasi Anda, lakukan hal berikut
lalu panggil dll dan Anda bisa gunakan seperti di bawah ini
sumber
DllImport akan bekerja dengan baik tanpa jalur lengkap yang ditentukan selama dll terletak di suatu tempat di jalur sistem. Anda mungkin dapat menambahkan folder pengguna untuk sementara waktu.
sumber
Jika semuanya gagal, cukup letakkan DLL di
windows\system32
folder. Kompiler akan menemukannya. Tentukan DLL untuk memuat dari dengan:,DllImport("user32.dll"...
setelEntryPoint = "my_unmanaged_function"
untuk mengimpor fungsi yang tidak dikelola yang Anda inginkan ke aplikasi C #:Sumber dan bahkan lebih banyak
DllImport
contoh: http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspxsumber