From b7be0f2c6e9e33b15a58d441a2fa225957be3207 Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Sun, 3 Nov 2024 23:51:18 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E5=8D=B8=E8=BD=BD?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E9=9B=86=E7=9A=84=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Library/Api/IFlowEnvironment.cs | 4 +-
NodeFlow/Env/FlowEnvironment.cs | 20 ++-
NodeFlow/Env/FlowEnvironmentDecorator.cs | 8 +-
NodeFlow/Env/RemoteFlowEnvironment.cs | 4 +-
NodeFlow/Tool/FlowLibrary.cs | 33 +++-
NodeFlow/Tool/FlowLibraryManagement.cs | 189 ++++++++++++++---------
WorkBench/MainWindow.xaml.cs | 4 +-
7 files changed, 171 insertions(+), 91 deletions(-)
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index 78b9319..0b545a8 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -672,12 +672,12 @@ namespace Serein.Library.Api
/// 从文件中加载Dll
///
///
- void LoadDll(string dllPath);
+ void LoadLibrary(string dllPath);
///
/// 移除DLL
///
/// 程序集的名称
- bool RemoteDll(string assemblyFullName);
+ bool UnloadLibrary(string assemblyFullName);
///
/// 清理加载的DLL(待更改)
diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs
index 495494f..de5343e 100644
--- a/NodeFlow/Env/FlowEnvironment.cs
+++ b/NodeFlow/Env/FlowEnvironment.cs
@@ -520,7 +520,7 @@ namespace Serein.NodeFlow.Env
foreach (var dllPath in dllPaths)
{
var dllFilePath = Path.GetFullPath(Path.Combine(filePath, dllPath));
- LoadDll(dllFilePath); // 加载项目文件时加载对应的程序集
+ LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集
}
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
@@ -722,10 +722,20 @@ namespace Serein.NodeFlow.Env
///
///
///
- public void LoadDll(string dllPath)
+ public void LoadLibrary(string dllPath)
{
- (var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibrary(dllPath);
- UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示
+ try
+ {
+ (var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibrary(dllPath);
+ if (mdInfos.Count > 0)
+ {
+ UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"{ex}");
+ }
}
@@ -734,7 +744,7 @@ namespace Serein.NodeFlow.Env
///
///
///
- public bool RemoteDll(string assemblyName)
+ public bool UnloadLibrary(string assemblyName)
{
return FlowLibraryManagement.UnloadLibrary(assemblyName);
diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs
index ff5c22d..458ec9e 100644
--- a/NodeFlow/Env/FlowEnvironmentDecorator.cs
+++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs
@@ -278,9 +278,9 @@ namespace Serein.NodeFlow.Env
}
- public void LoadDll(string dllPath)
+ public void LoadLibrary(string dllPath)
{
- currentFlowEnvironment.LoadDll(dllPath);
+ currentFlowEnvironment.LoadLibrary(dllPath);
}
public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath)
@@ -307,9 +307,9 @@ namespace Serein.NodeFlow.Env
}
- public bool RemoteDll(string assemblyName)
+ public bool UnloadLibrary(string assemblyName)
{
- return currentFlowEnvironment.RemoteDll(assemblyName);
+ return currentFlowEnvironment.UnloadLibrary(assemblyName);
}
public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs
index 393c31f..8983d8c 100644
--- a/NodeFlow/Env/RemoteFlowEnvironment.cs
+++ b/NodeFlow/Env/RemoteFlowEnvironment.cs
@@ -397,13 +397,13 @@ namespace Serein.NodeFlow.Env
Console.WriteLine("远程环境尚未实现的接口:ExitRemoteEnv");
}
- public void LoadDll(string dllPath)
+ public void LoadLibrary(string dllPath)
{
// 将dll文件发送到远程环境,由远程环境进行加载
Console.WriteLine("远程环境尚未实现的接口:LoadDll");
}
- public bool RemoteDll(string assemblyName)
+ public bool UnloadLibrary(string assemblyName)
{
// 尝试移除远程环境中的加载了的依赖
Console.WriteLine("远程环境尚未实现的接口:RemoteDll");
diff --git a/NodeFlow/Tool/FlowLibrary.cs b/NodeFlow/Tool/FlowLibrary.cs
index 4754353..87a18b4 100644
--- a/NodeFlow/Tool/FlowLibrary.cs
+++ b/NodeFlow/Tool/FlowLibrary.cs
@@ -28,7 +28,7 @@ namespace Serein.NodeFlow
this._assemblyFilePath = assemblyFilePath;
this.assembly = assembly;
this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
- LoadAssembly(assembly);
+
}
@@ -112,7 +112,7 @@ namespace Serein.NodeFlow
///
/// 程序集本身
///
- private bool LoadAssembly(Assembly assembly)
+ public bool LoadAssembly(Assembly assembly)
{
#region 检查入参
@@ -122,12 +122,26 @@ namespace Serein.NodeFlow
{
return false;
}
-
- List types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型
- if (types.Count < 0) // 防止动态程序集中没有类型信息?
+ List types;
+ try
{
+ types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型
+ if (types.Count < 0) // 防止动态程序集中没有类型信息?
+ {
+ return false;
+ }
+ }
+ catch (ReflectionTypeLoadException ex)
+ {
+ // 获取加载失败的类型
+ var loaderExceptions = ex.LoaderExceptions;
+ foreach (var loaderException in loaderExceptions)
+ {
+ Console.WriteLine(loaderException.Message);
+ }
return false;
}
+
#endregion
@@ -146,6 +160,15 @@ namespace Serein.NodeFlow
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性
foreach (var type in types)
{
+ if (type.Name.Equals("YoloFlowControl"))
+ {
+ //var ab = type.GetCustomAttribute();
+ //var attributes = assembly.GetCustomAttributes();
+ //foreach (var attribute in attributes)
+ //{
+ // Console.WriteLine(attribute.GetType().Name);
+ //}
+ }
if (type.GetCustomAttribute() is DynamicFlowAttribute dynamicFlowAttribute && dynamicFlowAttribute.Scan == true)
{
scanTypes.Add((type, dynamicFlowAttribute.Name));
diff --git a/NodeFlow/Tool/FlowLibraryManagement.cs b/NodeFlow/Tool/FlowLibraryManagement.cs
index aeddad1..80d5496 100644
--- a/NodeFlow/Tool/FlowLibraryManagement.cs
+++ b/NodeFlow/Tool/FlowLibraryManagement.cs
@@ -10,6 +10,7 @@ using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
+using System.Xml.Linq;
namespace Serein.NodeFlow.Tool
{
@@ -28,16 +29,18 @@ namespace Serein.NodeFlow.Tool
///
/// 缓存所有加载了的程序集
///
- private ConcurrentDictionary _myFlowLibrarys = new ConcurrentDictionary();
+ private readonly ConcurrentDictionary _myFlowLibrarys = new ConcurrentDictionary();
- public (NodeLibraryInfo,List) LoadLibrary(string libraryfilePath)
+ public (NodeLibraryInfo, List) LoadLibrary(string libraryfilePath)
{
+
return LoadDllNodeInfo(libraryfilePath);
}
public bool UnloadLibrary(string libraryName)
{
- if (_myFlowLibrarys.TryGetValue(libraryName, out var flowLibrary))
+ libraryName = libraryName.Split(',')[0];
+ if (_myFlowLibrarys.Remove(libraryName, out var flowLibrary))
{
try
{
@@ -49,7 +52,7 @@ namespace Serein.NodeFlow.Tool
Console.WriteLine($"尝试卸载程序集[{libraryName}]发生错误:{ex}");
return false;
}
-
+
}
else
{
@@ -64,10 +67,10 @@ namespace Serein.NodeFlow.Tool
/// 方法名称
/// 返回的方法描述
/// 是否获取成功
- public bool TryGetMethodDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodDetails md)
+ public bool TryGetMethodDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodDetails md)
{
- if(_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
- && flowLibrary.MethodDetailss.TryGetValue(methodName,out md))
+ if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
+ && flowLibrary.MethodDetailss.TryGetValue(methodName, out md))
{
return true;
}
@@ -77,7 +80,7 @@ namespace Serein.NodeFlow.Tool
return false;
}
}
-
+
///
/// 获取方法调用的委托
///
@@ -85,7 +88,7 @@ namespace Serein.NodeFlow.Tool
/// 方法名称
/// 返回的委托调用封装类
/// 是否获取成功
- public bool TryGetDelegateDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out DelegateDetails dd)
+ public bool TryGetDelegateDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out DelegateDetails dd)
{
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
&& flowLibrary.DelegateDetailss.TryGetValue(methodName, out dd))
@@ -109,9 +112,9 @@ namespace Serein.NodeFlow.Tool
{
List mds = [];
- foreach (var library in _myFlowLibrarys.Values)
+ foreach (var library in _myFlowLibrarys.Values)
{
- var t_mds = library.MethodDetailss.Values.Where(it => it.MethodDynamicType == nodeType).ToList();
+ var t_mds = library.MethodDetailss.Values.Where(it => it.MethodDynamicType == nodeType).ToList();
mds.AddRange(t_mds);
}
return mds;
@@ -124,13 +127,13 @@ namespace Serein.NodeFlow.Tool
public Dictionary> GetaAutoRegisterType()
{
Dictionary> rsTypes = new Dictionary>();
- foreach(var library in _myFlowLibrarys.Values)
+ foreach (var library in _myFlowLibrarys.Values)
{
- foreach(var kv in library.RegisterTypes)
+ foreach (var kv in library.RegisterTypes)
{
var @class = kv.Key;
var type = kv.Value;
- if(!rsTypes.TryGetValue(@class, out var tmpTypes))
+ if (!rsTypes.TryGetValue(@class, out var tmpTypes))
{
tmpTypes = new List();
rsTypes.Add(@class, tmpTypes);
@@ -149,10 +152,11 @@ namespace Serein.NodeFlow.Tool
public List GetAllLibraryMds()
{
List mds = new List();
- foreach (FlowLibrary library in _myFlowLibrarys.Values)
+ foreach (FlowLibrary library in _myFlowLibrarys.Values)
{
- var tmp = new LibraryMds {
- AssemblyName = library.FullName,
+ var tmp = new LibraryMds
+ {
+ AssemblyName = library.FullName,
Mds = library.MethodDetailss.Values.Select(md => md.ToInfo()).ToArray()
};
mds.Add(tmp);
@@ -179,15 +183,48 @@ namespace Serein.NodeFlow.Tool
///
/// 从文件路径中加载程序集,返回相应的信息
///
- ///
+ ///
///
- private (NodeLibraryInfo, List) LoadDllNodeInfo(string dllPath)
+ private (NodeLibraryInfo, List) LoadDllNodeInfo(string dllFilePath)
{
-
- var fileName = Path.GetFileName(dllPath); // 获取文件名
- Assembly assembly = Assembly.LoadFrom(dllPath); // 加载程序集
- FlowLibrary flowLibrary = new FlowLibrary(dllPath, assembly, () =>
+#if true
+ var fileName = Path.GetFileName(dllFilePath); // 获取文件名
+ var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径
+ var sereinFlowLibraryPath = Path.Combine(dir, $"{nameof(Serein)}.{nameof(Serein.Library)}.dll");
+ // 每个类库下面至少需要有“Serein.Library.dll”类库依赖
+ var flowAlc = new FlowLibraryAssemblyContext(sereinFlowLibraryPath, fileName);
+ Action actionUnload = () =>
+ {
+ flowAlc?.Unload(); // 卸载程序集
+ flowAlc = null;
+ GC.Collect(); // 强制触发GC确保卸载成功
+ GC.WaitForPendingFinalizers();
+ };
+ var assembly = flowAlc.LoadFromAssemblyPath(dllFilePath); // 加载指定路径的程序集
+ if (_myFlowLibrarys.ContainsKey(assembly.GetName().Name))
+ {
+ actionUnload.Invoke();
+ throw new Exception($"程序集[{assembly.GetName().FullName}]已经加载过!");
+ }
+
+ FlowLibrary flowLibrary = new FlowLibrary(dllFilePath, assembly, actionUnload);
+ if (flowLibrary.LoadAssembly(assembly))
+ {
+ _myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary);
+ (NodeLibraryInfo, List) result = (flowLibrary.ToInfo(),
+ flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList());
+ return result;
+ }
+ else
+ {
+ throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败");
+ }
+
+#else
+ var fileName = Path.GetFileName(dllFilePath); // 获取文件名
+ Assembly assembly = Assembly.LoadFrom(dllFilePath); // 加载程序集
+ FlowLibrary flowLibrary = new FlowLibrary(dllFilePath, assembly, () =>
{
Console.WriteLine("暂未实现卸载程序集");
//flowAlc.Unload(); // 卸载程序集
@@ -202,63 +239,73 @@ namespace Serein.NodeFlow.Tool
flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList());
return result;
-
-#if false
- var fileName = Path.GetFileName(dllPath); // 获取文件名
- var flowAlc = new AssemblyLoadContext(fileName, true);
- flowAlc.LoadFromAssemblyPath(dllPath); // 加载指定路径的程序集
- flowAlc.LoadFromAssemblyPath(@"F:\临时\project\yolo flow\OpenCvSharp.dll"); // 加载指定路径的程序集
-
- var assemblt = flowAlc.Assemblies.ToArray()[0]; // 目前只会加载一个程序集,所以这样获取
- FlowLibrary flowLibrary = new FlowLibrary(dllPath, assemblt, () =>
- {
- flowAlc.Unload(); // 卸载程序集
- flowAlc = null;
- GC.Collect(); // 强制触发GC确保卸载成功
- GC.WaitForPendingFinalizers();
- });
- _myFlowLibrarys.TryAdd(assemblt.GetName().Name, flowLibrary);
- return flowLibrary.ToInfo();
-
-
- //foreach (var assemblt in flowAlc.Assemblies)
- //{
- // FlowLibrary flowLibrary = new FlowLibrary(dllPath, assemblt, () =>
- // {
- // flowAlc.Unload(); // 卸载程序集
- // flowAlc = null;
- // GC.Collect(); // 强制触发GC确保卸载成功
- // GC.WaitForPendingFinalizers();
- // });
- //}
-
+
+
#endif
- //if (OperatingSystem.IsWindows())
- //{
- // UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibraryInfo, mdInfos))); // 通知UI创建dll面板显示
- //}
}
#endregion
-
-
-
- /////
- ///// 是否对程序集的引用
- /////
- //public void UnloadPlugin()
- //{
- // _pluginAssembly = null; // 释放对程序集的引用
- // Unload(); // 触发卸载
- // // 强制进行垃圾回收,以便完成卸载
- // GC.Collect();
- // GC.WaitForPendingFinalizers();
- //}
}
+ ///
+ /// 流程依赖加载
+ ///
+ public class FlowLibraryAssemblyContext : AssemblyLoadContext
+ {
+ private readonly AssemblyDependencyResolver _resolver;
+
+ ///
+ /// 创建新的加载上下文
+ ///
+ /// 类库主
+ ///
+ public FlowLibraryAssemblyContext(string sereinFlowLibraryPath, string name) : base(name, isCollectible: true)
+ {
+ _resolver = new AssemblyDependencyResolver(sereinFlowLibraryPath);
+ }
+
+ protected override Assembly? Load(AssemblyName assemblyName)
+ {
+ string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (!string.IsNullOrEmpty(assemblyPath))
+ {
+ var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
+ //var assembly = LoadFromAssemblyPath(assemblyPath);
+ return assembly;
+ }
+ else
+ {
+ return Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
+ }
+ // 构建依赖项的路径
+ //string assemblyPath = Path.Combine(AppContext.BaseDirectory, assemblyName.Name + ".dll");
+ //if (File.Exists(assemblyPath))
+ //{
+ // return LoadFromAssemblyPath(assemblyPath);
+ //}
+ //assemblyPath = Path.Combine(filePath, assemblyName.Name + ".dll");
+ //if (File.Exists(assemblyPath))
+ //{
+ // return LoadFromAssemblyPath(assemblyPath);
+ //}
+
+ //return null; // 如果没有找到,返回 null
+ }
+ }
+ public static class PluginAssemblyContextExtensions
+ {
+
+ public static Assembly FromAssemblyPath(this AssemblyLoadContext context, string path)
+ {
+
+ return context.LoadFromAssemblyPath(path);
+
+ }
+
+ }
}
diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs
index b8621aa..36fa9f7 100644
--- a/WorkBench/MainWindow.xaml.cs
+++ b/WorkBench/MainWindow.xaml.cs
@@ -341,7 +341,7 @@ namespace Serein.Workbench
var menu = new ContextMenu();
menu.Items.Add(CreateMenuItem("卸载", (s, e) =>
{
- if (this.EnvDecorator.RemoteDll(nodeLibraryInfo.AssemblyName))
+ if (this.EnvDecorator.UnloadLibrary(nodeLibraryInfo.AssemblyName))
{
DllStackPanel.Children.Remove(dllControl);
}
@@ -1165,7 +1165,7 @@ namespace Serein.Workbench
{
if (file.EndsWith(".dll"))
{
- EnvDecorator.LoadDll(file);
+ EnvDecorator.LoadLibrary(file);
}
}
}