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); } } }