Powershell: Memuat Perpustakaan Asli rusak

Dibuat pada 27 Apr 2019  ·  70Komentar  ·  Sumber: PowerShell/PowerShell

Mencoba memuat modul yang memerlukan p / pemanggilan dari pustaka asli pihak ketiga gagal memuat pustaka dari folder yang sama dengan DLL yang melakukan pemanggilan p /.

Ini _digunakan untuk bekerja_ di PowerShell Core 6.1.0 tetapi mengalami kemunduran antara 6.2.0-preview1 dan 6.2.0-preview2. Ini masih berfungsi di Windows PowerShell.

Perpustakaan asli yang gagal ditemukan terletak di folder yang sama dengan SkiaSharp.dll yang mencoba untuk menemukannya. Lihat https://github.com/PowerShell/PowerShell/issues/8861#issuecomment -462362391 dan utas masalah itu untuk mengetahui mengapa hal itu dilakukan dengan cara itu.

Langkah-langkah untuk mereproduksi

Install-Module PSWordCloud
New-WordCloud

Perilaku yang diharapkan

cmdlet New-WordCloud at command pipeline position 1
Supply values for the following parameters:
InputObject:

Perilaku sebenarnya

new-wordcloud : The type initializer for 'PSWordCloud.WCUtils' threw an exception.
At line:1 char:1
+ new-wordcloud
+ ~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], TypeInitializationException
+ FullyQualifiedErrorId : System.TypeInitializationException

Membuat kueri untuk pengecualian dasar dengan $Error[0].Exception.GetBaseException() | Format-List * -F menghasilkan yang berikut:

Message        : Unable to load DLL 'libSkiaSharp' or one of its dependencies: The specified module could not be
                 found. (Exception from HRESULT: 0x8007007E)
TypeName       :
Data           : {}
InnerException :
TargetSite     : IntPtr sk_fontmgr_ref_default()
StackTrace     :    at SkiaSharp.SkiaApi.sk_fontmgr_ref_default()
                    at SkiaSharp.SKFontManager.get_Default()
                    at PSWordCloud.WCUtils..cctor()
HelpLink       :
Source         : SkiaSharp
HResult        : -2146233052

Data lingkungan

Name                           Value
----                           -----
PSVersion                      6.2.0
PSEdition                      Core
GitCommitId                    6.2.0
OS                             Microsoft Windows 10.0.17763
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

_Catatan: _ Hal yang sama telah diuji di lingkungan Mac (terima kasih @steviecoaster!) Dan juga gagal dengan cara yang sama. Saya menduga itu akan identik pada distro Linux juga.

/ cc @ SteveL-MSFT

Issue-Question Resolution-Fixed

Komentar yang paling membantu

@bayu_joo

Saya benar-benar berharap saya tidak perlu mengkompilasi ulang ini untuk setiap platform karena ini adalah perakitan netstandard2. Tetapi AFAICT nilai nama lib yang diberikan ke atribut DllImport harus berupa konstanta waktu kompilasi. :-(

FWIW Anda dapat menghilangkan ekstensi untuk DllImport . Kami melakukannya untuk ketergantungan asli non-Windows PSES. Tidak membantu jika nama dasar tidak sama.

Anda mungkin juga dapat melewati arahan kompilator untuk panggilan LoadLibrary / dlopen dengan menyimpannya dalam implementasi antarmuka terpisah. Selama metode tersebut tidak JIT, metode ini tidak boleh dilempar saat runtime.

Semua 70 komentar

@adityapatwardhan, bisakah kamu lihat ini?

Anda dapat melihat jalur yang digunakan dengan utilitas procmon dan kemudian mencoba memperbaikinya dengan HintPath

Saya akan memberikan pandangan itu tetapi mengingat ini bukan perpustakaan yang saya muat secara langsung (itu dimuat oleh SkiaSharp.dll utama itu sendiri) Saya tidak begitu percaya diri dalam melakukan itu terlalu banyak. Fakta bahwa ada empat kemungkinan file perpustakaan untuk dimuat tergantung pada platform juga memperumit masalah.

Namun terlepas dari itu, ini sepertinya bukan perubahan yang disengaja sehingga regresi mungkin harus ditangani.

Tim MSFT bekerja keras pada vNext p / memanggil di .Net Core 3.0 dan mungkin mereka mem-porting sesuatu ke 2.1. Saya tidak ingat bahwa kami mengubah apa pun di pemuatan dll terkait PowerShell.

Ada # 8073 dan beberapa perubahan bertanda Internal di 6.2.0-preview2. Saya tidak tahu apa lagi yang mungkin berubah selama itu. 😕

Perubahan PR adalah memuat dll modul dari folder modul terlebih dahulu kemudian dari GAC. Masalah Anda adalah bahwa modul dll mereferensikan dll kedua dan pemuatan dll otomatis (tidak Core bukan PowerShell) tidak menemukan dll. Saran saya adalah menambahkan hintpath secara eksplisit ke dll dalam file proyek.

@iSazonov Saya menambahkan beberapa referensi HintPath ke csproj saya, tetapi yang saya dapatkan hanyalah peringatan ketika saya dotnet publish . Tidak ada perubahan perilaku modul setelah dimuat.

Dari sedikit membaca di atasnya, sepertinya HintPath digunakan untuk mereferensikan rakitan eksternal yang _tidak disertakan_ dalam build proyek itu sendiri. Rakitan SkiaSharp disertakan, jadi menambahkan HintPath untuk kompilasi / build tidak memengaruhi DLL Skia atau jalur yang dicari saat runtime.

Sebagai referensi, ini adalah csproj saya:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="PowerShellStandard.Library" Version="5.1.1" PrivateAssets="All" />
    <PackageReference Include="SkiaSharp" Version="1.68.0" />
    <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" />
  </ItemGroup>

</Project>

Saya menambahkan baris berikut tidak berpengaruh (selain peringatan waktu build / publish bahwa mereka tidak dapat ditemukan, karena, yah, mereka tidak ada sampai setelah publikasi selesai):

  <ItemGroup>
    <Reference Include="libSkiaSharp">
        <HintPath>win-x86/libSkiaSharp.dll</HintPath>
        <HintPath>win-x64/libSkiaSharp.dll</HintPath>
        <HintPath>osx/libSkiaSharp.dylib</HintPath>
        <HintPath>linux-x64/libSkiaSharp.dylib</HintPath>
    </Reference>
</ItemGroup>

Dari apa yang saya baca di HintPath, tampaknya tidak berpengaruh pada jalur apa yang dicari saat memuat DLL, atau bahwa DLL mencoba memuat DLL lain untuk tujuan p / pemanggilan.

_That_ HintPath adalah waktu kompilasi saja.

Cara Anda mengatakannya membuatnya terdengar seperti ada hal lain yang saya lewatkan?

@ vexx32 Jika Anda memiliki kode repo sederhana, Anda dapat bertanya di repositori CoreCLR.

Saya tidak yakin apakah saya bisa mengesampingkan perubahan khusus Powershell dulu. Saya mungkin harus melakukan tes kecil bersama untuk aplikasi konsol dan melihat bagaimana kelanjutannya.

Berkat tip dari @Jaykul , saya berhasil menyusun solusi stop-gap. Menjalankan ini dari PSM1 sebelum mengimpor cmdlet dan DLL Skia utama tampaknya menyelesaikan masalah.

Add-Type -TypeDefinition @"
    using System.Runtime.InteropServices;

    public class DllLoadPath
    {
        [DllImport("kernel32", CharSet=CharSet.Unicode)]
        public static extern int SetDllDirectory(string NewDirectory);
    }
"@

# ...

[DllLoadPath]::SetDllDirectory($NativeRuntimeFolder)

Namun, fakta bahwa _memerlukan_ tambahan p / pemanggilan untuk membuat ini berfungsi agak konyol. Saya juga sangat bingung tentang apa yang mengubah dan menghapus jalur direktori SkiaSharp.dll yang dimuat dari lokasi pencarian standar seperti yang didokumentasikan di sini .

@ vexx32 Saya tidak dapat mereproduksi masalah tersebut. Apakah saya melewatkan sesuatu di repro? Mungkin versi tertentu jika PSWordCloud?

PS> Install-Module PSWordCloud                                                               
Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its
InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): y

PS> New-WordCloud                                                                            
cmdlet New-WordCloud at command pipeline position 1
Supply values for the following parameters:
InputObject:
Path:

PS> $PSVersionTable                                                                          
Name                           Value
----                           -----
PSVersion                      6.2.0
PSEdition                      Core
GitCommitId                    6.2.0
OS                             Microsoft Windows 10.0.18885
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Maaf, seperti yang disebutkan, saya memperbaikinya di 2.1.1.

Jika Anda menginstal 2.1.0 secara khusus, Anda akan melihat kegagalan, karena tidak memiliki solusi p / pemanggilan di atas.

Oke, saya bisa tegur sekarang. Akan saya lihat.

@ vexx32 Sepertinya kita memuat semua berkas yang terdaftar di FileList dari psd1 . Apakah Anda perlu memilikinya? Ketika saya menghapus semua entri dari FileList saya tidak dapat melakukan repro masalah lagi.

Apakah begitu? Aneh sekali. Tidak, saya tidak terlalu membutuhkannya dan mungkin akan tetap menghapusnya karena saya tidak benar-benar ingin terus memperbaruinya, tetapi itu sudah ada untuk beberapa versi.

Tampaknya aneh bahwa hal itu akan memengaruhi pemuatan DLL secara tiba-tiba.

Tampaknya kami memuat semua file yang terdaftar di FileList dari psd1.

Kami memiliki masalah untuk mendukung "jika" dalam file .psd1. Jadi kita bisa memuat dll khusus platform.

@iSazon itu # 5541 . Saya akan segera memberi Anda pembaruan.

Itu adalah kemungkinan. Apa yang saya ingin tahu _now_ adalah jika hanya menambahkan jalur ke perpustakaan asli (semuanya) di FileList sudah cukup di sini ... Saya bahkan mungkin tidak memerlukan p / pemanggilan. Saya akan melakukan beberapa pengujian! 😊

EDIT: Tidak, saya tidak melihat itu berpengaruh. :(

Kami menyebut Assembly.Load() yang hanya memuat rakitan terkelola.

Kami membuat perubahan di 6.2.0-preview.2 di mana kami mulai memuat DLL dari folder modul sebelum mencari di GAC. Efek samping dari perubahan itu adalah kita juga memuat DLL di FileList.

Itu masuk akal. Apakah kami memiliki masalah dokumen terbuka untuk itu? Sepertinya perubahan semacam ini akan sangat sulit dilacak jika seseorang mengalaminya dan kami belum mendokumentasikannya. Kami mungkin harus mendokumentasikan bahwa Anda tidak ingin memasukkan majelis asli dalam daftar file karena alasan itu.

Hargai bantuan untuk melacaknya! Untuk saat ini solusi P / pemanggilan yang saya sebutkan memberi saya fleksibilitas paling besar, tetapi mungkin masuk akal untuk menambahkan penanganan untuk hal-hal seperti itu dalam pemuatan modul.

Misalnya, di PrivateData kita menambahkan entri yang disebut RuntimeLibPaths, yang merupakan hashtable di mana kunci cocok dengan nama / arsitektur platform yang didukung, dan nilainya adalah jalur ke folder yang berisi pustaka asli. Folder yang sesuai ditambahkan ke jalur pencarian dll saat diimpor oleh PowerShell secara internal daripada harus ditambahkan oleh modul itu sendiri saat diimpor.

Apakah kami memiliki masalah dokumen terbuka untuk itu?

Kami belum.

@adityapatwardhan @ SteveL-MSFT terima kasih kepada @TylerLeonhardt (tidak sengaja) mengingatkan saya bahwa ini harus bekerja pada hal-hal non-Windows juga jika saya ingin teliti ... Saya butuh solusi yang lebih lengkap.

Saat ini tampaknya tidak ada dokumentasi yang jelas tentang bagaimana interop asli seharusnya ditangani di .NET Core itu sendiri. Yang bisa saya temukan hanyalah halaman ini untuk .NETFramework di MS docs.

Apakah itu ditangani melalui Mono atau sesuatu? Jika melalui Mono, melihat halaman ini sebenarnya ada _bug di Mono_ pada Mac OS yang mencegah jalur pemuatan perpustakaan dipatuhi dengan benar ...

PowerShell ... perlu menangani ini. Saya tidak yakin _adalah cara untuk menangani ini dengan tepat dari semua platform dari modul itu sendiri.

@ vexx32 tidak tahu detailnya, jika Anda dapat menghasilkan repro di C # yang tidak menyertakan PowerShell, saya sarankan untuk membuka masalah di repo CoreCLR

@ steveL-msft itulah masalahnya, saya pikir. Tampaknya berfungsi dengan baik di tempat lain, dengan beberapa peringatan. Misalnya, memuat lib asli berfungsi jika berada di folder yang sama dengan SkiaSharp.dll utama, tetapi untuk PS yang tidak berfungsi karena perubahan yang disebutkan @adityapatwardhan . Itu hanya melipatgandakan kesulitan dalam kasus bekerja dengan PowerShell dan cara menangani modul.

Dalam kebanyakan situasi dalam bekerja dengan pustaka asli seperti itu, ini dapat disarikan, karena Anda bisa saja memaketkannya untuk setiap platform yang didukung. Kami benar-benar tidak memiliki kemewahan itu untuk modul PS, kecuali saya ingin mengemas dan memelihara tiga atau lebih modul galeri yang benar-benar terpisah, yang sayangnya sangat buruk untuk dapat ditemukan dan pengalaman pengguna akhir.

Mengingat bahwa PS adalah alat lintas platform, kami memerlukan beberapa cara untuk menangani pustaka asli dalam modul, atau kami memerlukan beberapa cara agar modul PackageManagement mengenali bahwa perlu ada pustaka terpisah yang diekstraksi pada basis per platform dari nupkg.

Saya terbuka untuk solusi alternatif, tetapi tampaknya dek di sini cukup langka. :bingung:

@ vexx32 Saya rasa Anda menginginkan sesuatu seperti ini https://github.com/PowerShell/PowerShellGet/issues/273

Di CoreFX 3.0 kami mendapatkan beberapa API baru untuk perpustakaan asli pinoke. Mereka baru berada di awal perjalanan, tetapi mereka ingin melakukan sesuatu yang lebih baik dari Mono.

Saat ini solusi solusi saya terlihat seperti ini:

  1. Library asli untuk Mac dan Linux ada di root modul, karena tampaknya itulah satu-satunya tempat saya dapat meletakkannya dan memuatnya dengan andal saat ini.
  2. A ScriptsToProcess menangani p / invoke pada impor modul untuk menambahkan baik versi 32 atau 64-bit dari pustaka asli Windows, yang harus disimpan dalam subfolder karena nama filenya, sayangnya, identik.

Tidak ideal, dan seperti yang dapat dibuktikan oleh @TylerLeonhardt dari alirannya hari ini, ini masih tidak berfungsi karena beberapa alasan saat berjalan di Azure Functions saat ini.

@ vexx32 tidak tahu detailnya, jika Anda dapat menghasilkan repro di C # yang tidak menyertakan PowerShell, saya sarankan untuk membuka masalah di repo CoreCLR

Tidak: Dalam C # ini bukan masalah

Di C # Anda juga:

  1. Kembangkan aplikasi, dan lepaskan _penginstal terpisah_ per platform, dan hanya perlu _satu jalur penelusuran perpustakaan_ yang perlu dikhawatirkan.
  2. Kembangkan perpustakaan, dan rilis _NuGet packages_, dan nuget memberi Anda pola jalur yang memberi tahu Anda di mana harus meletakkan perpustakaan Anda, dan kemudian studio / msbuild mengurus semuanya untuk Anda.

@ vexx32 Saya rasa Anda menginginkan sesuatu seperti ini PowerShell / PowerShellGet # 273

Tidak: Ini bukan masalah lintas platform

Karena itulah bug ini diajukan. Sejak PowerShell 6.2 @ vexx32 bahkan tidak bisa mendapatkan versi yang bekerja di _hanya Windows_ karena dia tidak tahu bagaimana membuat PowerShell menggunakan jalur pencarian perpustakaan yang benar.

Di Windows setidaknya ada ap / invoke yang tersedia yang memungkinkan saya mengotak-atiknya. Itu _workaround_ tapi sebenarnya kita butuh perbaikan yang lebih baik.

Pada sistem Unix, bagaimanapun, tidak ada p / invoke_ yang tersedia, dari apa yang dapat saya temukan, dan untuk alasan apa pun PowerShell tidak menanyakan variabel lingkungan yang biasanya menunjukkan jalur mana yang harus dicari ketika Anda mencoba DllImport dari perakitan dimuat oleh PS.

Jadi pada dasarnya semua platform ini ... sangat tidak ramah untuk melakukan apa pun. 😅

Karena saat ini berdiri, tampaknya tidak ada pilihan lain selain memasukkan setiap dan semua rakitan asli Unix secara langsung di folder root modul (yang membatasi platform yang dapat Anda dukung, karena sering kali ada benturan nama), dan kemudian berhati-hati tentang yang mana folder yang Anda tambahkan ke jalur pencarian di Windows dengan p / invoke (jika Anda peduli untuk mendukung x86 Windows - dan @TylerLeonhardt memberi tahu saya bahwa Azure Functions berjalan pada x86 karena alasan yang aneh, jadi itu suatu keharusan jika Anda ingin bekerja dengan itu.)

Inilah yang saya gunakan dalam file psm1 saya untuk memuat biner asli yang sesuai untuk platform. Catatan: Saya hanya menargetkan / menguji ini di Linux dan Windows:

# Module PSM1 File
$binPath = Join-Path $PSScriptRoot bin $(if ($IsWindows) {"Windows"} else {"Linux"})
Add-Type -Path $binPath\Acme.Api.dll

Dan ini adalah kode C # yang ada di Acme.Api.dll

[Flags]
public enum DLOpenFlags
{
    RTLD_LAZY = 1,
    RTLD_NOW = 2,
    RTLD_LOCAL = 4,
    RTLD_GLOBAL = 8,
}

public static class LinuxNativeMethods
{
    [DllImport("libdl.so", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr dlopen(
        string dlToOpen, 
        DLOpenFlags flags);
}

public static class Win32NativeMethods
{
    [DllImport("kernel32.dll", EntryPoint="LoadLibraryW")]
    public static extern IntPtr LoadLibrary(
        [InAttribute, MarshalAs(UnmanagedType.LPWStr)] string dllToLoad);
}

public static class FooNativeMethods
{
    // On Linux/macOS the shared lib must be named Foo.so and not libFoo.so
    public const string LibName = "Foo";

    static FooNativeMethods()
    {
        string assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 
        {
            string nativeDllPath = Path.Combine(assemblyDir, "Windows", $"{LibName}.dll");
            LibHandle = Win32NativeMethods.LoadLibrary(nativeDllPath);
        }
        else
        {
            string nativeSOPath = Path.Combine(assemblyDir, "Linux", $"{LibName}.so");
            LibHandle = LinuxNativeMethods.dlopen(nativeSOPath, DLOpenFlags.RTLD_LAZY);
        }
    }

    public static IntPtr LibHandle { get; private set; }

    [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
    public static extern int getLastError(
        StringBuilder buffer,
        ref int bufferLength);

    ...
}

@ vexx32 Anda dapat membuka masalah konsultasi di repo CoreCLR. Saya kira mereka harus mengatakan itu.

@rkeithhill ini hanya berfungsi untuk biner .NET. Saya perlu memuat biner .NET yang perlu menggunakan P / memanggil untuk memuat rakitan asli. Di situlah ia gagal dan hancur total. Saya tidak memiliki kendali atas pustaka ini, ini adalah pustaka pihak ketiga (SkiaSharp, dalam hal ini). Saya tidak dapat memuat pustaka ini di modul biner saya karena harus dimuat pada saat yang sama dengan kode dimuat dari modul saya, karena modul saya bergantung pada jenis yang hanya ada dan berfungsi dengan SkiaSharp dimuat.

@isazonov seperti yang telah dibahas beberapa kali dalam masalah ini sekarang, ini bukan masalah di .net Core karena Anda dapat membuat paket terpisah per platform untuk sebagian besar aplikasi.

PowerShell tidak memiliki cara yang mungkin untuk melakukan ini, dan secara tidak sengaja menghapus beberapa jalur standar dari jalur pencarian perpustakaan, membuatnya benar-benar tidak mungkin untuk bekerja dengan rakitan asli dalam beberapa konteks, dan sangat rapuh dan khusus dalam yang terjadi pada kerja.

@ vexx

Saya perlu memuat biner .NET yang perlu menggunakan P / memanggil untuk memuat rakitan asli. Di situlah di gagal dan berantakan sama sekali.

Uh, persis seperti itulah kode ini. Telah berhasil menggunakannya di 6.2 pada Windows dan Linux untuk sementara waktu sekarang.

Diubah, maaf, saya tidak jelas. Saya tidak dapat melakukan ini dalam biner saya sendiri, tipe harus ada saat modul saya sedang dimuat; Saya tidak dapat mengimpornya selama perintah instantiation .... Dan saya tidak perlu melakukannya.

PowerShell seharusnya tidak kurang berfungsi daripada inti .net dalam hal memuat binari.

Karena itu, saya akan melihat apakah saya dapat menyesuaikan beberapa hal dan meningkatkan solusi saya. Hargai contohnya!

Tapi sungguh ... Saya tidak mengerti mengapa saya harus bersusah payah untuk mengatasi ini. Seharusnya hanya bekerja, dan sampai PowerShell terlibat ... Itu berhasil.

Setuju pada rintangan yang menyebalkan. Baru saja mencoba menunjukkan bagaimana pemuatan lib asli dapat dilakukan melalui p / invoke di Windows dan Linux. :-)

@ vexx32 Saya melihat perpecahan dalam diskusi. Bisakah Anda mengklarifikasi bahwa itu adalah masalah - Anda memiliki modul dengan pustaka asli dan Anda perlu memuat pustaka yang tepat dengan ketergantungan pada platform tertentu?

Semacam. Ini adalah tata letak yang saya miliki yang saat ini berfungsi, tetapi berantakan karena semuanya. Apa pun yang tidak dapat saya tentukan jalurnya melalui p / pemanggilan harus ada di root modul (yang mengalami masalah tabrakan nama pada beberapa platform).

PSWordCloud/
|- PSWordCloudCmdlet.dll
|- SkiaSharp.dll
|- libSkiaSharp.so
|- libSkiaSharp.dylib
|- win-x86/
  |- libSkiaSharp.dll
|- win-x64/
  |- libSkiaSharp.dll

SkiaSharp.dll adalah .NET, dan merupakan rakitan referensi untuk dll cmdlet utama saya. Empat file libSkiaSharp adalah DLL asli per platform. Saya tidak mengontrol kode di SkiaSharp.dll, jadi saya tidak bisa memaksa DLL itu untuk memperbarui jalur sebelum p / memanggil.

Jadi ... Saya memiliki modul dengan ketergantungan NET. Ketergantungan ini bergantung pada pustaka asli yang dimuat dengan DllImport; file pastinya berbeda untuk tiap platform / arsitektur (tetapi beberapa file memiliki benturan nama, sehingga membatasi platform yang sebenarnya dapat saya dukung).

Menggunakan kode @rkeithhill , saya rasa saya mungkin bisa memindahkan pustaka asli Linux ke subfolder juga. Tidak yakin tentang MacOS, mungkin ada sesuatu yang serupa, tetapi saya belum menemukan dokumentasi apa pun tentang metode untuk itu.

_Ideally_, SEMUA file ini harus berada di subfolder yang paling buruk, seperti /PSWordCloud/runtime/<platform>/ - sayangnya, karena bagaimana PowerShell telah mengubah spesifikasi pemuatan jalur dari pengaturan standar, hanya file di root folder modul dapat dimuat melalui DllImport. Lokasi lain mungkin berfungsi, tetapi (setidaknya di Mac / Linux) _all_ variabel lingkungan pada platform tersebut yang seharusnya menentukan jalur pemuatan waktu proses asli sepenuhnya diabaikan oleh PowerShell.

Saya percaya $env:Path masih berfungsi (meskipun seharusnya tidak menggunakannya pada platform Unix, dan saya belum mengujinya sejak rilis 6.2, jadi itu mungkin rusak juga), tapi terus terang saya tidak berpikir itu adalah ide yang bagus untuk menambahkan banyak jalur ke variabel itu, itu membuat pengalaman pengguna yang sangat buruk jika pengguna perlu menggunakan variabel lingkungan itu sama sekali. Anda memuat modul, dan tiba-tiba variabel jalur Anda memiliki data tambahan yang tidak diharapkan yang mungkin seharusnya tidak ada di sana, tetapi _has_ berada di sana agar semuanya berfungsi dengan benar.

Saya pikir ini seharusnya bukan masalah; PowerShell harus memiliki metode yang lebih kuat dalam menangani jalur pemuatan perpustakaan asli jika itu akan menggantikan metode yang telah ditentukan .NET Core. Saya menyarankan memiliki beberapa cara untuk menentukan jalur pemuatan perpustakaan asli dalam manifes modul atau sesuatu di sepanjang baris tersebut, beberapa cara untuk menentukan di mana seharusnya mencari per platform ketika sesuatu perlu p / memanggil beberapa kode asli.

Saya memiliki masalah yang sama dan laporan @ vexx32 berisi semua elemen dalam rantai.
Saya setuju PowerShell harus memiliki metode yang lebih

Saya tidak berpikir itu ide yang baik sama sekali untuk menambahkan banyak jalur ke variabel itu

Setuju dan juga, saya tidak tertarik menggunakan SetDllDirectory karena itu juga (pwsh) proses yang luas. Manifes modul dapat diperbarui untuk memungkinkan pembuat modul menyediakan daftar dll asli per combo os-arch dan kemudian pwsh akan memuat dulu dll asli tersebut sebelum memuat rakitan apa pun sebelumnya. Yang mengatakan, jika seseorang akan menggunakan assembly Anda dari bahasa lain, assembly benar-benar harus memuat dll aslinya sendiri. Pengguna majelis tidak harus melakukan IMO itu.

@ vexx32 Saya "berpikir" dlopen berfungsi di macOS.

Saya akan mencobanya ketika saya bisa (VM mac agak ... aneh heh) tetapi dari memori, Mac tidak menggunakan .so file jadi saya pasti harus mencobanya - - sebagian besar lib asli yang saya lihat referensi di Mac tampaknya .dylib .

Assembly melakukan preloading sendiri di sini (setidaknya, saya cukup yakin melakukannya ...), saya hanya perlu membuat kasus khusus semuanya karena assembly tidak tahu ke mana harus mencari dengan cara PS menangani jalur. : /

Saya pikir macOS menggunakan .dylib jadi Anda perlu mengkompilasi perakitan itu tiga kali dengan makro berbeda yang ditentukan misalnya:

public static class FooNativeMethods
{
    // This unfortunately requires that you compile this assembly twice - once with WIN32 defined and again with
    // it not defined.
#if WIN32        
    public const string LibName = "Foo";
#elif MACOS
    public const string LibName = "libFoo.dylib";
#else
    public const string LibName = "libFoo.so";
#endif      

Saya benar-benar berharap saya tidak perlu mengkompilasi ulang ini untuk setiap platform karena ini adalah perakitan netstandard2. Tetapi AFAICT nilai nama lib yang diberikan ke atribut DllImport harus berupa konstanta waktu kompilasi. :-(

Ya. Karena ini adalah modul PowerShell, yang akhirnya saya lakukan adalah kompilasi impor dinamis dengan Add-Type -TypeDefinition $srcString dengan pemeriksaan platform dan kemudian arahkan ke direktori yang benar. Anda dapat menggunakan ScriptsToProcess dalam manifes modul untuk ini, untungnya.

Juga, apakah libdl.so perpustakaan yang valid dan dapat digunakan untuk melakukan ini di MacOS?

Tapi ya, kita butuh solusi yang lebih baik. 😄

@ vexx32 Apakah Anda melihat Mono MapDll? Fitur ini memungkinkan pemetaan otomatis ke pustaka asli yang sesuai berdasarkan kondisi (lengkungan, platform, dll.). Saya berharap itu yang Anda butuhkan. Mungkin SkiaSharp.dll harus dikompilasi ulang tetapi dapat dikonfigurasi di file konfigurasi eksternal. Hal serupa akan diterapkan di Core.

Jika saya ingat dengan benar, .NET Core tidak menggunakan Mono saat berjalan di Windows, jadi sekali lagi ini hanya akan menjadi solusi parsial.

Saya sangat menghargai semua opsi yang ditawarkan di sini, dan saya akan memeriksa secara menyeluruh apa opsi saya saat ini - tetapi pada akhirnya, PowerShell perlu memiliki sesuatu untuk modul lintas platform untuk digunakan untuk tujuan ini di masa mendatang. . 🙂

Jika saya ingat dengan benar, .NET Core tidak menggunakan Mono saat berjalan di Windows, jadi sekali lagi ini hanya akan menjadi solusi parsial.

Saya katakan tentang fitur baru. Lihat
Dokumen desain dllmap https://github.com/dotnet/coreclr/blob/c3e1bd5ccc482c9a7670762676bda95ebd34707d/Documentation/design-docs/dllmap.md
dan versi dokumen sebelumnya
https://github.com/dotnet/coreclr/blob/6a48f05c77e80b604d71a9b84cc6b014306e849e/Documentation/design-docs/dllmap.md#

Perbarui: CurrentContextualReflectionContext
https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md#rollforward

@bayu_joo

Saya benar-benar berharap saya tidak perlu mengkompilasi ulang ini untuk setiap platform karena ini adalah perakitan netstandard2. Tetapi AFAICT nilai nama lib yang diberikan ke atribut DllImport harus berupa konstanta waktu kompilasi. :-(

FWIW Anda dapat menghilangkan ekstensi untuk DllImport . Kami melakukannya untuk ketergantungan asli non-Windows PSES. Tidak membantu jika nama dasar tidak sama.

Anda mungkin juga dapat melewati arahan kompilator untuk panggilan LoadLibrary / dlopen dengan menyimpannya dalam implementasi antarmuka terpisah. Selama metode tersebut tidak JIT, metode ini tidak boleh dilempar saat runtime.

Saya menemukan solusi yang mungkin untuk masalah ini. Saya harus mencobanya. Saat ini saya sibuk mengerjakan beberapa rilis, jadi kemungkinan besar akan sampai pada awal minggu depan. Saya akan memperbarui masalah ini dengan temuan saya.

https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext.loadunmanageddll?view=netcore-2.2#System_Runtime_Loader_AssemblyLoadContext_LoadUnmanagedDll_System_String_

@adityapatwardhan Itu terlihat seperti pembungkus lintas platform di sekitar LoadLibrary / dlopen. Temuan bagus!

@adityapatwardhan Saya menemukan masalah ini di sekitar LoadUnmanagedDll. Mungkin tidak sesuai dengan kasus kita.

Kita harus mempertimbangkan untuk menambahkan callback yang memungkinkan orang memberikan kebijakan resolusi khusus untuk pustaka yang tidak dikelola. Kami bermaksud melakukannya dengan AssemblyLoadContext.LoadUnmanagedDll. Pada akhirnya, itu hanya membahas sebagian kecil dari mereka masalah karena tidak berfungsi untuk konteks beban default. Saat kami memperkenalkan AssemblyLoadContext.LoadUnmanagedDll pada awalnya, kami mengizinkan penggantian konteks pemuatan default juga, tetapi kemudian menghapus kemampuan ini dan tidak memikirkan dampaknya pada semua skenario.

Sumber: LoadUnmanagedDllFromPath / LoadUnmanagedDll tidak berfungsi di linux jika file .so ada di jalur lain selain direktori keluaran aplikasi

Saya pikir tautan saya di atas pada NativeLibrary API memberi kami solusi lintas platform (setelah pindah ke .Net Core 3.0).

CurrentContextualReflectionContext https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/AssemblyLoadContext.ContextualReflection.md

Ini untuk model plug-in. Ini dapat menyelesaikan masalah, # 6724, # 6426, dan beberapa lainnya.
/ cc @ daxian-dbw

@adityapatwardhan ada update sejak bulan lalu? Kami ingin menerbitkan panduan dokumen yang tepat tentang ini

Belum bisa fokus pada hal ini, saat ini saya sibuk dengan item prioritas tinggi lainnya. Saya akan memperbaruinya minggu depan.

@adamdriscoll mungkin dapat memberikan beberapa saran karena dia mungkin harus melakukan beberapa hal kreatif di UniversalDashboard dan AvaloniaUI + PowerShell.

FWIW kode yang saya sajikan di atas adalah apa yang kami gunakan untuk modul internal yang menggunakan lib asli. Ini berjalan tanpa masalah di Windows dan Linux. Belum mengujinya di macOS tetapi selain tweak ke dlopen pinvoke dan mengubah nama lib bersama di mac, itu seharusnya berfungsi. Ini ada di .NET Core 2.2.

UD melakukan hal yang hampir sama dengan solusi @rkeithhill . Ini berfungsi di MacOSX serta Linux dan Windows. https://github.com/ironmansoftware/universal-dashboard/blob/master/src/UniversalDashboard/Server/CustomAssemblyLoadContext.cs

Masalahnya adalah mungkin ada lebih banyak variasi dari jalur folder untuk memuat berbagai binari jadi ini sedikit kluge. Jika tipe biner baru adalah keluaran berdasarkan ketergantungan baru, folder baru mungkin muncul dan UD tidak akan memuat perakitan.

@rkeithhill apakah Anda menggabungkan semua binari WindowsLinux ke dalam satu folder? (EDIT: Sebenarnya, saya mengerti. Anda memiliki rakitan pembungkus DLL yang hanya memuat rakitan asli target. Apakah Anda melakukan ini untuk semua rakitan asli Anda?)

UD hanya menyimpan folder runtime publish sama seperti ketika diproduksi dengan dotnet publish dan kemudian kelas yang direferensikan mencoba memuat semua biner asli berdasarkan arsitektur platform yang benar.

Ini berfungsi tetapi saya berharap PS bisa melihat folder runtime dan memuat binari yang benar. Sepertinya dotnet harus melakukan itu ketika dijalankan jadi mungkin ada beberapa kode untuk ditangkap di sana.

Saya mengubah tempat saya mencari rakitan asli berdasarkan hasil RuntimeInformation.IsOSPlatform(OSPlatform.Windows) . Jika hasilnya benar, saya memuat dari folder Windows dari dir tempat perakitan C # berada. Jika tidak, saya memuat dari folder Linux .

Apakah Anda melakukan ini untuk semua majelis asli Anda?)

Ya, tetapi kami hanya punya satu dan tidak bergantung pada apa pun selain libs sistem (msvcrt, dll).

Bisakah kita memiliki beberapa struktur file standar (mungkin berdasarkan nama runtime)? Ini akan memungkinkan kami untuk meningkatkan resolver dll asli.

UD hanya menyimpan folder runtime publish sama seperti ketika diproduksi dengan dotnet publish dan kemudian kelas yang direferensikan mencoba memuat semua biner asli berdasarkan arsitektur platform yang benar.

Jadi, jika saya memahaminya dengan benar, Anda menghindari penggunaan PowerShell untuk memuat biner asli dengan hanya meminta .NET memuatnya di bawah tenda saat Anda memuat biner terkelola yang bergantung pada biner asli khusus RID?

Ini berfungsi tetapi saya berharap PS bisa melihat folder runtime dan memuat binari yang benar. Sepertinya dotnet harus melakukan itu ketika dijalankan jadi mungkin ada beberapa kode untuk ditangkap di sana.

Ya, saya menduga ada beberapa kesadaran RID yang terjadi dalam runtime .NET itu sendiri. Saya telah mencoba untuk menghindari kita harus melalui jalan itu, tetapi sepertinya kita mungkin perlu melakukannya pada suatu saat.

@joeya

Jadi, jika saya memahaminya dengan benar, Anda menghindari penggunaan PowerShell untuk memuat biner asli dengan hanya meminta .NET memuatnya di bawah tenda saat Anda memuat biner terkelola yang bergantung pada biner asli khusus RID?

Ya, tepat sekali. UD hanya memuat segalanya untuk target RID. Jadi tidak terlalu pintar tentang apa itu dependensi yang sebenarnya. Tidak yakin apakah ada cara yang baik untuk mendeteksinya.

@ vexx32 Saya telah menginstal PSWordCloud. Apakah struktur foldernya umum? Jika demikian, kami dapat membuat resolver kami lebih pintar. Jika saya ingin bertanya - dapatkah Anda menyiapkan pengujian Pester baru berdasarkan PSWordCloud yang disederhanakan (cukup bagi kami untuk mendapatkan sesuatu yang sederhana dari perpustakaan asli seperti "Hello World")? Kami akan menggabungkannya sebagai tertunda dan itu akan membuka jalan untuk bekerja pada resolver kami. Terima kasih!

Saat ini struktur folder PSWordCloud tidak umum, tidak. Itu bisa diubah, tentu saja.

Ya, saya benar-benar dapat membuat tempat tidur percobaan sederhana untuk itu. Pester agak rumit untuk dilakukan, hanya berdasarkan sifat output yang saya kira, tetapi saya _think_ ketika gagal menghasilkan pengecualian yang dapat Anda tangkap, meskipun pengecualian itu dilemparkan dari penginisialisasi tipe jika saya ingat dengan benar.

Apakah lebih tepat jika pengujian itu sendiri menarik DLL Skia melalui dotnet, atau akankah lebih baik untuk mengemasnya dalam repo? Saya kira jika kita menariknya setiap kali, itu bukan masalah besar jika ditandai sebagai uji fitur setelah tidak lagi tertunda? :berpikir:

Jika Anda dapat menerapkan tes nodul dengan ketergantungan tidak langsung dengan dll Skia alangkah baiknya. Dan ya kita bisa menginstal Skia dari nuget. Meskipun pengujian akan lebih andal jika kita langsung meletakkan dll dengan modul pengujian di ~ folder modul pengujian kami ~ folder assert.
Tampaknya praktik terbaik Nuget adalah meletakkan dll asli di folder bernama RID. Iya?

Ya, saya bisa melakukan itu! Saya akan melihatnya besok. : blush:

Saya memperbarui komentar saya - harap letakkan modul pengujian di folder test assert.

@rjmholt Saya melakukan beberapa upaya untuk menggunakan kode yang Anda berikan untuk membuatnya berfungsi untuk PSWordCloud, tetapi masih tidak berfungsi sama sekali. Operasi pemuatan berhasil diselesaikan, tetapi memuat SkiaSharp.dll yang dikelola setelah pustaka yang tidak dikelola masih gagal untuk menggunakan DLL yang dimuat pada platform Unix. Itu berhasil bekerja untuk Windows, entah bagaimana.

Lihat cabang WIP saya di sini https://github.com/vexx32/PSWordCloud/tree/FixPInvokes - jalankan build.ps1 untuk membangun modul dan meletakkan semua file di tempat yang tepat. Ini akan mengimpor modul untuk Anda juga. Memanggil New-WordCloud setelah menjalankan build berfungsi di Windows, tetapi gagal sepenuhnya di Mac OS.

@SeeminglyScience Anda melihat sesuatu yang saya lakukan salah di sana? : /

GitHub
Ciptakan awan kata yang cantik dengan PowerShell! Berkontribusi pada pengembangan vexx32 / PSWordCloud dengan membuat akun di GitHub.

@ SteveL-MSFT Apa rencana untuk menangani lib asli saat ini? 😕

: tada: Masalah v7.0.0-rc.1 .: tada:

Tautan yang berguna:

Apakah halaman ini membantu?
0 / 5 - 0 peringkat