Net coreのホットプラグ機構とアンインストールに関する問題点ヘルプガイド
I. 依存関係ファイル *.deps.json を読み込む。
依存関係ファイルの内容は、以下の通りです。通常、コンパイルディレクトリにあります
{
"runtimeTarget": {
"name": "NETCoreApp,Version=v3.1",
"signature": ""
},
"compilationOptions": {},
"targets": {
"NETCoreApp,Version=v3.1": {
"PluginSample/1.0.0": {
"dependencies": {
"Microsoft.Extensions.Hosting.Abstractions": "5.0.0-rc.2.20475.5"
},
"runtime": {
"PluginSample.dll": {}
}
},
"Microsoft.Extensions.Configuration.Abstractions/5.0.0-rc.2.20475.5": {
"dependencies": {
"Microsoft.Extensions.Primitives": "5.0.0-rc.2.20475.5"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.47505"
}
}
...
DependencyContextJsonReaderを使った依存関係設定ファイルの読み込み ソースコードビュー
using (var dependencyFileStream = File.OpenRead("Sample.deps.json"))
{
using (DependencyContextJsonReader dependencyContextJsonReader = new DependencyContextJsonReader())
{
// get the corresponding entity file
var dependencyContext =
Read(dependencyFileStream);
//define the runtime environment, if not, then it is platform-wide.
string currentRuntimeIdentifier= dependencyContext;
//The dll file needed to run
var assemblyNames= dependencyContext;
RuntimeLibraries; }
}
ネットコアのマルチプラットフォームRID(RuntimeIdentifier)定義。
{NETCore. Microsoft.NETCore.Platforms パッケージをインストールし、runtime.json ランタイム定義ファイルを探します。{
"runtimes": {
"win-arm64": {
"#import": [
"win"
]
},
"win-arm64-aot": {
"#import": [
"win-aot",
"win-arm64"
]
},
"win-x64": {
"#import": [
"win"
]
},
"win-x64-aot": {
"#import": [
"win-aot",
"win-x64"
]
},
}
NET Core RIDの依存関係概略図
{{コード
win7-x64 win7-x86
\ / |
| win7 |
|||
win-x64 | win-x86
\ /
win
| win
any
public class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginFolder, params string[] commonAssemblyFolders) : base(isCollectible: true)
{
this.ResolvingUnmanagedDll += PluginLoadContext_ResolvingUnmanagedDll;
this.Resolving += PluginLoadContext_Resolving;
// Step 1, parse the des.json file and call the Load and LoadUnmanagedDll functions
_resolver = new AssemblyDependencyResolver(pluginFolder);
//step 6, through steps 4 and 5, the dll that still failed to resolve will automatically try to call the assembly in the main program,
//If it fails, it will throw an error that the assembly cannot be loaded.
}
private Assembly PluginLoadContext_Resolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
{
// Step 4, the event executed after the Load function failed to load the assembly
}
private IntPtr PluginLoadContext_ResolvingUnmanagedDll(Assembly assembly, string unmanagedDllName)
{
//Step 5, the event executed when LoadUnmanagedDll fails to load the native dll
}
protected override Assembly Load(AssemblyName assemblyName)
{
//step 2, first execute the assembly load function
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
//step 3, the native dll load logic is executed first
}
}
{{コード
class PluginLoadContext : AssemblyLoadContext { private AssemblyDependencyResolver _resolver; public PluginLoadContext(string pluginPath) { _resolver = new AssemblyDependencyResolver(pluginPath); } protected override Assembly Load(AssemblyName assemblyName) { string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); if (assemblyPath ! = null) { //load the assembly return LoadFromAssemblyPath(assemblyPath); } //return null, then load the main project assembly directly return null; } protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); if (libraryPath ! = null) { //load native dll file return LoadUnmanagedDllFromPath(libraryPath); } //return IntPtr.Zero, i.e. null pointer. will load the dll in the runtimes folder of the main project return IntPtr.Zero; } }
{{コード
namespace PluginSample { public class SimpleService { public void Run(string name) { Console.WriteLine($"Hello World!"); } } }
{{コード
{{コード ランタイム.jsonを見る
namespace Test
{
public class PluginLoader
{
pubilc AssemblyLoadContext assemblyLoadContext;
public Assembly assembly;
public Type type;
public MethodInfo method;
public void Load()
{
assemblyLoadContext = new PluginLoadContext("Plugin folder");
assembly = alc.Load(new AssemblyName("PluginSample"));
type = assembly.GetType("PluginSample.SimpleService");
method = type.GetMethod()
}
}
}
{{コード
{{コード
{{コード
{{コード
public void Load(out WeakReference weakReference)
{
var assemblyLoadContext = new PluginLoadContext("plugin folder");
weakReference = new WeakReference(pluginLoadContext, true);
assemblyLoadContext.UnLoad();
}
public void Check()
{
WeakReference weakReference=null;
Load(out weakReference);
// Generally the second time, IsAlive will become False, that is, AssemblyLoadContext unload failed.
for (int i = 0; weakReference.IsAlive && (i < 10); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
public class SimpleService
{
//Synchronous execution, plug-in uninstallation success
public void Run(string name)
{
Console.WriteLine($"Hello {name}! ");
}
// Asynchronous execution, unload success
public Task RunAsync(string name)
{
Console.WriteLine($"Hello {name}! ");
CompletedTask;
}
// Asynchronous execution, unload successful
public Task RunTask(string name)
{
return Task.Run(() => {
Console.WriteLine($"Hello {name}! ");
});
}
// Asynchronous execution, unload successful
public Task RunWaitTask(string name)
{
return Task.Run( async ()=> {
while (true)
{ while (true)
if (CancellationTokenSource.IsCancellationRequested)
{
break;
}
await Task.Delay(1000);
Console.WriteLine($"Hello {name}! ");
}
});
}
// Asynchronous execution, uninstallation success
public Task RunWaitTaskForCancel(string name, CancellationToken cancellation)
{
return Task.Run(async () => {
while (true)
{
if (cancellation.IsCancellationRequested)
{
break;
}
await Task.Delay(1000);
Console.WriteLine($"Hello {name}! ");
}
});
}
// Asynchronous execution, unload failed
public async Task RunWait(string name)
{
while (true)
{
if (CancellationTokenSource.IsCancellationRequested)
{
break;
}
await Task.Delay(1000);
Console.WriteLine($"Hello {name}! ");
}
}
// Asynchronous execution, unload failed
public Task RunWaitNewTask(string name)
{
Return Task.Factory.StartNew(async ()=> {
while (true)
{
if (CancellationTokenSource.IsCancellationRequested)
{
break;
}
await Task.Delay(1000);
Console.WriteLine($"Hello {name}! ");
}
},TaskCreationOptions.DenyChildAttach);
}
}
{{コード
linux-x64
linux-arm
1. net core runtime.json file provided by Microsoft:
View runtime.json
.
3. 手動でロードする場合、deps.json ファイルで定義されたランタイムに従って、現在のプラットフォームのアンマネージド dll ファイルをロードすることができます。
これらのプラットフォームに関連するDLLファイルは、通常、配布ディレクトリのruntimesフォルダに配置されています。
IV. プラグインプロジェクトは、メインプロジェクトと同じランタイムを使用する必要があります。
- メインプロジェクトが.net core 3.1の場合、プラグインプロジェクトは.net core 2.0などを選択できず、.net標準ライブラリも選択できない
- そうでなければ、予測できない問題が発生します。
- net標準では、プロジェクトファイルの修正が必要です。
netstandard;netcoreapp3.1 . - これは、.net core プロジェクトとして公開されます。
- メインプロジェクトのnugetパッケージが現在のプラットフォームに適していない場合、例外 Not Support Platform がスローされます。この場合、メインプロジェクトがwindowsであれば、プロジェクトのリリースターゲットをwin-x64に設定する必要があります。
AssemblyLoadContext.UnLoad()で例外がスローされない。
アセンブリが解放されたと思ってプラグインをアンインストールするためにAssemblyLoadContext.UnLoad()を呼ぶと、間違っている場合があります。公式ドキュメントによると、アンインストールに失敗するとInvalidOperationExceptionがスローされ、アンインストールができなくなるようです。
公式の説明書
.
しかし、実際のテストでは、アンインストールは失敗しましたが、エラーは報告されませんでした。
VI. リフレクションアセンブリに関連する変数の定義が、プラグインアセンブリのアンインストールを妨げているのはなぜですか?
プラグイン
2. runtime.json, under the runeims node, defines all the RID dictionary tables and RID tree relationships.
プラグインを読み込む
3. The RID identifier of the assembly definition in the *.deps.json dependency file is used to determine whether the dll pointed to in the dependency file can run on a particular platform.
AssemblyLoadContext、Assembly、Type、MethodInfoなどは、メインプロジェクトアプリケーション内のどのクラスにも直接定義することはできません。
そうでない場合は、プラグインのアンインストールに失敗します。このとき、アンインストールが成功するかどうかをテストするために、手動でロード、実行、アンインストールを1000回繰り返します。
{1000回アンインストールを行いましたが、アンインストールに成功しませんでした。
メモリが増え続けるとアンインストールに失敗します。
3. WeakReference を使用して AssemblyLoadContext を関連付け、アンインストールが成功したかどうかを判断します。
4. When the application is released in compatibility mode, we can use the runtime.json file to selectively load the platform dll and run it.
4. 上記の問題を解決するために 必要な変数をスタティックディクショナリに入れればよい。Unloadの前に対応するKey値を削除する。
VII. アセンブリ内の非同期関数の実行により、プラグインのアンロードが妨げられるのはなぜですか?
AssemblyLoadContext loading principle
public class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginFolder, params string[] commonAssemblyFolders) : base(isCollectible: true)
{
this.ResolvingUnmanagedDll += PluginLoadContext_ResolvingUnmanagedDll;
this.Resolving += PluginLoadContext_Resolving;
// Step 1, parse the des.json file and call the Load and LoadUnmanagedDll functions
_resolver = new AssemblyDependencyResolver(pluginFolder);
//step 6, through steps 4 and 5, the dll that still failed to resolve will automatically try to call the assembly in the main program,
//If it fails, it will throw an error that the assembly cannot be loaded.
}
private Assembly PluginLoadContext_Resolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
{
// Step 4, the event executed after the Load function failed to load the assembly
}
private IntPtr PluginLoadContext_ResolvingUnmanagedDll(Assembly assembly, string unmanagedDllName)
{
//Step 5, the event executed when LoadUnmanagedDll fails to load the native dll
}
protected override Assembly Load(AssemblyName assemblyName)
{
//step 2, first execute the assembly load function
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
//step 3, the native dll load logic is executed first
}
}
The official Microsoft sample code is as follows:
Example specifics
{{コード
class PluginLoadContext : AssemblyLoadContext { private AssemblyDependencyResolver _resolver; public PluginLoadContext(string pluginPath) { _resolver = new AssemblyDependencyResolver(pluginPath); } protected override Assembly Load(AssemblyName assemblyName) { string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); if (assemblyPath ! = null) { //load the assembly return LoadFromAssemblyPath(assemblyPath); } //return null, then load the main project assembly directly return null; } protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); if (libraryPath ! = null) { //load native dll file return LoadUnmanagedDllFromPath(libraryPath); } //returns the IntPtr.Zero, i.e. null pointer. will load the dll in the runtimes folder of the main project return IntPtr.Zero; } }
- {{コード
- {{コード
- {{コード
- {{コード
関連
-
ASP + ajaxはトップを達成するために、同じサポートと反対側のコードのステップ
-
オンライン圧縮・解凍のためのASPコード
-
asp バッチの追加・変更・削除操作のサンプルコード
-
aspとphpの時限式ページ生成のためのアイデアとコード
-
FluentValidationを使ったルール検証のためのNET Core
-
asp createTextFileはutf8をサポートしたテキストファイルを生成します。
-
ASPでは、.NETのStringオブジェクトと同様に、文字部分に対してPadLeftとPadRightの関数が実装されています。
-
perl による生物学的突然変異のランダムシミュレーションコード
-
perlのsrand()とtime関数の使い方の紹介
-
コンストラクタでのPerlメソッド使用法入門
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
プロセス分析によるhttpsリクエストのチャールズベースクローリング
-
指定したフォルダーにあるリンク切れファイルのシンボリックリンクを自動的に削除するスクリプト
-
ASP RecordSet OpenとConnection.Executeのいくつかの違いと共有すべき詳細な内容
-
ASPは、コンテンツ内のすべての画像パスSRCを正規表現で抽出するためのコードです。
-
C言語による配列への要素の追加と削除
-
数字を漢数字(大文字の金額)に変換するASP機能
-
SELECT ドロップダウンメニューで VALUE と TEXT 値を同時に取得する ASP コード
-
従来のいくつかの方法によるASPエラーの捕捉
-
Perlの単一行コメントと複数行コメントの紹介
-
スペースがセパレータである場合の perl qw 問題の解決法