add:插件

屎山
This commit is contained in:
PrefacedCorg
2026-04-10 01:24:57 +08:00
parent 256b3c0887
commit 84b626d344
17 changed files with 842 additions and 1 deletions
+37
View File
@@ -1,5 +1,6 @@
using H.NotifyIcon;
using Ink_Canvas.Helpers;
using Ink_Canvas.Plugins;
using Ink_Canvas.Properties;
using iNKORE.UI.WPF.Modern.Controls;
using Microsoft.Win32;
@@ -1075,6 +1076,18 @@ namespace Ink_Canvas
var mainWindow = new MainWindow();
MainWindow = mainWindow;
// 注册 InkCanvas 服务供插件使用
try
{
var inkCanvasService = new Plugins.InkCanvasService(mainWindow);
Plugins.PluginManager.Instance.RegisterService<Plugins.IInkCanvasService>(inkCanvasService);
LogHelper.WriteLogToFile("InkCanvasService registered for plugins");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Failed to register InkCanvasService: {ex.Message}", LogHelper.LogType.Error);
}
// 主窗口加载完成后关闭启动画面
mainWindow.Loaded += (s, args) =>
{
@@ -1161,6 +1174,18 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"初始化上传帮助类时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 加载插件
try
{
LogHelper.WriteLogToFile("开始加载插件");
await PluginManager.Instance.LoadAllAsync();
LogHelper.WriteLogToFile(string.Format("插件加载完成,共加载 {0} 个插件", PluginManager.Instance.Plugins.Count));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile(string.Format("加载插件时出错: {0}", ex.Message), LogHelper.LogType.Error);
}
}
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
@@ -1437,6 +1462,18 @@ namespace Ink_Canvas
private void App_Exit(object sender, ExitEventArgs e)
{
// 卸载所有插件
try
{
LogHelper.WriteLogToFile("正在卸载插件...");
PluginManager.Instance.UnloadAll();
LogHelper.WriteLogToFile("插件卸载完成");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"卸载插件时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 仅在软件内主动退出时关闭看门狗,并写入退出信号
try
{
+5
View File
@@ -141,7 +141,12 @@
<PackageReference Include="AForge.Video.DirectShow" Version="2.2.5" />
<PackageReference Include="AForge.Imaging" Version="2.2.5" />
<PackageReference Include="AForge.Math" Version="2.2.5" />
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
<PackageReference Include="WebDav.Client" Version="2.9.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\InkCanvas.PluginSdk\InkCanvas.PluginSdk.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(MSBuildRuntimeType)' == 'Full'">
<COMReference Include="IWshRuntimeLibrary">
+62
View File
@@ -0,0 +1,62 @@
using Ink_Canvas.Plugins;
using System;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace Ink_Canvas.Plugins
{
public class InkCanvasService : IInkCanvasService
{
private readonly MainWindow _mainWindow;
public InkCanvasService(MainWindow mainWindow)
{
_mainWindow = mainWindow;
}
public void OpenWhiteboard()
{
if (_mainWindow != null)
{
_mainWindow.Dispatcher.Invoke(() =>
{
try
{
_mainWindow.UnFoldFloatingBar_MouseUp(null, null);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error opening whiteboard: {ex.Message}");
}
});
}
}
public void CloseWhiteboard()
{
if (_mainWindow != null)
{
_mainWindow.Dispatcher.Invoke(() =>
{
try
{
_mainWindow.FoldFloatingBar_MouseUp(null, null);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error closing whiteboard: {ex.Message}");
}
});
}
}
public async Task OpenWhiteboardAsync(int delayMilliseconds = 0)
{
if (delayMilliseconds > 0)
{
await Task.Delay(delayMilliseconds);
}
OpenWhiteboard();
}
}
}
+287
View File
@@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
namespace Ink_Canvas.Plugins
{
public class PluginManager : IPluginHost
{
private static PluginManager _instance;
public static PluginManager Instance
{
get
{
if (_instance == null)
{
_instance = new PluginManager();
}
return _instance;
}
}
private readonly Dictionary<Type, object> _services = new Dictionary<Type, object>();
private readonly string _pluginsDirectory;
private readonly List<PluginInfo> _plugins = new List<PluginInfo>();
private readonly Dictionary<string, AssemblyLoadContext> _assemblyContexts = new Dictionary<string, AssemblyLoadContext>();
public IReadOnlyList<PluginInfo> Plugins
{
get { return _plugins.AsReadOnly(); }
}
public event EventHandler<PluginInfo> PluginLoaded;
public event EventHandler<PluginInfo> PluginUnloaded;
public event EventHandler<string> LogMessage;
private PluginManager()
{
_pluginsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
if (!Directory.Exists(_pluginsDirectory))
{
Directory.CreateDirectory(_pluginsDirectory);
}
}
public async Task LoadAllAsync()
{
try
{
if (!Directory.Exists(_pluginsDirectory))
{
Log("Plugins directory does not exist, skipping plugin loading");
return;
}
Log(string.Format("Loading plugins from: {0}", _pluginsDirectory));
var pluginFiles = new List<string>();
try
{
var topLevelFiles = Directory.GetFiles(_pluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly);
pluginFiles.AddRange(topLevelFiles);
Log(string.Format("Found {0} top-level plugin files", topLevelFiles.Length));
}
catch (Exception ex)
{
LogError(string.Format("Error getting top-level plugin files: {0}", ex.Message));
}
try
{
var subDirectories = Directory.GetDirectories(_pluginsDirectory);
foreach (var subDir in subDirectories)
{
try
{
var subDirFiles = Directory.GetFiles(subDir, "*.dll", SearchOption.TopDirectoryOnly);
pluginFiles.AddRange(subDirFiles);
Log(string.Format("Found {0} plugin files in directory: {1}", subDirFiles.Length, Path.GetFileName(subDir)));
}
catch (Exception ex)
{
LogError(string.Format("Error scanning subdirectory {0}: {1}", Path.GetFileName(subDir), ex.Message));
}
}
}
catch (Exception ex)
{
LogError(string.Format("Error getting subdirectories: {0}", ex.Message));
}
Log(string.Format("Found total {0} potential plugin files", pluginFiles.Count));
foreach (var pluginFile in pluginFiles)
{
try
{
await LoadPluginAsync(pluginFile);
}
catch (Exception ex)
{
LogError(string.Format("Failed to load plugin from {0}", Path.GetFileName(pluginFile)), ex);
}
}
_plugins.Sort((a, b) => a.Order.CompareTo(b.Order));
Log(string.Format("Plugin loading complete. Loaded {0} plugins", _plugins.Count));
}
catch (Exception ex)
{
LogError("Failed to load plugins", ex);
}
}
private async Task LoadPluginAsync(string pluginFile)
{
var fileName = Path.GetFileName(pluginFile);
Log(string.Format("Loading plugin: {0}", fileName));
var alc = new PluginAssemblyLoadContext(pluginFile, isCollectible: true);
try
{
var assembly = alc.LoadFromAssemblyPath(pluginFile);
var pluginTypes = assembly.GetTypes()
.Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract && t.IsClass);
foreach (var pluginType in pluginTypes)
{
var pluginInstance = Activator.CreateInstance(pluginType) as IPlugin;
if (pluginInstance == null) continue;
var pluginInfo = new PluginInfo
{
Id = pluginInstance.Id,
Name = pluginInstance.Name,
Version = pluginInstance.Version,
Description = pluginInstance.Description,
Author = pluginInstance.Author,
Order = pluginInstance.Order,
Instance = pluginInstance,
IsLoaded = true
};
_plugins.Add(pluginInfo);
_assemblyContexts[pluginInfo.Id] = alc;
try
{
pluginInstance.Initialize(this);
Log(string.Format("Plugin loaded: {0} v{1} by {2}", pluginInfo.Name, pluginInfo.Version, pluginInfo.Author));
OnPluginLoaded(pluginInfo);
}
catch (Exception ex)
{
LogError(string.Format("Failed to initialize plugin {0}", pluginInfo.Name), ex);
_plugins.Remove(pluginInfo);
_assemblyContexts.Remove(pluginInfo.Id);
alc.Unload();
}
}
}
catch
{
alc.Unload();
throw;
}
await Task.CompletedTask;
}
public void UnloadPlugin(PluginInfo plugin)
{
try
{
plugin.Instance.Shutdown();
_plugins.Remove(plugin);
plugin.IsLoaded = false;
if (_assemblyContexts.TryGetValue(plugin.Id, out var alc))
{
_assemblyContexts.Remove(plugin.Id);
alc.Unload();
}
Log(string.Format("Plugin unloaded: {0}", plugin.Name));
OnPluginUnloaded(plugin);
}
catch (Exception ex)
{
LogError(string.Format("Failed to unload plugin {0}", plugin.Name), ex);
}
}
public void UnloadAll()
{
foreach (var plugin in _plugins.ToList())
{
UnloadPlugin(plugin);
}
}
public void Log(string message)
{
OnLogMessage(message);
System.Diagnostics.Debug.WriteLine(string.Format("[Plugin] {0}", message));
}
public void LogError(string message, Exception ex = null)
{
var fullMessage = ex != null ? string.Format("{0}: {1}", message, ex.Message) : message;
OnLogMessage(string.Format("ERROR: {0}", fullMessage));
System.Diagnostics.Debug.WriteLine(string.Format("[Plugin ERROR] {0}", fullMessage));
if (ex != null)
{
System.Diagnostics.Debug.WriteLine(ex.StackTrace);
}
}
public T GetService<T>() where T : class
{
if (_services.TryGetValue(typeof(T), out var service))
{
return service as T;
}
return null;
}
public void RegisterService<T>(T service) where T : class
{
_services[typeof(T)] = service;
}
protected virtual void OnPluginLoaded(PluginInfo pluginInfo)
{
var handler = PluginLoaded;
if (handler != null)
{
handler(this, pluginInfo);
}
}
protected virtual void OnPluginUnloaded(PluginInfo pluginInfo)
{
var handler = PluginUnloaded;
if (handler != null)
{
handler(this, pluginInfo);
}
}
protected virtual void OnLogMessage(string message)
{
var handler = LogMessage;
if (handler != null)
{
handler(this, message);
}
}
private class PluginAssemblyLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public PluginAssemblyLoadContext(string pluginPath, bool isCollectible)
: base(string.Format("PluginContext_{0}", Path.GetFileNameWithoutExtension(pluginPath)), isCollectible)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly Load(AssemblyName assemblyName)
{
var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
}
}
}
@@ -0,0 +1,24 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.PluginPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="插件"
mc:Ignorable="d">
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="24">
<TextBlock Text="🎨 插件管理" Style="{DynamicResource TitleTextBlockStyle}" Margin="0,0,0,16" />
<Border x:Name="StatusBorder" Background="{DynamicResource InfoAcrylicFillColorDefaultBrush}" Padding="12" CornerRadius="8" Margin="0,0,0,20">
<TextBlock x:Name="PluginCountText" Text="正在加载插件..." Foreground="{DynamicResource InfoTextFillColorPrimaryBrush}" />
</Border>
<StackPanel x:Name="PluginContainer" />
</StackPanel>
</ScrollViewer>
</Grid>
</ui:Page>
@@ -0,0 +1,120 @@
using Ink_Canvas.Plugins;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class PluginPage : iNKORE.UI.WPF.Modern.Controls.Page
{
public PluginPage()
{
InitializeComponent();
Loaded += PluginPage_Loaded;
}
private void PluginPage_Loaded(object sender, RoutedEventArgs e)
{
LoadPlugins();
}
public void LoadPlugins()
{
try
{
var pluginManager = PluginManager.Instance;
var plugins = pluginManager.Plugins;
PluginCountText.Text = string.Format("已加载 {0} 个插件", plugins.Count);
if (plugins.Count == 0)
{
PluginContainer.Children.Clear();
var noPluginText = new TextBlock
{
Text = "没有找到插件,请将插件文件放置在 Plugins 目录中",
TextWrapping = TextWrapping.Wrap,
Foreground = Brushes.Gray,
Margin = new Thickness(0, 10, 0, 0)
};
PluginContainer.Children.Add(noPluginText);
return;
}
PluginContainer.Children.Clear();
foreach (var pluginInfo in plugins)
{
var pluginCard = CreatePluginCard(pluginInfo);
PluginContainer.Children.Add(pluginCard);
}
}
catch (Exception ex)
{
PluginCountText.Text = string.Format("加载插件时出错:{0}", ex.Message);
}
}
private Border CreatePluginCard(PluginInfo pluginInfo)
{
var card = new Border
{
Background = Brushes.White,
BorderBrush = Brushes.LightGray,
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8),
Margin = new Thickness(0, 0, 0, 10),
Padding = new Thickness(15)
};
var stackPanel = new StackPanel();
var titlePanel = new DockPanel { LastChildFill = true };
var nameText = new TextBlock
{
Text = pluginInfo.Name,
FontSize = 16,
FontWeight = FontWeights.Bold,
Foreground = Brushes.Black,
Margin = new Thickness(0, 0, 10, 0)
};
DockPanel.SetDock(nameText, Dock.Left);
var versionText = new TextBlock
{
Text = string.Format("v{0}", pluginInfo.Version),
FontSize = 12,
Foreground = Brushes.Gray,
VerticalAlignment = VerticalAlignment.Center
};
titlePanel.Children.Add(nameText);
titlePanel.Children.Add(versionText);
var descriptionText = new TextBlock
{
Text = pluginInfo.Description,
FontSize = 12,
Foreground = Brushes.DarkGray,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 5, 0, 5)
};
var authorText = new TextBlock
{
Text = string.Format("作者:{0}", pluginInfo.Author),
FontSize = 11,
Foreground = Brushes.Gray
};
stackPanel.Children.Add(titlePanel);
stackPanel.Children.Add(descriptionText);
stackPanel.Children.Add(authorText);
card.Child = stackPanel;
return card;
}
}
}
@@ -0,0 +1,14 @@
<ui:Page
x:Class="Ink_Canvas.Windows.SettingsViews.Pages.PluginSettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="插件设置"
mc:Ignorable="d">
<Grid>
<ContentControl x:Name="PluginSettingsContent" Margin="24" />
</Grid>
</ui:Page>
@@ -0,0 +1,71 @@
using Ink_Canvas.Plugins;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class PluginSettingsPage : iNKORE.UI.WPF.Modern.Controls.Page
{
private static PluginInfo _currentPlugin;
public static PluginInfo CurrentPlugin
{
get { return _currentPlugin; }
set
{
_currentPlugin = value;
}
}
public PluginSettingsPage()
{
InitializeComponent();
Loaded += PluginSettingsPage_Loaded;
}
private void PluginSettingsPage_Loaded(object sender, RoutedEventArgs e)
{
LoadPluginSettings();
}
public void LoadPluginSettings()
{
if (_currentPlugin == null)
{
return;
}
try
{
var settingsView = _currentPlugin.Instance.GetSettingsView();
if (settingsView != null && settingsView is UIElement uiElement)
{
var parent = VisualTreeHelper.GetParent(uiElement);
if (parent != null)
{
if (parent is Decorator decorator)
{
decorator.Child = null;
}
else if (parent is ContentControl contentControl)
{
contentControl.Content = null;
}
else if (parent is Panel panel)
{
panel.Children.Remove(uiElement);
}
}
PluginSettingsContent.Content = uiElement;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(string.Format("加载插件设置时出错: {0}", ex.Message));
}
}
}
}
@@ -228,6 +228,15 @@
</ui:NavigationViewItem.MenuItems>
</ui:NavigationViewItem>
<ui:NavigationViewItemHeader Content="插件设置"/>
<ui:NavigationViewItem
x:Name="PluginItem"
Content="插件"
Tag="PluginPage"
ToolTipService.ToolTip="插件管理">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Puzzle}"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationView.MenuItems>
<ui:NavigationView.FooterMenuItems>
@@ -6,6 +6,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Navigation;
using System.Windows.Media;
using MessageBox = System.Windows.MessageBox;
using Screen = System.Windows.Forms.Screen;
@@ -15,6 +16,7 @@ namespace Ink_Canvas.Windows.SettingsViews
{
private readonly Dictionary<string, Type> _pageTypes;
private readonly Dictionary<string, object> _pages = new Dictionary<string, object>();
private readonly Dictionary<string, Ink_Canvas.Plugins.PluginInfo> _pluginPages = new Dictionary<string, Ink_Canvas.Plugins.PluginInfo>();
// 保存窗口原始位置和大小
private double _originalLeft;
@@ -44,7 +46,9 @@ namespace Ink_Canvas.Windows.SettingsViews
{ "FontsPage", typeof(FontsPage) },
{ "StartupPage", typeof(StartupPage) },
{ "AboutPage", typeof(AboutPage) },
{ "Settings", typeof(SettingsPage) }
{ "Settings", typeof(SettingsPage) },
{ "PluginPage", typeof(PluginPage) },
{ "PluginSettingsPage", typeof(PluginSettingsPage) }
};
// 默认选中首页
@@ -65,6 +69,7 @@ namespace Ink_Canvas.Windows.SettingsViews
{
SetMaxSizeAndCenter();
RegisterDpiChangedListener();
LoadPluginSettingsPages();
};
// 窗口关闭时释放资源
@@ -258,6 +263,12 @@ namespace Ink_Canvas.Windows.SettingsViews
string tag = selectedItem.Tag as string;
if (!string.IsNullOrEmpty(tag) && _pageTypes.ContainsKey(tag))
{
// 如果是插件设置页面,设置当前插件
if (_pluginPages.TryGetValue(tag, out var plugin))
{
PluginSettingsPage.CurrentPlugin = plugin;
}
// 避免重复导航到当前页面
if (rootFrame.SourcePageType != _pageTypes[tag])
{
@@ -458,6 +469,44 @@ namespace Ink_Canvas.Windows.SettingsViews
return items;
}
private void LoadPluginSettingsPages()
{
try
{
var pluginManager = Ink_Canvas.Plugins.PluginManager.Instance;
var plugins = pluginManager.Plugins;
foreach (var plugin in plugins)
{
var settingsView = plugin.Instance.GetSettingsView();
if (settingsView != null)
{
var pageTag = string.Format("PluginSettings_{0}", plugin.Id);
_pageTypes[pageTag] = typeof(PluginSettingsPage);
_pluginPages[pageTag] = plugin;
var navItem = new NavigationViewItem
{
Content = string.Format("{0} 设置", plugin.Name),
Tag = pageTag
};
navItem.Icon = new FontIcon
{
Glyph = "\uE713"
};
NavigationViewControl.MenuItems.Add(navItem);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(string.Format("加载插件设置页面时出错: {0}", ex.Message));
}
}
#endregion
}
}
+33
View File
@@ -82,6 +82,16 @@
"MdXaml.Plugins": "1.27.0"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Direct",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.International.Converters.PinYinConverter": {
"type": "Direct",
"requested": "[1.0.0, )",
@@ -179,6 +189,16 @@
"System.CodeDom": "10.0.5"
}
},
"System.Private.Uri": {
"type": "Direct",
"requested": "[4.3.2, )",
"resolved": "4.3.2",
"contentHash": "o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.1",
"Microsoft.NETCore.Targets": "1.1.3"
}
},
"WebDav.Client": {
"type": "Direct",
"requested": "[2.9.0, )",
@@ -221,11 +241,21 @@
"resolved": "1.27.0",
"contentHash": "We7LtBdoukRg9mqTfa1f5n8z/GQPMKBRj3URk9DiMuqzIHkW1lTgK5njVPSScxsRt4YzW22423tSnLWNm2MJKg=="
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
},
"Microsoft.NETCore.Targets": {
"type": "Transitive",
"resolved": "1.1.3",
"contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ=="
},
"Microsoft.Win32.Registry": {
"type": "Transitive",
"resolved": "5.0.0",
@@ -308,6 +338,9 @@
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
},
"inkcanvas.pluginsdk": {
"type": "Project"
}
},
"net6.0-windows10.0.19041/win-arm64": {
+12
View File
@@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
namespace Ink_Canvas.Plugins
{
public interface IInkCanvasService
{
void OpenWhiteboard();
void CloseWhiteboard();
Task OpenWhiteboardAsync(int delayMilliseconds = 0);
}
}
+19
View File
@@ -0,0 +1,19 @@
using System;
namespace Ink_Canvas.Plugins
{
public interface IPlugin
{
string Id { get; }
string Name { get; }
string Version { get; }
string Description { get; }
string Author { get; }
int Order { get; }
void Initialize(IPluginHost host);
void Shutdown();
object GetMainView();
object GetSettingsView();
}
}
+12
View File
@@ -0,0 +1,12 @@
using System;
namespace Ink_Canvas.Plugins
{
public interface IPluginHost
{
void Log(string message);
void LogError(string message, Exception ex = null);
T GetService<T>() where T : class;
void RegisterService<T>(T service) where T : class;
}
}
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<UseWPF>true</UseWPF>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<RootNamespace>Ink_Canvas.Plugins</RootNamespace>
</PropertyGroup>
</Project>
+60
View File
@@ -0,0 +1,60 @@
using System;
namespace Ink_Canvas.Plugins
{
public abstract class PluginBase : IPlugin
{
protected IPluginHost Host { get; private set; }
public abstract string Id { get; }
public abstract string Name { get; }
public abstract string Version { get; }
public abstract string Description { get; }
public abstract string Author { get; }
public abstract int Order { get; }
public virtual void Initialize(IPluginHost host)
{
Host = host;
}
public virtual void Shutdown()
{
}
public virtual object GetMainView()
{
return null;
}
public virtual object GetSettingsView()
{
return null;
}
protected void Log(string message)
{
if (Host != null)
{
Host.Log(message);
}
}
protected void LogError(string message, Exception ex = null)
{
if (Host != null)
{
Host.LogError(message, ex);
}
}
protected T GetService<T>() where T : class
{
if (Host != null)
{
return Host.GetService<T>();
}
return null;
}
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
namespace Ink_Canvas.Plugins
{
public class PluginInfo
{
public string Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string Description { get; set; }
public string Author { get; set; }
public int Order { get; set; }
public IPlugin Instance { get; set; }
public bool IsLoaded { get; set; }
}
}