Runtime: Menangani p/panggilan untuk berbagai platform dan diskusi tentang dllmap

Dibuat pada 5 Mei 2015  ·  207Komentar  ·  Sumber: dotnet/runtime

Saat ini, coreclr tidak memiliki cara yang baik untuk menangani perbedaan antar platform dalam hal p/memanggil pustaka asli.

Misalnya bayangkan saya ingin memanggil fungsi openssl, di Unix perpustakaan disebut libssl.so/dylib tetapi di Windows libeay32.dll (ada banyak contoh lain, misalnya libz.so vs zlib1.dll ).

corefx "menyelesaikan" ini dengan mengompilasi kode p/invoke secara kondisional untuk setiap platform dengan nama perpustakaan yang benar. Ini tampak seperti overhead organisasi utama dan rapuh ketika platform baru ditambahkan.

Mono melakukan dua hal untuk membuatnya lebih mudah:

  1. Secara otomatis menyelidiki variasi string yang diteruskan ke DllImport , misalnya jika saya menentukan DllImport("myFoo") ia mencoba myFoo.dll , myFoo.so , libmyFoo.so dan segera. Dalam kasus bahagia ini cukup untuk menemukan perpustakaan di semua platform.
  2. DllMap : ini untuk kasus di mana nama perpustakaan terlalu berbeda, atau jika hanya fungsi tertentu yang harus dialihkan, atau hanya pada beberapa platform tertentu.

Menurut pendapat saya, coreclr harus mengimplementasikan sesuatu seperti itu. Saya tahu bahwa menggunakan DllMap 1:1 sebenarnya tidak mungkin karena tidak ada tumpukan System.Configuration di .NET Core, tetapi setidaknya harus memandu implementasi alternatif.

Bagaimana menurutmu?

edit Saya mengusulkan menambahkan atribut DllMap di https://github.com/dotnet/coreclr/issues/930#issuecomment -100675743 :

[assembly: DllMap("foo", "foo.dll", OSName.Windows)]
[assembly: DllMap("foo", "libfoo.dylib", OSName.OSX)]
[assembly: DllMap(Dll="foo", Target="libfoo.so", OS=OSName.Linux)]
[assembly: DllMap(Dll="dl", Name="dlclose", Target="libc.so", OS="FreeBSD")]
area-Interop-coreclr port

Komentar yang paling membantu

Di Linux saya menemukan solusi praktis untuk masalah ini, yang tidak melibatkan modifikasi DllImports sama sekali.

Idenya adalah untuk menghasilkan sebuah rintisan shared library yang diberi nama seperti Windows DLL dan berisi referensi ke nama library sebenarnya untuk Linux. DllImport dalam kode .NET tetap tidak berubah dan menggunakan nama Windows DLL (tanpa akhiran .dll). Pemuat asli inti .NET kemudian akan memuat objek bersama rintisan. Tindakan ini memanggil pemuat dinamis Linux (ld.so) yang kemudian menyelesaikan ketergantungan stub pada pustaka asli dan secara otomatis memetakan semua simbol dari pustaka asli ke dalam rintisan.

Untuk menghasilkan perpustakaan rintisan, lakukan hal berikut:

touch empty.c
gcc -shared -o libLinuxName.so empty.c    
gcc -Wl,--no-as-needed -shared -o libWindowsName.so -fPIC -L. -l:libLinuxName.so
rm -f libLinuxName.so

Hasilnya dapat diperiksa menggunakan perintah readelf :

$ readelf -d libWindowsName.so
Dynamic section at offset 0xe38 contains 22 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libLinuxName.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x4b8
 0x000000000000000d (FINI)               0x600
...

Setiap rintisan sangat kecil (8 KB) dan dengan demikian dapat dengan mudah disertakan dalam paket NuGet lintas platform.
Dimungkinkan untuk menghasilkan perpustakaan rintisan dengan pemanggilan sederhana ld karena tidak ada kompilasi yang benar-benar terlibat, tetapi saya tidak dapat menemukan argumen yang benar untuk melakukan itu.

Contoh dunia nyata dari teknik ini dapat dilihat di https://github.com/surban/managedCuda/tree/master/StubsForLinux

Semua 207 komentar

cc @stephentoub

Terkait dengan dotnet/runtime#4032

myFoo.so seharusnya tidak pernah ada. Semua alat yang saya tahu menerapkan konvensi penamaan untuk memiliki awalan lib untuk setiap perpustakaan bersama. Di sisi lain, hal-hal dapat menjadi lebih rumit dengan akhiran versi ABI, yaitu libFoo.so.0.6 .

@janhenke secara teori ya, Mono hanya mencoba beberapa kombinasi yang masuk akal.

Ini adalah masalah yang menarik untuk ditangani. Saya ingin tahu apa pengalaman Anda dengan DllMap? Apa yang bekerja dengan baik dan apa yang tidak?

Selama kita menyimpan kait konteks beban perakitan sehingga tuan rumah dapat memutuskan cara kerjanya, maka saya senang. Saya pikir inti untuk harus mencoba mengurangi sebanyak mungkin kebijakan/konfigurasi ootb (ini berbau pengalihan yang mengikat).

Bukankah Host juga mengizinkan Anda mengonfigurasi jalur untuk impor dll? Mungkin itu dapat diperpanjang untuk memungkinkan penyelidikan lebih banyak hal (ekstensi berbeda, dll).

@sergiy-k Saya bukan ahli DllMap, tapi saya tidak punya terlalu banyak masalah dalam penggunaan saya yang terbatas. Masalah utama yang saya lihat adalah di mana meletakkan barang-barang itu (file .config tidak ada lagi di dunia baru ini). Saya akan senang dengan langkah pertama yang hanya menyelidiki variasi yang berbeda jika lib tidak dapat ditemukan oleh string kata demi kata.

@davidfowl bagaimana aplikasi terhubung ke itu?

Satu hal lagi, beberapa lib di aspnet menghindari impor dll dan sebagai gantinya memuat pustaka menggunakan pustaka A lain yang disediakan oleh dnx untuk membantu menemukan lib asli:

https://github.com/aspnet/KestrelHttpServer/blob/dev/src/Microsoft.AspNet.Server.Kestrel/Networking/PlatformApis.cs

Dan penggunaan:

https://github.com/aspnet/KestrelHttpServer/blob/dev/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs

Bagaimana aplikasi menanganinya? Saya tidak punya ide. Saya tidak melihat clr inti sebagai model aplikasi. Saya melihatnya sebagai blok bangunan dengan kebijakan yang dapat dikonfigurasi dan API hosting yang bagus.

Akan ada cara untuk mengekspresikan dependensi asli di project.json dan dalam paket nuget. Tuan rumah dnx akan mengaitkan acara dan melakukan sesuatu yang cerdas :)

Hal utama yang perlu diingat untuk ini adalah kompatibilitas.

Ini berarti solusinya perlu mengasumsikan Anda menjalankan aplikasi .Net yang dikompilasi (.exe/.dll) dan memiliki referensi hard-code ke nama perpustakaan yang berfungsi di windows. Menambahkan abstraksi kelas khusus platform tidak akan berfungsi dalam skenario ini. Biner .Net sudah dikompilasi dan sumbernya mungkin tidak tersedia.

Contoh dunia nyata yang baik adalah panggilan ke runtime C ke "memcpy". Nama perpustakaan sama sekali berbeda di windows dari platform lain "msvcrt.dll" versus "libc.so". Penghancuran nama tidak akan memperbaikinya dan penggantian kode keras menghadirkan beban pemeliharaan untuk mengikuti setiap rilis versi runtime baru.

Berkenaan dengan mangling nama, saya lebih suka melihat substitusi tunggal, yang diketahui dan didokumentasikan terjadi: "MyFoo.dll" -> "libMyFoo.so". Mengikat lebih dari satu substitusi seperti yang dilakukan Mono dapat membuka pintu untuk tabrakan nama file. Masalah utama yang hadir dengan pendekatan ini adalah sensitivitas kasus. Kode .Net mengasumsikan platform windows dan tidak peka huruf besar-kecil seperti unix. Jadi "MyFoo.dll" dan "myfoo.dll" tidak sama.

Mempertimbangkan semua hal di atas, satu-satunya solusi realistis adalah file konfigurasi gaya "DllMap" yang dapat ditambahkan bersama biner yang sudah dikompilasi.

Untuk kelengkapan, cara yang tepat untuk menentukan DllImport adalah dengan memberi nama hanya nama file perpustakaan tanpa ekstensi. Jadi contoh di atas akan ditulis sebagai [DllImport("MyFoo")]. Akhiran khusus platform (.dll/.so) ditambahkan oleh runtime. Ini akan menyiratkan perilaku yang benar untuk runtime untuk juga menambahkan awalan khusus platform (lib). Apa pun di luar ini perlu ditentukan oleh solusi gaya "DllMap".

Satu komentar lain tentang .so versi: Ini biasanya diselesaikan dengan menyediakan symlink ke perpustakaan bernama verison eksplisit.

Kode .Net mengasumsikan platform windows dan tidak peka huruf besar-kecil seperti unix.

Saya percaya ini tidak terjadi. .Net sangat peka huruf besar-kecil, tetapi hanya implementasi sistem file yang mendasarinya di Windows yang tidak peka. Saya juga cukup yakin bahwa CLR tidak mengasumsikan apa pun, dan bahkan jika memang demikian, CoreCLR dibangun dari bawah ke atas untuk tidak mengasumsikan hal-hal khusus platform semacam ini.

@OtherCrashOverride

Ini berarti solusinya perlu mengasumsikan Anda menjalankan aplikasi .Net yang dikompilasi (.exe/.dll) dan memiliki referensi hard-code ke nama perpustakaan yang berfungsi di windows. Menambahkan abstraksi kelas khusus platform tidak akan berfungsi dalam skenario ini. Biner .Net sudah dikompilasi dan sumbernya mungkin tidak tersedia.

Ini bukan masalah karena rakitan yang ada tidak berfungsi pada .NET Core dan perlu dikompilasi ulang.

Saya setuju bahwa apa pun solusinya, itu tidak boleh terlalu mengandalkan sihir dan didokumentasikan dengan baik.

Biarkan saya memperluasnya sedikit:
[Penulis kode .Net yang ada mengasumsikan [operasi pada] platform windows dan [dengan sistem file yang] tidak peka huruf besar-kecil seperti unix.

Ini khusus untuk binari .Net yang sudah ada sebelumnya yang dibuat oleh pihak ketiga yang opsi kompilasi ulangnya tidak tersedia. Itu alasan Mono menerapkan "DllMap".

Binari yang sudah ada sebelumnya tidak terbatas hanya pada warisan. Mungkin bagi seseorang untuk membuat rakitan .Net Core baru yang P/Memanggil runtime C hari ini. Mungkin juga majelis ini adalah pihak ke-3 dan dirilis tanpa kode.

Apakah mungkin untuk mengizinkan perpustakaan/nama modul DllImport untuk mengizinkan alokasi readonly statis entah bagaimana? Dengan begitu kita bisa menunjuk ke mana perpustakaan harus dimuat dalam konstruktor statis kelas. Saat ini parameter atribut sedang mengkompilasi hal-hal waktu sehingga akan menjadi masalah. Namun itu akan menghindari kebutuhan file .config eksternal yang tampaknya tidak ada di .NET Core.

Satu komentar lain tentang .so versi: Ini biasanya diselesaikan dengan menyediakan symlink ke perpustakaan bernama verison eksplisit.

Kata kuncinya di sini adalah "biasanya". Meskipun berlaku untuk banyak perpustakaan, itu sebenarnya bukan persyaratan formal. Anda memang harus mengharapkan menemukan perpustakaan bersama dengan symlink ke libFoo.so . Kami tidak boleh memaksakan pembuatan symlink seperti itu hanya untuk CoreCLR atau memutus dalam kasus itu.

Metode AssemblyLoadContext.LoadUnmanagedDll yang dapat diganti dirancang untuk memungkinkan penyesuaian pemuatan target DllImport. Metode ini mendapatkan nama dll dan mengembalikan pegangan OS. Algoritme yang disediakan pengguna dapat menyelidiki versi .so, menggunakan pengikatan gaya dllmap, atau melakukan sesuatu yang pintar seperti dnx ;-)

Akan lebih baik untuk mempublikasikan sampel yang mengimplementasikan dllmap menggunakan AssemblyLoadContext.

@jkotas Itu bagus, tapi saya masih berpikir perlu ada sesuatu yang sederhana di dalamnya. Misalnya dlopen() (yang merupakan inti dari LoadUnmanagedDll) diimplementasikan di libdl di Linux dan OS X, tetapi FreeBSD mengimplementasikannya di libc . Saya tidak berpikir setiap aplikasi harus berurusan dengan itu.

@akoeplinger Tidak bisakah itu diterapkan di PAL, atau apakah saya salah paham?

@akoeplinger Kedengarannya masuk akal untuk menambahkan API pembantu di AssemblyLoadContext untuk itu. dotnet/coreclr#937

Saya setuju bahwa solusi apa pun yang disajikan harus 'terpasang'. Penting bahwa itu menjadi deterministik dan konsisten untuk semua orang. Jika setiap orang 'menggulung sendiri sesuai kebutuhan menggunakan kait', maka penyedia perpustakaan tidak akan dapat menawarkan dukungan untuk apa pun selain 'pemuat' mereka sendiri yang mungkin tidak kompatibel atau menawarkan fitur 'pemuat' milik pelanggan.

+1 untuk plugin berbasis konvensi sederhana -1 untuk apa pun seperti peta dll. Clr inti harus menjaga model hosting yang ringan tanpa menimbulkan kebijakan dan file konfigurasi. Biarkan implementasi host melakukan itu. Konsol corerun/inti dapat memanggang sesuatu dengan level yang lebih tinggi

Saya telah membuat perpustakaan untuk ini , tetapi tidak didukung oleh AOT karena menghasilkan implementasi saat runtime menggunakan Emit.

Sebagai prinsip enkapsulasi, jika coreclr adalah orang yang mengimplementasikan dan mendefinisikan perilaku DllImport, maka ia juga bertanggung jawab untuk mengizinkan konfigurasinya. Tuan rumah, (corerun, dll) seharusnya tidak mempedulikan detail operasional dari sesuatu yang berada di luar cakupannya. Ini adalah masalah inti, menjadikannya perhatian tuan rumah hanyalah "mengeluarkan uang" untuk menjadikannya "masalah orang lain" alih-alih harus menghadapinya.

Kebutuhannya adalah bahwa seseorang yang bukan pengembang dapat menyalin proyek ke target. Jika target itu bukan Windows, ada metode sederhana dan sepele bagi mereka atau penyedia perpustakaan untuk mengesampingkan asumsi nama perpustakaan yang dikodekan secara keras. Ini harus bekerja secara deterministik dan konsisten terlepas dari apakah corerun adalah host atau runtime di-host sebagai bagian dari aplikasi yang lebih besar.

Ini harus bekerja secara deterministik dan konsisten terlepas dari apakah corerun adalah host atau runtime di-host sebagai bagian dari aplikasi yang lebih besar.

Default yang masuk akal selalu bagus dan saya pikir semua orang setuju dengan itu. Kebijakan tidak boleh dimasukkan ke dalam CLR, dan jika ya, kebijakan tersebut harus minimal dan dapat ditimpa oleh host.

Tidak mungkin bagi tuan rumah untuk mengantisipasi penggunaan atau maksud DllImport di perpustakaan pihak ke-3. Oleh karena itu, tidak akan pernah ada kebutuhan bagi tuan rumah untuk mengesampingkan perilaku. Ini sepenuhnya di luar lingkup perhatian tuan rumah.

Ini adalah contoh dunia nyata untuk diskusi:

[DllImport("msvcrt", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr Memcpy(IntPtr dest, IntPtr src, UIntPtr count);

Solusi yang disajikan "biarkan tuan rumah khawatir tentang hal itu" berarti setiap tuan rumah akan membutuhkan pengetahuan bahwa memcpy tidak ada di msvcrt.dll di linux. Sebaliknya, itu harus memuat libc.so. Mono menyertakan pengetahuan bawaan bahwa msvcrt harus dipetakan ke libc di linux, jadi pemetaan ini tidak pernah diperlukan oleh pengguna akhir atau penulis perpustakaan yang menggunakannya.

Solusi "menulis ulang perpustakaan untuk menggunakan abstraksi" yang disajikan memiliki dampak yang cukup besar bagi penulis perpustakaan (pemfaktoran ulang dan pengujian kode) dan menempatkan beban pengetahuan platform ke setiap perpustakaan menggunakan DllImport.

Solusi "DllMap" adalah satu-satunya yang disajikan sejauh ini yang memenuhi kebutuhan semua orang:
1) [nama rakitan].[exe | dll].config memungkinkan per perpustakaan daripada penggantian global.
2) Override dapat ditentukan per titik masuk untuk sistem seperti FreeBSD di mana suatu fungsi mungkin ada di perpustakaan yang berbeda.
3) Pengguna akhir dan integrator dapat membuat pemetaan sesuai kebutuhan.
4) Platform target baru tidak memerlukan perpustakaan untuk ditulis ulang.
5) Kode yang ada tidak perlu ditulis ulang.

Hanya untuk memasukkan sesuatu yang lebih spesifik ke dalam campuran, dnx adalah host CLR inti, tidak menggunakan corerun atau konsol inti. Sebagai gantinya, ia menggunakan API host asli secara langsung dan menghosting CLR dalam proses asli. Sebagai bagian dari urutan boot, kami menghubungkan AssemblyLoadContext.LoadNativeDll dan memiliki cara untuk memuat rakitan asli dari paket NuGet. Saya tidak yakin saya ingin peta dll sama sekali dalam situasi itu karena sebagai Host, model aplikasi bahkan mungkin tidak memilih untuk mengekspos primitif yang sama.

PS: Ini mengingatkan saya pada pengalihan yang mengikat, yang tidak diinginkan siapa pun lagi :smile:

Jadi bagaimana DNX mengatasi contoh memcpy yang disebutkan di atas?

Saya juga harus menunjukkan bahwa

[DllImport("libc.so", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr Memcpy(IntPtr dest, IntPtr src, UIntPtr count);

Sama-sama valid C# dan berfungsi di Linux, tetapi tidak di windows. Pustaka pihak ke-3 dapat berisi keduanya. Jadi ini bukan hanya "masalah platform lain". Ini juga masalah Windows. Apakah DNX siap menangani pemetaan itu?

Tidak, tetapi saya mengatakan tidak apa-apa untuk memiliki sesuatu yang sangat mendasar di clr inti untuk sesuatu yang serendah memcopy. Kemudian lagi jika perpustakaan Anda memanggil memcopy atau uname atau apa pun secara langsung maka itu akan mendeteksi di mana ia berjalan.

Btw ada metode memcopy di Buffer sekarang yang merupakan abstraksi yang lebih baik untuk panggilan khusus itu.

Jika menulis loader saya sendiri adalah solusi saat rilis, maka saya akan melakukannya. Begitu juga orang lain. Beberapa akan mengikuti Mono DllMap. Orang lain tidak akan. Pada akhirnya akan terjadi persaingan 'standar' yang menyebabkan frustasi bagi penyedia perpustakaan dan pelanggan. Inilah mengapa saya merasa penting untuk menyelesaikan ini lebih awal. Jika saya hanya dapat memilih satu platform saat menggunakan DNX + P/Invoke, maka itu adalah Linux.

Anda dapat menulis loader Anda sendiri. Lihatlah ekosistem saat ini di windows. Itu yang mereka lakukan (dengan panggilan API).

Saya hanya berpikir mereka adalah cara yang berbeda untuk menulis dan mendistribusikan perpustakaan, di mana nuget akan menjadi cara utama untuk melakukan itu (toh kasus 80%).

Kita sedang berbicara tentang penulis perpustakaan di sini kan?

Mungkin kita harus menyebutkan skenarionya:

  • perpustakaan asli tersedia di os (libc)
  • perpustakaan asli yang dikompilasi dan tersedia di LD_LIBRARY_PATH
  • perpustakaan asli yang dibawa bersama dengan paket

Ini tidak eksklusif untuk penyedia perpustakaan. Itu hanya contoh dunia nyata. Ini berlaku untuk CoreCLR sebagai runtime yang dapat di-host baik dengan ASP.Net dan di luar. Ada banyak skenario di mana NuGet tidak diinginkan, jadi solusinya harus independen dari itu. CoreCLR minimalis menempatkan kepentingan yang lebih besar pada perpustakaan pihak ke-3 baik yang dikelola maupun asli.

Tentu itu sebabnya saya memanggil skenario sebagai gantinya (dnx juga tidak spesifik asp.net tetapi saya tidak akan membahasnya). Saya tidak ingin mengoceh tetapi memikirkan inti clr sebagai pengganti mono, mungkin di situlah ada kebingungan. Coreclr.dll/so/dylib adalah API dan Corerun adalah executable asli yang menggunakan perpustakaan itu. Dalam skenario di mana nuget tidak diinginkan, apakah Anda menggunakan Corerun? Jika demikian, maka yakinkan kebijakan itu ke dalam Host spesifik itu, bukan perpustakaan itu sendiri.

Pengganti Mono persis seperti cara berpikir saya dan banyak orang lain. Mono dirancang dengan cara yang sama seperti memiliki aplikasi host dan perpustakaan runtime yang dapat disematkan dalam aplikasi host yang berbeda. Secara pribadi, saya mengantisipasi menggunakan Corerun lebih dari DNX. Namun, saat ini tidak ada yang menawarkan solusi untuk dilema 'memcpy' (tujuannya sebenarnya bukan untuk menyalin memori, melainkan hanya contoh dari masalah terburuk. Oleh karena itu, alternatif memcpy bukanlah yang diperlukan).

Dunia tidak akan berakhir jika fungsionalitas "DllMap" tidak disertakan dalam CoreCLR. Seperti yang disebutkan sebelumnya, saya dapat membuat aplikasi host saya sendiri atau bahkan mengubah runtime sendiri untuk mendukung apa yang saya butuhkan. Sayangnya pada saat itu, itu menjadi 'produk saya' dan saya harus mempertahankan garpu yang tidak sesuai dengan 'produk Anda'.

Saya meminta jika tidak ada rencana untuk menawarkan dukungan gaya 'DllMap' pada rilis CoreCLR, itu dinyatakan di sini secara ringkas.

Cara lain untuk mengungkapkannya adalah: Jika ada Permintaan Tarik yang menambahkan fungsionalitas "DllMap", apakah itu akan diterima?

Itu pertanyaan untuk @jkotas :smile:

Banyak pengembang melihat .NET Core sebagai pengganti Mono, ini tidak termasuk penggunaan ASP.net. CoreCLR sendiri sangat menarik di platform selain Windows. Saya pikir para pengembang .NET perlu lebih menyadari hal ini dan lebih terbuka tentang implikasinya atau mungkin menghasilkan solusi terfragmentasi untuk masalah yang tidak (dan bisa saja) kami tangani oleh tim.

Ketika saya mengatakan itu bukan pengganti, maksud saya itu bukan setetes pengganti. Hal-hal bekerja secara berbeda (tidak ada GAC ​​dll). Mono pada dasarnya adalah implementasi CLR penuh, CoreCLR jauh lebih sederhana, jadi banyak manajemen kebijakan yang biasa Anda gunakan tidak ada (tidak ada System.Configuration) dan saya pikir itu sepenuhnya dirancang.

Saya hanya berpikir, mengapa tidak menambahkan parameter tambahan ke DllImport? Sebagai contoh:
[DllImport("foolib", ModuleLinux="foolib.so", ModuleMacOSX="foolib.dylib", ModuleWin="foolib.dll" ModuleFreeBSD="foolib"]

Jika atribut ModuleLinux tidak disetel untuk runtime di linux, atribut tersebut akan mengikat menggunakan parameter pertama. Sama dengan platform lainnya.

Solusi titik tengah yang tidak memerlukan penggunaan file .config.

Ini memang bisa juga ditambahkan ke .NET Framework dan Mono.

Jika ini tidak dapat diterima dan file .config tidak akan ditambahkan ke CoreCLR, solusi lain apa yang mungkin ada yang tidak melibatkan modifikasi cara kerja CoreCLR atau mengganti kode. Jika keputusan tim terlalu menurunkan fungsionalitas seperti itu kepada pengguna, maka biarlah. Apa keputusan akhir?

Bentuk DllImport yang diperluas masih menempatkan beban pengetahuan platform pada penulis perpustakaan. Selain itu, ini menyebabkan pembaruan runtime untuk semua platform ketika platform baru didukung. "ModuleXYZ" perlu ditambahkan ke metadata.

Alternatif pada platform yang mendukungnya, seperti Linux, menggunakan symlink: msvcrt.dll -> libc.so.6. Ini tidak memperhitungkan situasi di mana sebagian besar fungsi berada di pustaka yang sama tetapi satu fungsi mungkin berada di pustaka bersama yang berbeda dengan nama yang berbeda. Alternatifnya adalah dengan mengkompilasi pembungkus pustaka bersama yang mengekspor semua simbol dan secara internal memanggil dlopen untuk melewati setiap fungsi ke pustaka yang sesuai. Ini secara signifikan meningkatkan hambatan masuk untuk dukungan lintas platform.

Interpretasi saya tentang argumen yang menentang penerapan DllMap adalah bahwa hal itu dapat berdampak buruk pada DNX. Ini juga pemahaman saya bahwa DNX tidak memetakan nama DLL dan hanya menggunakan pengait untuk mengubah lokasi tempat perpustakaan dimuat. Tampaknya dotnet/coreclr#937 dirancang untuk mengatasi ini.

Ironisnya, fungsionalitas yang disediakan oleh DllMap juga memperbaiki DNX karena tidak akan berfungsi pada platform di mana pustaka asli tidak seperti yang ditentukan oleh pembuat pustaka.

Saya juga harus menyatakan bahwa ini bukan masalah preferensi seperti kebijakan. Ini masalah pekerjaan atau crash. Untuk platform dan perpustakaan tertentu, ada satu DllMap yang benar. Tujuannya adalah untuk menyampaikan informasi ini ke loader dll asli. Penting juga untuk diingat ini bukan masalah Unix/Lainnya karena perpustakaan dapat menentukan nama perpustakaan linux, bsd, osx untuk DllImport dan masih dianggap benar.

Ini masalah pekerjaan atau crash. Untuk platform dan perpustakaan tertentu, ada satu DllMap yang benar.

@OtherCrashOverride bukankah itu mungkin dengan API Host?

Sebagai alternatif untuk dllmap berbasis file, kami juga dapat menyandikannya ke dalam Majelis yang p/memanggil langsung melalui atribut (dan menggunakan OSName API baru dari https://github.com/dotnet/corefx/pull/1494):

[assembly: DllMap("foo", "foo.dll", OSName.Windows)]
[assembly: DllMap("foo", "libfoo.dylib", OSName.OSX)]
[assembly: DllMap(Dll="foo", Target="libfoo.so", OS=OSName.Linux)]
[assembly: DllMap(Dll="dl", Name="dlclose", Target="libc.so", OS="FreeBSD")]

Manfaat dari pendekatan ini adalah jauh lebih mudah untuk diimplementasikan dalam runtime (tidak ada penguraian XML yang aneh) dan masih menyediakan logika pemetaan yang sudah dikenal (saya pikir kita dapat mengimplementasikan semua yang saat ini didukung oleh dllmap Mono melalui atribut-atribut itu, dan Mono akhirnya dapat memasukkannya demikian juga).

Penulis perpustakaan memegang kendali penuh alih-alih harus bergantung pada tuan rumah untuk melakukan hal yang benar. Satu-satunya downside adalah bahwa konsumen perpustakaan tidak dapat mengubah pemetaan jika platform baru tiba, sesuatu yang secara pribadi dapat saya jalani (dengan berharap perpustakaan yang di-porting ke CoreCLR sering menerima pembaruan karena partisipasi komunitas).

Pikiran?

Suka itu

bukankah itu mungkin dengan API Host?

Tentu saja mungkin, tetapi mengharuskan setiap host untuk menerapkan pengetahuan dari setiap DllMap yang ada saat ini atau yang akan ada di masa depan untuk bekerja secara deterministik dan konsisten.

kami juga dapat menyandikannya ke dalam majelis yang p/panggil langsung melalui atribut

Saya mempertimbangkan skenario yang melibatkan penyuntikan informasi ke dalam rakitan, semuanya memiliki masalah yang sama: Tanda tangan digital majelis diubah. Anda tidak memiliki cara untuk memverifikasi keasliannya.

Apa yang kita bicarakan dengan DllMap adalah file yang mungkin ada secara opsional :

MyFoo.dll.config

File ini berisi informasi pemetaan yang dapat dimodifikasi pengguna akhir untuk berbagai platform. Saat platform baru muncul, Anda dapat menambahkannya ke konten file atau membiarkannya apa adanya jika Anda tidak terpengaruh olehnya.

Penulis perpustakaan dapat menyertakan file ini sebagai bagian dari paket NuGet mereka. Kehadirannya tidak berbahaya jika nama DLL yang ditulis cocok di platform Anda. Jika runtime tidak dapat menemukan kecocokan, maka runtime akan berkonsultasi dengan file ini untuk nama alternatif keputusan berdasarkan informasi tentang memuat perpustakaan sambil mempertahankan kontrol mutlak. Kasus di mana host tidak mengaitkan panggilan ini, harus default ke runtime memuat nama yang dikoreksi.

Mungkin itu namanya yang buruk. Bagaimana jika kita menyebutnya:

MyFoo.dll.map

@OtherCrashOverride

Saya mempertimbangkan skenario yang melibatkan penyuntikan informasi ke dalam rakitan, semuanya memiliki masalah yang sama: Tanda tangan digital majelis diubah. Anda tidak memiliki cara untuk memverifikasi keasliannya.

Untuk memperjelas hal ini: penulis perpustakaan secara eksplisit menambahkan atribut ini ke assemblies mereka , sehingga atribut tersebut akan disertakan saat tanda tangan diterapkan.

Mungkin namanya yang buruk [...]

Ya, punya ide yang sama. Ini tidak menyelesaikan masalah yang jauh lebih besar dalam mengimplementasikan parsing XML di runtime.

penulis perpustakaan secara eksplisit menambahkan atribut ini

Masalahnya tetap bahwa itu menempatkan beban pengetahuan platform pada penulis. Seorang penulis windows tidak harus tahu OSX untuk merilis perpustakaan dan sebaliknya.

Ini tidak menyelesaikan masalah yang jauh lebih besar dalam mengimplementasikan penguraian XML

Tidak harus XML. Ini bisa menjadi format biner selama alat disediakan yang memungkinkan pengguna akhir dan integrator untuk membuat dan memodifikasi file. Selain itu, harapannya adalah fungsionalitas DllMap untuk membaca file konfigurasi akan dilakukan dalam kode terkelola di mana System.XML, dll tersedia.

Selain itu, harapannya adalah fungsionalitas DllMap untuk membaca file konfigurasi akan dilakukan dalam kode terkelola di mana System.XML, dll tersedia.

System.Xml tidak tersedia dan sepenuhnya opsional sehingga kode akan diimplementasikan dalam kode asli. CoreCLR memiliki System.Runtime (mscorlib baru), itu saja.

Tidak bisakah kita mengkompilasi sesuatu ke format biner menggunakan konstruksi dan metadata yang sudah dipahami runtime?

contohnya adalah

[assembly: DllMap("foo", "foo.dll", OSName.Windows)]
[assembly: DllMap("foo", "libfoo.dylib", OSName.OSX)]
[assembly: DllMap(Dll="foo", Target="libfoo.so", OS=OSName.Linux)]
[assembly: DllMap(Dll="dl", Name="dlclose", Target="libc.so", OS="FreeBSD")]

Bisakah kita membuatnya dapat dikompilasi dan berdiri sendiri sehingga MyFoo.dll.map sebenarnya adalah sebuah perakitan?

@akoeplinger Saya suka contoh Anda, saya pikir ini solusi yang bagus. Lebih baik daripada milik saya (di mana Anda harus mengetik lebih banyak untuk setiap DllImport jika Anda ingin mengimpor lebih banyak fungsi).

Saya akan baik-baik saja dengan alat offline yang mengambil file teks XML, JSON, sebagai input dan mengkompilasi file .dll.map sebagai output.

@OtherCrashOverride Apakah Anda khawatir tidak dapat memodifikasi DllMap perpustakaan sumber tertutup? Seperti yang dikatakan @akoeplinger , sepertinya itu satu-satunya kelemahan untuk menjadikannya waktu kompilasi. Seseorang mungkin akan membuat alat untuk memodifikasi dan mengkompilasi ulang atribut DllMap dari suatu Majelis selama Majelis tidak ditandatangani dengan kuat jika itu penting.

Tidak sekali pun saya perlu memodifikasi dllmap dalam file .config untuk perpustakaan atau executable yang bukan milik saya, biasanya itu adalah sesuatu yang harus dikhawatirkan oleh pembuat perpustakaan - setidaknya itulah pengalaman saya, yang lain mungkin berbeda.

Apakah Anda khawatir tidak dapat memodifikasi DllMap dari perpustakaan sumber tertutup?

Kekhawatirannya adalah bahwa penulis perpustakaan dan penulis host mungkin tidak mempertimbangkan platform yang saya jalankan saat menulis. Kekhawatirannya adalah bahwa juga sebagai penulis, saya tidak perlu khawatir tentang setiap platform di luar sana. Ini bukan spekulasi tentang apa yang mungkin terjadi. Pelajarannya diambil dari Mono.

DllMap (atau serupa) adalah fungsionalitas yang saya butuhkan. Karena itu, ini adalah fungsionalitas yang akan saya tambahkan jika tidak ada dukungan resmi. Saya tidak memohon untuk penyertaan fitur. Saya mencoba untuk bernegosiasi sehingga saya tidak perlu membuat fork proyek yang tidak kompatibel bahkan sebelum dirilis. Ide dan informasi yang disajikan di sini sangat membantu untuk memandu arah solusi saya sendiri.

CoreCLR memilih untuk menjadi anggota komunitas multi-platform. Masalah ini adalah contoh tanggung jawab pilihan itu.

Untuk mengilustrasikan poin dengan lebih baik, mari kembali ke contoh:

[assembly: DllMap(Dll="foo", Target="libfoo.so", OS=OSName.Linux)]
[assembly: DllMap(Dll="dl", Name="dlclose", Target="libc.so", OS="FreeBSD")]

Apa yang terjadi dengan FreeBSD di sana? Apa yang terjadi ketika *BSD berikutnya dimunculkan? Siapa yang berada dalam posisi yang lebih baik untuk memetakan dll? Penulis perpustakaan atau penulis host yang tidak tahu platform itu ada? Atau pihak ketiga dengan pengetahuan platform yang dapat membuat file MyFoo.dll.map dari XML?

Sayangnya itu hanya berlaku untuk sebagian kecil dari hal-hal tingkat rendah yang dipilih oleh inti clr untuk abstrak. Jika perpustakaan Anda memutuskan untuk memanggil ke perpustakaan asli acak, itu benar-benar terserah perpustakaan untuk melakukannya dengan benar, bukan tuan rumah. Platform lain menangani ini secara berbeda dan mungkin juga masuk akal demi kelengkapan untuk melihatnya.

Platform lain menangani ini secara berbeda dan mungkin juga masuk akal demi kelengkapan untuk melihatnya.

Kita pasti harus melihat semua opsi yang tersedia! Namun, seseorang akan secara eksplisit menjelaskan apa itu sehingga kita dapat mendiskusikannya.

Jika solusi akhirnya adalah pengganti DllImport, maka mari kita jelajahi itu.

Setiap penulis Majelis terkelola yang menggunakan perpustakaan asli yang tidak dikelola mungkin akan mengetahui apa yang dikatakan OS/platform perpustakaan asli bekas yang didukung. Mungkin ada kasus di mana perpustakaan terkelola dapat mendukung platform lain jika perpustakaan asli yang direferensikan ada di dalamnya, tetapi seberapa sering platform baru muncul di dunia komputasi yang memerlukan tindakan seperti itu? Belum lagi itu akan dibatasi oleh serangkaian platform yang ditentukan yang didukung oleh CoreCLR. Apakah itu benar-benar masalah besar bagi mono?

Jika pengembang menargetkan CoreCLR, saya yakin mereka akan mencoba menambahkan DllMap ke semua platform yang didukung oleh CoreCLR.

Berikut adalah contoh dunia nyata lainnya:
https://github.com/mono/opentk/blob/master/Source/OpenTK/OpenTK.dll.config

<configuration>
  <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
  <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
  <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
  <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
  <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
  <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
  <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
</configuration>

Yang ini juga menarik:
https://github.com/mispy/FNA/blob/master/FNA.dll.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <dllmap dll="SDL2.dll" os="windows" target="SDL2.dll"/>
    <dllmap dll="SDL2.dll" os="osx" target="libSDL2-2.0.0.dylib"/>
    <dllmap dll="SDL2.dll" os="linux" target="libSDL2-2.0.so.0"/>

    <dllmap dll="SDL2_image.dll" os="windows" target="SDL2_image.dll"/>
    <dllmap dll="SDL2_image.dll" os="osx" target="libSDL2_image-2.0.0.dylib"/>
    <dllmap dll="SDL2_image.dll" os="linux" target="libSDL2_image-2.0.so.0"/>

    <dllmap dll="soft_oal.dll" os="windows" target="soft_oal.dll"/>
    <dllmap dll="soft_oal.dll" os="osx" target="libopenal.1.dylib"/>
    <dllmap dll="soft_oal.dll" os="linux" target="libopenal.so.1"/>

    <dllmap dll="libvorbisfile.dll" os="windows" target="libvorbisfile.dll"/>
    <dllmap dll="libvorbisfile.dll" os="osx" target="libvorbisfile.3.dylib"/>
    <dllmap dll="libvorbisfile.dll" os="linux" target="libvorbisfile.so.3"/>

    <dllmap dll="libtheoraplay.dll" os="windows" target="libtheoraplay.dll"/>
    <dllmap dll="libtheoraplay.dll" os="osx" target="libtheoraplay.dylib"/>
    <dllmap dll="libtheoraplay.dll" os="linux" target="libtheoraplay.so"/>
</configuration>

seberapa sering platform baru muncul di dunia komputasi yang membutuhkan tindakan seperti itu?

Perhatikan "soft_oal.dll". Bagaimana Anda memetakannya? Siapapun dengan pengetahuan platform dapat memberitahu Anda itu adalah nama alternatif untuk "openal32.dll" pada Windows. Adakah yang bisa memberi tahu saya apa yang dipetakannya di iOS?
Tentu saja Anda tidak bisa karena CoreCLR bahkan tidak tahu bahwa iOS adalah sebuah platform. Bagaimana dengan Android? Apakah penulis perpustakaan atau host mempertimbangkan platform itu?

Sebenarnya, .NET Framework bahkan tidak mendukung dllmap di windows. Jadi itu akan menggunakan apa pun yang disediakan untuk atribut DllImport dan mengabaikan dllmap di .config. masalah soft_oal.dll/openal32.dll mudah diselesaikan bagi saya dengan mengganti nama perpustakaan asli tersebut ke yang benar. (Banyak orang mengganti nama soft_oal.dll menjadi openal32.dll karena banyak aplikasi masih mengharapkan nama itu).

Saya rasa FNA bahkan tidak mendukung Android, jadi mengapa khawatir? Basis OpenTK tidak mendukung Android, namun fork OpenTK Xamarin mendukung Android dan Anda tidak perlu mengedit file .config agar semuanya berfungsi di sana.

Saya dapat melihat poin yang Anda coba sampaikan, tetapi saya masih berpikir jika penulis perpustakaan bermaksud mendukung platform, mereka akan menambahkan DllMap sesuai dengan nama terbaik. soft_oal.dll dan openal32.dll keduanya dapat diterima.

Parameter OSName dari solusi yang diusulkan dapat memiliki nama/nilai enum tambahan yang ditetapkan untuk bekerja dengan platform di luar CoreCLR. Dalam hal ini CoreCLR akan mengabaikan pemetaan tersebut sementara runtime lain mungkin menggunakannya. Fungsionalitas tersebut juga akan ditambahkan ke .NET Framework dan Mono.

Saya juga berpikir masalah ini terutama terbatas pada rakitan pihak ketiga sumber tertutup, baik FNA dan OpenTK dapat memiliki kode yang diedit oleh pengguna jika nama dllmap buruk, dalam hal ini masalah harus diangkat tentang hal itu dan mudah-mudahan diganti namanya untuk memperbaikinya di repositori utama.

Sebenarnya, .NET Framework bahkan tidak mendukung dllmap di windows.

Ini adalah kekeliruan utama berpikir di sini. Ketika .Net adalah "hanya Windows" ada perjanjian tersirat bahwa Anda menulis sesuatu untuk Windows dan kemudian membuatnya bekerja di Mono. Anggapan itu tidak lagi benar. Pustaka mungkin berasal dari OSX atau Linux dan sekarang tidak berfungsi di Windows tanpa fungsionalitas DllMap.

Saya dapat melihat poin yang Anda coba sampaikan, tetapi saya masih berpikir jika penulis perpustakaan bermaksud mendukung platform, mereka akan menambahkan DllMap sesuai dengan nama terbaik.

Intinya adalah ada banyak pengetahuan khusus domain yang mungkin tidak disadari oleh penulis host dan yang mungkin tidak dipedulikan oleh penulis perpustakaan. Ini adalah tentang penyebaran xcopy yang memungkinkan biner yang sama untuk berjalan di beberapa platform dengan sedikit usaha: DllMap menyediakan fungsionalitas itu hari ini. Kami mencoba mencari alternatif yang lebih cocok untuk CoreCLR. Mengecualikan fungsi ini bukanlah pilihan.

Saya mungkin juga harus eksplisit tentang ini:
1) tujuannya bukan untuk menyalin memori seperti pada contoh msvcrt.dll
2) tujuannya bukan untuk membuat FNA, OpenTK berfungsi.

Alternatif dan/atau alasan untuk ini bukanlah yang dicari di sini.

Nuget adalah mekanisme distribusi default untuk .NET Core library. Nuget memiliki dukungan untuk distribusi implementasi khusus platform dari perpustakaan yang sama. Ini cukup sering digunakan saat ini, misalnya untuk menyediakan API umum di seluruh perangkat. Versi khusus platform https://github.com/dotnet/corefx akan menggunakan mekanisme distribusi Nuget yang sama.

Apakah ada sesuatu yang benar-benar hilang dalam konstruksi yang ada - DllImport reguler, kompilasi bersyarat, dan distribusi perpustakaan khusus platform melalui Nuget - untuk mengatasi masalah pembuatan perpustakaan khusus platform untuk .NET Core. Apa yang mencegah FNA atau OpenTk untuk .NET Core ada dalam paket ini?

Meretas binari yang ada untuk membuatnya berjalan di tempat yang tidak dirancang untuknya adalah kasus yang berbeda. Itu tergantung pada keberuntungan apakah hasilnya akan berhasil 100%. Saya tidak berpikir bahwa runtime harus memiliki dukungan bawaan untuk itu. Dalam kasus di mana itu perlu dilakukan sebagai jalan pintas untuk membuat sesuatu bekerja, saya pikir itu harus ditangani dengan alat penulisan ulang IL, misalnya menggunakan Cecil. Saya tidak akan merekomendasikannya sebagai solusi produksi.

@jkotas IL memiliki manfaat sebagai platform-agnostik. Ada banyak perpustakaan asli (sumber terbuka atau tidak) yang menyediakan API yang sama di seluruh platform. Pengaturan perakitan DllMap yang disematkan akan memungkinkan pengembang untuk membuat lintas platform, kompilasi sekali perpustakaan/rakitan, seperti yang telah mereka lakukan dengan .NET Framework/Mono. Jika .NET Core melihat ini dengan cara yang berbeda (yaitu harus ada perakitan terpisah untuk setiap platform) maka akan ada overhead tambahan.

Beberapa fungsi DllImport perlu ditentukan untuk setiap platform dukungan, dan jika pengembang memutuskan untuk tidak mengkompilasi rakitan terkelola terpisah untuk setiap percabangan run-time platform akan terjadi untuk mengakses fungsi asli yang benar untuk platform itu.

Menggunakan Nuget akan membantu masalah distribusi jika itu masalahnya, tetapi itu masih berarti kita perlu mendefinisikan beberapa DllImport yang sama dengan hanya nama modul yang sedikit diubah berdasarkan platform (foo.dll, foo.dylib, foo.so) .

Pengaturan perakitan DllMap yang disematkan akan memungkinkan pengembang untuk membuat lintas platform, kompilasi sekali perpustakaan/rakitan, seperti yang telah mereka lakukan dengan .NET Framework/Mono.

Sangat baik dikatakan. Memang itulah kebutuhan akan fitur ini.

Apakah ada sesuatu yang benar-benar hilang dalam konstruksi yang ada - DllImport reguler, kompilasi bersyarat, dan distribusi perpustakaan khusus platform melalui Nuget - untuk mengatasi masalah pembuatan perpustakaan khusus platform untuk .NET Core.

Perbedaan utama di sini adalah bahwa kita tidak berbicara tentang mendistribusikan binari asli. Kita berbicara tentang pembungkus untuk binari asli yang ada. Pengembangan yang didorong oleh IF-DEF menempatkan kita dalam situasi di mana pembungkus perlu didistribusikan kembali untuk setiap platform. Jika saya xcopy deploy, MyFoo.dll (managed wrapper) mungkin untuk Linux tetapi target penyebaran saya adalah Windows atau OSX sehingga crash dengan DllNotFoundException. Ini sangat berbeda dari perpustakaan CoreCLR di mana seluruh OS API berbeda dari platform ke platform dan abstraksi lebih masuk akal. OpenCL adalah API yang sama di setiap platform, tetapi nama perpustakaannya tidak. Kontra produktifnya mengharuskan semua orang untuk mengkompilasi ulang (sehingga #ifdef mengubah nama perpustakaan) untuk setiap platform target.

Kita harus tetap berpegang pada bentuk paling sederhana dari ini: Kompilasi sekali, jalankan di mana saja.

@xanather Menambahkan akhiran khusus platform (foo.dll, foo.dylib, foo.so) tidak menjadi masalah. Itu sudah dilakukan di CoreCLR. Kerangka mengambil keuntungan dari itu. Periksa misalnya https://github.com/dotnet/corefx/blob/master/src/Common/src/Interop/Unix/Interop.Libraries.cs. Diskusi ini tentang kasus kompleks yang memerlukan penulisan ulang PINvoke khusus platform khusus.

@OtherCrashOverride xcopy penyebaran aplikasi mandiri dari satu platform ke platform yang berbeda bukanlah tujuan .NET Core dirancang. Kerangka kerja adalah aplikasi-lokal di .NET Core. Ini berarti bahwa binari aplikasi mentah adalah khusus platform karena kerangka kerja khusus platform sebagian disertakan.

Anda akan dapat xcopy men-deploy aplikasi project.json, dan kemudian membiarkan dnx mengurus pemuatan bagian-bagian khusus platform yang tepat seperlunya.

@jkotas , saya tidak percaya saya tidak menyadarinya. Apakah itu secara otomatis memeriksa direktori lain juga berdasarkan platform? Yaitu jika menentukan DllImport["OpenAL"] di Mac OS X akan mencari /System/Library/Frameworks/OpenAL.framework/OpenAL? Ada beberapa skenario di mana nama file/lokasi dari pustaka asli yang sama pada platform yang berbeda bisa sangat berbeda, meskipun API-nya sama.

Sunting:
saya melihat bahwa
https://github.com/dotnet/corefx/blob/master/src/Common/src/Interop/OSX/Interop.Libraries.cs
Harus secara eksplisit mengetikkan nama lengkap untuk perpustakaan OS X, jadi menggunakan OpenAL pada banyak platform menggunakan DllImport tunggal tidak dimungkinkan dalam kondisi saat ini? (bisa salah).

xcopy penyebaran aplikasi mandiri dari satu platform ke platform yang berbeda bukanlah tujuan .NET Core dirancang.

Jika fungsionalitas DllMap dianggap tidak cocok untuk CoreCLR, itu tidak masalah. Seperti yang dinyatakan sebelumnya, dunia tidak akan berakhir. Apa yang diminta, sebagaimana disebutkan sebelumnya, adalah pernyataan resmi untuk itu:
"Fungsi DllMap tidak akan didukung dan permintaan tarik untuk itu tidak akan diterima." Kemudian semua orang dapat melanjutkan hidup mereka. ;-)

Aku mulai kehilangan arah pembicaraan. Saya pikir alasan utamanya adalah kalian memberikan beberapa contoh bagus tentang hal-hal yang seharusnya berhasil atau yang menyebabkan masalah di masa lalu, tetapi semua itu sekarang tersembunyi di beberapa halaman teks. Apakah mungkin untuk meringkas itu dan tetap up-to-date entah bagaimana?

Semakin saya memikirkannya, semakin saya menyukai .dll.map sebagai perakitan. Tidak hanya membuatnya jelas dapat diuraikan ke runtime, itu juga dapat ditandatangani secara digital.

Kuncinya tampaknya adalah perpustakaan yang tidak xcopy digunakan dengan aplikasi tetapi sebaliknya tersedia di "sistem". Saya pikir penting bagi penulis perpustakaan untuk melakukan "sesuatu" untuk meningkatkan kehidupan mereka (dllmap/api sesuatu) untuk memuat lib asli yang tepat pada waktu yang tepat dari tempat yang tepat. DllImport cukup terbatas dalam hal ini

C# memiliki konsep "Extension Methods", bagaimana jika kita melihat ini sebagai konsep "Extension Assemblies"? Cara menghias rakitan yang sudah ada (dengan rakitan lain).

MyFoo.dll
(Perakitan pembungkus terkelola)

MyFoo.dll.[nama_platform].dll

MyFoo.dll.Linux.dll
[perakitan: DllMap(Dll="foo", Target="libfoo.so")]

MyFoo.dll.FreeBSD.dll
[perakitan: DllMap(Dll="foo", Target="libfoo.so")]
[perakitan: DllMap(Dll="foo", EntryPoint="bar", Target="libother.so", TargetEntryPoint="foobar")]

MyFoo.dll.Windows.dll
[perakitan: DllMap(Dll="foo", Target="foo.dll")]

Detail ini perlu dipikirkan dengan lebih baik, tetapi sebagai titik awal yang kasar, ini memecahkan banyak masalah.

@jkotas Menambahkan .so/.dylib/.dll hanyalah sebagian kecil dari permainan karena namanya mungkin sangat berbeda (lihat contoh saya tentang libssl.so/dylib vs libeay32.dll ). Tentu saja salah satu cara untuk menyelesaikan ini adalah dengan mengompilasi nama dan DllImports secara kondisional dan menghasilkan satu Majelis per platform, tetapi seperti yang saya katakan, saya pikir ini adalah overhead organisasi utama bagi penulis perpustakaan. Poin @davidfowl juga bagus.

Pembantu built-in untuk memungkinkan satu perakitan untuk bekerja pada beberapa platform melalui misalnya atribut DllMap sederhana tampak seperti kemenangan besar untuk pekerjaan yang sangat sedikit bagi saya.

@Tragetaschen Deskripsi masalah di posting asli saya masih akurat, saya pikir.

@akoeplinger Saya pikir solusi yang Anda usulkan adalah yang terbaik. Ini akan membantu menulis rakitan lintas platform yang menghabiskan banyak waktu untuk perpustakaan asli lintas platform - yang menurut saya merupakan alasan utama mengapa masalah ini ada.

Bagi mereka yang tidak dapat menemukannya, akoeplinger memposting ini:

C# [assembly: DllMap("foo", "foo.dll", OSName.Windows)] [assembly: DllMap("foo", "libfoo.dylib", OSName.OSX)] [assembly: DllMap(Dll="foo", Target="libfoo.so", OS=OSName.Linux)] [assembly: DllMap(Dll="dl", Name="dlclose", Target="libc.so", OS="FreeBSD")]

@xanather , catatan samping tentang dllimport dan penggantian nama binari (saya tidak sepenuhnya yakin apakah itu berlaku untuk runtime P/Invoke dan coreclr secara umum):

Windows menyajikan beberapa keterbatasan dengan dllimport, yang dapat dihindari dengan menggunakan pendekatan 'delay load'. Saat dikompilasi, VC linker dapat membuang LNK1194 , jika data 'diimpor' dari biner yang dieksekusi oleh 'biner yang diimpor'. Faktanya, mungkin tidak ada cara 'layak' untuk memperbaikinya. Agaknya, vtable perlu disesuaikan (dapatkah 'adjustor thunks' dimodifikasi untuk mengganti nama jalur dllimport?) dan artikel kering ini mungkin memiliki beberapa petunjuk: http://www.cultdeadcow.com/tools/pewrap.html.

Kasus di tangan, permintaan tarik ini https://github.com/iojs/io.js/pull/1251 dan banyak laporan bug terkait di sana mengeluarkan pelacak untuk: _mengganti nama node.exe menjadi random_name.exe atau iojs.exe ke myFavoriteTool. exe gagal memuat paket npm dengan C++ modules_.

Pada *nix dan *BSD, tidak ada masalah seperti itu dan solusi terbatas seperti penundaan pemuatan tidak diperlukan.

Itu mengingatkan saya pada masalah terkait. Saat mencoba memuat DLL yang gagal dimuat karena dependensi DLL hilang, pengecualian yang dilemparkan adalah DllNotFoundException. Ini menyebabkan banyak kebingungan dengan DllMap Mono karena tersirat nama DLL (yang dipetakan) tidak ditemukan ketika sebenarnya ada kesalahan memuat DLL karena ketergantungan.

Ini memunculkan pertanyaan: apakah kita memerlukan DllLoadException untuk melengkapi DllNotFoundException?

Ini adalah ide yang sangat buruk untuk menanamkan pemetaan pada biner. Ini mungkin berhasil untuk beberapa kasus, tetapi dalam kehidupan nyata, pustaka sistem, versi, dan penyebaran versi yang berbeda adalah masalah yang dihadapi pengguna akhir.

Ini benar-benar metode yang nyaman bagi orang yang menggunakan aplikasi untuk menyesuaikannya dengan idiom pada sistem operasi host, sesuatu yang biasanya tidak diingat oleh pengembang asli.

Saya harus mengatakan saya setuju dengan @migueldeicaza di sini. Saya tidak mengatakan menggunakan sistem pemetaan yang digunakan mono adalah jalan ke depan, tetapi saya pikir pemetaan harus diizinkan ada di luar Majelis itu sendiri. Juga, @davidfowl , Anda memberi tahu saya kemarin bahwa ada pekerjaan yang sedang dilakukan tentang cara menangani rakitan asli dalam paket nuget, bagaimana jika pemetaan dapat hidup dalam paket nuget tempat Anda mendapatkan gambar asli? Dengan begitu (setidaknya untuk Core CLR) Anda tidak perlu meretas variabel PATH env.

bagaimana jika pemetaan bisa hidup dalam paket nuget tempat Anda mendapatkan gambar asli? Dengan begitu (setidaknya untuk Core CLR) Anda tidak perlu meretas variabel PATH env.

@Alxandr membaca seluruh utas lagi :smile: .

Karena terus dibesarkan, saya pikir saya harus mengulangi bahwa NuGet bukanlah pilihan untuk memecahkan masalah ini. Sementara pengembangan CoreCLR tampaknya saat ini berfokus pada laser pada ASP.Net, masalah ini mewakili fakta bahwa tidak semua konsumen CoreCLR akan menggunakannya untuk ASP.Net. NuGet adalah ketergantungan yang sangat beracun bagi mereka: Itu tidak diinginkan atau diizinkan.

Nah jika itu adalah ide yang buruk untuk diintegrasikan dengan Majelis itu sendiri dan file .config sekolah lama eksternal dikesampingkan maka satu-satunya opsi lain yang saya lihat adalah:

  1. Tambahkan sesuatu seperti .config tetapi jauh lebih ringan dan tidak dimuat secara implisit. Gunakan penguraian XML untuk itu. File XML sederhana yang dapat diberi nama apa pun yang diputuskan oleh pemrogram (.config masih dapat digunakan, atau .xml). Memungkinkan untuk memanggil metode dari domain yang sedang berjalan untuk memuat file tersebut secara eksplisit. Bisa jauh lebih ringan daripada implementasi System.Configuration dan hanya akan digunakan untuk dllmapping. Ini berarti CoreCLR harus dapat mengurai file XML.
  2. Lakukan apa yang disebutkan OtherCrashOverride dan gunakan perakitan satelit untuk menentukan pemetaan.
  3. Gunakan beberapa jenis format file lain (json?).
  4. Singkirkan ide sama sekali yang dapat menyebabkan garpu CoreCLR yang tidak perlu dan kemungkinan implementasi pihak ketiga yang mencoba meniru dllmap Mono (ini dijamin akan terjadi, dan sudah, karena CoreCLR lebih banyak digunakan di luar ASP.NET/NuGet).

Secara pribadi saya pikir 1. adalah yang terbaik, meskipun saya pikir itu sudah dikesampingkan.

Kami sudah membuka Kotak Pandora yang satu ini. Saat ini, saya berencana untuk menggunakan pendekatan perakitan satelit karena dapat ditandatangani secara digital tanpa memperkenalkan sesuatu yang baru ke runtime. Selain itu, saya berencana untuk membuat alat baris perintah untuk menghasilkan rakitan dari format XML Mono sebagai input.

Perlu disebutkan bahwa ini bukan tanpa prioritas di CoreCLR. Gambar asli juga menggunakan teknik serupa: mscorlib.ni.dll

Kita harus melihat kemungkinan menyelaraskan dengan pekerjaan yang dilakukan di sana. (#1034, dotnet/coreclr#1035)

@OtherCrashOverride Anda terus menyebutnya sebagai "beban", tapi saya tidak melihat apa yang memberatkan tentang mengharuskan pengembang perpustakaan untuk _mengetahui nama-nama dependensinya_. Dia harus mengetahui informasi itu untuk membangun dan menguji pada berbagai platform yang dia dukung, jadi itu tidak masalah.

Tapi bagaimana dengan platform lain yang tidak dia dukung? Itu tampaknya menjadi kekhawatiran utama Anda, dan jawabannya ada di sana sebagai bagian dari pertanyaan: dia tidak mendukung mereka. Ini benar-benar sesederhana itu, dan mengatakan "tetapi lintas platform!" tidak mengubah itu.

Saya tahu jika saya membuat sesuatu yang seharusnya berjalan di Windows dan Android, dan seseorang mulai mengirimkan laporan bug terhadap OSX, saya akan menutupnya sebagai Tidak Didukung karena saya tidak memiliki Mac. Saya akan memberi tahu penulis laporan bug bahwa dia tahu di mana repo itu, dan jika dia ingin membuat kode saya berfungsi di Mac, dia bebas _membuat kode saya berfungsi_ di Mac, tapi itu bukan masalah saya. Dan begitu seseorang pergi dan membuat kodenya berfungsi, mereka dapat menyumbangkan tambalan kembali untuk diintegrasikan ke dalam pustaka inti, dan kemudian nama pustaka baru untuk platform baru akhirnya dikompilasi ke dalam sumbernya lagi dan semua orang senang. Jadi apa masalahnya?

Jadi apa masalahnya?

Diskusi ini telah melakukan pekerjaan yang sangat baik untuk menggambarkan masalah. Lihat banyak contoh seperti 'memcpy' yang tidak mungkin bahkan untuk pengembang "terbebani" yang "tahu nama dependensinya"

jawabannya ada di sana sebagai bagian dari pertanyaan: dia tidak mendukung mereka.

dia bebas membuat kode saya berfungsi di Mac, tapi itu bukan masalah saya.

Ini menggambarkan dengan sempurna mengapa proposal fitur ini wajib.

@OtherCrashOverride Saya membaca seluruh diskusi sebelum saya menjawab, dan saya tidak melihat satu pun contoh dari sesuatu yang mustahil.

Jika saya menggunakan memcpy , saya memasukkan nama untuk platform yang saya dukung. Jika nama salah satu pustaka itu berubah di versi mendatang, maka _mereka bukan pustaka yang sama lagi, dan itu tidak didukung._ Atau apakah Anda dengan jujur ​​menyarankan bahwa rakitan "kompilasi sekali, jalankan di mana saja" harus bertransisi dengan mulus dari asli libFoo v. X ke libFoo v. Y tanpa intervensi dari luar? Karena siapa pun yang pernah benar-benar bekerja secara ekstensif dengan kode asli dapat memberi tahu Anda bahwa itu tidak akan terjadi; cukup sulit untuk menjaga kompatibilitas antar versi ketika Anda memiliki rakitan yang menggambarkan diri sendiri dengan metadata yang kaya untuk membantu tautan keluar!

Jadi begitu kita membuang kotak jerami konyol seperti itu, saya tidak melihat kemustahilan yang berkaitan dengan tidak dapat mengetahui masa depan; tidak perlu dan tidak mau. Biarkan versi berikutnya khawatir tentang apa yang akan terjadi di masa depan.

jawabannya ada di sana sebagai bagian dari pertanyaan: dia tidak mendukung mereka.

dia bebas membuat kode saya berfungsi di Mac, tapi itu bukan masalah saya.

Ini menggambarkan dengan sempurna mengapa proposal fitur ini wajib.

Saya pasti melewatkan sesuatu, karena poin yang saya buat adalah bahwa ini menggambarkan dengan sempurna mengapa ini _tidak_ diperlukan. Keberatan mengisi beberapa tautan dalam rantai penalaran?

Saya tidak melihat satu pun contoh dari sesuatu yang mustahil.

Saat ini kami memiliki DllImport yang memungkinkan menentukan satu nama perpustakaan. Runtime C memiliki nama yang berbeda pada platform yang berbeda. Oleh karena itu, saat ini tidak mungkin untuk menggunakan lintas platform "memcpy".

Atau apakah Anda dengan jujur ​​menyarankan bahwa perakitan "kompilasi sekali, jalankan di mana saja" Anda harus bertransisi dengan mulus dari libFoo v. X asli ke libFoo v. Y tanpa intervensi dari luar?

Meskipun bukan maksud dari proposal ini, tentu saja mungkin karena "memcpy" memiliki tanda tangan API yang sama di beberapa versi runtime MS VC serta Linux libc.so.X.

Karena siapa pun yang pernah benar-benar bekerja secara ekstensif dengan kode asli dapat memberi tahu Anda bahwa itu tidak akan terjadi

Ini adalah keyakinan saya bahwa saya dan orang lain (seperti @migueldeicaza yang berkomentar sebelumnya) telah bekerja dengan kode asli dan cukup kompeten dan cukup memenuhi syarat untuk berbicara tentang masalah ini.

Keberatan mengisi beberapa tautan dalam rantai penalaran?

"dia tidak mendukung mereka." dan "tapi itu bukan masalah saya." persis mengapa fitur ini diperlukan. Mereka yang mendukung platform dan memiliki masalah adalah orang-orang yang membutuhkan cara sederhana untuk menyelesaikannya. Membuat akun GitHub, mengkloning repo, membuat cabang, mengirimkan tambalan yang tidak akan pernah diterima di hulu (karena DllImport hanya mengizinkan satu nama), memelihara tambalan di seluruh versi hulu, menerbitkan dan memelihara biner dengan nama yang sama persis dengan hulu, dll rumit. Jauh lebih mudah untuk menambahkan satu file "DllMap".

TL;DR - Saya tidak peduli Anda tidak peduli dengan platform saya atau penggunaan CoreCLR. Anda tidak harus. Proposal ini menyelamatkan Anda dari "beban" itu. #Sama-sama

[sunting: Saya meninggalkannya sebagai flamebait. "Anda" memang ejaan yang tidak tepat]

Membuat akun GitHub, mengkloning repo, membuat cabang, mengirimkan tambalan yang tidak akan pernah diterima di hulu (karena DllImport hanya mengizinkan satu nama), memelihara tambalan di seluruh versi hulu, menerbitkan dan memelihara biner dengan nama yang sama persis dengan hulu, dll rumit.

Aku mengerti. Anda berpikir bahwa karena saya tidak percaya melakukan _your_ thing, saya pikir solusi yang tepat adalah melakukan _nothing_? Ini tidak benar; status quo jelas tidak memadai, tetapi sebelum Anda menegur orang lain tentang tidak membaca seluruh utas, Anda benar-benar harus melihat proposal untuk atribut [assembly: DllMap()] hipotetis. Itu akan melakukan pekerjaan yang jauh lebih baik untuk menyelesaikan ini, karena itu akan membantu menyelesaikan masalah mendasar yang lebih penting juga: memastikan itu benar-benar berfungsi.

Tahukah Anda apa yang terjadi jika saya menulis dan menerbitkan libAwesome untuk Windows, dan kemudian Bob McCodeMonkey mengemasnya kembali dengan tambalan untuk membuatnya berfungsi di Mac, dan kemudian karena perbedaan tingkat platform yang halus, itu tidak benar-benar _work_ di Mac?

Apa yang terjadi adalah saya akhirnya mendapatkan lusinan laporan bug Mac dari pihak ketiga yang tidak tahu apa-apa yang tidak menyadari bahwa saya tidak ada hubungannya dengan proses membuatnya bekerja di Mac, dan Bob akhirnya mendapatkan lusinan laporan bug tentang fungsionalitas inti dari Pengguna Mac meskipun masalah mendasar ada di basis kode saya, yang saya _tidak_ dapatkan. Kabel tersangkut di mana-mana dan produktivitas menurun. Penambalan eksternal idealnya bukan sesuatu yang ingin kita aktifkan sama sekali; _mengabadikannya sebagai fitur inti_ benar-benar gila!

Cara yang tepat untuk menangani ini adalah membuat Bob bekerja membuat dan mengirimkan tambalan (yang akan diterima dan digabungkan karena atribut DllMap memungkinkan beberapa resolusi ada berdampingan) dan kemudian versi baru dengan dukungan Mac akan diterbitkan. Ketika laporan bug khusus Mac masuk, saya menugaskannya ke Bob, yang secara resmi ada di radar sekarang karena dia harus bekerja dengan saya untuk membuat patchnya terintegrasi, dan ketika bug tingkat umum ditemukan dari pengguna Mac, saya mendapatkannya dan saya bisa memperbaikinya. Menang/menang.

sebelum Anda menegur orang lain tentang tidak membaca seluruh utas, Anda benar-benar harus melihat proposal untuk atribut [perakitan: DllMap()] hipotetis.

Percakapan kemudian terus berkembang dan mendalilkan memungkinkan atribut ada di rakitan "satelit" eksternal. Saat ini kami tidak memiliki atribut DllMap. Ini adalah artefak dari proposal ini.

Apa yang terjadi adalah saya akhirnya mendapatkan lusinan laporan bug Mac dari pihak ketiga yang tidak mengerti yang tidak menyadari bahwa saya tidak ada hubungannya dengan proses membuatnya bekerja di Mac

Aspek tanda tangan digital yang disebutkan sebelumnya dari proposal mencakup kasus ini.

Cara yang tepat untuk menangani ini adalah membuat Bob bekerja membuat dan mengirimkan tambalan

Seperti disebutkan sebelumnya di utas, skenario mencakup kasus di mana perpustakaan disediakan oleh vendor pihak ketiga dan kode sumber tidak tersedia (dan pembongkaran atau modifikasi biner merupakan pelanggaran terhadap EULA). Ini adalah masalah dunia nyata, bukan skenario hipotetis. CoreCLR tidak dalam posisi untuk mendikte dogma untuk bisnis.

Penambalan eksternal idealnya bukan sesuatu yang ingin kita aktifkan sama sekali; mengabadikannya sebagai fitur inti adalah kegilaan belaka!

Ini bukan proposal untuk menambal. Ini adalah proposal untuk menetapkan metode standar untuk mengomunikasikan informasi tambahan ke runtime untuk membantu penaut dinamis. Itu tidak mengubah kode atau tanda tangan P/Invoke. Juga seperti yang disebutkan sebelumnya, ini bukan tanpa prioritas: DNX sudah menyediakan bantuan untuk linker dinamis dan file "satelit" digunakan untuk memuat gambar asli yang dikompilasi.

Secara anekdot, Mono telah mengimplementasikan solusi DllMap selama bertahun-tahun. Kekacauan tidak terjadi sehingga dunia open source terjerumus ke dalam kegelapan. Jadi contoh "Bob McCodeMonkey" yang diberikan sebelumnya mungkin hanya hiperbola.

Apakah ini masih terjadi? Jika ada banyak gesekan untuk menambahkan fitur DllMap berbasis atribut lalu bagaimana dengan apa yang dikatakan jkotas beberapa waktu lalu tetapi sesuatu yang lebih seperti plugin dan ramah.

jkotas berkomentar pada 6 Mei:

Metode AssemblyLoadContext.LoadUnmanagedDll yang dapat diganti dirancang untuk memungkinkan penyesuaian pemuatan target DllImport. Metode ini mendapatkan nama dll dan mengembalikan pegangan OS. Algoritme yang disediakan pengguna dapat menyelidiki versi .so, menggunakan pengikatan gaya dllmap, atau melakukan sesuatu yang pintar seperti dnx ;-)

Akan lebih baik untuk mempublikasikan sampel yang mengimplementasikan dllmap menggunakan AssemblyLoadContext.

Saat ini saya tidak tahu bagaimana mengganti AssemblyLoadContext dengan benar dan saya merasa pengguna .NET Core tidak perlu melakukan hal seperti itu. Tutorial/contoh akan dihargai.

Bagaimana dengan mengekspos metode pada AppContext yang memungkinkan Anda untuk mengatur panggilan balik/delegasi khusus yang berhubungan dengan memuat pustaka asli berdasarkan nama yang diminta? Mengembalikan IntPtr.Zero akan memaksanya untuk mencoba memuatnya dengan cara yang ditentukan default/CLR. Kalau tidak, contoh yang menunjukkan cara mengganti metode AssemblyLoadContext sudah cukup.

Tidak apa-apa, ini lebih mudah dari yang saya kira. Bagi mereka yang belum tahu, cukup berikan turunan dari AssemblyLoadContext turunan ke https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs# L247.

Saya pikir https://github.com/dotnet/coreclr/issues/937 harus ditangani sebelum penggunaan AssemblyLoadContext bisa menjadi solusi yang tepat untuk masalah ini.

Juga mengikat AssemblyLoadContext khusus tampaknya tidak mungkin saat ini dengan DNX (atau saya hanya salah menggunakannya: https://github.com/dotnet/coreclr/issues/1119 - saat ini tidak ada dokumen/contoh penggunaan ini - tampaknya mudah di API jadi mungkin rusak).

Salin/Tempel bagian yang relevan dari diskusi dari dotnet/coreclr#1257

Saya tidak memiliki masalah dengan kebijakan yang mencegah DllImport terjadi kecuali telah secara eksplisit 'dipetakan' untuk platform target. Namun, kita perlu dotnet/coreclr#930 sebelum itu bisa terjadi. Saya membayangkan ini di mana DllImport (atau penggantinya) memegang token yang mewakili perpustakaan asli. Kemudian ada beberapa cara eksplisit untuk menyatakan, di luar Majelis, nama perpustakaan apa yang akan dipetakan.

[Sunting]
Jenis seperti bagaimana string diinternasionalkan.

Melihat lebih jauh ke dalam peta dll sebagai analog dengan internasionalisasi terlihat menjanjikan. String untuk pustaka lengkap bernama disimpan dalam file sumber daya (resx) dan alih-alih budaya, pengidentifikasi platform digunakan. File sumber daya dapat disematkan dalam rakitan untuk platform yang diketahui penulis, dan dapat menjadi satelit untuk platform yang tidak disertakan oleh penulis.

Sekali lagi, patch eksternal melalui "satelit" adalah ide yang buruk, karena mendorong kebingungan pengguna akhir tentang siapa yang mendukung apa. Hanya karena Anda tidak menyadari hal itu terjadi di Mono--sebuah sistem yang, sejujurnya, tidak pernah mendapat pangsa pasar yang sangat besar karena komunitas .NET pada umumnya dan komunitas Open Source/FLOSS pada umumnya melihatnya sebagai "orang luar," untuk alasan mereka sendiri yang berbeda--tidak berarti bahwa itu tidak akan menjadi masalah nyata ketika CoreCLR lintas-platform, yang didukung oleh Microsoft, menjadi "resmi."

Membandingkannya dengan internasionalisasi string menyesatkan, karena string yang dikodekan dengan keras tidak mengubah perilaku suatu program, (kecuali tentu saja konfigurasi, skrip, atau serupa, yang umumnya tidak diinternasionalkan,) tetapi versi eksternal yang berbeda perpustakaan lakukan.

Kita dapat menambahkan tanda sebagai parameter ke atribut baru yang dapat ditentukan oleh pembuat perpustakaan untuk melarang penggunaan sumber daya eksternal untuk resolusi. Namun, itu mulai terdengar seperti Manajemen Hak Digital pada saat itu.

Dengan risiko terdengar kurang ajar, saya tidak berpikir Microsoft benar-benar peduli atau bahkan menginginkan fitur ini. Jadi kemungkinan kita akan berakhir dengan semua orang menggulirkan solusi mereka sendiri. Dalam hal itu, siapa pun dapat mengizinkan atau melarang fitur apa pun yang mereka inginkan.

Tunggu, menurut Anda Microsoft tidak menginginkan _what_, khususnya? Karena beberapa cara otoritatif untuk menyelesaikan impor lintas platform jelas diperlukan untuk lintas platform, dan jika mereka tidak menginginkan lintas platform, kami tidak akan melihat semua platform lain yang didukung ditampilkan dengan jelas di halaman utama proyek.. .

Mereka (Microsoft) memiliki solusi lintas platform yang mereka inginkan di dotnet/coreclr#1257.

...yang tampaknya mereka pertimbangkan kembali sekarang karena implikasi keamanan dari "solusi" itu telah ditunjukkan.

@OtherCrashOverride Saya belum pernah melihat siapa pun dari perusahaan kami mengatakan bahwa dotnet/coreclr#1257 adalah solusi terakhir kami. Dari apa yang Anda simpulkan seperti itu? Tapi sepertinya kami belum menemukan solusi akhir yang masuk akal dan kami membutuhkan sesuatu untuk sementara sampai kami tiba di sana.

Dari apa yang Anda simpulkan seperti itu?

@janvorli
Terutama dari kelalaian. Masalah ini dibuka 5 Mei. Pada 9 Mei dalam masalah ini, tanggapan dari Microsoft diminta. Sekarang 17 Juli. Selama waktu itu saya mengharapkan seseorang untuk mengatakan "kami sedang menyelidiki ini" jika itu masalahnya.

Niat saya bukan untuk bermusuhan. Namun, orang-orang mencoba menjalankan bisnis dan perlu menjalankan rencana. Pentingnya pernyataan itu adalah sebagai berikut: Tidak ada yang bersifat pribadi.

sementara itu kami membutuhkan sesuatu sampai kami tiba di sana.

Saya mengakui itu di edisi lain. Saya juga mendukung itu dalam masalah lain.

Tujuannya adalah untuk bekerja sama untuk menyelesaikan sesuatu dalam hal ini. Namun, itu adalah percakapan yang sangat sepihak.

Saya mulai membuat prototipe ini. Itu dapat dilakukan tanpa perubahan runtime. Itu hanya menjadi lebih bertele-tele daripada DllImport. "NativeLibrary" adalah kelas yang menangani semuanya secara transparan. Ini adalah kelas dasar abstrak dengan metode statis. Platform baru hanya mensubklasifikasikannya dan semuanya berfungsi (setidaknya secara teori, ini masih hanya prototipe dengan PosixNativeLibrary dan Win32NativeLibrary).

    class CRuntimeInterop
    {
        static string LibName = "libc.so";


        static CRuntimeInterop()
        {
            // In the future, this will be read automatically from somewhere
            NativeLibrary.ProcessMapping(PlatformID.Win32NT, LibName, "memcpy", "msvcrt.dll", "memcpy");
        }


        #region memcpy

        // Calling convention and [MarshalAs] from DllImport go here
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        delegate IntPtr memcpy_prototype(IntPtr dest, IntPtr source, IntPtr count);

        // Keep the delegate to speed up subsequent calls
        static memcpy_prototype memcpy_delegate;

        // Expose the native function
        public static IntPtr Memcpy(IntPtr dest, IntPtr source, IntPtr count)
        {
            if (memcpy_delegate == null)
            {
                // Demand load the delegate
                memcpy_delegate = NativeLibrary.Map<memcpy_prototype>(LibName, "memcpy");
            }

            return memcpy_delegate(dest, source, count);
        }

        #endregion
    }

Saat ini menggunakan DllImport untuk memanggil rutinitas perpustakaan khusus OS. Ini dapat diubah untuk memanggil PAL alih-alih menghilangkan kebutuhan untuk subclass.

Saya juga harus mencatat bahwa persyaratannya adalah kompatibel dengan AOT. Jadi saya menghindari melakukan patching IL atau pembuatan on-the-fly dari stub.

Mengorbankan pemuatan permintaan dan menambahkan atribut khusus menghasilkan representasi yang lebih ringkas.

    class CRuntimeInterop
    {
        const string LibName = "libc.so";


        static CRuntimeInterop()
        {
            // In the future, this will be read automatically from somewhere
            NativeLibrary.ProcessMapping(PlatformID.Win32NT, LibName, "memcpy", "msvcrt.dll", "memcpy");
            NativeLibrary.Bind(typeof(CRuntimeInterop));
        }


        #region memcpy

        // Calling convention and [MarshalAs] from DllImport go here
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate IntPtr memcpy_prototype(IntPtr dest, IntPtr source, IntPtr count);

        [NativeLibrary(LibName, "memcpy")]
        public static readonly memcpy_prototype Memcpy;

        #endregion
    }

Memanggilnya tidak berbeda dengan memanggil fungsi DllImport.

CRuntimeInterop.Memcpy(handleDest.AddrOfPinnedObject(), handleSource.AddrOfPinnedObject(), (IntPtr)source.Length);

Pendekatan ini menghasilkan metode tercepat yang tersedia untuk P/Invoke. Pengorbanan adalah waktu pengikatan tambahan di static cctor(). Selain itu, jika titik masuk tidak ditemukan, penginisialisasi tipe gagal.

Sebagai bonus, ini sepenuhnya 100% kompatibel dengan .Net dan Mono penuh.

Ini dia refactored sedikit dan pemetaan dipindahkan ke atribut:

    class CRuntimeInterop
    {
        const string UnixLibName = "libc.so";
        const string WindowsLibName = "msvcrt.dll";


        static CRuntimeInterop()
        {
            NativeLibrary.Bind(typeof(CRuntimeInterop));
        }


        #region memcpy

        // Calling convention and [MarshalAs] from DllImport go here
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate IntPtr memcpy_prototype(IntPtr dest, IntPtr source, IntPtr count);

        [NativeLibraryBinding(PlatformID.Unix, UnixLibName, "memcpy")]
        [NativeLibraryBinding(PlatformID.Win32NT, WindowsLibName, "memcpy")]
        public static readonly memcpy_prototype Memcpy;

        #endregion
    }

Tampaknya delegasi tidak akan mengambil "__arglist" sehingga metode ini kehilangan dukungan C vaargs.
(kecuali ada peretasan yang pintar)

Saya menambahkan dukungan pemetaan eksternal sebagai file AssemblyName.NativeBindings.xml. Perubahan tak terduga dari DllMap adalah karena tidak ada lagi pemetaan tunggal, Anda tidak dapat memetakan menurut nama pustaka asli. Sebaliknya pemetaan dilakukan pada jenis dan nama Bidang.

<?xml version="1.0" encoding="utf-8" ?>
<NativeBindings>
  <CRuntimeInterop>
    <Printf platform="Win32NT" libraryName="msvcrt.dll" entryPoint="printf"/>
  </CRuntimeInterop>
</NativeBindings>

Dengan pemetaan yang sekarang dilakukan pada nama bidang, nama titik masuk dapat menjadi opsional seperti halnya dengan DllImport. Berikut ini mengilustrasikan konversi DllImport ke format baru:

        //[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
        //public static extern IntPtr memset(IntPtr dest, int c, int count);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = false)]
        public delegate IntPtr memsetDelegate(IntPtr dest, int c, int count);

        [NativeLibraryBinding(PlatformID.Win32NT, WindowsLibName)]
        public static readonly memsetDelegate memset = null;

Beberapa perpustakaan seperti OpenGL mungkin secara opsional menyertakan titik masuk tertentu. Saya menambahkan dukungan untuk ini dengan tanda "IsOptional" baru ke atribut. Jika titik masuk tidak ditemukan, delegasi tetap nol. Ini memungkinkan pengujian dalam kode untuk ketidakhadirannya dan menghasilkan NullReferenceException jika kode panggilan mencoba menggunakannya.

        public delegate void glBufferStorageEXTDelegate (int target, IntPtr size, IntPtr data, int flags);

        [NativeLibraryBinding(PlatformID.MacOSX, LibName, IsOptional = true)]
        public static readonly glBufferStorageEXTDelegate glBufferStorageEXT = null;

(Catatan: Tidak seperti contoh sebelumnya, panggilan tidak benar-benar diuji)

Apa implikasi kinerja dari mengganti extern dengan delegasi statis?

Itu juga pertanyaan yang saya tanyakan dari awal. Saya akhirnya akan benchmark. Karena tidak ada pilihan lain yang tersedia bagi saya, kinerjanya tidak relevan. Itu adalah pertanyaan memiliki kode yang berfungsi atau tidak.

Tonggak berikutnya adalah membuat fungsi yang secara otomatis mengonversi kode DllImport lama ke format baru ini. Setelah saya memilikinya, saya dapat menguji penggunaan dunia nyata. Saya juga akan berada dalam posisi yang lebih baik untuk memberikan informasi pembandingan secara keseluruhan (apakah itu memiliki dampak yang nyata?).

Menjalankan program konversi dan masalah pertama yang muncul adalah bahwa DllImport memungkinkan fungsi yang berlebihan tetapi setiap delegasi dan bidang harus memiliki nama yang unik. Ini terutama berdampak pada fungsi kenyamanan di mana DllImport ramah C# ditambahkan menggunakan nama yang sama dengan tanda tangan yang berbeda.

Tolok ukur sederhana

100,000 Iterations each of memcpy (Release)
Time in seconds

DllImport
-------
0.0034425
0.0033392
0.003338
0.0033484
0.0033807
0.003338
0.003338
0.003338
0.0033419
0.003338
0.003338
0.003338
0.0033553
0.0033392
0.0033384
0.003338
0.003338
0.0033469
0.0033377
0.0033377


Delegate
--------
0.007208
0.0071596
0.0072514
0.0059994
0.0054384
0.005438
0.0053585
0.0054384
0.0054341
0.0054384
0.005438
0.0054218
0.0054384
0.0054384
0.0054303
0.0054384
0.005438
0.0054825
0.0054403
0.005438

Saya mengintegrasikan prototipe dengan beberapa proyek dan hasilnya bagus. Oleh karena itu, saya merasa yakin untuk mengusulkan proposal berikut:

Tambahkan Atribut baru ke CoreCLR yang disebut NativeLibraryImportAttribute. Atribut baru ini menggantikan DllImport dalam kode lintas platform. DllImport harus tetap untuk alasan kompatibilitas sampai waktu tersebut dapat [Usang].

NativeLibraryImportAttribute mengambil bentuk yang sama dengan DllImportAttribute saat ini dengan menambahkan satu penentu platform wajib serta memungkinkan beberapa instance (atribut):

[NativeLibraryImport (PlatformID, LibraryName)] adalah bentuk minimumnya. Ini adalah perilaku yang tidak ditentukan untuk menentukan beberapa atribut dengan PlatformID yang sama untuk satu impor (kemungkinan penggunaan di masa mendatang dapat memungkinkan untuk menentukan nama alternatif pada satu platform) . Semua properti opsional saat ini dari DllImport harus disertakan seperti EntryPoint dan CallingConvention. Runtime harus memproses hanya atribut yang cocok dengan PlatformID aktual yang menjalankannya, mengabaikan yang lainnya. Jika tidak ada PlatformID yang cocok dengan lingkungan runtime, pengecualian harus dimunculkan tanpa adanya mekanisme pemetaan. Selain itu, DllNotFoundException harus diganti dengan pengecualian bernama lebih tepat seperti NativeLibraryLoadException.

Amandemen proposal ini di masa mendatang harus menentukan mekanisme dan format untuk memuat atribut [NativeLibraryImport] secara eksternal. Mekanisme ini harus dapat diakses oleh pengguna akhir. Item apa pun yang menentukan [NativeLibraryImport] dianggap memenuhi syarat untuk pemetaan. Peta hanya dikonsultasikan jika PlatformID yang cocok dengan lingkungan runtime saat ini tidak ditemukan. Jika setelah berkonsultasi dengan peta, kecocokan untuk platform saat ini tidak dapat ditemukan, pengecualian harus diajukan. Peta terdiri dari Nama Ekspor (sama seperti yang ditentukan oleh 'ekstern'), PlatformID dan LibraryName minimal dan secara opsional menyertakan salah satu atau semua properti [NativeLibraryImport].

Saya lupa menyebutkan secara eksplisit bahwa dengan [NativeLibraryImport], tidak ada nama yang diubah atau diganti. Nama perpustakaan yang ditentukan adalah nama yang dikirim ke linker dinamis untuk dimuat. Ini berarti nama harus menyertakan awalan dan akhiran platform yang diharapkan.

[sunting]
Juga harus diperhatikan bahwa perilaku pengikatan titik masuk sama dengan perilaku DllImport (malas). Ekspor yang tidak pernah dipanggil oleh runtime tidak pernah diselesaikan.

[sunting2]
PlatformID harus berupa enum independen yang ditentukan dan diversi untuk digunakan dengan [NativeLibraryImport] saja. Ini akan memungkinkan adopsi cepat nilai-nilai baru untuk memenuhi kebutuhan yang tidak diantisipasi oleh penentu platform yang lebih umum.

Menjalankan program konversi dan masalah pertama yang muncul adalah bahwa DllImport memungkinkan fungsi yang berlebihan tetapi setiap delegasi dan bidang harus memiliki nama yang unik. Ini terutama berdampak pada fungsi kenyamanan di mana DllImport ramah C# ditambahkan menggunakan nama yang sama dengan tanda tangan yang berbeda.

Tidak dalam pengalaman saya. Ketika Anda memiliki fungsi asli yang mengambil pointer ke struct sebagai input, di mana NULL juga merupakan nilai yang valid, Anda memerlukan dua kelebihan yang keduanya memiliki atribut DllImport . Satu mengambil argumen yang relevan sebagai ref MyStruct dan yang lain menganggapnya sebagai IntPtr . Jika kode berbasis delegasi tidak berfungsi untuk pola ini, itu akan merusak banyak impor asli.

Saat Anda memiliki fungsi asli yang mengambil pointer ke struct sebagai input, di mana NULL juga merupakan nilai yang valid, Anda memerlukan dua kelebihan yang keduanya memiliki atribut DllImport.

Mungkin saya hanya tidak menjelaskan semuanya dengan baik. Ya, kode dunia nyata memiliki eksternal yang sama dengan tanda tangan yang berbeda. Itu saat ini dimungkinkan dengan [DllImport] dan juga dimungkinkan dengan [NativeLibrary] yang diusulkan. [DllImport] adalah apa yang kita miliki saat ini, [NativeLibraryBinding] adalah prototipe yang dibuat untuk mengeksplorasi solusi untuk masalah ini, dan [NativeLibrary] adalah proposal yang dihasilkan untuk dimasukkan ke dalam CoreCLR.

Mulai hari ini, saya berencana untuk mengembangkan [NativeLibraryBinding] sebagai solusi akhir saya. Pengujian dunia nyata tidak menunjukkan dampak yang berarti secara statistik pada beban kerja yang tidak 'terikat' dengan API. Mudah-mudahan ini akan menceraikan saya dari frustrasi, drama, dan pengabaian seputar masalah ini.

Jika kode berbasis delegasi tidak berfungsi untuk pola ini, itu akan merusak banyak impor asli.

Situasi ini ditemukan sebagai hasil pengujian dengan kode dunia nyata. Masalahnya tidak berbeda dengan yang dihadapi oleh perpustakaan C karena mereka juga tidak dapat memiliki kelebihan. Resolusinya adalah mengubah nama dalam beberapa cara seperti yang dilakukan C++ (nama mangling). Untuk pengujian ini, saya cukup menambahkan ordinal indeks: someDelagate, someDelegate2, someDelegate3 untuk mewakili nama delegasi dan bidang yang berbeda. Properti "EntryPoint" digunakan untuk memastikan mereka semua memanggil fungsi yang sama.

itu akan merusak banyak impor asli.

Banyak impor asli SUDAH saat ini rusak dengan [DllImport]. Informasi yang saya sajikan dilakukan dengan itikad baik. Akan menyenangkan melihat CoreCLR mengadopsi solusi untuk masalah ini; Namun, tidak ada konsekuensinya jika tidak. Berdasarkan asumsi bahwa API baru tidak berhasil masuk ke .Net dalam semalam, saya skeptis terhadap CoreCLR termasuk resolusi apa pun untuk masalah ini selain mangling nama saat ini dan pendekatan lihat-apa-tidak-gagal [DllImport].

@OtherCrashOverride
Saya suka cara ini berkembang. Namun, saya selalu lebih suka apa pun selain XML untuk konfigurasi. ASP.NET 5 berusaha keras untuk menghindari seluruh tumpukan XML jika tidak secara eksplisit diperlukan oleh aplikasi dan ini akan mengembalikannya pada platform non-Windows menggunakan Kestrel.

Saya juga ingin solusi non-XML. Dalam hal itu, proposal [NativeLibrary] awal TIDAK menentukan pemetaan eksternal apa pun. XML digunakan dalam prototipe saya untuk menguji desain pemetaan eksternal.

Perlu juga dicatat bahwa kemungkinan kode untuk menyertakan atribut [DllImport] dan [NativeLibrary] pada ekspor yang sama. Ini memberi kami kompatibilitas mundur maksimum. Dalam kasus CoreCLR, ia harus memilih atribut [NativeLibrary]. Pada runtime yang lebih lama, atribut [NativeLibrary] yang baru akan diabaikan.

Ini adalah contoh bagaimana [NativeLibrary] dan [DllImport] akan digunakan bersama:

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
[NativeLibrary(PlatformID.Win32NT, "msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
[NativeLibrary(PlatformID.Unix, "libc.so.6", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr memset(IntPtr dest, int c, int count);

Berikut ini dapat kembali ke [DllImport] tanpa adanya [NativeLibrary] untuk platform tertentu.

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
[NativeLibrary(PlatformID.Unix, "libc.so.6", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr memset(IntPtr dest, int c, int count);

Baiklah, contoh itu sangat masuk akal. Jadi tentang apa semua hal delegasi di atas?

Jadi tentang apa semua hal delegasi di atas?

Itu adalah prototipe untuk membuktikan konsep desain dan sampai ke [Perpustakaan Asli]. Ini bukan proposal untuk CoreCLR (oleh saya).

[NativeLibrary] adalah [DllImport] dengan ..
1) Menambahkan penentu platform yang secara eksplisit menyatakan apa yang dimaksudkan oleh penulis.
2) Beberapa contoh. Anda dapat memiliki lebih dari satu atribut untuk menentukan impor untuk lebih dari satu platform.
3) Penghapusan mangling nama. Nama perpustakaan yang ditentukan bersifat eksplisit. Runtime tidak akan pernah mencoba menafsirkan atau memprosesnya. Ini diteruskan apa adanya ke linker dinamis.

Berikut ini adalah setara:

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
[NativeLibrary(PlatformID.Win32NT, "msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]

Berikut ini mungkin tidak setara:

[DllImport("msvcrt", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
[NativeLibrary(PlatformID.Win32NT, "msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]

Berikut ini akan gagal dengan NativeLibraryLoadException:

[NativeLibrary(PlatformID.Win32NT, "msvcrt", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]

LGTM

@OtherCrashOverride Jadi, untuk bersenang-senang, saya telah melakukan percobaan dengan autogenerating delegasi dan semacamnya menggunakan Roslyn dan precompiler DNX (hasil akhirnya dapat digunakan (setidaknya secara teori) oleh kerangka kerja apa pun, tetapi perlu dibangun menggunakan DNU ).

Ini memungkinkan saya untuk membuat kelas seperti ini:

    abstract class FooBar : NativeMethods
    {
        [NativeMethod("somelib", CallingConvention.Cdecl, BestFitMapping = false)]
        public abstract void Test();

        [NativeMethod("otherlib", CallingConvention.Cdecl)]
        public abstract int OtherTest(bool foo);
    }

Dan itu menghasilkan kelas implementasi pada waktu kompilasi:
image

Metode GetNativeMethodDelegate Anda lihat di tangkapan layar adalah virtual (didefinisikan dalam NativeMethods , sehingga Anda dapat menimpanya di FooBar untuk mengubah logika pengikatan menjadi apa pun yang Anda inginkan, dan itu malas mengikat seperti [DllImport] .

Batasan pewarisan menjadi kendala di beberapa tempat. Kelas mungkin sudah mewarisi dari sesuatu yang kebetulan juga memiliki beberapa P/Panggilan. Contoh langsung yang terlintas dalam pikiran adalah dalam prototipe di mana saya memiliki kelas dasar NativeLibrary dan Win32NativeLibrary/PosixNativeLibrary mewarisi dari itu. Setiap subclass menentukan P/Invoke-nya sendiri ke dalam dynamic loader yang sesuai.

@OtherCrashOverride secara umum, jika Anda membutuhkannya, yang saya lakukan hanyalah membuat kelas terpisah hanya dengan P/Invoke, dan memiliki this . Maksud saya, biasanya, dengan [DllImport] saya selalu membuat metode statis. Dan mereka selalu bersifat pribadi/internal.

Dampak terhadap basis kode yang ada juga dipertimbangkan. Dengan atribut [NativeLibrary] yang diusulkan, banyak basis kode dapat dikonversi dengan find/replace.

Temukan: [DllImpor(
Ganti: [NativeLibrary(PlatformID.Win32NT,

@OtherCrashOverride Saya tidak memberikan ini sebagai solusi. Saya memberikan ini sebagai "Saya dapat membuat sesuatu bekerja sementara para politisi mendiskusikan solusi apa yang mungkin mereka inginkan suatu saat di versi 3" ^^

@OtherCrashOverride Saya suka ide di balik proposal Anda. Saya pikir itu harus dibangun sebagai alat atau pustaka yang dipisahkan dari runtime inti.

Idealnya, itu harus menghasilkan kode pada waktu kompilasi seperti yang disarankan @Alxandr . Menghasilkan kode pada waktu kompilasi lebih disukai karena memiliki kinerja startup runtime yang lebih baik dan cenderung lebih ramah AOT.

Kami telah berupaya untuk memisahkan sebagian besar mesin interop dari runtime inti, dan memindahkannya ke komponen independen - MCG (Marshalling Code Generator). Detail lebih lanjut tentang cara kerja MCG ada di posting blog ini yang ditulis oleh @yizhang82. Kami berencana untuk membuka MCG sumber di masa depan.

Beberapa generator kode interop yang berbeda dapat hidup berdampingan. Setiap proyek dapat memilih teknik interop yang paling sesuai dengan kebutuhannya. SWIG , atau generator interop SharpDX adalah contoh generator kode interop berbeda yang ada saat ini.

Kami terbuka untuk menambahkan API pembantu ke dalam runtime inti yang sulit atau tidak mungkin untuk dibangun secara mandiri. Contoh API tersebut adalah ICastable yang memungkinkan tipe COM untuk berpartisipasi dalam casting, tanpa memiliki logika casting COM yang rumit di runtime inti. Beri tahu kami jika Anda melihat perlunya lebih banyak API dalam kategori ini.

Idealnya, itu harus menghasilkan kode pada waktu kompilasi seperti yang disarankan @Alxandr . Menghasilkan kode pada waktu kompilasi lebih disukai karena memiliki kinerja startup runtime yang lebih baik dan cenderung lebih ramah AOT.

Saya pikir ada beberapa miskomunikasi di sini. Saya secara eksplisit menyatakan:

Saya juga harus mencatat bahwa persyaratannya adalah kompatibel dengan AOT. Jadi saya menghindari melakukan patching IL atau pembuatan on-the-fly dari stub.

[NativeLibraryBinding] adalah prototipe untuk mengeksplorasi kebutuhan dunia nyata dari masalah ini. Itu tidak menghasilkan kode apa pun. Itu tidak mengubah atau menghasilkan IL apapun. Di belakang layar ia menggunakan Marshal.GetDelegateForFunctionPointer yang akhirnya memanggil runtime itu sendiri
https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs#L2522

Prototipe menggunakan delegasi menunjukkan tiga aspek yang tidak diinginkan. 1) __arglist tidak mungkin digunakan. 2) Delegasi tidak dapat kelebihan beban. 3) Lebih lambat dari [DllImport].

Ini mengarah pada proposal aktual yang merupakan modernisasi dari dukungan [DllImport] yang ada saat ini di runtime. Perilaku baru ini dikaitkan dengan atribut baru yang disebut [NativeLibrary]. Ini dimaksudkan sebagai pengganti [DllImport] yang menggunakan kode yang ada di runtime untuk [DllImport] yang diperbarui untuk mendukung perilaku baru. Ini hanyalah evolusi [DllImport] dengan pertimbangan yang diberikan untuk kebutuhan pemetaan di masa depan.

Setelah membaca artikel MCG, tampaknya tidak akan memenuhi kebutuhan masalah ini. Itu bergantung pada tautan dinamis yang membaca pengikatan perpustakaan dari .dll seperti halnya .dll asli lainnya. Ini masih membuat kita dilema tentang BAGAIMANA pengikatan itu harus ditentukan (msvcrt.dll vs libc.so.6).

SWIG dan SharpDX melakukan fungsi yang sama sekali berbeda. Keduanya menghasilkan [DllImport] dari file eksternal seperti file header C. Ini bukan masalah yang coba dipecahkan oleh masalah ini.

Untuk PInvoke, MCG mengambil deskripsi tingkat tinggi dari PInvoke yang ditetapkan sebagai DllImport dan menghasilkan kode tingkat rendah untuknya. Misalnya, jika MCG melihat:

[DllImport("mydll.dll")]
extern static public void foo();

Itu dapat menghasilkan kode C# seperti (fragmen kode serupa, tetapi bukan apa yang sebenarnya terjadi hari ini):

static IntPtr p_foo = IntPtr.Zero;

public static void foo()
{
    if (p_foo == null)
        p_foo = init_foo();

     // Marshalling code if there are any arguments

     // Intrinsic that is expanded into calli IL instruction by .Net Native toolchain
     CalliIntrinsics.Call(p_foo);    

     // Unmarshalling code if there are any return values or cleanup
}

private static IntPtr init_foo()
{
    IntPtr libraryHandle = McgHelpers.LoadLibrary("mydll.dll");
    return McgHelpers.GetProcAddress(libraryHandle, "foo");
}

Kode C# ini kemudian ditautkan ke .dll yang menggunakan PINvoke.

Pola umum yang sama digunakan oleh generator kode interop lain yang telah saya sebutkan: ambil deskripsi singkat tentang metode yang memerlukan interop, buat tumpukan kode boilerplate tingkat rendah darinya.

Untuk generator kode interop Anda, kode yang dihasilkan bisa serupa. Misalnya kode yang dihasilkan untuk spesifikasi ini:

[NativeLibrary(PlatformID.Win32NT, "msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
[NativeLibrary(PlatformID.Unix, "libc.so.6", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
extern static public void foo();

Bisa seperti - menggunakan delegasi di bawah:

public static void foo()
{
    if (p_foo == null)
        p_foo = init_foo();

     p_foo();
}

delegate void foo_delegate();

static foo_delegate p_foo = null;

private static void init_foo()
{
    if (NativeLibraryHelpers.IsWin32NT)
    {
        IntPtr libraryHandle = NativeLibraryHelpers.LoadLibrary("msvcrt.dll");
        IntPtr p = NativeLibraryHelpers.GetProcAddress(libraryHandle, "memcpy");
        return Marshal.GetDelegateForFunctionPointer<foo_delegate>(p);
    }
    if (NativeLibraryHelpers.IsUnix)
    {
        IntPtr libraryHandle = NativeLibraryHelpers.LoadLibrary("libc.so.6");
        IntPtr p = NativeLibraryHelpers.GetProcAddress(libraryHandle, "memcpy");
        return Marshal.GetDelegateForFunctionPointer<foo_delegate>(p);
    }
    throw new PlatformNotSupportedException();
}

Seperti yang Anda perhatikan, delegasi memiliki beberapa overhead dibandingkan dengan PIN mentah. Overhead ini dapat dihindari dengan menggunakan calli intrinsik seperti kode yang dihasilkan MCG. Sayangnya, CoreCLR tidak memiliki CalliIntrinsics yang tersedia saat ini. Kita dapat menjelajahi apa yang diperlukan untuk menambahkannya. Jika dilakukan dengan benar, itu akan memperbaiki masalah dengan memanggil fungsi vararg juga.

@Alxandr Abstrak/virtual tidak terlalu dibutuhkan. Anda bisa mengganti kelas yang ada dengan yang ditulis ulang menggunakan Roslyn.

@Tragetaschen Itu (saya pikir) akan bermasalah sehubungan dengan bagaimana Anda menulisnya. Seperti, apakah Anda hanya mematikan metode asli? Cara saya melakukannya, saya tidak perlu mengubah apa pun, saya hanya membuat kelas baru. IMHO lebih sederhana.

Saya pikir bagian tersulit dalam kedua pendekatan (membuat penggantian baru vs. mengisi implementasi untuk metode yang ada) adalah kebutuhan untuk memanggil Roslyn dua kali: pertama untuk mengidentifikasi metode yang dianotasi dengan atribut, dan kedua kalinya untuk mengompilasinya secara nyata dengan interop yang dihasilkan

Idealnya, Roslyn akan mendukung pembuat kode/penulis ulang untuk membuat ini lebih sederhana di masa depan (mirip dengan bagaimana penganalisis kode hari ini).

Dari segi kinerja - membuat penggantian baru akan lebih lambat saat runtime karena overhead panggilan virtual.

Diskusi ini, meskipun mendidik, agak kehilangan arah. Dasar dari masalah ini adalah:

[DllImport("mydll.dll")]
public static extern void foo();

Jika itu dikompilasi ke dalam program terkelola CoreCLR, panggil "Bar.dll". Bagaimana cara membuatnya berfungsi pada platform non-windows?

Metode yang mudah bagi penulis perpustakaan untuk menentukan pemetaan platform non-windows diinginkan. Selain itu, cara mudah bagi pengguna akhir untuk menentukan pemetaan platform non-jendela diinginkan.

Setiap jenis pembuatan kode membuat masalah ini menjadi lebih sulit untuk dipecahkan oleh kedua belah pihak dengan mempertimbangkan hal-hal berikut: 1) penulis mungkin tidak mendukung platform target pengguna akhir. 2) Pengguna akhir mungkin tidak memiliki akses ke kode sumber penulis. Jika ada pembuatan kode yang terlibat, itu harus setransparan [DllImport] hari ini.

Kompromi yang diusulkan adalah:

[NativeLibrary(PlatformID.Win32NT, "mydll.dll")]
public static extern void foo();

Ini memberi penulis perpustakaan kemampuan untuk secara eksplisit mendukung platform. Selain itu, menyediakan informasi dan perilaku yang diperlukan untuk mekanisme pemetaan untuk memungkinkan pengguna akhir untuk menentukan pemetaan. Pembenaran untuk ini disajikan sebelumnya dalam diskusi panjang ini.

Impornya bahwa ini menjadi bagian runtime standar dan seragam. Ini diperlukan untuk memungkinkan ekosistem perpustakaan pihak ketiga berkembang. Selain itu, ini menyederhanakan bagian dari CoreCLR itu sendiri (cari basis kode untuk [DllImport]).

Sementara saya memikirkannya ...

Beberapa perpustakaan menyertakan bitness dalam namanya:
"SomeLibrary32.dll" dan "SomeLibrary64.dll".

Dua cara untuk mendukung ini segera muncul dalam pikiran:

PlatformID harus berupa enum independen yang ditentukan dan diversi untuk digunakan dengan [NativeLibraryImport] saja. Ini akan memungkinkan adopsi cepat nilai-nilai baru untuk memenuhi kebutuhan yang tidak diantisipasi oleh penentu platform yang lebih umum.

Kita dapat memiliki PlatformID.Win32NT dan PlatformID.Win64NT.

atau

Ini adalah perilaku yang tidak ditentukan untuk menentukan beberapa atribut dengan PlatformID yang sama untuk satu impor (kemungkinan penggunaan di masa mendatang dapat memungkinkan untuk menentukan nama alternatif pada satu platform) .

[NativeLibrary(PlatformID.WinNT, "SomeLibrary32.dll")]
[NativeLibrary(PlatformID.WinNT, "SomeLibrary64.dll")]
public static extern void foo();

Saya tidak yakin mana yang lebih disukai.

@jkotas du memiliki kemampuan ini. Saya mengubah objek kompilasi Roslyn _selama_ build. Tidak ada sihir msbuild.

Saya tidak yakin mana yang lebih disukai.

Saya lebih suka cara yang membuatnya tidak ambigu. WinNT 32 bit adalah OS yang berbeda dari WinNT 64 bit, meskipun sebagian besar kompatibel. Bitness DLL terkait adalah salah satu cara spesifik di mana mereka _tidak_ kompatibel.

Salah satu solusi sederhana adalah membuat PlatformID menjadi [Flags] enum, memungkinkan Anda untuk menentukan nama yang sama untuk beberapa arsitektur serupa. Grup tertentu bahkan dapat ditentukan sebelumnya untuk kenyamanan, seperti WinNT = WinNT32 | WinNT64 . Namun, ini akan membatasi jumlah platform yang didukung hingga 64. Adakah perkiraan realistis tentang seberapa besar kemungkinan itu menjadi masalah?

@OtherCrashOverride @masonwheeler harap dicatat bahwa PlatformID tidak akan datang ke .NET Core, itu diganti dengan API OSName: https://github.com/dotnet/corefx/pull/1494

Kalian hanya skimming sekarang, bukan? :mengedip:

PlatformID harus berupa enum independen yang ditentukan dan diversi untuk digunakan dengan [NativeLibraryImport] saja. Ini akan memungkinkan adopsi cepat nilai-nilai baru untuk memenuhi kebutuhan yang tidak diantisipasi oleh penentu platform yang lebih umum.

@akoeplinger Maksud dari PlatformID adalah unik untuk [NativeLibrary]. Ini memiliki nama yang sama dengan System.PlatformID hanya karena itulah jenis prototipe yang digunakan. Mungkin harus diganti namanya untuk membedakannya. Dalam prototipe ini berfungsi sebagai tingkat abstraksi untuk memungkinkan terjemahan dari .Net/Mono PlatformID penuh atau CoreCLR OSName.

Alasan independennya adalah untuk memperhitungkan kebutuhan tak terduga untuk membedakan platform. Contoh yang mungkin adalah berbagai BSD. Keluarganya mungkin BSD, tetapi rasanya bisa salah satu dari banyak:
https://en.wikipedia.org/wiki/List_of_BSD_operating_systems

Ini menjawab pertanyaan @masonwheeler : Tidak, 64 platform (dibagi 2 untuk 32/64bit) bukan batas yang realistis.

Kita mungkin harus menambahkan flag opsional ke atribut [NativeLibrary] yang membedakan ukuran kata asli jika diperlukan:

[Flags]
public enum NativeWordSize
{
  Bits32 = (1 << 0);
  Bits64 = (1 << 1);
  Any = (Bits32 | Bits64);
}

Nilai defaultnya adalah NativeWordSize.Any. Ini bertepatan dengan flag kode terkelola yang menentukan 32bit, 64bit, atau AnyCpu.

Contoh penggunaan:

[NativeLibrary(PlatformID.WinNT, "SomeLibrary32.dll", NativeWordSize = NativeWordSize.Bits32]
[NativeLibrary(PlatformID.WinNT, "SomeLibrary64.dll", NativeWordSize = NativeWordSize.Bits64]
public static extern void SomeLibraryExport(IntPtr thisChanges);

[NativeLibrary(PlatformID.WinNT, "SomeOtherLibrary.dll"]
public static extern void SameLibraryNameForAnyBitSizeExport();

@OtherCrashOverride ah ya, saya bingung karena namanya sama, maaf. Yang mengatakan, saya tidak suka memiliki enum lain yang menentukan OS/Platform, cukup buruk kami memiliki pemisahan PlatformID di Desktop.NET dan OSName di Core. Prototipe Anda sangat menarik, saya ingin melihat seperti apa hasil akhirnya :senyum:

@Alxandr Jika memungkinkan - dapatkah Anda membagikan tautan ke kode yang menunjukkan bagaimana Anda mengubah objek kompilasi Roslyn selama pembuatan?

@OtherCrashOverride Saya setuju bahwa fitur ini sangat berguna untuk implementasi perpustakaan lintas platform tertentu. Namun, penting untuk diimplementasikan sebagai komponen terpisah - ini tidak mencegahnya untuk digunakan secara umum oleh perpustakaan dan menjadi standar de-facto.

Secara historis, .NET Runtime klasik adalah kotak monolitik besar yang telah melakukan banyak hal berbeda. .NET Core adalah awal baru dalam memfaktorkan .NET menjadi beberapa komponen kecil yang terpisah yang dapat dikembangkan secara independen, dan sering dikirimkan. Kami mencoba untuk sangat berhati-hati tentang batasan arsitektur di .NET Core.

Ini adalah utas yang panjang untuk dibaca, tetapi saya pikir (setidaknya dalam pikiran saya), ada kesimpulan logis: tidak boleh menjadi bagian dari runtime, melainkan sebagai pola atau praktik. Saya mengatakan ini karena semua permutasi/kombinasi yang perlu dimasukkan ke dalam runtime untuk mencakup semua kasus lintas-plat adalah pekerjaan yang kemungkinan besar akan gagal dalam beberapa situasi aneh!

@OtherCrashOverride - apakah Anda tidak puas dengan solusi yang Anda usulkan? Atau apakah Anda mengatakan bahwa itu memberatkan? Bisakah Anda menguraikan skenario yang tidak akan diselesaikan dengan memiliki generator kode eksternal, baik itu sesuatu yang didasarkan pada Roslyn selama membangun atau menghasilkan delegasi?

FYI, seseorang tampaknya sudah memiliki ide serupa dan menulisnya lebih dari setahun yang lalu ... https://github.com/Giorgi/Dynamic-PInvoke

Namun, penting untuk diimplementasikan sebagai komponen yang terpisah

mscorlib menggunakan [DllImport] itu sendiri. Tidak praktis untuk menjadikannya komponen terpisah ketika runtime itu sendiri bergantung padanya.

apakah Anda tidak puas dengan solusi yang Anda usulkan?

Seperti yang dinyatakan sebelumnya

Prototipe menggunakan delegasi menunjukkan tiga aspek yang tidak diinginkan. 1) __arglist tidak mungkin digunakan. 2) Delegasi tidak dapat kelebihan beban. 3) Lebih lambat dari [DllImport].

Bisakah Anda menguraikan skenario yang tidak akan diselesaikan dengan memiliki generator kode eksternal, baik itu sesuatu yang didasarkan pada Roslyn selama membangun atau menghasilkan delegasi?

Memiliki generator kode eksternal memperburuk masalah. Selain memiliki kode sumber, pengguna akhir kini juga diharuskan memiliki generator khusus yang digunakan. Perbedaannya adalah dari menambahkan file DllMap sederhana, seperti yang dilakukan Mono hari ini, hingga memiliki alat pembuatan lengkap dan kode sumber untuk setiap pustaka yang digunakan. Tidak realistis untuk mengharapkan bahwa setiap perpustakaan akan memerlukan pembuat kodenya sendiri selain dependensi perpustakaannya sendiri.


Masalah ini juga memengaruhi Microsoft seperti yang terlihat di dotnet/coreclr#1257. Perbedaannya adalah bahwa Microsoft hanya mengubah perilaku [DllImport] untuk memenuhi kebutuhan mereka. Jika setiap orang mempertahankan garpu CoreCLR yang tidak kompatibel dengan perilaku [DllImport] yang mereka inginkan adalah solusinya, maka lepaskan saja. Semua yang diminta adalah bahwa pernyataan RESMI untuk itu diberikan.

Saya pikir faktor penting dalam masalah ini adalah bahwa CoreCLR dipandang sebagai "kejahatan yang diperlukan" (sesuatu yang tidak Anda sukai tetapi Anda tahu harus ada atau terjadi) untuk mengeluarkan ASP.Net. Tampaknya pengembangan CoreCLR hanya terlihat melalui kacamata ASP.Net. Jadi izinkan saya menjelaskan tentang ini: Ada keinginan untuk menggunakan CoreCLR di mana ASP.Net tidak akan hadir. Ada keinginan untuk menggunakan CoreCLR di mana DNX tidak akan hadir.

Memiliki standar, kesepakatan, resolusi untuk masalah yang dihadapi [DllImport] sangat penting. Seperti yang dinyatakan sebelumnya, bahkan mscorlib tidak akan ada tanpanya. [DllImport] dirancang dan diimplementasikan ketika lintas platform berarti "platform Windows apa pun". CoreCLR bergerak di luar batasan itu dan harus [DllImport].

Saya setuju bahwa solusi apa pun yang disajikan harus 'terpasang'. Penting bahwa itu menjadi deterministik dan konsisten untuk semua orang. Jika setiap orang 'menggulung sendiri sesuai kebutuhan menggunakan kait', maka penyedia perpustakaan tidak akan dapat menawarkan dukungan untuk apa pun selain 'pemuat' mereka sendiri yang mungkin tidak kompatibel atau menawarkan fitur 'pemuat' milik pelanggan.

Itulah skenario yang kami coba hindari dengan mengatasi masalah ini. Ya, setiap orang dapat membuat fork CoreCLR mereka sendiri, atau codegenerator mereka sendiri, atau loader mereka sendiri. Masalahnya adalah bahwa alih-alih satu cara bagi pengguna akhir untuk membuat kode COMPILED bekerja pada platform target mereka, sekarang ada banyak: Alih-alih O(1), sekarang menjadi masalah O(n).

Jadi sekali lagi saya akan bertanya: jika permintaan tarik untuk peningkatan [NativeLibrary] dari [DllImport] yang diusulkan diajukan, apakah itu akan diterima (dengan asumsi patch bersih)? Tidak apa-apa untuk mengatakan "Tidak". Tidak apa-apa untuk mengatakan "Kami akan meninjau kembali masalah ini beberapa saat setelah rilis." Apa pun selain diam dapat diterima. Ini bukan upaya untuk memaksakan suatu posisi. Sederhananya, sementara masalah ini diam, dunia terus berputar dan ada jadwal lain yang harus diselaraskan.

@jkotas di seluler, jadi mendapatkan dan berbagi tautan agak lebih rumit dari biasanya, tetapi ini adalah implementasi dan penggunaan .

Saya hanya ingin mendukung apa yang dikatakan

Saya tidak berpikir saran yang ditunjukkan oleh @jkotas atau saya menyarankan bahwa ASP.NET == CoreCLR, itu adalah kendaraan pengiriman yang teridentifikasi dengan baik.

Saya pikir faktor penting dalam masalah ini adalah bahwa CoreCLR dipandang sebagai "kejahatan yang diperlukan" (sesuatu
yang tidak Anda sukai tetapi yang Anda tahu pasti ada atau terjadi) untuk mengeluarkan ASP.Net. Tampaknya pengembangan CoreCLR hanya terlihat melalui kacamata ASP.Net. Jadi biarkan aku menjadi eksplisit
tentang ini: Ada keinginan untuk menggunakan CoreCLR di mana ASP.Net tidak akan hadir. Ada keinginan
untuk menggunakan CoreCLR di mana DNX tidak akan hadir.

Ini tidak benar. CoreCLR juga tidak dianggap sebagai kejahatan yang perlu, juga masalah khusus ini tidak terkait dengan diskusi itu sama sekali.

Memiliki generator kode eksternal memperburuk masalah. Selain memiliki kode sumber,
pengguna akhir sekarang juga diharuskan memiliki generator khusus yang digunakan. Perbedaannya mulai dari
menambahkan file DllMap sederhana, seperti yang dilakukan Mono hari ini, untuk memiliki alat build lengkap dan kode sumber untuk
setiap perpustakaan yang digunakan. Tidak realistis untuk mengharapkan bahwa setiap perpustakaan akan membutuhkan pembuat kodenya sendiri di
tambahan untuk dependensi perpustakaannya sendiri.

Cukup jelas dalam pikiran saya bahwa ini dapat diimplementasikan tanpa kerja sama runtime CoreCLR, dan itu adalah hal yang baik. Bayangkan fitur atribut DLLImport tidak ada, dan Anda harus memanggil kode pembantu yang ada di dalam perpustakaan lain. Seseorang akan melakukan apa yang Anda sarankan. Maksud saya runtime hari ini melakukan sesuatu yang sangat mirip untuk Pinvoke Stubs.

Seperti @jkotas mengatakan, CoreCLR adalah awal baru macam, dan sementara dllimport berguna dalam membantu kerangka inti port upaya mungkin untuk perpustakaan benar-benar cross-plat kita dapat mengatakan salah satu kebutuhan untuk menerapkan solusi di perpustakaan penulis akhir. Dan @jkotas menegaskan kembali bahwa API apa pun yang perlu diekspos untuk mempermudah melakukan ini akan menjadi kandidat untuk dimasukkan dalam corefx. Pola yang kami siapkan di sini dapat diadopsi dan didorong secara luas sebagai "resmi" atau "panduan yang sangat bagus".

Jadi sekali lagi saya akan bertanya: apakah permintaan tarik untuk peningkatan [NativeLibrary] dari [DllImport]
diusulkan diajukan, apakah akan diterima (dengan asumsi patch bersih)? Tidak apa-apa untuk mengatakan "Tidak". Tidak apa-apa untuk > mengatakan "Kami akan meninjau kembali masalah ini beberapa saat setelah rilis." Apa pun selain diam dapat diterima.
Ini bukan upaya untuk memaksakan suatu posisi. Hanya saja sementara masalah ini tidak digunakan, dunia
terus berputar dan ada jadwal lain yang harus diselaraskan.

Saya mengerti pertanyaan Anda, tapi saya rasa kita belum memutuskan jalan ke depan, kan? @jkotas dapat menjawab dengan pasti, tetapi sepertinya ini milik komponen eksternal dan bukan di dalam CoreCLR.

Bayangkan fitur atribut DLLImport tidak ada, dan Anda harus memanggil kode pembantu yang ada di dalam perpustakaan lain.

Mengapa? Seperti yang sudah dia tunjukkan, itu _tidak_ ada, dan bahkan _digunakan di dalam mscorlib itu sendiri_. Mengingat itu, membuatnya menjadi eksternal akan menjadi gila.

Omong-omong; jika ada yang ingin melihat pembuatan kode saya untuk menangani impor asli (masih hanya WIP), itu dilacak di sini: https://github.com/YoloDev/YoloDev.Dnx.Utils/pull/1

CoreCLR juga tidak dianggap sebagai kejahatan yang perlu, juga masalah khusus ini tidak terkait dengan diskusi itu sama sekali.

Sebelumnya dalam diskusi ini dinyatakan bahwa DNX dan NuGet harus menangani ini.

Bayangkan fitur atribut DLLImport tidak ada, dan Anda harus memanggil kode pembantu yang ada di dalam perpustakaan lain.

Sebagai bagian dari spesifikasi ECMA 335

I.9.3
Kode tidak terkelola
Dimungkinkan untuk meneruskan data dari kode yang dikelola CLI ke kode yang tidak dikelola. Ini selalu melibatkan
transisi dari kode terkelola ke tidak terkelola, yang memiliki beberapa biaya runtime, tetapi data seringkali dapat
ditransfer tanpa menyalin. Ketika data harus diformat ulang, VES memberikan alasan yang masuk akal
spesifikasi perilaku default, tetapi dimungkinkan untuk menggunakan metadata untuk secara eksplisit meminta lainnya
bentuk-bentuk penyusunan (yaitu, menyalin diformat ulang). Metadata juga memungkinkan akses ke unmanaged
metode tertentu melalui mekanisme yang sudah ada sebelumnya.

II.23.1.1 0
flag untuk metode [MethodAttributes ]
Implementasi PInvokeImpl 0x2000 diteruskan melalui PInvoke

Ini adalah fitur untuk runtime seperti yang dinyatakan oleh standar.

@OtherCrashOverride

jika permintaan tarik untuk peningkatan [NativeLibrary] dari [DllImport] yang diusulkan diajukan, apakah akan diterima

Implementasi yang dibangun ke dalam runtime inti tidak akan diterima. Kami akan dengan senang hati mendukung implementasi yang dibangun sebagai komponen terpisah.

@masonwheeler

itu bahkan digunakan di dalam mscorlib itu sendiri. Mengingat itu, membuatnya menjadi eksternal akan menjadi gila

Hal ini tidak gila. Kode yang diperluas untuk PInvoke dapat dibuat menggunakan alat terpisah bahkan untuk mscorlib. (BTW: Ini adalah apa yang kami lakukan di .NET Native untuk semua marshalling PInvoke.)

Implementasi yang dibangun ke dalam runtime inti tidak akan diterima. Kami akan dengan senang hati mendukung implementasi yang dibangun sebagai komponen terpisah.

Terima kasih.

@Alxandr

YoloDev/YoloDev.Dnx.Utils#1

Saya menantikan apa yang akan Anda buat. Akan berguna untuk mencobanya pada beberapa kode besar yang sudah ada, seperti apa yang telah dilakukan @OtherCrashOverride untuk prototipe sebelumnya.

@jkotas alat yang Anda bicarakan, yang menghasilkan kode marshaling, bukan open source (belum)?

Benar, alat MCG bukan open source (belum).

Apakah ada metode dalam kerangka kerja yang melakukan pemuatan lintas platform dari rakitan asli dan mendapatkan alamat penunjuk untuk fungsi, atau apakah semua ini tersembunyi dan internal (dan hanya windows)?

Tidak ada sekarang. https://github.com/dotnet/coreclr/issues/937 terkait.

Beberapa konsep untuk dukungan lintas-plat dapat dipinjam/terinspirasi dari mono, Ruby foreign function interface : https://github.com/ffi/ffi/wiki , proposal FFI node.js: https://github.com /nodejs/node/pull/1750 (mereka saat ini menghadapinya dengan cara yang sulit: https://github.com/nodejs/node-gyp/issues/629).

Menurut Wikipedia (_yes wikiepdia masih ada.._):

Java mengacu pada FFI sebagai JNI ( Java Native Interface ) atau JNA ( Java Native Access )

yang mungkin menjadi sumber inspirasi lain dalam menghadirkan P/Invoke lintas platform di CoreCLR.

Sepertinya alat CLI baru membawa kembali dukungan .config untuk .NET Core. Dapat digunakan untuk dllmapping jika pernah terjadi.

https://github.com/dotnet/cli/commit/91acc03a137c4dd6d44ca17aa7ce2078824cc672

Masalah ini diangkat 5 Maret. Sekarang 11 Desember (9 bulan kemudian). Permintaan tarik tidak diterima dan tidak ada panduan lebih lanjut yang diberikan. Saya tidak berpikir tidak adil untuk mengatakan "kereta ini tidak akan tiba di stasiun dalam waktu dekat."

Sekarang hanya masalah memutuskan apakah akan melakukan fork rilis dan memodifikasinya atau hanya beralih ke sesuatu yang lain seperti Swift. Jangka pendek, pengembang mungkin menginginkan runtime yang dimodifikasi yang berguna di luar lingkungan ASP.net. Jangka panjang, mungkin jauh lebih sedikit pekerjaan untuk bermigrasi.

@OtherCrashOverride Maaf karena tidak membalas lebih cepat. Saya ingin menegaskan kembali poin @jkotas bahwa kami tidak akan menerima permintaan tarik ke runtime untuk fitur khusus ini dan kami ingin melihat apakah kami dapat menambahkan ini ke MCG, yang akan dicolokkan ke alat CLI untuk menyediakan integrasi tanpa batas, seolah-olah itu adalah fitur runtime. Kami bekerja keras untuk memastikan MCG bekerja pada semua platform yang menarik, seperti .NET Native, CoreRT, CoreCLR, dan kami juga berencana untuk memulai pekerjaan pada sumber terbuka MCG (kami telah membuat kemajuan yang signifikan dalam pembukaan sumber bagian dari pustaka pembantu dukungan MCG). MCG akan menjadi platform masa depan di mana fungsionalitas baru seperti ini akan ditambahkan. Saya akan membagikan lebih banyak pembaruan ketika kami siap untuk membagikannya.

@OtherCrashOverride Sudahkah Anda melihat alat CLI? Ini tidak lagi terikat dengan pengembangan web, saya sangat menyukainya. Sehubungan dengan dllmapping, saya menerapkan solusi dasar untuk proyek cross-plat saya sendiri seperti 5 bulan yang lalu yang bekerja pada semua implementasi CLR, itu hanya menggunakan dlopen/LoadLibrary secara langsung, kemudian memperoleh pointer fungsi (dan mengonversinya menjadi instance delegasi terkelola) menggunakan dlsym/GetProcAddress. Saya masih tertarik dengan masalah ini karena akan lebih baik jika itu adalah sesuatu yang merupakan bagian dari kerangka pengembangan inti .net.

Jika orang tidak tahu apa artinya yizhang82 dengan MCG: http://blogs.msdn.com/b/dotnet/archive/2014/06/13/net-native-deep-dive-debugging-into-interop- kode.aspx

Saya menerapkan solusi dasar untuk proyek lintas-plat saya sendiri seperti 5 bulan yang lalu yang berfungsi pada semua implementasi CLR, itu hanya menggunakan dlopen/LoadLibrary secara langsung, kemudian memperoleh pointer fungsi (dan mengonversinya menjadi instance delegasi terkelola) menggunakan dlsym/GetProcAddress.

Seperti yang dicatat di utas ini, saya melakukan hal yang sama. Masalah dengannya juga dicatat: 1) tidak ada kemampuan untuk membebani delegasi dan 2) delegasi lebih lambat dari p/panggilan. 3) lebih banyak kode untuk diterapkan daripada [DllImport] sederhana.

MCG tidak menyelesaikan masalah ini, itu hanya mengaburkannya. Bagaimana, menggunakan MCG, saya menentukan nama perpustakaan yang berbeda untuk platform yang berbeda tanpa perlu mengkompilasi ulang/ifdef untuk masing-masing seperti yang saat ini diperlukan untuk [DllImport] ?

[sunting]
MCG - Generator Kode Marshal tampaknya melakukan hal itu: mengurangi tipe terkelola menjadi tipe primitif. Panggilan akhir tampaknya masih dilakukan dengan [DllImport]. Dengan demikian, ia mewarisi masalah ini.

@xanather kembali:

Sepertinya alat CLI baru membawa kembali dukungan .config untuk .NET Core. Dapat digunakan untuk dllmapping jika pernah terjadi.

Itu hanya untuk .NET Framework penuh, bukan .NET Core.

@OtherCrashOverride MCG akan menjadi teknologi interop untuk semua implementasi interop runtime, seperti CoreCLR, .NET Native, CoreRT, dll. CoreCLR hari ini mengimplementasikan interop (pinvoke, COM interop, WinRT) di VM karena alasan historis, tapi itu bukan di mana kita ingin berada di masa depan. Anda dapat menganggap MCG sebagai alat yang secara ajaib memahami konstruksi interop (atribut apa pun, flag/tabel metadata, metode interop, dll.) dan menyediakan implementasinya (bukan runtime) sebagai bagian dari kompilasi (.NET native toolchain, C# kompilasi, dll). Faktanya, .NET native tidak mengimplementasikan interop apa pun selain dukungan paling dasar untuk pinvoke/calli/[NativeCallable] untuk tipe primitif.

Ya, MCG tidak melakukan apa yang Anda inginkan hari ini, tetapi menyediakan platform untuk fungsionalitas seperti itu untuk dibangun karena menyediakan implementasi C# nyata untuk pinvoke Anda (serta interop COM dan metode WinRT), dan itulah peluang sempurna untuk menyediakan lebih banyak penyesuaian dalam kebijakan LoadLibrary/dlopen, yang dapat disesuaikan baik melalui atribut atau file konfigurasi (tidak segera jelas bagi saya apa kebijakan terbaik di sini. Kami perlu melakukan penyelidikan lebih lanjut).

Alasan terbesar mengapa kami tidak akan menerima calon PR ke dalam CoreCLR untuk proposal khusus ini adalah karena kami yakin CoreCLR bukanlah tempat yang tepat untuk menerapkan kebijakan semacam itu di masa mendatang - MCG adalah solusi pilihan. Waktu proses seharusnya hanya mendukung blok penyusun interop paling dasar dan fitur/kebijakan interop sebenarnya harus diserahkan kepada MCG.

Semoga ini memperjelas arah yang kita tuju. Jangan ragu untuk memberi tahu kami jika Anda memiliki pertanyaan lebih lanjut.

MCG tidak melakukan apa yang Anda inginkan hari ini

memberikan lebih banyak penyesuaian dalam kebijakan LoadLibrary/dlopen, yang dapat disesuaikan baik melalui atribut atau file konfigurasi (tidak segera jelas bagi saya apa kebijakan terbaik di sini. Kami perlu melakukan penyelidikan lebih lanjut)

Itulah poin komentar saya sebelumnya. Masalah ini baru saja pindah dari [DllImport] ke MCG. Ini belum diselesaikan.

Untuk memperjelas, saya tidak memerlukan fungsi apa pun yang disediakan MCG. Saya mampu menyusun tipe terkelola ke tipe primitif dengan apa yang tersedia saat ini. Marshaling tidak menjadi perhatian untuk masalah ini. Bahkan jika [DllImport] hanya mendukung tipe primitif seperti yang disebutkan, itu tidak masalah. Ketika masalah ini diselesaikan untuk MCG, itu juga diselesaikan untuk [DllImport] karena ini adalah masalah yang sama persis. Yang membawa kita ke inti masalah yaitu: mari selesaikan masalah dan semua orang senang!

Sekali lagi, untuk kejelasan:

yang dapat dikustomisasi baik melalui atribut atau file konfigurasi

Hanya itu yang perlu kita ketahui agar [DllImport] berfungsi. Kami tidak membutuhkan MCG. Kita tidak perlu memodifikasi Roslyn. Kita hanya perlu mengetahui atribut khusus atau file konfigurasi mana yang harus ada dan apa efeknya.

Saya memahami tujuan MCG dan kebutuhannya dalam skenario AOT. Tidak perlu menganjurkan atau menjelaskannya. Namun, MCG tidak boleh diajukan sebagai solusi, padahal MCG juga membutuhkan penyelesaian masalah ini.

Inti dari masalah ini adalah untuk menentukan hal-hal berikut:

yang dapat dikustomisasi baik melalui atribut atau file konfigurasi

Haruskah kita membuka edisi baru untuk MCG?

Alih-alih masalah saat ini berjudul "Menangani p/panggilan untuk berbagai platform dan diskusi tentang dllmap", saya dapat membuat masalah berjudul "Menangani p/memanggil untuk berbagai platform dan diskusi tentang MCG".

@OtherCrashOverride Terima kasih atas sarannya. Mari kita jaga agar utas ini tetap aktif untuk saat ini karena utas ini memiliki banyak diskusi dan wawasan yang berharga, dan masalahnya belum terselesaikan. Setelah MCG bersumber terbuka, saya akan memindahkan semua masalah interop terkait ke tempat baru.

Mungkin sudah waktunya untuk mempertimbangkan kembali ini karena kami memiliki host terpadu sekarang.

Menanggapi kebutuhan untuk proyek LLVMSharp dan masalah ini saya telah menulis PInvokeCompiler di mana kami mengambil Majelis sebagai input yang berisi PInvokeImpls dan menghasilkan rakitan yang diperluas dengan informasi marshalling dan juga membuatnya sadar akan situasi xplat melalui atribut. Atributnya ada di sini: https://www.nuget.org/packages/NativeLibraryAttribute

Idenya adalah Anda dapat menentukan referensi modul yang berbeda (nama dll) & titik masuk (nama fungsi) berdasarkan Pengidentifikasi Platform dan ukuran Pointer.

Alat ini masih dalam masa pertumbuhan tetapi mendukung ide-ide yang diangkat di sini kecuali Native Long (yang menurut saya masih merupakan sesuatu yang harus diurus atau tidak diekspos oleh binding asli kepada konsumen), tetapi jika ada manfaat asli, yaitu memang pada kenyataannya memudahkan pengembangan pembungkus, maka dimungkinkan untuk memasukkannya.

Juga mengingat tumpang tindih yang kemungkinan akan ada dengan MCG, saya akan bekerja dengan @yizhang82 untuk melihat bagaimana kita dapat merasionalisasi semua ini menjadi satu alat hebat untuk .NET daripada beberapa alat.

@mjsabby Tentu saja. Mari ngobrol offline dan lihat bagaimana kita bisa bekerja sama untuk memajukan ini.

Atributnya ada di sini: https://www.nuget.org/packages/NativeLibraryAttribute

Apakah ada kode sumber untuk itu? Nuget dilarang di banyak lingkungan karena kendala keamanan.

kecuali Native Long (yang menurut saya masih merupakan sesuatu yang harus diurus atau tidak diekspos ke konsumen oleh binding asli)

Bisakah Anda memberikan contoh penggunaan kode tentang bagaimana Anda membayangkan ini bekerja? Atau apakah solusinya masih menulis dua binding/rakitan berbeda untuk API yang sama?

Saya tidak mengerti mengapa mungkin untuk berpartisipasi dalam pemuatan terkelola melalui suatu acara, tetapi tidak tidak terkelola. Peristiwa yang mirip dengan peristiwa Penyelesaian untuk rakitan terkelola akan sangat bagus—beri tahu saya saat runtime mencoba memuat pustaka yang tidak terkelola dan izinkan saya (mencoba) memberikan pegangan saya sendiri ke pustaka asli yang dimuat. Jika semua event handler mengembalikan IntPtr.Zero (atau menunjukkan kegagalan dengan cara lain), kembali ke pemuatan bawaan "asli".

Di Linux saya menemukan solusi praktis untuk masalah ini, yang tidak melibatkan modifikasi DllImports sama sekali.

Idenya adalah untuk menghasilkan sebuah rintisan shared library yang diberi nama seperti Windows DLL dan berisi referensi ke nama library sebenarnya untuk Linux. DllImport dalam kode .NET tetap tidak berubah dan menggunakan nama Windows DLL (tanpa akhiran .dll). Pemuat asli inti .NET kemudian akan memuat objek bersama rintisan. Tindakan ini memanggil pemuat dinamis Linux (ld.so) yang kemudian menyelesaikan ketergantungan stub pada pustaka asli dan secara otomatis memetakan semua simbol dari pustaka asli ke dalam rintisan.

Untuk menghasilkan perpustakaan rintisan, lakukan hal berikut:

touch empty.c
gcc -shared -o libLinuxName.so empty.c    
gcc -Wl,--no-as-needed -shared -o libWindowsName.so -fPIC -L. -l:libLinuxName.so
rm -f libLinuxName.so

Hasilnya dapat diperiksa menggunakan perintah readelf :

$ readelf -d libWindowsName.so
Dynamic section at offset 0xe38 contains 22 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libLinuxName.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x4b8
 0x000000000000000d (FINI)               0x600
...

Setiap rintisan sangat kecil (8 KB) dan dengan demikian dapat dengan mudah disertakan dalam paket NuGet lintas platform.
Dimungkinkan untuk menghasilkan perpustakaan rintisan dengan pemanggilan sederhana ld karena tidak ada kompilasi yang benar-benar terlibat, tetapi saya tidak dapat menemukan argumen yang benar untuk melakukan itu.

Contoh dunia nyata dari teknik ini dapat dilihat di https://github.com/surban/managedCuda/tree/master/StubsForLinux

@surban +1, perhatikan bahwa perpustakaan tidak boleh memiliki titik di namanya. Loader berhenti menebak titik pertama.
Masalah lain adalah bahwa nama akan bertentangan untuk sistem *nix. Anda tidak dapat menempatkan kedua bit Linux/OSX secara bersamaan karena loader menebak nama yang sama di bawah platform. Solusinya adalah melepaskan biner secara dinamis sebelum panggilan P/Invoke pertama.

Silakan lihat di sini untuk contoh aktual:
https://github.com/Microsoft/GraphEngine/blob/master/src/Trinity.Core/Trinity/Runtime/TrinityC.cs

@yizhang82
Apakah MCG sudah open source? Jika tidak, di mana dan bagaimana kita bisa menggunakannya?

Interop dengan pustaka asli pada proposal .Net Standard.

Kami memiliki pustaka C yang disediakan oleh pihak ketiga pada platform Linux dan Windows. Nama perpustakaan tidak mengikuti konvensi penamaan. Katakanlah nama perpustakaan adalah linuxlib.so dan winLib.dll. Kami ingin membuat proyek .Net Standard yang menggunakan pustaka ini. Kami menggunakan atribut DllImport. Tantangannya adalah bagaimana memuat perpustakaan yang benar untuk platform OS. Dalam .Net Standard 2.0 tidak ada dukungan untuk ini. Saya ingin mengusulkan peningkatan sederhana ke .Net Standard yang akan menyelesaikan masalah ini.

Kode di bawah ini menunjukkan bagaimana kode pengguna akan terlihat. Peta antara OS dan perpustakaan ditambahkan ke kelas DllImportMap.
Atribut DllImport tidak hanya mengecualikan nama perpustakaan, tetapi juga nama peta.
///


/// Kelas ini adalah bagian dari perakitan yang menggunakan pustaka C pada platform Windows dan Linux
/// Peta harus ditambahkan ke konstruktor statis dari kelas yang memiliki deklarasi fungsi C eksternal
///

Fungsi Eksternal kelas publik
{
Fungsi Eksternal statis ()
{
DllImportMap.AddMap("NamaPeta", Kamus baru{ { OSPlatform.Windows, "winlib.dll" }, { OSPlatform.Linux, "linuxlib.so" } });

}

private const string LibName = "MapName";

[DllImport(LibName)]
public static extern int CFunc(string val);

}

Kode di bawah ini harus ditambahkan oleh Microsoft ke .Net Standard

///


/// Kelas ini harus diimplementasikan oleh Microsoft
/// Itu harus ditambahkan ke Majelis dan namespace yang sama dengan kelas DllImportAttribute.
/// Memungkinkan penambahan peta yang menjelaskan pustaka C mana yang harus digunakan pada platform mana.
/// Beberapa peta dapat ditambahkan.
/// DllImportMap adalah kelas statis.
///

kelas statis publik DllImportMap
{
Kamus statis pribadi> masterMap = Kamus baru>();
AddMap public static void (kunci string, Kamuspeta)
{
jika (masterMap.ContainsKey(kunci))
throw new Exception(string.Format($"Key {key} sudah ada di masterMap"));
masterMap.Add(kunci, peta);
}

internal static Dictionary<OSPlatform, string> ByKey(string key)
{
  Dictionary<OSPlatform, string> map;
  masterMap.TryGetValue(key, out map);
  return map;
}

}

///


/// Diasumsikan bahwa Microsoft memiliki kelas yang memproses DllImportAttribute.
/// Metode baru DllNameAnalyzer harus ditambahkan. Ini memproses parameter dllName dari DllImportAttribute
///

kelas publik DllImportProcessingClass
{
///
/// Parameter dllName dari DllImportAttribute sekarang dapat menerima nama peta.
/// Metode ini memeriksa apakah parameter libName adalah kunci peta dan mengambil nama pustaka yang sesuai dengan platform OS
///

/// Nama perpustakaan atau kunci di peta
///
string pribadi DllNameAnalyzer(string libName)
{
jika (string.IsNullOrWhiteSpace(libName))
kembali libName;
// pertama periksa apakah libName adalah kunci di masterMap
Kamuspeta = DllImportMap.ByKey(libName);
if (map == null) // bukan kunci, jadi itu adalah nama perpustakaan
kembali libName;

  // what is an OS platform?
  OSPlatform platform;
  if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    platform = OSPlatform.Windows;
  else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
    platform = OSPlatform.Linux;
  else
    platform = OSPlatform.OSX;

  // retrieve a library for the OS, or null if not specified
  string foundLibName;
  map.TryGetValue(platform, out foundLibName);
  return foundLibName;
}

}

<dllmap> di Mono tidak diimplementasikan dengan ketergantungan pada System.Configuration, ini diimplementasikan secara independen dari tumpukan itu.

@efimackerman Apa yang Anda usulkan terdengar seperti dotnet/corefx#17135; jika itu masalahnya, maukah Anda "meningkatkan" masalah itu (ikon jempol ke atas di bawah deskripsi masalah)?

@qmfrederik Saya tidak melihat bagaimana proposal saya mirip dengan yang Anda sebutkan dalam komentar Anda.

Saya baru-baru ini merilis perpustakaan yang memecahkan sebagian besar (jika tidak semua) masalah yang disebutkan di atas, dan menggabungkan pendekatan berbasis delegasi lintas platform ke dalam API yang sederhana dan mudah digunakan, yang juga mendukung Mono DllMaps, serta implementasi pencarian perpustakaan kustom. Backend secara dangkal mirip dengan API yang diusulkan di dotnet/corefx#17135, tetapi diabstraksikan untuk kemudahan penggunaan.

Selain itu, ini memungkinkan beberapa ekstensi yang lebih menarik ke sistem P/Invoke, seperti pengaturan langsung T? dan ref T? .

https://github.com/Firwood-Software/AdvanceDLSupport

Saya ingin melihat DllMap (atau yang setara) didukung selain https://github.com/dotnet/corefx/issues/17135.

Saya menyuarakan 'masalah' yang saya miliki dengan alternatif di sini: https://github.com/dotnet/corefx/issues/17135#issuecomment -374248831

@jeffschwMSFT Lihat sekitar 3 tahun ke atas utas percakapan masalah ini untuk sekelompok diskusi tentang mencoba meniru sistem XML DllMap Mono dan mengapa itu ide yang sangat buruk. Mari kita tolong tidak membangkitkan itu sekarang.

mencoba meniru sistem XML DllMap Mono dan mengapa itu ide yang sangat buruk

:+1:

@jeffschwMSFT Berdasarkan pembacaan cepat dari diskusi awal, saya juga akan merekomendasikan terhadap DLLMap. Ini adalah menggunakan kembali desain yang dikandung untuk tujuan yang berbeda.

Jadi, bagaimana saya melakukannya sekarang di .NET-Core ?
Saya memiliki SharpFont yang memuat freetype melalui dllmap, dan itu tidak berfungsi pada .NET Core.
Saya harus secara manual mengubah atribut dllimport ke filename (dan itu hanya berfungsi dengan fullpath+filename ?

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <dllmap dll="freetype6" os="linux" target="libfreetype.so.6" />
    <dllmap dll="freetype6" os="osx" target="/Library/Frameworks/Mono.framework/Libraries/libfreetype.6.dylib" />
    <dllmap dll="freetype6" os="freebsd" target="libfreetype.so.6" />
</configuration>

Dan mengapa .NET memuat fungsi perakitan asli dengan atribut waktu kompilasi, ketika ia kemudian benar-benar melakukan dlsym pada saat runtime ketika dipanggil?
LoadLibrary & dlopen ditulis dalam C, dan meskipun demikian mereka tidak terlalu fleksibel.

Dan mengapa memiliki fitur itu?
Akan jauh lebih baik untuk memiliki pembungkus lintas platform di sekitar LoadLibrary/dlopen dan GetProcAddress/dlsym, sehingga orang tidak dapat melakukannya dengan cara ini sejak awal.

Saat ini, seperti yang saya lihat, saya perlu membuat kelas statis dengan banyak delegasi, kemudian melakukan dlopen dan dlsym secara manual, untuk mengganti semua freetype-internal - yaitu, jika saya tidak ingin membuat 3 versi perakitan untuk setiap platform karena nama objek bersama berbeda.

Dan mengapa dllimport tidak dapat mengambil fungsi statis sebagai argumen atributnya, selain const-string ?
Pemilihan nama kemudian dapat dilakukan dengan cara yang ditentukan pengguna, dan masalah ini akan diselesaikan, dan semua kode lama akan tetap berfungsi.

Karena atribut tidak mengizinkan panggilan balik, inilah contoh yang saya maksud:

namespace NetStandardReporting
{


    // [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
    [System.AttributeUsage(System.AttributeTargets.Method, Inherited = false)]
    public class DynamicDllImportAttribute
        : System.Attribute
    {
        protected string m_dllName;


        public string Value
        {
            get
            {
                return this.m_dllName;
            }
        }

        public string EntryPoint;
        public System.Runtime.InteropServices.CharSet CharSet;
        public bool SetLastError;
        public bool ExactSpelling;
        public System.Runtime.InteropServices.CallingConvention CallingConvention;
        public bool BestFitMapping;
        public bool PreserveSig;
        public bool ThrowOnUnmappableChar;


        public DynamicDllImportAttribute(string dllName)
            : base()
        {
            this.m_dllName = dllName;
        }


        private static System.Type CreateDelegateType(System.Reflection.MethodInfo methodInfo)
        {
            System.Func<System.Type[], System.Type> getType;
            bool isAction = methodInfo.ReturnType.Equals((typeof(void)));

            System.Reflection.ParameterInfo[] pis = methodInfo.GetParameters();
            System.Type[] types = new System.Type[pis.Length + (isAction ? 0: 1)];

            for (int i = 0; i < pis.Length; ++i)
            {
                types[i] = pis[i].ParameterType;
            }

            if (isAction)
            {
                getType = System.Linq.Expressions.Expression.GetActionType;
            }
            else
            {
                getType = System.Linq.Expressions.Expression.GetFuncType;
                types[pis.Length] = methodInfo.ReturnType;
            }

            return getType(types);
        }


        private static System.Delegate CreateDelegate(System.Reflection.MethodInfo methodInfo, object target)
        {
            System.Type tDelegate = CreateDelegateType(methodInfo);

            if(target != null)
                return System.Delegate.CreateDelegate(tDelegate, target, methodInfo.Name);

            return System.Delegate.CreateDelegate(tDelegate, methodInfo);
        }


        protected delegate string getName_t();

        public DynamicDllImportAttribute(System.Type classType, string delegateName)
            : base()
        {
            System.Reflection.MethodInfo mi = classType.GetMethod(delegateName,
                  System.Reflection.BindingFlags.Static
                | System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.NonPublic
            );

            // System.Delegate getName = CreateDelegate(mi, null);
            // object name = getName.DynamicInvoke(null);
            // this.m_dllName = System.Convert.ToString(name);

            // System.Func<string> getName = (System.Func<string>)CreateDelegate(mi, null);
            // this.m_dllName = getName();

            getName_t getName = (getName_t)System.Delegate.CreateDelegate(typeof(getName_t), mi);
            this.m_dllName = getName();
        }


    } // End Class DynamicDllImportAttribute 


    public static class DynamicDllImportTest 
    {

        private static string GetFreetypeName()
        {
            if (System.Environment.OSVersion.Platform == System.PlatformID.Unix)
                return "libfreetype.so.6";

            return "freetype6.dll";
        }


        // [DynamicDllImport("freetype6")]
        // [DynamicDllImport(typeof(DynamicDllImportTest), nameof(GetFreetypeName))]
        // [DynamicDllImport("foo", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
        [DynamicDllImport(typeof(DynamicDllImportTest), nameof(GetFreetypeName), CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
        public static string bar()
        {
            return "foobar";
        }


        // NetStandardReporting.DynamicDllImportTest.Test();
        public static void Test()
        {
            System.Reflection.MethodInfo mi = typeof(DynamicDllImportTest).GetMethod("bar",
                  System.Reflection.BindingFlags.Static
                | System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.NonPublic);

            object[] attrs = mi.GetCustomAttributes(true);
            foreach (object attr in attrs)
            {
                DynamicDllImportAttribute importAttr = attr as DynamicDllImportAttribute;
                if (importAttr != null)
                {
                    System.Console.WriteLine(importAttr.Value);
                }
            } // Next attr 

        } // End Sub Test 


    } // End Class 


} // End Namespace 

@ststeiger Anda SOL dengan .NET Core normal. Namun, saya telah membuat perpustakaan yang memecahkan hampir semua masalah yang disebutkan di utas ini. Mungkin patut dicoba: https://github.com/Firwood-Software/AdvanceDLSupport

@Nihlus :
Bagus, dan dengan menggunakan antarmuka, seseorang dapat menggunakan DependcyInjection pada pustaka asli.
Pasti layak untuk dilihat.

Saya sudah membuat hal serupa, dulu (memuat oracle native-dlls di asp.net dan mono).
Namun, mengubah kode sumber DllImportAttribute saat ini menjadi salah satu dari DynamicDllImportAttribute akan jauh lebih mudah untuk mem-porting kode lama.

Tidak ada masalah kompatibilitas mundur apa pun.
Dengan revisi ini, Linq juga tidak diperlukan, bahkan kompatibel dengan .NET 2.0.

Anda sekarang bahkan dapat mengambil nama dll dari database.
Sekarang akan lucu untuk menyimpan delegasi menjadi variabel anggota, dan mengubah nilainya menjadi:

    public string Value
    {
        get
        {
            return getName();
        }
    }

Kemudian Anda secara teoritis bahkan dapat menggunakan versi freetype yang berbeda per penyewa dalam database tergantung pada HttpContext (domain), jika tersedia;)
(dengan asumsi tanda tangan dan yang lainnya tetap sama)
Akan sedikit meretas, tetapi itu akan berhasil.
Tapi saya kira itu akan mengacaukan hmodule.
Dengan asumsi itu memuat delegasi setiap kali, yang mungkin tidak.

Apa yang kita perlukan sekarang adalah atribut ekstensi dengan properti ekstensi, untuk menambahkan ini secara surut ke kerangka .net lengkap tanpa mengubah sumbernya.

Akan jauh lebih baik untuk memiliki pembungkus lintas platform di sekitar LoadLibrary/dlopen dan GetProcAddress/dlsym

Ya, inilah tepatnya yang kami rencanakan. Dilacak oleh https://github.com/dotnet/corefx/issues/32015.

Menutup masalah ini, masalah ini dilacak oleh dotnet/corefx#32015.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat