using Newtonsoft.Json;
|
using System;
|
using System.Collections.Generic;
|
using System.Diagnostics;
|
using System.Linq;
|
using System.Reflection;
|
using System.Runtime.Loader;
|
using System.Text;
|
using System.Threading.Tasks;
|
|
namespace LB_VisionProcesses
|
{
|
public class PluginManager
|
{
|
private static readonly List<Assembly> _loadedAssemblies = new List<Assembly>();
|
private static readonly Dictionary<string, AssemblyLoadContext> _loadContexts =
|
new Dictionary<string, AssemblyLoadContext>();
|
private static readonly object _lock = new object();
|
|
/// <summary>
|
/// 已加载的工具字典(键:显示名称,值:类型完全限定名)
|
/// </summary>
|
public static Dictionary<string, string> LoadedProcesses { get; } = new Dictionary<string, string>();
|
|
/// <summary>
|
/// 按分类组织的工具
|
/// </summary>
|
public static Dictionary<string, Dictionary<string, string>> CategorizedProcesses { get; } =
|
new Dictionary<string, Dictionary<string, string>>();
|
|
/// <summary>
|
/// 插件信息
|
/// </summary>
|
public static List<PluginInfo> PluginInfos { get; } = new List<PluginInfo>();
|
|
/// <summary>
|
/// 插件目录路径
|
/// </summary>
|
public static string PluginDirectory { get; set; } = "Plugins";
|
|
/// <summary>
|
/// 插件加载事件
|
/// </summary>
|
public static event EventHandler<PluginEventArgs> PluginLoaded;
|
public static event EventHandler<PluginEventArgs> PluginUnloaded;
|
|
static PluginManager()
|
{
|
// 确保插件目录存在
|
if (!Directory.Exists(PluginDirectory))
|
{
|
Directory.CreateDirectory(PluginDirectory);
|
}
|
}
|
|
/// <summary>
|
/// 加载所有插件
|
/// </summary>
|
public static void LoadAllPlugins()
|
{
|
lock (_lock)
|
{
|
ClearLoadedAssemblies();
|
|
// 加载主程序集的工具
|
ScanAssemblyForProcesses(Assembly.GetExecutingAssembly(), "LB_VisionProcesses");
|
|
// 加载插件目录的DLL
|
LoadAssembliesFromDirectory(PluginDirectory);
|
|
// 加载配置文件指定的插件
|
LoadFromConfig();
|
}
|
}
|
|
/// <summary>
|
/// 加载指定程序集
|
/// </summary>
|
public static bool LoadAssembly(string assemblyPath, string pluginName = null)
|
{
|
lock (_lock)
|
{
|
try
|
{
|
if (!File.Exists(assemblyPath))
|
{
|
Debug.WriteLine($"程序集文件不存在: {assemblyPath}");
|
return false;
|
}
|
|
// 检查是否已加载
|
var fileName = Path.GetFileName(assemblyPath);
|
if (_loadContexts.ContainsKey(fileName))
|
{
|
Debug.WriteLine($"程序集已加载: {fileName}");
|
return false;
|
}
|
|
// 创建隔离的加载上下文
|
var context = new PluginLoadContext(assemblyPath);
|
var assembly = context.LoadFromAssemblyPath(assemblyPath);
|
|
_loadedAssemblies.Add(assembly);
|
_loadContexts[fileName] = context;
|
|
// 扫描工具类
|
var pluginInfo = ScanAssemblyForProcesses(assembly, pluginName ?? Path.GetFileNameWithoutExtension(assemblyPath));
|
PluginInfos.Add(pluginInfo);
|
|
// 触发事件
|
PluginLoaded?.Invoke(null, new PluginEventArgs(pluginInfo));
|
|
return true;
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"加载程序集失败: {assemblyPath}, 错误: {ex.Message}");
|
return false;
|
}
|
}
|
}
|
|
/// <summary>
|
/// 加载指定目录下所有DLL
|
/// </summary>
|
public static void LoadAssembliesFromDirectory(string directoryPath)
|
{
|
if (!Directory.Exists(directoryPath))
|
{
|
Debug.WriteLine($"插件目录不存在: {directoryPath}");
|
return;
|
}
|
|
// 加载主DLL
|
var dllFiles = Directory.GetFiles(directoryPath, "*.dll")
|
.Where(f => !f.EndsWith(".resources.dll") &&
|
!f.Contains("\\ref\\") &&
|
!Path.GetFileName(f).StartsWith("System.") &&
|
!Path.GetFileName(f).StartsWith("Microsoft."))
|
.ToList();
|
|
// 先加载没有依赖的,再加载可能有依赖的
|
var orderedFiles = dllFiles.OrderBy(f =>
|
File.ReadAllBytes(f).Length).ToList(); // 按文件大小排序,小的先加载
|
|
foreach (var dllFile in orderedFiles)
|
{
|
try
|
{
|
LoadAssembly(dllFile);
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"加载DLL失败: {dllFile}, 错误: {ex.Message}");
|
}
|
}
|
}
|
|
/// <summary>
|
/// 卸载指定插件
|
/// </summary>
|
public static bool UnloadPlugin(string pluginName)
|
{
|
lock (_lock)
|
{
|
var pluginInfo = PluginInfos.FirstOrDefault(p => p.Name == pluginName);
|
if (pluginInfo == null)
|
return false;
|
|
try
|
{
|
// 从工具字典中移除
|
foreach (var process in pluginInfo.Processes)
|
{
|
LoadedProcesses.Remove(process.Key);
|
|
// 从分类字典中移除
|
if (CategorizedProcesses.ContainsKey(process.Value.Category))
|
{
|
CategorizedProcesses[process.Value.Category].Remove(process.Key);
|
}
|
}
|
|
// 触发事件
|
PluginUnloaded?.Invoke(null, new PluginEventArgs(pluginInfo));
|
|
// 从列表中移除
|
PluginInfos.Remove(pluginInfo);
|
|
// 尝试卸载程序集(需要.NET Core 3.0+)
|
var fileName = Path.GetFileName(pluginInfo.AssemblyPath);
|
if (_loadContexts.TryGetValue(fileName, out var context))
|
{
|
_loadContexts.Remove(fileName);
|
if (context.IsCollectible)
|
{
|
context.Unload();
|
}
|
}
|
|
return true;
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"卸载插件失败: {pluginName}, 错误: {ex.Message}");
|
return false;
|
}
|
}
|
}
|
|
/// <summary>
|
/// 扫描程序集中的工具类
|
/// </summary>
|
private static PluginInfo ScanAssemblyForProcesses(Assembly assembly, string pluginName)
|
{
|
var pluginInfo = new PluginInfo
|
{
|
Name = pluginName,
|
AssemblyPath = assembly.Location,
|
Version = assembly.GetName().Version?.ToString() ?? "1.0.0",
|
LoadedTime = DateTime.Now
|
};
|
|
try
|
{
|
var processTypes = assembly.GetTypes()
|
.Where(t => t.IsClass && !t.IsAbstract &&
|
typeof(IProcess).IsAssignableFrom(t))
|
.ToList();
|
foreach (var type in processTypes)
|
{
|
try
|
{
|
if (type.IsDefined(typeof(ProcessAttribute), false) || type.IsDefined(typeof(CategoryAttribute), false))
|
{
|
// 使用特性获取元数据,避免实例化
|
var processAttr = type.GetCustomAttribute<ProcessAttribute>();
|
var categoryAttr = type.GetCustomAttribute<CategoryAttribute>();
|
|
string displayName;
|
string category = "其他";
|
string description = "";
|
int order = 0;
|
|
if (processAttr != null)
|
{
|
displayName = processAttr.DisplayName;
|
category = processAttr.Category;
|
description = processAttr.Description;
|
order = processAttr.Order;
|
}
|
else
|
{
|
// 如果没有特性,使用类型名称
|
displayName = type.Name;
|
}
|
|
if (categoryAttr != null)
|
{
|
category = categoryAttr.Category;
|
}
|
|
// 避免重复名称
|
int suffix = 1;
|
string originalName = displayName;
|
while (LoadedProcesses.ContainsKey(displayName))
|
{
|
displayName = $"{originalName}_{suffix}";
|
suffix++;
|
}
|
|
string fullName = type.AssemblyQualifiedName;
|
|
// 添加到总字典
|
LoadedProcesses[displayName] = fullName;
|
|
// 按分类组织
|
if (!CategorizedProcesses.ContainsKey(category))
|
{
|
CategorizedProcesses[category] = new Dictionary<string, string>();
|
}
|
CategorizedProcesses[category][displayName] = fullName;
|
|
// 添加到插件信息
|
pluginInfo.Processes[displayName] = new ProcessInfo
|
{
|
TypeName = fullName,
|
DisplayName = displayName,
|
Category = category,
|
Description = description,
|
Order = order
|
};
|
|
Debug.WriteLine($"注册工具: {displayName} ({category})");
|
}
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"注册工具类型 {type.Name} 失败: {ex.Message}");
|
}
|
}
|
}
|
catch (ReflectionTypeLoadException ex)
|
{
|
Debug.WriteLine($"扫描程序集 {assembly.FullName} 时类型加载失败: {ex.Message}");
|
foreach (var loaderEx in ex.LoaderExceptions)
|
{
|
Debug.WriteLine($"加载异常: {loaderEx?.Message}");
|
}
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"扫描程序集 {assembly.FullName} 失败: {ex.Message}");
|
}
|
|
return pluginInfo;
|
}
|
|
/// <summary>
|
/// 从配置文件加载插件
|
/// </summary>
|
private static void LoadFromConfig()
|
{
|
var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugin.config.json");
|
if (File.Exists(configPath))
|
{
|
try
|
{
|
var config = File.ReadAllText(configPath);
|
var pluginConfigs = JsonConvert.DeserializeObject<List<PluginConfig>>(config);
|
|
foreach (var pluginConfig in pluginConfigs)
|
{
|
if (pluginConfig.Enabled)
|
{
|
var pluginPath = Path.Combine(PluginDirectory, pluginConfig.AssemblyName);
|
LoadAssembly(pluginPath, pluginConfig.Name);
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"读取插件配置失败: {ex.Message}");
|
}
|
}
|
}
|
|
/// <summary>
|
/// 清空已加载的程序集
|
/// </summary>
|
private static void ClearLoadedAssemblies()
|
{
|
lock (_lock)
|
{
|
_loadedAssemblies.Clear();
|
_loadContexts.Clear();
|
LoadedProcesses.Clear();
|
CategorizedProcesses.Clear();
|
PluginInfos.Clear();
|
}
|
}
|
|
/// <summary>
|
/// 重新加载所有插件
|
/// </summary>
|
public static void ReloadPlugins()
|
{
|
lock (_lock)
|
{
|
ClearLoadedAssemblies();
|
LoadAllPlugins();
|
}
|
}
|
|
/// <summary>
|
/// 获取所有分类
|
/// </summary>
|
public static List<string> GetCategories()
|
{
|
return CategorizedProcesses.Keys.OrderBy(k => k).ToList();
|
}
|
|
/// <summary>
|
/// 获取指定分类的工具
|
/// </summary>
|
public static Dictionary<string, string> GetProcessesByCategory(string category)
|
{
|
return CategorizedProcesses.ContainsKey(category)
|
? CategorizedProcesses[category]
|
: new Dictionary<string, string>();
|
}
|
|
/// <summary>
|
/// 创建工具实例
|
/// </summary>
|
public static IProcess CreateProcess(string displayName)
|
{
|
lock (_lock)
|
{
|
if (!LoadedProcesses.TryGetValue(displayName, out string typeName))
|
{
|
throw new KeyNotFoundException($"未找到工具: {displayName}");
|
}
|
|
try
|
{
|
var type = Type.GetType(typeName);
|
if (type == null)
|
{
|
// 尝试从已加载的程序集中查找
|
foreach (var assembly in _loadedAssemblies)
|
{
|
type = assembly.GetType(typeName.Split(',')[0]);
|
if (type != null) break;
|
}
|
}
|
|
if (type == null)
|
{
|
throw new TypeLoadException($"无法加载类型: {typeName}");
|
}
|
|
return Activator.CreateInstance(type) as IProcess;
|
}
|
catch (Exception ex)
|
{
|
throw new ApplicationException($"创建工具实例失败: {displayName}", ex);
|
}
|
}
|
}
|
}
|
}
|