Files
community/Ink Canvas/Helpers/Plugins/PluginManager.cs
T
2025-07-16 13:50:32 +08:00

1463 lines
66 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件管理器,负责插件的加载、卸载和管理
/// </summary>
public class PluginManager
{
private static readonly string PluginsDirectory = Path.Combine(App.RootPath, "Plugins");
private static readonly string PluginConfigFile = Path.Combine(App.RootPath, "PluginConfig.json");
private static readonly string PluginConfigBackupFile = Path.Combine(App.RootPath, "PluginConfig.json.bak");
private static PluginManager _instance;
private static SemaphoreSlim _configLock = new SemaphoreSlim(1, 1);
/// <summary>
/// 插件管理器单例
/// </summary>
public static PluginManager Instance
{
get
{
if (_instance == null)
{
_instance = new PluginManager();
}
return _instance;
}
}
/// <summary>
/// 已加载的插件集合
/// </summary>
public ObservableCollection<IPlugin> Plugins { get; } = new ObservableCollection<IPlugin>();
/// <summary>
/// 插件配置信息
/// </summary>
public Dictionary<string, bool> PluginStates { get; private set; } = new Dictionary<string, bool>();
/// <summary>
/// 配置是否已更改但未保存
/// </summary>
private bool _configDirty = false;
/// <summary>
/// 配置自动保存计时器
/// </summary>
private System.Timers.Timer _autoSaveTimer;
/// <summary>
/// 加载的程序集缓存
/// </summary>
private Dictionary<string, Assembly> _loadedAssemblies = new Dictionary<string, Assembly>();
/// <summary>
/// 插件文件哈希缓存,用于热重载检测
/// </summary>
private Dictionary<string, string> _pluginHashes = new Dictionary<string, string>();
private PluginManager()
{
// 确保插件目录存在
if (!Directory.Exists(PluginsDirectory))
{
Directory.CreateDirectory(PluginsDirectory);
}
// 加载插件配置
LoadConfig();
// 初始化自动保存计时器(3秒)
_autoSaveTimer = new System.Timers.Timer(3000);
_autoSaveTimer.Elapsed += (s, e) =>
{
if (_configDirty)
{
SaveConfigAsync().ConfigureAwait(false);
}
};
_autoSaveTimer.AutoReset = false;
// 注册插件状态变更事件处理
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
// 应用退出时强制保存配置
if (_configDirty)
{
SaveConfig();
}
};
}
/// <summary>
/// 初始化插件系统
/// </summary>
public void Initialize()
{
try
{
LogHelper.WriteLogToFile("开始初始化插件系统", LogHelper.LogType.Info);
// 加载配置
LoadConfig();
LogHelper.WriteLogToFile($"已从配置文件加载 {PluginStates.Count} 个插件状态记录", LogHelper.LogType.Info);
// 加载内置插件
LogHelper.WriteLogToFile("正在加载内置插件...", LogHelper.LogType.Info);
LoadBuiltInPlugins();
// 加载外部插件
LogHelper.WriteLogToFile("正在加载外部插件...", LogHelper.LogType.Info);
LoadExternalPlugins();
// 启用已配置为启用的插件
LogHelper.WriteLogToFile("正在应用配置的插件状态...", LogHelper.LogType.Info);
EnableConfiguredPlugins();
// 设置定期检查热重载
StartHotReloadWatcher();
// 保存初始化后的配置(可能有新插件)
SaveConfig();
LogHelper.WriteLogToFile($"插件系统初始化完成,共加载 {Plugins.Count} 个插件", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化插件系统时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载内置插件
/// </summary>
private void LoadBuiltInPlugins()
{
try
{
// 获取当前程序集
Assembly currentAssembly = Assembly.GetExecutingAssembly();
// 查找实现了IPlugin接口的所有类型
var pluginTypes = currentAssembly.GetTypes()
.Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract && t.IsClass);
foreach (var pluginType in pluginTypes)
{
try
{
// 创建插件实例
IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);
// 只处理内置插件
if (plugin.IsBuiltIn)
{
plugin.Initialize();
Plugins.Add(plugin);
LogHelper.WriteLogToFile($"已加载内置插件: {plugin.Name} v{plugin.Version}", LogHelper.LogType.Info);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载内置插件 {pluginType.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载内置插件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载外部插件
/// </summary>
private void LoadExternalPlugins()
{
try
{
// 检查插件目录是否存在
if (!Directory.Exists(PluginsDirectory))
{
Directory.CreateDirectory(PluginsDirectory);
return;
}
// 获取所有插件文件
var pluginFiles = Directory.GetFiles(PluginsDirectory, "*.iccpp", SearchOption.TopDirectoryOnly);
foreach (var pluginFile in pluginFiles)
{
LoadExternalPlugin(pluginFile);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载外部插件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载单个外部插件
/// </summary>
/// <param name="pluginPath">插件文件路径</param>
/// <returns>加载的插件实例,加载失败则返回null</returns>
public IPlugin LoadExternalPlugin(string pluginPath)
{
try
{
// 计算文件哈希
string fileHash = CalculateFileHash(pluginPath);
_pluginHashes[pluginPath] = fileHash;
// 加载插件程序集
Assembly pluginAssembly = LoadPluginAssembly(pluginPath);
if (pluginAssembly == null) return null;
// 查找实现了IPlugin接口的类型
var pluginTypes = pluginAssembly.GetTypes()
.Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract && t.IsClass);
foreach (var pluginType in pluginTypes)
{
try
{
// 创建插件实例
IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);
// 设置插件路径
if (plugin is PluginBase pluginBase)
{
pluginBase.PluginPath = pluginPath;
}
plugin.Initialize();
Plugins.Add(plugin);
LogHelper.WriteLogToFile($"已加载外部插件: {plugin.Name} v{plugin.Version} 来自 {Path.GetFileName(pluginPath)}",
LogHelper.LogType.Info);
return plugin;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"实例化插件 {pluginType.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件 {Path.GetFileName(pluginPath)} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
return null;
}
/// <summary>
/// 加载插件程序集
/// </summary>
/// <param name="pluginPath">插件文件路径</param>
/// <returns>加载的程序集</returns>
private Assembly LoadPluginAssembly(string pluginPath)
{
try
{
// 检查是否已加载该程序集
if (_loadedAssemblies.TryGetValue(pluginPath, out var loadedAssembly))
{
return loadedAssembly;
}
// 加载程序集
Assembly pluginAssembly = Assembly.LoadFrom(pluginPath);
_loadedAssemblies[pluginPath] = pluginAssembly;
return pluginAssembly;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件程序集 {Path.GetFileName(pluginPath)} 时出错: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
/// <summary>
/// 启用已配置为启用的插件
/// </summary>
private void EnableConfiguredPlugins()
{
int enabledCount = 0;
int disabledCount = 0;
int errorCount = 0;
foreach (var plugin in Plugins)
{
try
{
string pluginTypeName = plugin.GetType().FullName;
// 检查配置中的插件状态
if (PluginStates.TryGetValue(pluginTypeName, out bool enabled))
{
// 获取当前实际状态
bool currentState = plugin is PluginBase pluginBase && pluginBase.IsEnabled;
// 如果配置状态与当前状态不一致,则应用配置状态
if (currentState != enabled)
{
// 注册插件状态变更事件
if (plugin is PluginBase pb)
{
pb.EnabledStateChanged += Plugin_EnabledStateChanged;
}
if (enabled)
{
plugin.Enable();
enabledCount++;
LogHelper.WriteLogToFile($"根据配置启用插件: {plugin.Name}", LogHelper.LogType.Info);
}
else
{
plugin.Disable();
disabledCount++;
LogHelper.WriteLogToFile($"根据配置禁用插件: {plugin.Name}", LogHelper.LogType.Info);
}
}
else
{
// 状态一致,只注册事件
if (plugin is PluginBase pb)
{
pb.EnabledStateChanged += Plugin_EnabledStateChanged;
}
}
}
else
{
// 插件不在配置中,添加默认状态(禁用)
PluginStates[pluginTypeName] = false;
_configDirty = true;
// 注册插件状态变更事件
if (plugin is PluginBase pb)
{
pb.EnabledStateChanged += Plugin_EnabledStateChanged;
}
// 如果当前是启用状态,则禁用
if (plugin is PluginBase pluginBase && pluginBase.IsEnabled)
{
plugin.Disable();
disabledCount++;
LogHelper.WriteLogToFile($"插件不在配置中,默认禁用: {plugin.Name}", LogHelper.LogType.Info);
}
}
}
catch (Exception ex)
{
errorCount++;
LogHelper.WriteLogToFile($"应用插件 {plugin.Name} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
// 如果有配置变更,启动自动保存
if (_configDirty)
{
TriggerAutoSave();
}
LogHelper.WriteLogToFile($"已应用插件配置: 启用 {enabledCount} 个,禁用 {disabledCount} 个,错误 {errorCount} 个", LogHelper.LogType.Info);
}
/// <summary>
/// 插件状态变更事件处理
/// </summary>
private void Plugin_EnabledStateChanged(object sender, bool isEnabled)
{
try
{
if (sender is IPlugin plugin)
{
string pluginTypeName = plugin.GetType().FullName;
// 更新配置状态
if (!PluginStates.ContainsKey(pluginTypeName) || PluginStates[pluginTypeName] != isEnabled)
{
PluginStates[pluginTypeName] = isEnabled;
_configDirty = true;
LogHelper.WriteLogToFile($"插件状态变更: {plugin.Name} = {(isEnabled ? "" : "")}", LogHelper.LogType.Info);
// 立即同步保存配置(不再使用延迟自动保存)
SaveConfig();
LogHelper.WriteLogToFile($"插件 {plugin.Name} 状态已立即保存到配置文件", LogHelper.LogType.Info);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理插件状态变更事件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 触发自动保存计时器
/// </summary>
private void TriggerAutoSave()
{
// 重置并启动计时器
_autoSaveTimer.Stop();
_autoSaveTimer.Start();
}
/// <summary>
/// 启动热重载监视器
/// </summary>
private void StartHotReloadWatcher()
{
// 创建定时检查任务
Task.Run(async () =>
{
while (true)
{
try
{
// 每5秒检查一次
await Task.Delay(5000);
// 获取所有外部插件
var externalPlugins = Plugins.OfType<PluginBase>()
.Where(p => !p.IsBuiltIn && !string.IsNullOrEmpty(p.PluginPath))
.ToList();
foreach (var plugin in externalPlugins)
{
// 检查插件文件是否存在
if (!File.Exists(plugin.PluginPath))
{
continue;
}
// 计算当前文件哈希
string currentHash = CalculateFileHash(plugin.PluginPath);
// 比较哈希值是否变化
if (_pluginHashes.TryGetValue(plugin.PluginPath, out string oldHash) &&
!string.IsNullOrEmpty(oldHash) &&
oldHash != currentHash)
{
// 文件已变化,执行热重载
Application.Current.Dispatcher.Invoke(() =>
{
ReloadPlugin(plugin);
});
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"热重载检查出错: {ex.Message}", LogHelper.LogType.Error);
}
}
});
}
/// <summary>
/// 重新加载插件
/// </summary>
/// <param name="plugin">要重新加载的插件</param>
private void ReloadPlugin(PluginBase plugin)
{
try
{
LogHelper.WriteLogToFile($"开始重载插件: {plugin.Name}", LogHelper.LogType.Info);
// 记录插件状态和信息
bool wasEnabled = plugin.IsEnabled;
string pluginPath = plugin.PluginPath;
string pluginTypeName = plugin.GetType().FullName;
// 记录日志,方便排查问题
LogHelper.WriteLogToFile($"重载前插件状态 - 类型: {pluginTypeName}, 状态: {(wasEnabled ? "" : "")}", LogHelper.LogType.Info);
// 如果配置中有该插件的状态,记录配置中的状态
if (PluginStates.TryGetValue(pluginTypeName, out bool currentConfigState))
{
LogHelper.WriteLogToFile($"配置中插件状态: {(currentConfigState ? "" : "")}", LogHelper.LogType.Info);
}
// 卸载插件,但不从PluginStates中移除状态信息
UnloadPlugin(plugin);
// 清除程序集缓存,确保加载最新版本
_loadedAssemblies.Remove(pluginPath);
// 重新加载插件
IPlugin newPlugin = LoadExternalPlugin(pluginPath);
if (newPlugin != null)
{
// 更新配置中的插件状态
string newPluginTypeName = newPlugin.GetType().FullName;
// 如果插件类型名称变化,需要更新配置
if (newPluginTypeName != pluginTypeName && PluginStates.ContainsKey(pluginTypeName))
{
bool state = PluginStates[pluginTypeName];
PluginStates.Remove(pluginTypeName);
PluginStates[newPluginTypeName] = state;
LogHelper.WriteLogToFile($"插件类型名称已变更: {pluginTypeName} -> {newPluginTypeName}, 已更新配置", LogHelper.LogType.Info);
}
// 应用正确的状态
bool shouldBeEnabled = false;
if (PluginStates.TryGetValue(newPluginTypeName, out bool storedConfigState))
{
shouldBeEnabled = storedConfigState;
LogHelper.WriteLogToFile($"从配置获取插件状态: {(shouldBeEnabled ? "" : "")}", LogHelper.LogType.Info);
}
else
{
shouldBeEnabled = wasEnabled;
PluginStates[newPluginTypeName] = shouldBeEnabled;
LogHelper.WriteLogToFile($"使用之前的状态: {(shouldBeEnabled ? "" : "")}", LogHelper.LogType.Info);
}
// 获取重载后的实际状态
bool currentState = newPlugin is PluginBase pluginBaseState && pluginBaseState.IsEnabled;
LogHelper.WriteLogToFile($"重载后实际状态: {(currentState ? "" : "")}", LogHelper.LogType.Info);
// 根据应该启用的状态启用或禁用插件
if (shouldBeEnabled != currentState)
{
if (shouldBeEnabled)
{
newPlugin.Enable();
LogHelper.WriteLogToFile($"插件 {newPlugin.Name} 已重载并启用", LogHelper.LogType.Info);
}
else
{
newPlugin.Disable();
LogHelper.WriteLogToFile($"插件 {newPlugin.Name} 已重载并禁用", LogHelper.LogType.Info);
}
// 检查状态是否正确应用
currentState = newPlugin is PluginBase reloadedBase && reloadedBase.IsEnabled;
LogHelper.WriteLogToFile($"应用状态后实际状态: {(currentState ? "" : "")}", LogHelper.LogType.Info);
if (currentState != shouldBeEnabled)
{
LogHelper.WriteLogToFile($"警告: 插件状态应用失败,目标状态: {(shouldBeEnabled ? "" : "")}, 实际状态: {(currentState ? "" : "")}", LogHelper.LogType.Warning);
}
}
else
{
LogHelper.WriteLogToFile($"插件 {newPlugin.Name} 已重载并保持{(shouldBeEnabled ? "" : "")}状态", LogHelper.LogType.Info);
}
// 保存插件设置
if (newPlugin is PluginBase pluginBaseInstance)
{
try
{
// 保存插件设置(与启用状态无关)
pluginBaseInstance.SavePluginSettings();
LogHelper.WriteLogToFile($"已保存插件 {newPlugin.Name} 设置", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件 {newPlugin.Name} 设置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
// 立即保存配置
LogHelper.WriteLogToFile($"重载后保存插件配置...", LogHelper.LogType.Info);
SaveConfig();
}
else
{
LogHelper.WriteLogToFile($"插件 {plugin.Name} 重载失败: 无法加载新插件", LogHelper.LogType.Error);
}
// 更新UI
NotifyUIRefresh();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"重新加载插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 卸载插件
/// </summary>
/// <param name="plugin">要卸载的插件</param>
/// <param name="removeFromConfig">是否从配置中移除插件状态(默认为false</param>
public void UnloadPlugin(IPlugin plugin, bool removeFromConfig = false)
{
try
{
// 如果插件已启用,先禁用它
if (plugin is PluginBase pluginBase && pluginBase.IsEnabled)
{
plugin.Disable();
}
// 执行插件清理
plugin.Cleanup();
// 从插件集合中移除
Plugins.Remove(plugin);
// 从配置中移除(如果需要)
if (removeFromConfig && plugin.GetType() != null)
{
string pluginTypeName = plugin.GetType().FullName;
if (PluginStates.ContainsKey(pluginTypeName))
{
PluginStates.Remove(pluginTypeName);
SaveConfig();
}
}
LogHelper.WriteLogToFile($"已卸载插件: {plugin.Name}", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"卸载插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 删除插件
/// </summary>
/// <param name="plugin">要删除的插件</param>
/// <returns>删除是否成功</returns>
public bool DeletePlugin(IPlugin plugin)
{
try
{
// 只能删除外部插件
if (plugin.IsBuiltIn)
{
return false;
}
// 获取插件路径
string pluginPath = null;
if (plugin is PluginBase pluginBase)
{
pluginPath = pluginBase.PluginPath;
}
if (string.IsNullOrEmpty(pluginPath) || !File.Exists(pluginPath))
{
return false;
}
// 卸载插件(并从配置中移除状态)
UnloadPlugin(plugin, true);
// 删除插件文件
File.Delete(pluginPath);
// 清理缓存
_loadedAssemblies.Remove(pluginPath);
_pluginHashes.Remove(pluginPath);
// 保存配置
SaveConfig();
LogHelper.WriteLogToFile($"已删除插件: {plugin.Name}", LogHelper.LogType.Info);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"删除插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 切换插件启用状态
/// </summary>
/// <param name="plugin">目标插件</param>
/// <param name="enable">是否启用</param>
public void TogglePlugin(IPlugin plugin, bool enable)
{
try
{
// 检查当前状态是否已经是目标状态
bool currentState = plugin is PluginBase pluginBase && pluginBase.IsEnabled;
if (currentState == enable)
{
// 已经是目标状态,无需操作
LogHelper.WriteLogToFile($"插件 {plugin.Name} 已经是 {(enable ? "" : "")} 状态,无需切换", LogHelper.LogType.Info);
return;
}
// 记录插件信息,用于日志
string pluginName = plugin.Name;
string pluginTypeName = plugin.GetType().FullName;
LogHelper.WriteLogToFile($"开始切换插件 {pluginName} 状态为: {(enable ? "" : "")}", LogHelper.LogType.Info);
// 首先更新配置状态
PluginStates[pluginTypeName] = enable;
_configDirty = true;
// 更新插件状态
try
{
// 注册事件(无需检查事件是否为null)
if (plugin is PluginBase pb)
{
// 先取消可能已有的订阅,避免重复订阅
pb.EnabledStateChanged -= Plugin_EnabledStateChanged;
// 重新订阅
pb.EnabledStateChanged += Plugin_EnabledStateChanged;
}
// 更新插件状态
if (enable)
{
plugin.Enable();
LogHelper.WriteLogToFile($"插件 {pluginName} 已启用", LogHelper.LogType.Info);
}
else
{
// 禁用前先记录是否为内置插件
bool isBuiltIn = plugin.IsBuiltIn;
LogHelper.WriteLogToFile($"尝试禁用{(isBuiltIn ? "" : "")}插件 {pluginName}", LogHelper.LogType.Info);
// 禁用插件
plugin.Disable();
// 禁用后立即检查状态,确保禁用成功
bool actuallyDisabled = !(plugin is PluginBase pb2 && pb2.IsEnabled);
if (!actuallyDisabled)
{
LogHelper.WriteLogToFile($"警告: 插件 {pluginName} 禁用失败,再次尝试禁用", LogHelper.LogType.Warning);
plugin.Disable(); // 再次尝试禁用
// 再次检查
actuallyDisabled = !(plugin is PluginBase pb3 && pb3.IsEnabled);
if (!actuallyDisabled)
{
LogHelper.WriteLogToFile($"错误: 插件 {pluginName} 禁用失败,强制设置禁用状态", LogHelper.LogType.Error);
// 强制设置状态
if (plugin is PluginBase pb4)
{
// 使用反射强制设置禁用状态
var enabledProperty = typeof(PluginBase).GetProperty("IsEnabled");
if (enabledProperty != null)
{
enabledProperty.SetValue(pb4, false);
LogHelper.WriteLogToFile($"已通过反射强制设置插件 {pluginName} 为禁用状态", LogHelper.LogType.Info);
}
}
}
}
LogHelper.WriteLogToFile($"插件 {pluginName} 已禁用", LogHelper.LogType.Info);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更改插件 {pluginName} 状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 立即保存配置
SaveConfigAsync().ConfigureAwait(false);
// 插件状态切换后,始终进行重载(无论是启用还是禁用)
if (plugin is PluginBase pluginInstance)
{
// 对于内置插件,执行专门的处理
if (pluginInstance.IsBuiltIn)
{
LogHelper.WriteLogToFile($"处理内置插件 {pluginName} 状态变更", LogHelper.LogType.Info);
// 对于内置插件,我们需要确保状态正确应用
bool finalState = pluginInstance.IsEnabled;
bool expectedState = enable;
if (finalState != expectedState)
{
LogHelper.WriteLogToFile($"内置插件状态不匹配: 当前={finalState}, 期望={expectedState},尝试纠正", LogHelper.LogType.Warning);
// 再次尝试设置状态
if (expectedState)
{
plugin.Enable();
}
else
{
plugin.Disable();
// 最后一次检查,如果仍然不匹配,强制设置
if (pluginInstance.IsEnabled != expectedState)
{
var enabledProperty = typeof(PluginBase).GetProperty("IsEnabled");
if (enabledProperty != null)
{
enabledProperty.SetValue(pluginInstance, expectedState);
LogHelper.WriteLogToFile($"已通过反射强制设置内置插件 {pluginName} 状态为 {(expectedState ? "" : "")}", LogHelper.LogType.Info);
}
}
}
}
// 通知UI刷新
NotifyUIRefresh();
}
else
{
// 外部插件,执行热重载
try
{
if (!string.IsNullOrEmpty(pluginInstance.PluginPath) && File.Exists(pluginInstance.PluginPath))
{
LogHelper.WriteLogToFile($"开始重载外部插件 {pluginName}", LogHelper.LogType.Info);
// 使用调度器确保在UI线程执行热重载
if (Application.Current != null && Application.Current.Dispatcher != null)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
ReloadPlugin(pluginInstance);
LogHelper.WriteLogToFile($"插件 {pluginName} 已重载以应用{(enable ? "" : "")}状态", LogHelper.LogType.Info);
}));
}
else
{
// 当前不在UI线程,直接重载
ReloadPlugin(pluginInstance);
LogHelper.WriteLogToFile($"插件 {pluginName} 已重载以应用{(enable ? "" : "")}状态", LogHelper.LogType.Info);
}
}
else
{
LogHelper.WriteLogToFile($"外部插件 {pluginName} 文件不存在,无法重载", LogHelper.LogType.Warning);
NotifyUIRefresh();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"重载插件 {pluginName} 时出错: {ex.Message}", LogHelper.LogType.Error);
// 出错时也要刷新UI
NotifyUIRefresh();
}
}
}
else
{
// 通知UI刷新
NotifyUIRefresh();
}
LogHelper.WriteLogToFile($"插件 {pluginName} 状态切换完成", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用插件实时状态
/// </summary>
/// <param name="plugin">目标插件</param>
/// <param name="enable">是否启用</param>
private void ApplyPluginRealTimeState(IPlugin plugin, bool enable)
{
try
{
// 确保当前实例状态正确
bool currentState = plugin is PluginBase pluginBase && pluginBase.IsEnabled;
if (currentState != enable)
{
if (enable)
{
plugin.Enable();
LogHelper.WriteLogToFile($"实时应用: 已启用插件 {plugin.Name}", LogHelper.LogType.Info);
}
else
{
plugin.Disable();
LogHelper.WriteLogToFile($"实时应用: 已禁用插件 {plugin.Name}", LogHelper.LogType.Info);
}
// 同步状态到插件自身的配置
if (plugin is PluginBase pluginSettings)
{
try
{
// 保存插件设置(与启用状态无关)
pluginSettings.SavePluginSettings();
LogHelper.WriteLogToFile($"实时应用: 已保存插件 {plugin.Name} 设置", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"实时应用: 保存插件 {plugin.Name} 设置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
// 对于外部插件,尝试执行热重载以确保状态立即生效
if (plugin is PluginBase externalPlugin && !externalPlugin.IsBuiltIn)
{
string pluginPath = externalPlugin.PluginPath;
if (!string.IsNullOrEmpty(pluginPath) && File.Exists(pluginPath))
{
// 记录插件类型名称,用于后续状态检查
string pluginTypeName = plugin.GetType().FullName;
bool targetState = enable;
// 使用调度器确保在UI线程执行热重载
if (Application.Current != null && Application.Current.Dispatcher != null)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 热重载前再次确认配置状态正确
if (PluginStates.TryGetValue(pluginTypeName, out bool storedStateUi) && storedStateUi != targetState)
{
LogHelper.WriteLogToFile($"热重载前发现状态不一致,修正配置: {plugin.Name}, 配置={storedStateUi}, 目标={targetState}", LogHelper.LogType.Warning);
PluginStates[pluginTypeName] = targetState;
SaveConfig();
}
// 执行热重载
ReloadPlugin(externalPlugin);
LogHelper.WriteLogToFile($"插件 {plugin.Name} 已成功热重载以应用实时状态", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"热重载插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}));
}
else
{
// 当前不在UI线程,直接重载
// 热重载前再次确认配置状态正确
if (PluginStates.TryGetValue(pluginTypeName, out bool storedStateNonUi) && storedStateNonUi != targetState)
{
LogHelper.WriteLogToFile($"热重载前发现状态不一致,修正配置: {plugin.Name}, 配置={storedStateNonUi}, 目标={targetState}", LogHelper.LogType.Warning);
PluginStates[pluginTypeName] = targetState;
SaveConfig();
}
ReloadPlugin(externalPlugin);
}
}
}
LogHelper.WriteLogToFile($"插件 {plugin.Name} 实时状态已应用: {(enable ? "" : "")}", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用插件实时状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 通知UI刷新
/// </summary>
private void NotifyUIRefresh()
{
try
{
// 通知UI刷新
if (Application.Current != null && Application.Current.Dispatcher != null)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() => {
// 通知任何可能打开的插件设置窗口刷新
foreach (Window window in Application.Current.Windows)
{
if (window is Windows.PluginSettingsWindow pluginWindow)
{
pluginWindow.RefreshPluginList();
break;
}
}
}));
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"通知UI刷新时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载插件配置
/// </summary>
private void LoadConfig()
{
const int maxRetries = 3; // 最大重试次数
const int retryDelayMs = 300; // 重试延迟时间(毫秒)
LogHelper.WriteLogToFile($"开始从配置文件加载插件状态: {PluginConfigFile}", LogHelper.LogType.Info);
// 确保至少有一个默认配置
Dictionary<string, bool> defaultConfig = new Dictionary<string, bool>();
// 尝试获取配置锁
_configLock.Wait();
try
{
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
if (File.Exists(PluginConfigFile))
{
string json;
// 使用共享读取模式,允许其他进程同时读取但不允许写入
using (FileStream fs = new FileStream(PluginConfigFile, FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader reader = new StreamReader(fs))
{
json = reader.ReadToEnd();
}
var loadedStates = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, bool>>(json);
if (loadedStates != null && loadedStates.Count > 0)
{
PluginStates = loadedStates;
_configDirty = false; // 重置脏标记
LogHelper.WriteLogToFile($"成功从配置文件加载了 {PluginStates.Count} 个插件状态", LogHelper.LogType.Info);
return; // 成功加载,提前退出
}
else
{
LogHelper.WriteLogToFile("配置文件解析为空,尝试使用备份", LogHelper.LogType.Warning);
// 尝试加载备份
if (File.Exists(PluginConfigBackupFile))
{
try
{
string backupJson = File.ReadAllText(PluginConfigBackupFile);
var backupStates = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, bool>>(backupJson);
if (backupStates != null && backupStates.Count > 0)
{
PluginStates = backupStates;
_configDirty = true; // 从备份加载,需要重新保存主配置
LogHelper.WriteLogToFile($"已从备份恢复 {PluginStates.Count} 个插件状态", LogHelper.LogType.Info);
return; // 成功从备份加载,提前退出
}
}
catch (Exception backupEx)
{
LogHelper.WriteLogToFile($"从备份恢复配置失败: {backupEx.Message}", LogHelper.LogType.Error);
}
}
// 备份也失败,使用默认配置
PluginStates = defaultConfig;
_configDirty = true;
}
}
else
{
LogHelper.WriteLogToFile($"配置文件不存在,尝试使用备份: {PluginConfigFile}", LogHelper.LogType.Warning);
// 尝试加载备份
if (File.Exists(PluginConfigBackupFile))
{
try
{
string backupJson = File.ReadAllText(PluginConfigBackupFile);
var backupStates = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, bool>>(backupJson);
if (backupStates != null && backupStates.Count > 0)
{
PluginStates = backupStates;
_configDirty = true; // 从备份加载,需要重新保存主配置
LogHelper.WriteLogToFile($"已从备份恢复 {PluginStates.Count} 个插件状态", LogHelper.LogType.Info);
return; // 成功从备份加载,提前退出
}
}
catch (Exception backupEx)
{
LogHelper.WriteLogToFile($"从备份恢复配置失败: {backupEx.Message}", LogHelper.LogType.Error);
}
}
PluginStates = defaultConfig;
_configDirty = true;
LogHelper.WriteLogToFile("使用默认空配置", LogHelper.LogType.Warning);
}
// 没有成功加载或使用备份,使用默认配置
break;
}
catch (Exception ex)
{
if (attempt < maxRetries)
{
LogHelper.WriteLogToFile($"加载配置失败 (尝试 {attempt}/{maxRetries}): {ex.Message},将在 {retryDelayMs}ms 后重试", LogHelper.LogType.Warning);
System.Threading.Thread.Sleep(retryDelayMs);
}
else
{
LogHelper.WriteLogToFile($"加载插件配置失败,已达最大重试次数 ({maxRetries}): {ex.Message}", LogHelper.LogType.Error);
// 最终失败,使用默认配置
PluginStates = defaultConfig;
_configDirty = true;
}
}
}
}
finally
{
// 释放配置锁
_configLock.Release();
}
}
/// <summary>
/// 异步保存插件配置
/// </summary>
public async Task SaveConfigAsync()
{
// 如果配置没有变化,无需保存
if (!_configDirty)
{
return;
}
// 尝试获取配置锁(异步)
if (!await _configLock.WaitAsync(0))
{
// 已有保存操作在进行中,触发自动保存延迟
TriggerAutoSave();
return;
}
try
{
// 创建配置任务
await Task.Run(() => SaveConfig());
}
finally
{
// 释放配置锁
_configLock.Release();
}
}
/// <summary>
/// 保存插件配置
/// </summary>
public void SaveConfig()
{
// 如果配置没有变化,无需保存
if (!_configDirty)
{
return;
}
const int maxRetries = 3; // 最大重试次数
const int retryDelayMs = 500; // 重试延迟时间(毫秒)
try
{
LogHelper.WriteLogToFile($"开始保存插件配置到: {PluginConfigFile}", LogHelper.LogType.Info);
// 生成JSON数据
string json = Newtonsoft.Json.JsonConvert.SerializeObject(PluginStates, Newtonsoft.Json.Formatting.Indented);
string tempFile = PluginConfigFile + ".temp"; // 临时文件路径
// 确保目录存在
string configDir = Path.GetDirectoryName(PluginConfigFile);
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
LogHelper.WriteLogToFile($"创建配置目录: {configDir}", LogHelper.LogType.Info);
}
// 先备份当前配置
try
{
if (File.Exists(PluginConfigFile))
{
File.Copy(PluginConfigFile, PluginConfigBackupFile, true);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"备份配置文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
// 直接写入目标文件
File.WriteAllText(PluginConfigFile, json);
// 验证写入是否成功
if (File.Exists(PluginConfigFile))
{
// 重置脏标记
_configDirty = false;
LogHelper.WriteLogToFile($"插件配置已成功保存到磁盘: {PluginConfigFile}, 共 {PluginStates.Count} 个插件状态", LogHelper.LogType.Info);
return;
}
}
catch (Exception ex)
{
if (attempt < maxRetries)
{
LogHelper.WriteLogToFile($"保存配置失败 (尝试 {attempt}/{maxRetries}): {ex.Message},将在 {retryDelayMs}ms 后重试", LogHelper.LogType.Warning);
System.Threading.Thread.Sleep(retryDelayMs);
}
else
{
LogHelper.WriteLogToFile($"保存插件配置失败,已达最大重试次数 ({maxRetries}): {ex.Message}", LogHelper.LogType.Error);
// 尝试使用临时文件方式
try
{
// 删除可能存在的旧临时文件
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
// 写入临时文件
File.WriteAllText(tempFile, json);
// 如果目标文件存在,先删除
if (File.Exists(PluginConfigFile))
{
File.Delete(PluginConfigFile);
}
// 重命名临时文件
File.Move(tempFile, PluginConfigFile);
// 重置脏标记
_configDirty = false;
LogHelper.WriteLogToFile($"使用临时文件方式成功保存配置: {PluginConfigFile}", LogHelper.LogType.Info);
return;
}
catch (Exception fallbackEx)
{
LogHelper.WriteLogToFile($"临时文件保存方式也失败: {fallbackEx.Message}", LogHelper.LogType.Error);
}
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件配置时发生未处理异常: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 计算文件哈希
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>文件哈希值</returns>
private string CalculateFileHash(string filePath)
{
try
{
using (var md5 = MD5.Create())
using (var stream = File.OpenRead(filePath))
{
byte[] hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"计算文件哈希值时出错: {ex.Message}", LogHelper.LogType.Error);
return string.Empty;
}
}
/// <summary>
/// 从配置文件重新加载所有插件状态并应用
/// </summary>
public void ReloadPluginsFromConfig()
{
try
{
LogHelper.WriteLogToFile("开始从配置文件重新加载插件状态", LogHelper.LogType.Info);
// 保存当前配置状态,以便在加载失败时回滚
Dictionary<string, bool> previousStates = new Dictionary<string, bool>(PluginStates);
// 重新加载配置文件
LoadConfig();
// 如果配置文件加载失败,PluginStates可能为空,这时使用之前的状态
if (PluginStates == null || PluginStates.Count == 0)
{
LogHelper.WriteLogToFile("加载的配置为空,恢复到之前的状态", LogHelper.LogType.Warning);
PluginStates = previousStates;
return;
}
LogHelper.WriteLogToFile($"已加载 {PluginStates.Count} 个插件状态,开始应用...", LogHelper.LogType.Info);
// 对比配置,查找变更的插件
foreach (var plugin in Plugins.ToList()) // 创建副本进行遍历,避免集合修改异常
{
string pluginTypeName = plugin.GetType().FullName;
// 检查插件在配置中是否存在
if (PluginStates.TryGetValue(pluginTypeName, out bool shouldBeEnabled))
{
bool currentlyEnabled = plugin is PluginBase pluginBase && pluginBase.IsEnabled;
// 如果状态需要变更
if (currentlyEnabled != shouldBeEnabled)
{
LogHelper.WriteLogToFile($"应用插件 {plugin.Name} 的配置状态: {(shouldBeEnabled ? "" : "")}", LogHelper.LogType.Info);
if (shouldBeEnabled)
{
try
{
plugin.Enable();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启用插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
else
{
try
{
// 记录禁用信息,特别是内置插件
bool isBuiltIn = plugin.IsBuiltIn;
LogHelper.WriteLogToFile($"尝试禁用{(isBuiltIn ? "" : "")}插件 {plugin.Name}", LogHelper.LogType.Info);
// 禁用插件
plugin.Disable();
// 对于内置插件,特别检查禁用状态
if (isBuiltIn && plugin is PluginBase builtInPluginBase)
{
if (builtInPluginBase.IsEnabled)
{
LogHelper.WriteLogToFile($"内置插件 {plugin.Name} 禁用失败,尝试强制禁用", LogHelper.LogType.Warning);
// 强制设置禁用状态
var enabledProperty = typeof(PluginBase).GetProperty("IsEnabled");
if (enabledProperty != null)
{
enabledProperty.SetValue(builtInPluginBase, false);
LogHelper.WriteLogToFile($"已通过反射强制禁用内置插件 {plugin.Name}", LogHelper.LogType.Info);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
// 如果是外部插件,执行重载
if (!plugin.IsBuiltIn && plugin is PluginBase externalPlugin && !string.IsNullOrEmpty(externalPlugin.PluginPath))
{
try
{
ReloadPlugin(externalPlugin);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"重载外部插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
else
{
// 插件不在配置中,将其添加为禁用状态
PluginStates[pluginTypeName] = false;
LogHelper.WriteLogToFile($"插件 {plugin.Name} 不在配置中,默认设置为禁用状态", LogHelper.LogType.Info);
// 如果当前是启用状态,则禁用它
if (plugin is PluginBase pluginBase && pluginBase.IsEnabled)
{
try
{
bool isBuiltIn = plugin.IsBuiltIn;
LogHelper.WriteLogToFile($"尝试禁用未配置的{(isBuiltIn ? "" : "")}插件 {plugin.Name}", LogHelper.LogType.Info);
plugin.Disable();
// 对于内置插件,特别检查禁用状态
if (isBuiltIn && pluginBase.IsEnabled)
{
LogHelper.WriteLogToFile($"未配置的内置插件 {plugin.Name} 禁用失败,尝试强制禁用", LogHelper.LogType.Warning);
// 强制设置禁用状态
var enabledProperty = typeof(PluginBase).GetProperty("IsEnabled");
if (enabledProperty != null)
{
enabledProperty.SetValue(pluginBase, false);
LogHelper.WriteLogToFile($"已通过反射强制禁用未配置的内置插件 {plugin.Name}", LogHelper.LogType.Info);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用未配置插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
// 保存更新后的配置
SaveConfig();
// 通知UI更新
if (Application.Current != null && Application.Current.Dispatcher != null)
{
Application.Current.Dispatcher.Invoke(() => {
// 通知任何可能打开的插件设置窗口刷新
foreach (Window window in Application.Current.Windows)
{
if (window is Windows.PluginSettingsWindow pluginWindow)
{
pluginWindow.RefreshPluginList();
}
}
});
}
LogHelper.WriteLogToFile("插件状态已从配置文件重新加载完成", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从配置文件重新加载插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}