サードパーティのネイティブライブラリからp / invokeを必要とするモジュールをロードしようとすると、p / invokingを実行しているDLLと同じフォルダからライブラリをロードできません。
これはPowerShellCore 6.1.0で_以前は機能していました_が、6.2.0-preview1と6.2.0-preview2の間で後退しました。 WindowsPowerShellでも引き続き機能します。
検索に失敗したネイティブライブラリは、検索しようとしているSkiaSharp.dllと同じフォルダーにあります。 https://github.com/PowerShell/PowerShell/issues/8861#issuecomment -462362391と、その方法で行われた理由については、その問題のスレッドを参照して
Install-Module PSWordCloud
New-WordCloud
cmdlet New-WordCloud at command pipeline position 1
Supply values for the following parameters:
InputObject:
new-wordcloud : The type initializer for 'PSWordCloud.WCUtils' threw an exception.
At line:1 char:1
+ new-wordcloud
+ ~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], TypeInitializationException
+ FullyQualifiedErrorId : System.TypeInitializationException
$Error[0].Exception.GetBaseException() | Format-List * -F
を使用して基本例外をクエリすると、次のようになります。
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
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
_注:_同じことがMac環境でテストされ(@steviecoasterに感謝します!)、同じ方法で失敗しました。 Linuxディストリビューションでも同じだと思います。
/ cc @ SteveL-MSFT
@adityapatwardhanこれを見ていただけますか?
procmon
ユーティリティで使用済みパスを調べてから、 HintPathで修正してみてください。
見てみましょうが、これは直接ロードしているライブラリではないため(メインのSkiaSharp.dll自体によってロードされます)、あまり多くのことを行うことに自信がありません。 プラットフォームに応じてロードできるライブラリファイルが4つあるという事実も、事態を複雑にします。
しかし、それにもかかわらず、これは意図的な変更のようには見えないので、おそらくリグレッションに対処する必要があります。
MSFTチームは、.Net Core3.0のvNextp / invokesに懸命に取り組んでおり、おそらく何かを2.1にバックポートしました。 PowerShell関連のdllの読み込みで何かを変更したことを覚えていません。
6.2.0-preview2には、#8073といくつかのInternal
マークの変更がありました。 その間に他に何が変わったのか私にはわかりません。 😕
PRの変更は、最初にモジュールフォルダからモジュールdllをロードし、次にGACからロードすることです。 あなたの問題は、モジュールdllが2番目のdllを参照し、自動dllロード(PowerShellではなくCoreを実行する)がdllを見つけられないことです。 私の提案は、プロジェクトファイルのdllにヒントパスを明示的に追加することです。
@iSazonov csprojにいくつかのHintPath参照を追加しましたが、 dotnet publish
ときに表示されるのは警告だけです。 ロードされたモジュールの動作に変更はありません。
少し読んでみると、HintPathは、プロジェクトビルド自体に_含まれていない_外部アセンブリを参照するために使用されているようです。 SkiaSharpアセンブリが含まれているため、コンパイル/ビルド用のHintPathを追加しても、SkiaDLLや実行時に検索するパスには影響しません。
参考までに、これは私のcsprojです。
<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>
次の行を追加しても効果はありません(ビルド/公開時に見つからないという警告を除いて、とにかく公開が完了するまで存在しないためです)。
<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>
私がHintPathで読んだことから、DLLをロードするときに検索されるパス、またはDLLがp / invokeの目的で他のDLLをロードしようとすることに影響はないようです。
_That_HintPathはコンパイル時のものだけです。
あなたがそれを言う方法は、私が逃した別のものがあるように聞こえますか?
@ vexx32単純なリポジトリコードがある場合は、CoreCLRリポジトリで質問できます。
Powershell固有の変更をまだ除外できるかどうかはわかりません。 コンソールアプリの小さなテストをまとめて、それがどのように行われるかを確認する必要があるかもしれません。
@Jaykulからのヒントのおかげで、私はなんとか
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)
ただし、この作業を行うために追加のp / invokeが_必要_であるという事実は一種のばかげています。 また、ここに記載されているように、ロードされたSkiaSharp.dll自体のディレクトリパスを標準の検索場所から変更および削除したものについても非常に混乱しています。
@ vexx32問題を再現できませんでした。 私は再現で何かを逃しましたか? 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
申し訳ありませんが、前述のように、2.1.1で修正しました。
特に2.1.0をインストールすると、上記のp / invokeの回避策がないため、失敗が表示されます。
わかりました、今再現できます。 あとで見てみます。
@ vexx32 psd1
FileList
にリストされているすべてのファイルをロードしているようです。 あなたはそれを持っている必要がありますか? FileList
からすべてのエントリを削除すると、問題を再現できなくなります。
そうですか? それはとても奇妙です。 いいえ、特に必要はありません。常に更新する必要はないので、とにかく削除する予定ですが、いくつかのバージョンで使用されています。
しかし、それがDLLのロードに突然影響を与えるのは奇妙に思えます。
psd1のFileListにリストされているすべてのファイルをロードしているようです。
.psd1ファイルで「if」をサポートするための問題があります。 したがって、プラットフォーム固有のdllをロードできます。
@iSazonovそれは#5541です。 すぐに更新します。
それは可能性です。 私が今疑問に思っているのは、FileListのネイティブライブラリ(すべて)へのパスをここで追加するだけで十分かどうかです...私はp / invokeさえ必要ないかもしれません。 テストをします! 😊
編集:いいえ、それが効果があるとは思いません。 :(
管理アセンブリのみをロードするAssembly.Load()
と呼びます。
6.2.0-preview.2で変更を加え、GACで検索する前にモジュールフォルダーからDLLの読み込みを開始しました。 この変更の副作用は、FileListにDLLもロードすることです。
それは理にかなっている。 そのためのドキュメントの問題がありますか? この種の変更は、誰かがそれに遭遇し、私たちがそれを文書化していない場合、追跡するのが非常に難しいようです。 そのため、ネイティブアセンブリをファイルリストに含めたくないことを文書化する必要があります。
それを追跡する助けに感謝します! 今のところ、私が言及したP / invokeソリューションは私に最も柔軟性を与えますが、モジュールのロードでそのようなものの処理を追加することは賢明かもしれません。
たとえば、PrivateDataに、RuntimeLibPathsというエントリを追加するとします。これは、キーがサポートされているプラットフォーム名/アーキテクチャと一致するハッシュテーブルであり、値はネイティブライブラリを含むフォルダへのパスです。 適切なフォルダーは、インポート時にモジュール自体によって追加される必要はなく、PowerShellによるインポート時に内部的にdll検索パスに追加されます。
そのためのドキュメントの問題がありますか?
私たちはしていません。
@adityapatwardhan @ SteveL-MSFT @TylerLeonhardtのおかげで(意図せず)が、これは私が徹底的になりたい場合は...私はより完全なソリューションを必要としすぎて、Windows以外のものに仕事に持っていることを私に思い出させます。
現在、ネイティブ相互運用機能が.NETCore自体でどのように処理されるかについての明確なドキュメントはないようです。 私が見つけることができるのは、MSドキュメントの.NETFrameworkのこのページだけです。
モノか何かで処理されていますか? Mono経由の場合、このページを見ると、実際にはMac OSのMonoに_バグがあり、ライブラリのロードパスが適切に尊重されないようになっています...
PowerShell ...はこれを処理する必要があります。 モジュール自体からすべてのプラットフォームからこれを適切に処理する方法があるかどうかはわかりません。
@ vexx32は詳細が。PowerShellを含まないC#で再現を作成できる場合は、CoreCLRリポジトリで問題を開くことをお勧めします。
@ steveL-msftが問題だと思います。 それは他の場所ではうまく機能しているようですが、まだいくつかの注意点があります。 たとえば、ネイティブlibのロードは、メインのSkiaSharp.dllと同じフォルダーにある場合は機能しますが、PSの場合は、
このようなネイティブライブラリを使用するほとんどの状況では、サポートされているプラットフォームごとにパッケージ化できるため、抽象化できます。 3つ以上の完全に別個のギャラリーモジュールをパッケージ化して維持したい場合を除いて、PSモジュールにはそれほど贅沢はありません。残念ながら、これは発見可能性とエンドユーザーエクスペリエンスにとって非常に悪いことです。
PSがクロスプラットフォームツールであることを考えると、モジュール内のネイティブライブラリを処理する方法が必要です。または、プラットフォームごとにnupkgから抽出される個別のライブラリが必要であることをPackageManagementモジュールに認識させる方法が必要です。
私は別の解決策を受け入れていますが、ここのデッキはかなり不足しているようです。 :混乱:
@ vexx32このようなものが必要だと思いますhttps://github.com/PowerShell/PowerShellGet/issues/273
CoreFX 3.0では、ピノケネイティブライブラリ用のいくつかの新しいAPIを取得します。 彼らは道のりの始まりに過ぎませんが、Monoよりもさらに良いことをしたいと思っています。
現在、私のソリューションの回避策は次のようになっています。
ScriptsToProcess
は、モジュールのインポート時にp / invokeを処理して、32ビットバージョンまたは64ビットバージョンのWindowsネイティブライブラリを追加します。残念ながら、ファイル名は同じであるため、サブフォルダーに格納する必要があります。理想的ではありません。 @ TylerLeonhardtが今日のストリームから証明できるように、現時点でAzure Functionsで実行している場合、これは何らかの理由でまだ機能しません。
@ vexx32は詳細が。PowerShellを含まないC#で再現を作成できる場合は、CoreCLRリポジトリで問題を開くことをお勧めします。
C#では、次のいずれかを行います。
@ vexx32このPowerShell / PowerShellGet#273のようなものが必要だと思います
そのため、このバグが報告されました。 PowerShell 6.2以降、 @ vexx32は、 PowerShellに正しいライブラリ検索パスを使用させる方法がわからないため、_Windows_だけで機能するバージョンを取得することさえできません。
少なくともWindowsでは、ap / invokeを使用してそれをいじることができます。 これは_回避策_ですが、実際にはもっと良い修正が必要です。
ただし、Unixシステムでは、私が見つけたものから、そのようなp / invokeは利用できません。また、何らかの理由で、PowerShellは、DllImportを実行しようとしたときに検索するパスを通常示す環境変数をクエリしません。 PSによってロードされたアセンブリ。
したがって、基本的にすべてのプラットフォームで、これは...何もするのに非常に不親切です。 😅
現在のところ、モジュールのルートフォルダーに直接すべてのUnixネイティブアセンブリを含める以外に他のオプションはないようです(これにより、サポートできるプラットフォームが制限され、名前の衝突が発生する可能性があります)、次に注意する必要がありますp / invokeを使用してWindowsの検索パスに追加するフォルダー(x86 Windowsのサポートに関心がある場合-そして
これは、プラットフォームに適切なネイティブバイナリをプリロードするためにpsm1ファイルで使用するものです。 注:これはLinuxとWindowsでのみターゲット/テストしました。
# Module PSM1 File
$binPath = Join-Path $PSScriptRoot bin $(if ($IsWindows) {"Windows"} else {"Linux"})
Add-Type -Path $binPath\Acme.Api.dll
そしてこれはAcme.Api.dllに含まれるC#コードです
[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);
...
}
@ vexx32CoreCLRリポジトリでコンサルティングの問題を開くことができます。 私は彼らが言うことを持っていると思います。
@rkeithhillこれは.NETバイナリでのみ機能します。 ネイティブアセンブリをロードするためにP / invokeを使用する必要がある.NETバイナリをロードする必要があります。 それは失敗し、完全に崩壊するところです。 私はこのライブラリを制御できません。これはサードパーティのライブラリ(この場合はSkiaSharp)です。 このライブラリは、SkiaSharpがロードされた状態でのみ存在し機能するタイプに依存しているため、モジュールからコードをロードすると同時にロードする必要があるため、バイナリモジュールにロードできません。
@isazonovは、この問題で数回取り上げられていますが、ほとんどのアプリケーションでプラットフォームごとに個別のパッケージを生成できるため、 .netCoreでは問題になりません。
PowerShellにはこれを行うための可能な方法がなく、ライブラリ検索パスからいくつかの標準パスが偶然に削除されるため、一部のコンテキストでネイティブアセンブリを操作することが文字通り不可能になり、非常に壊れやすく、特に作業。
@ vexx32
ネイティブアセンブリをロードするためにP / invokeを使用する必要がある.NETバイナリをロードする必要があります。 それはで失敗し、完全に崩壊するところです。
ええと、それはまさにこのコードが行うことです。 しばらくの間、WindowsとLinuxの両方の6.2で正常に使用されています。
修正しました、申し訳ありませんが、はっきりしませんでした。 自分のバイナリでこれを行うことはできません。自分のモジュールがロードされているときに型が存在する必要があります。 コマンドのインスタンス化中にインポートすることはできません。インポートする必要はありません。
バイナリの読み込みに関しては、PowerShellは.netCoreよりも機能がません。
そうは言っても、私はいくつかのことを適応させ、回避策を改善できるかどうかを確認します。 例に感謝します!
しかし、本当に...これを回避するためになぜ私がそのような長さに行かなければならないのか分かりません。 それはうまくいくはずで、PowerShellが関与するまで...それは機能します。
フープが苦痛であることに同意した。 WindowsとLinuxでp / invokeを介してネイティブライブラリの読み込みを実行する方法を示しようとしていました。 :-)
@ vexx32議論の中で分裂が見られます。 それが問題であることを明確にできますか?ネイティブライブラリを備えたモジュールがあり、特定のプラットフォームに依存する適切なライブラリをロードする必要がありますか?
ある種。 これは私が現在機能しているレイアウトですが、すべての面倒です。 p / invokeを介してパスを定義できないものはすべて、モジュールルートにある必要があります(一部のプラットフォームでは名前の衝突の問題が発生します)。
PSWordCloud/
|- PSWordCloudCmdlet.dll
|- SkiaSharp.dll
|- libSkiaSharp.so
|- libSkiaSharp.dylib
|- win-x86/
|- libSkiaSharp.dll
|- win-x64/
|- libSkiaSharp.dll
SkiaSharp.dllは.NETであり、メインコマンドレットdllの参照アセンブリです。 4つのlibSkiaSharpファイルは、プラットフォームごとのネイティブDLLです。 私はSkiaSharp.dllのコードを制御していないので、そのDLLにp / invokesの前にパスを更新させることはできません。
だから...私は.NET依存関係を持つモジュールを持っています。 この依存関係は、DllImportでロードしているネイティブライブラリに依存します。 正確なファイルはプラットフォーム/アーキテクチャごとに異なります(ただし、ファイルのいくつかには名前の衝突があるため、実際にサポートできるプラットフォームが制限されます)。
@rkeithhillのコードを使用すると、Linuxネイティブライブラリをサブフォルダーに移動することもできると思います。 MacOSについてはよくわかりませんが、おそらく似たようなものがありますが、その方法に関するドキュメントはまだ見つかりません。
_理想的には_、これらのファイルはすべて、最悪の場合、 /PSWordCloud/runtime/<platform>/
ようなサブフォルダーに配置する必要があります。残念ながら、PowerShellがパスの読み込み仕様を標準設定から変更したため、ルート内のファイルのみが変更されました。モジュールのフォルダは、DllImportを介してロードできます。 他の場所でも機能する可能性がありますが、(少なくともMac / Linuxでは)ネイティブランタイムロードパスを定義することになっているプラットフォーム上の環境変数のすべてがPowerShellによって
$env:Path
まだ機能すると思います(ただし、Unixプラットフォームでは使用しないでください。また、6.2リリース以降はテストしていないため、問題が発生する可能性もあります)が、率直に言ってテストしていません。その変数に大量のパスを追加することはまったく良い考えだと思います。ユーザーがその環境変数を使用する必要がある場合、それは本当にひどいユーザーエクスペリエンスになります。 モジュールをロードすると、突然、パス変数に予期しないデータが追加され、おそらくそこにあるべきではありませんが、正しく機能するためにそこにある必要があります。
これは問題ではないと思います。 PowerShellは、.NET Coreの事前定義されたメソッドをオーバーライドする場合、ネイティブライブラリのロードパスを処理するためのより堅牢なメソッドを備えている必要があります。 モジュールマニフェストまたはそれらの行に沿った何かでネイティブライブラリのロードパスを指定する方法、何かがネイティブコードをp / invokeする必要があるときにプラットフォームごとにどこを探すべきかを定義する方法を提案します。
同じ問題があり、 @ vexx32のレポートにはチェーン内のすべての要素が含まれています。
PowerShellには、ネイティブライブラリを処理するためのより堅牢な方法が必要であることに同意します。
その変数に大量のパスを追加するのはまったく良い考えではないと思います
同意し、同様に、 SetDllDirectory
も(pwsh)プロセス全体であるため、使用することに熱心ではありません。 モジュールマニフェストを更新して、モジュールの作成者がos-archコンボごとにネイティブdllのリストを提供できるようにし、pwshがアセンブリをプリロードする前にそれらのネイティブdllをプリロードすることができます。 とはいえ、誰かが別の言語からアセンブリを使用する場合、アセンブリは実際には独自のネイティブdllをプリロードする必要があります。 アセンブリのユーザーは、そのIMOを実行する必要はありません。
@ vexx32 dlopen
はmacOSでも動作すると「思います」。
できる限り試してみますが(Mac VMは少し...変です)、メモリからはMacは.so
ファイルを使用しないので、ぜひ試してみる必要があります- --Macで参照されているネイティブライブラリのほとんどは.dylib
です。
アセンブリはここで独自のプリロードを実行します(少なくとも、確実に実行します...)。アセンブリはPSがパスを処理する方法をどこで確認するかわからないため、すべてを特殊なケースにする必要があります。 :/
macOSは.dylib
使用していると思うので、次のように定義された異なるマクロを使用して、そのアセンブリを3回コンパイルする必要があります。
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
これはnetstandard2アセンブリであるため、プラットフォームごとにこれを再コンパイルする必要がないことを本当に望んでいます。 ただし、 DllImport
属性に提供されるlib名の値は、コンパイル時定数である必要があります。 :-(
ええ。 これはPowerShellモジュールであるため、実際に実行するのは、プラットフォームチェックを使用したAdd-Type -TypeDefinition $srcString
した動的なインポート時のコンパイルであり、正しいディレクトリをポイントします。 ありがたいことに、このためのモジュールマニフェストでScriptsToProcess
を使用できます。
また、 libdl.so
、MacOSでこれを行うための有効で使用可能なライブラリですか?
しかし、うん、もっと良い解決策が必要だ。 😄
@ vexx32 Mono MapDllを見ましたか? この機能により、条件(アーチ、プラットフォームなど)に基づいて適切なネイティブライブラリに自動マッピングできます。 私はあなたが必要としていることだと思います。 おそらくSkiaSharp.dllを再コンパイルする必要がありますが、外部構成ファイルで構成できます。 同様のことがCoreに実装されます。
正しく思い出せば、.NET CoreはWindowsで実行しているときにMonoを使用しないため、これも部分的な解決策にすぎません。
ここで提供されているすべてのオプションに感謝します。現在のオプションを徹底的に調べますが、最終的には、PowerShellは、クロスプラットフォームモジュールがこの目的で使用できるようにする必要があります。 。 🙂
正しく思い出せば、.NET CoreはWindowsで実行しているときにMonoを使用しないため、これも部分的な解決策にすぎません。
私は新機能について言います。 見る
Dllmapデザインドキュメントhttps://github.com/dotnet/coreclr/blob/c3e1bd5ccc482c9a7670762676bda95ebd34707d/Documentation/design-docs/dllmap.md
および以前のバージョンのドキュメント
https://github.com/dotnet/coreclr/blob/6a48f05c77e80b604d71a9b84cc6b014306e849e/Documentation/design-docs/dllmap.md#
更新: CurrentContextualReflectionContext
https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md#rollforward
@rkeithhill
これはnetstandard2アセンブリであるため、プラットフォームごとにこれを再コンパイルする必要がないことを本当に望んでいます。 ただし、DllImport属性に提供されるlib名の値はコンパイル時定数である必要があります。 :-(
FWIW DllImport
の拡張子は省略できます。 PSESのWindows以外のネイティブ依存関係に対してこれを行います。 ただし、ベース名が同じでない場合は役に立ちません。
LoadLibrary
/ dlopen
呼び出しのコンパイラ指令を個別のインターフェイス実装に格納することで、それらをスキップすることもできます。 メソッドがJITされていない限り、実行時にスローされるべきではありません。
私はこの問題の可能な解決策を見つけました。 私はそれを試してみる必要があります。 私は現在いくつかのリリースに取り組んでいるので、おそらく来週初めにこれに到達するでしょう。 この問題を私の調査結果で更新します。
@adityapatwardhanこれは、LoadLibrary / dlopenのクロスプラットフォームラッパーのように見えます。 素敵な発見!
@adityapatwardhanLoadUnmanagedDllの周りでこの問題を見つけました。 たぶん私たちの場合には無関係です。
管理されていないライブラリのカスタム解決ポリシーを提供できるようにするコールバックを追加することを検討する必要があります。 AssemblyLoadContext.LoadUnmanagedDllを使用してこれを行うことを意図しています。 最後に、デフォルトのロードコンテキストでは機能しないため、問題のごく一部にしか対処しませんでした。 AssemblyLoadContext.LoadUnmanagedDllを最初に導入したとき、デフォルトのロードコンテキストもオーバーライドすることを許可しましたが、この機能を削除し、すべてのシナリオへの影響を考慮していませんでした。
ソース: .soファイルがアプリケーション出力ディレクトリ以外のパスに存在する場合、LoadUnmanagedDllFromPath / LoadUnmanagedDllはLinuxで機能しません
上記のNativeLibraryAPIに関するリンクは、クロスプラットフォームソリューションを提供すると思います(.Net Core 3.0に移行した後)。
CurrentContextualReflectionContext https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/AssemblyLoadContext.ContextualReflection.md
プラグインモデル用です。 これにより、#6724、#6426などの問題が解決する可能性があります。
/ cc @ daxian-dbw
@adityapatwardhan先月以降の更新はありますか? これに関する適切なドキュメントガイダンスを公開したいと思います
これに集中することができませんでした、現在他の優先度の高いアイテムで忙しいです。 来週中に更新します。
@adamdriscollは、UniversalDashboardとAvaloniaUI + PowerShellでクリエイティブな作業をしなければならなかったので、アドバイスを提供できるかもしれません。
上で示したコードは、ネイティブライブラリを使用する内部モジュールに使用するものです。 WindowsとLinuxでは問題なく動作します。 macOSでテストしていませんが、 dlopen
pinvokeを微調整し、Macで共有ライブラリの名前を変更する以外は機能するはずです。 これは.NETCore2.2にあります。
UDは、 @ rkeithhillのソリューションとほぼ同じことを行っています。 これは、MacOSXだけでなく、LinuxとWindowsでも機能します。 https://github.com/ironmansoftware/universal-dashboard/blob/master/src/UniversalDashboard/Server/CustomAssemblyLoadContext.cs
問題は、さまざまなバイナリをロードするためのフォルダパスのバリエーションがさらに多くなる可能性があるため、少し手間がかかることです。 新しい依存関係に基づいて新しいバイナリタイプが出力されると、新しいフォルダがポップアップし、UDがアセンブリをロードしない場合があります。
@rkeithhillは、すべてのWindowsLinuxバイナリを1つのフォルダにマージしていますか? (編集:実際、わかりました。ターゲットのネイティブアセンブリをロードするだけのラッパーDLLアセンブリがあります。これはすべてのネイティブアセンブリに対して行いますか?)
UDは、公開ランタイムフォルダーをdotnet publishで作成されたときと同じに保ち、参照されるクラスが正しいプラットフォームアーキテクチャに基づいてすべてのネイティブバイナリを読み込もうとします。
それは機能しますが、PSがランタイムフォルダを見て正しいバイナリをロードできればいいのですが。 dotnetは起動時にそれを実行している必要があるようですので、そこにナブするコードがあるかもしれません。
結果RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
基づいて、ネイティブアセンブリを探す場所を変更します。 それがtrueを返す場合は、C#アセンブリが配置されているディレクトリのWindows
フォルダーからロードします。それ以外の場合は、 Linux
フォルダーからロードします。
すべてのネイティブアセンブリに対してこれを行いますか?)
はい。ただし、1つしかなく、システムライブラリ(msvcrtなど)以外には依存しません。
標準のファイル構造(おそらくランタイム名に基づく)を持つことはできますか? これにより、ネイティブdllリゾルバーを拡張できます。
UDは、公開ランタイムフォルダーをdotnet publishで作成されたときと同じに保ち、参照されるクラスが正しいプラットフォームアーキテクチャに基づいてすべてのネイティブバイナリを読み込もうとします。
したがって、私が正しく理解している場合は、RID固有のネイティブバイナリに依存するマネージバイナリをロードするときに、.NETに内部でロードさせるだけで、PowerShellを使用してネイティブバイナリをロードすることを避けていますか?
それは機能しますが、PSがランタイムフォルダを見て正しいバイナリをロードできればいいのですが。 dotnetは起動時にそれを実行している必要があるようですので、そこにナブするコードがあるかもしれません。
ええ、.NETランタイム自体の中でRIDの認識が起こっているのではないかと思います。 私は私たちがその道を進む必要がないように努めてきましたが、ある時点で必要になるかもしれないようです。
@joeyaiello
したがって、私が正しく理解している場合は、RID固有のネイティブバイナリに依存するマネージバイナリをロードするときに、.NETに内部でロードさせるだけで、PowerShellを使用してネイティブバイナリをロードすることを避けていますか?
ええ、その通りです。 UDは、ターゲットRIDのすべてをロードするだけです。 したがって、実際の依存関係が何であるかについてはあまり賢くありません。 それを検出する良い方法があるかどうかはわかりません。
@ vexx32PSWordCloudをインストールしました。 フォルダ構造は一般的ですか? もしそうなら、リゾルバーをよりスマートにすることができます。 質問したい場合は、簡略化されたPSWordCloudに基づいて新しいPesterテストを準備してください(「HelloWorld」などのネイティブライブラリから単純なものを取得するだけで十分です)。 これを保留中としてマージすると、リゾルバーでの作業への道が開かれます。 ありがとう!
現在、PSWordCloudのフォルダ構造は一般的ではありません。 もちろん、それは変更できます。
ええ、私はそのための簡単なテストベッドを絶対に組み立てることができます。 Pesterは、私が推測する出力の性質に基づいて、実行するのが少し難しいですが、失敗すると、キャッチできる例外が生成されると思います。ただし、正しくリコールすると、その例外は型初期化子からスローされます。
テスト自体にdotnet経由でSkiaDLLをプルさせるのが適切でしょうか、それともリポジトリにパッケージ化する方がよいでしょうか? 保留が終了した後で機能テストとしてフラグが立てられた場合、それがそれほど大したことではないたびにそれらをプルすると思いますか? :考え:
Skia dllを使用して間接的に依存するテスト小結節を実装できれば、それは素晴らしいことです。 はい、nugetからSkiaをインストールできます。 テストモジュールを含むdllを〜テストモジュールフォルダ〜アサーションフォルダに直接配置すると、テストの信頼性が高まります。
Nugetのベストプラクティスは、ネイティブdllをRIDという名前のフォルダーに配置することです。 はい?
うん、できるよ! 明日見ていきます。 :赤面:
コメントを更新しました-テストモジュールをテストアサートフォルダに配置してください。
@rjmholt PSWordCloudで機能させるために、提供されたコードを使用しようと試みましたが、それでもまったく機能しません。 ロード操作は正常に完了しますが、アンマネージライブラリの後にマネージSkiaSharp.dllをロードしても、UnixプラットフォームでロードされたDLLを利用できません。 しかし、どういうわけか、それはWindowsでうまく機能します。
ここで私のWIPブランチを参照してくださいhttps://github.com/vexx32/PSWordCloud/tree/FixPInvokes--$#$ build.ps1
を実行してモジュールをビルドし、すべてのファイルを適切な場所に配置します。 モジュールもインポートされます。 ビルドの実行後にNew-WordCloud
呼び出すと、Windowsでは機能しますが、MacOSでは完全に失敗します。
@SeeminglyScienceあなたは私がそこで間違ったことを何か見ますか? :/
GitHubPowerShellできれいなワードクラウドを作成しましょう! GitHubでアカウントを作成して、vexx32 / PSWordCloudの開発に貢献します。
@ SteveL-MSFT現在、ネイティブライブラリを処理するための計画は何ですか? 😕
最も参考になるコメント
@rkeithhill
FWIW
DllImport
の拡張子は省略できます。 PSESのWindows以外のネイティブ依存関係に対してこれを行います。 ただし、ベース名が同じでない場合は役に立ちません。LoadLibrary
/dlopen
呼び出しのコンパイラ指令を個別のインターフェイス実装に格納することで、それらをスキップすることもできます。 メソッドがJITされていない限り、実行時にスローされるべきではありません。