620 lines
23 KiB
C#
620 lines
23 KiB
C#
using Api.Framework.Events;
|
|
using Api.Framework.SDK;
|
|
using Api.Framework.Tools;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
using static System.Net.Mime.MediaTypeNames;
|
|
using System.Windows.Forms;
|
|
namespace Api.Framework
|
|
{
|
|
/// <summary>
|
|
/// 插件管理
|
|
/// </summary>
|
|
public class PluginClient
|
|
{
|
|
/// <summary>
|
|
/// 插件集合
|
|
/// </summary>
|
|
public static List<Plugin> Plugins { get; private set; }
|
|
/// <summary>
|
|
/// 插件AES加密
|
|
/// </summary>
|
|
private static byte[] Iv, Key;
|
|
|
|
static PluginClient()
|
|
{
|
|
Plugins = new List<Plugin>();
|
|
Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 };
|
|
Iv = new byte[] { 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 };
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取插件新版本
|
|
/// </summary>
|
|
/// <param name="plugin">需要获取的插件</param>
|
|
public static List<PluginVersion> FindVersion(Plugin plugin)
|
|
{
|
|
if (string.IsNullOrEmpty(plugin.VersionXML)) return null;
|
|
List<PluginVersion> versions = new List<PluginVersion>();
|
|
|
|
Util util = new Util();
|
|
var doc = new XmlDocument();
|
|
doc.Load(plugin.VersionXML);
|
|
var nodes = doc.SelectNodes("Version");
|
|
foreach (XmlNode item in nodes)
|
|
{
|
|
var v = new PluginVersion()
|
|
{
|
|
CurMessage = item["CurMessage"].ToString(),
|
|
CurVersion = Version.Parse(item["CurVersion"].ToString()),
|
|
SoftVersion = Version.Parse(item["SoftVersion"].ToString()),
|
|
DownURL = item["DownURL"].ToString()
|
|
};
|
|
versions.Add(v);
|
|
}
|
|
versions.OrderByDescending(p => p.CurVersion);
|
|
return versions;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将指定文件的转换成插件,并保存
|
|
/// </summary>
|
|
/// <param name="filePath">文件路径</param>
|
|
/// <returns></returns>
|
|
public static Plugin FileConvertPlugin(string filePath)
|
|
{
|
|
if (!File.Exists(filePath)) throw new Exception("文件不存在,请查证。");
|
|
if (filePath.EndsWith(".dll", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
Util util = new Util();
|
|
string fileName = Path.GetFileName(filePath);
|
|
string path = filePath.Substring(0, filePath.Length - 4);
|
|
byte[] inputBuffer = util.ReadFile(filePath);// File.ReadAllBytes(filePath);
|
|
Assembly al = ConvertAssembly(inputBuffer);
|
|
Plugin p = null;
|
|
if (al != null)
|
|
{
|
|
p = ConvertPlugin(al);
|
|
if (p == null)
|
|
throw new Exception("该文件不能不符合插件接口标准!");
|
|
// string filep = Path.Combine(Util.MapPath("Plugin"), fileName.Substring(0, fileName.Length - 4) + ".plugin");
|
|
string savePath = path + ".plugin";
|
|
byte[] bytes = Aes.Create().CreateEncryptor(Key, Iv).TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
|
|
File.WriteAllBytes(savePath, bytes);
|
|
p.CurVersion = al.GetName().Version;
|
|
p.FilePath = savePath;
|
|
return p;
|
|
}
|
|
else
|
|
throw new Exception("该文件不能不符合插件接口标准。");
|
|
}
|
|
else if (filePath.EndsWith(".plugin", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
var plugin = GetPlugin(filePath);
|
|
if (plugin == null) throw new Exception("该文件不能不符合插件接口标准。");
|
|
return plugin;
|
|
}
|
|
else
|
|
throw new Exception("该文件不能不符合插件接口标准。");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 加载插件
|
|
/// </summary>
|
|
/// <param name="reload">重新加载</param>
|
|
public static void LodingPlugin(bool reload = false)
|
|
{
|
|
EventClient.StopParsing = true;
|
|
ApiClient.GetSession().Context.Utilities.RemoveCacheAll();
|
|
Task.Factory.StartNew(delegate
|
|
{
|
|
try
|
|
{
|
|
if (reload)
|
|
{
|
|
ApiClient.Cache.Cleaner();
|
|
lock (Plugins)
|
|
{
|
|
foreach (var item in Plugins)
|
|
{
|
|
try
|
|
{
|
|
item.Stop();
|
|
item.Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EventClient.OnEvent(item, $"停止失败{item.Name}:{ex.Message}");
|
|
}
|
|
}
|
|
|
|
Plugins.Clear();
|
|
}
|
|
}
|
|
string[] plugin_files = Directory.GetFiles(Util.MapPath("Plugin", true), "*.plugin", SearchOption.TopDirectoryOnly);
|
|
string[] dll_files = Directory.GetFiles(Util.MapPath("Plugin", true), "*.dll", SearchOption.TopDirectoryOnly);
|
|
List<string> pluginFiles = new List<string>();
|
|
pluginFiles.AddRange(plugin_files);
|
|
pluginFiles.AddRange(dll_files);
|
|
lock (Plugins)
|
|
{
|
|
foreach (var item in pluginFiles)
|
|
{
|
|
try
|
|
{
|
|
var p = GetPlugin(item);
|
|
if (p == null)
|
|
continue;
|
|
else
|
|
{
|
|
#region 显示的插件,不显示的continue了
|
|
if (ApiClient.ShowPluginList != null)
|
|
{
|
|
if (!p.FilePath.ToLower().Contains("\\main.dll"))
|
|
{
|
|
var flag = ApiClient.ShowPluginList.FirstOrDefault(f => p.FilePath.Contains(f));
|
|
if (string.IsNullOrWhiteSpace(flag))
|
|
{
|
|
p?.Stop();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
|
|
Plugin oldPlugin = Plugins.FirstOrDefault(f => f.GetType().ToString() == p.GetType().ToString());
|
|
if (oldPlugin != null)//发现旧插件
|
|
{
|
|
if (oldPlugin.FilePath != item)//可是路径不相同
|
|
{
|
|
EventClient.OnEvent("插件系统", new Events.LogEvents("文件:" + oldPlugin.FilePath + "与" + p.FilePath + "冲突!") { });
|
|
}
|
|
else
|
|
{
|
|
if (oldPlugin.Md5 != p.Md5)
|
|
{
|
|
Plugins.Remove(oldPlugin);
|
|
Plugins.Add(p);
|
|
bool isStart = (oldPlugin.IsRun ? true : false);
|
|
|
|
if (isStart)
|
|
{
|
|
oldPlugin.Stop();
|
|
oldPlugin.Dispose();
|
|
p.Start();
|
|
p.IsRun = true;
|
|
}
|
|
}
|
|
//else
|
|
//{
|
|
// if (!p.IsNet)
|
|
// {
|
|
// DynnamicDLL dll = p as DynnamicDLL;
|
|
// if (dll != null)
|
|
// {
|
|
|
|
// dll.Dispose();
|
|
// }
|
|
// }
|
|
//}
|
|
// p.Level = oldPlugin.Level;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p.Level = int.MaxValue;
|
|
Plugins.Add(p);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
EventClient.OnEvent(null, new Events.LogEvents("处理文件:" + item + ",发生异常:" + e.Message) { Exception = e });
|
|
}
|
|
}
|
|
|
|
var _plugins = ApiClient.Setting.Plugins;
|
|
for (int i = 1; i <= _plugins.Count; i++)
|
|
{
|
|
Plugin plugin = Plugins.FirstOrDefault(f => f.GetType().ToString() == _plugins[i - 1].ToString());
|
|
if (plugin != null)
|
|
{
|
|
try
|
|
{
|
|
plugin.Level = i;
|
|
if (!plugin.IsRun)
|
|
{
|
|
plugin.Level = i;
|
|
plugin.Start();
|
|
plugin.IsRun = true;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
EventClient.OnEvent(null, new Events.LogEvents(string.Format("启动“{0}”插件失败:{1}", plugin.Name, e.Message)) { Exception = e });
|
|
plugin.IsRun = false;
|
|
}
|
|
}
|
|
}
|
|
Plugins.Sort();
|
|
}
|
|
EventClient.OnEvent(null, MethodType.刷新应用);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//Console.WriteLine();
|
|
}
|
|
finally
|
|
{
|
|
EventClient.StopParsing = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新插件
|
|
/// </summary>
|
|
/// <param name="version">版本</param>
|
|
/// <returns></returns>
|
|
public static bool UpdatePlugin(PluginVersion version)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 添加插件
|
|
/// </summary>
|
|
/// <param name="filePath">插件路径</param>
|
|
/// <returns></returns>
|
|
public static Plugin AddPlugin(string filePath)
|
|
{
|
|
try
|
|
{
|
|
Plugin p = FileConvertPlugin(filePath);
|
|
Plugin o = Plugins.FirstOrDefault(f => f.GetType().ToString() == p.GetType().ToString());
|
|
if (o != null)
|
|
{
|
|
lock (Plugins)
|
|
{
|
|
try
|
|
{
|
|
if (o.IsRun)
|
|
{
|
|
o.Stop();
|
|
// o.UnInstall();
|
|
o.Dispose();
|
|
}
|
|
p.Level = o.Level;
|
|
Plugins.Remove(o);
|
|
Plugins.Add(p);
|
|
if (o.IsRun)
|
|
{
|
|
// p.Install();
|
|
p.Start();
|
|
p.IsRun = true;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception(string.Format("更新插件“{0}”出错:{1}", p.FilePath, e.Message));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lock (Plugins)
|
|
{
|
|
Plugins.Add(p);
|
|
}
|
|
EventClient.OnEvent(null, MethodType.刷新应用);
|
|
return p;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 插件安装
|
|
/// </summary>
|
|
/// <param name="plugin">要安装的插件对象</param>
|
|
public static void Install(Plugin plugin)
|
|
{
|
|
string type = plugin.GetType().ToString();
|
|
plugin.Start();
|
|
plugin.IsRun = true;
|
|
int level = ApiClient.Setting.Plugins.IndexOf(type);
|
|
plugin.Level = (level == -1 ? ApiClient.Setting.Plugins.Count : level);
|
|
|
|
if (!ApiClient.Setting.Plugins.Contains(plugin.GetType().ToString()))
|
|
ApiClient.Setting.Plugins.Add(type);
|
|
SavePlugin();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 插件卸载
|
|
/// </summary>
|
|
/// <param name="plugin">要卸载的插件对象</param>
|
|
public static void UnInstall(Plugin plugin)
|
|
{
|
|
if (plugin.IsRun)
|
|
{
|
|
try
|
|
{
|
|
plugin.IsRun = false;
|
|
plugin.Level = 1000;
|
|
plugin.Stop();
|
|
plugin.Dispose();
|
|
plugin._SDK = null;
|
|
}
|
|
catch { }
|
|
string type = plugin.GetType().ToString();
|
|
if (ApiClient.Setting.Plugins.Contains(type))
|
|
{
|
|
ApiClient.Setting.Plugins.Remove(type);
|
|
SavePlugin();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 删除插件
|
|
/// </summary>
|
|
/// <param name="plugin">要删除的插件对象</param>
|
|
public static void Delete(Plugin plugin)
|
|
{
|
|
try
|
|
{
|
|
string type = plugin.GetType().ToString();
|
|
lock (Plugins)
|
|
{
|
|
UnInstall(plugin);
|
|
if (Plugins.Contains(plugin))
|
|
{
|
|
Plugins.Remove(plugin);
|
|
}
|
|
plugin.Dispose();
|
|
if (ApiClient.Setting.Plugins.Contains(type))
|
|
{
|
|
ApiClient.Setting.Plugins.Remove(type);
|
|
SavePlugin();
|
|
}
|
|
if (File.Exists(plugin.FilePath))
|
|
File.Delete(plugin.FilePath);
|
|
|
|
}
|
|
EventClient.OnEvent(null, MethodType.刷新应用);
|
|
}
|
|
catch (Exception)
|
|
{ }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存修通配置
|
|
/// </summary>
|
|
private static void SavePlugin()
|
|
{
|
|
Util.Save(ApiClient.Setting.Plugins, "系统.插件配置");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 调换插件顺序
|
|
/// </summary>
|
|
/// <param name="plugin1">被调换的插件下标</param>
|
|
/// <param name="plugin2">需要调换的插件下标</param>
|
|
public static void MovePlugin(int plugin1, int plugin2)
|
|
{
|
|
try
|
|
{
|
|
var plugin = Plugins[plugin1];
|
|
|
|
Plugins.Remove(plugin);
|
|
if (plugin2 > Plugins.Count)
|
|
Plugins.Insert(Plugins.Count, plugin);
|
|
else if (plugin2 < 0)
|
|
Plugins.Insert(0, plugin);
|
|
else
|
|
Plugins.Insert(plugin2, plugin);
|
|
|
|
ApiClient.Setting.Plugins.Clear();
|
|
int i = 0;
|
|
foreach (var item in Plugins)
|
|
{
|
|
i++;
|
|
if (item.IsRun)
|
|
ApiClient.Setting.Plugins.Add(item.GetType().ToString());
|
|
}
|
|
SavePlugin();
|
|
}
|
|
catch (Exception)
|
|
{ }
|
|
}
|
|
public static string MovePlugin(Plugin plugin1, Plugin plugin2)
|
|
{
|
|
try
|
|
{
|
|
if (plugin1 == plugin2)
|
|
return "插件不能相同!";
|
|
if (!Plugins.Contains(plugin1) || !Plugins.Contains(plugin2))
|
|
return "插件不存在!";
|
|
int level = plugin1.Level;
|
|
plugin1.Level = plugin2.Level;
|
|
plugin2.Level = level;
|
|
|
|
int p1 = -1;
|
|
int p2 = -1;
|
|
for (int i = 0; i < Plugins.Count; i++)
|
|
{
|
|
if (Plugins[i] == plugin1)
|
|
p1 = i;
|
|
else if (Plugins[i] == plugin2)
|
|
p2 = i;
|
|
if (p1 != -1 && p2 != -1)
|
|
break;
|
|
}
|
|
if (p1 != -1 && p2 != -1)
|
|
{
|
|
var temp = Plugins[p1];
|
|
Plugins[p1] = Plugins[p2];
|
|
Plugins[p2] = temp;
|
|
Plugins.Sort();
|
|
|
|
ApiClient.Setting.Plugins.Clear();
|
|
foreach (Plugin item in Plugins)
|
|
{
|
|
if (item.IsRun)
|
|
ApiClient.Setting.Plugins.Add(item.GetType().ToString());
|
|
}
|
|
SavePlugin();
|
|
return null;
|
|
}
|
|
else
|
|
return "没有找到元素位置!";
|
|
}
|
|
catch (Exception)
|
|
{ }
|
|
return null;
|
|
}
|
|
|
|
#region 私有函数
|
|
/// <summary>
|
|
/// 寻找一个程序集
|
|
/// </summary>
|
|
/// <param name="path">文件路径</param>
|
|
/// <returns></returns>
|
|
private static Assembly FindAssembly(string path)
|
|
{
|
|
Util util = new Util();
|
|
Assembly assembly = null;
|
|
try
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path) || !System.IO.File.Exists(path))
|
|
{
|
|
return assembly;
|
|
}
|
|
|
|
if (path.EndsWith(".plugin", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
byte[] inputBuffer = util.ReadFile(path);
|
|
byte[] mingwen = Aes.Create().CreateDecryptor(Key, Iv).TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
|
|
assembly = Assembly.Load(mingwen);
|
|
}
|
|
else if (path.EndsWith(".dll", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
assembly = Assembly.Load(util.ReadFile(path));
|
|
}
|
|
}
|
|
catch { }
|
|
return assembly;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通过文件路径获取插件对象
|
|
/// </summary>
|
|
/// <param name="filePath">文件路径</param>
|
|
/// <returns></returns>
|
|
private static Plugin GetPlugin(string filePath)
|
|
{
|
|
try
|
|
{
|
|
var al = FindAssembly(filePath);
|
|
if (al != null)
|
|
{
|
|
var plugin = ConvertPlugin(al);
|
|
if (plugin != null)
|
|
{
|
|
plugin.FilePath = filePath;
|
|
plugin.Md5 = (Util.GetFileMd5Code(filePath));
|
|
return plugin;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//try
|
|
//{
|
|
// DynnamicDLL dll = ConvertDynnamicDLL(fileName);
|
|
// if (dll == null)
|
|
// {
|
|
// throw new Exception("无法识别动态连DLL!");
|
|
// }
|
|
// if (dll.CheckDLL())
|
|
// {
|
|
// return dll;
|
|
// }
|
|
// else
|
|
// {
|
|
// throw new Exception("无法正确识别插件信息!");
|
|
// }
|
|
|
|
//}
|
|
//catch (Exception ex)
|
|
//{
|
|
// Client.ClientPro.OnLog("插件系统","转换插件异常:" + ex.Message + "," + fileName);
|
|
//}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{ }
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将字节集转换成 程序集
|
|
/// </summary>
|
|
/// <param name="body"></param>
|
|
/// <returns></returns>
|
|
private static Assembly ConvertAssembly(byte[] body)
|
|
{
|
|
try
|
|
{
|
|
return Assembly.Load(body);
|
|
}
|
|
catch (Exception)
|
|
{ }
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将程序集转换成Plugin
|
|
/// </summary>
|
|
/// <param name="assembly"></param>
|
|
/// <returns></returns>
|
|
private static Plugin ConvertPlugin(Assembly assembly)
|
|
{
|
|
try
|
|
{
|
|
if (assembly != null)
|
|
{
|
|
foreach (System.Type type in assembly.GetTypes())
|
|
{
|
|
if (type.IsSubclassOf(typeof(Plugin)))
|
|
{
|
|
Plugin plugin = (Activator.CreateInstance(type) as Plugin);
|
|
plugin.CurVersion = assembly.GetName().Version;
|
|
return plugin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{ }
|
|
return null;
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
}
|