原理
使用COM机制来加载调用插件,并在宿主程序中对插件进行加载与释放。
方法
接口定义
[GeneratedComInterface]
[Guid("C71CB0B9-3AB9-44C5-851E-A25C7025FE42")]
public partial interface IPlugin
{
/// <summary>
/// 插件名称
/// </summary>
/// <returns></returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetPluginName();
/// <summary>
/// 插件版本
/// </summary>
/// <returns></returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetPluginVersion();
/// <summary>
/// 插件是否在运行
/// </summary>
/// <returns></returns>
[return: MarshalAs(UnmanagedType.Bool)]
bool IsPluginRun();
/// <summary>
/// 显示UI
/// </summary>
void ShowUI();
//其他内容
}
COM Server声明
public static class ComWrappersHelper
{
public static StrategyBasedComWrappers Wrappers { get; } = new();
}
COM Client 声明
public static class ComWrappersHelper
{
public static StrategyBasedComWrappers Wrappers { get; } = new();
}
COM之间传递指针
// Client
//在插件中实现,统一创建插件并返回指针对象,创建可导出模块显示
private const int S_OK = 0;
private const int E_POINTER = unchecked((int)0x80004003);
private const int E_FAIL = unchecked((int)0x80004005);
private const int E_INVALIDARG = unchecked((int)0x80070057);
[UnmanagedCallersOnly(EntryPoint = "CreatePluginFactory")]
public static int CreatePluginFactory(nint* factory)
{
if (factory is null)
{
return E_POINTER;
}
try
{
*factory = ComWrappersHelper.Wrappers.GetOrCreateComInterfaceForObject(
new PluginClassFactory(),
CreateComInterfaceFlags.None);
return S_OK;
}
catch (COMException ex)
{
*factory = 0;
return ex.HResult;
}
catch
{
*factory = 0;
return E_FAIL;
}
}
加载插件
// 指定插件模块的入口函数
const string CreatePluginFactoryExportName = "CreatePluginFactory";
// 拿到COM加载器
StrategyBasedComWrappers wrappers = ComWrappersHelper.Wrppers;
nint modelHaldle = 0;
nint factoryPointer = 0;
//PluginEventSink eventSink = new();
// 通过Load的方式来加载插件,返回一个uint的指针数据
modelHaldle = NativeLibrary.Load(path);
//通过此指针来定位到入口函数处
nint export = NativeLibrary.GetExport(modelHaldle, CreatePluginFactoryExportName);
//获得入口函数指针位置
int result = CreatePluginFactory(export, out factoryPointer);
// 检查是否错误
Marshal.ThrowExceptionForHR(result);
// 通过COM加载器强转类型
IPluginFactory factoryProxy = (IPluginFactory)wrappers.GetOrCreateObjectForComInstance(factoryPointer, CreateObjectFlags.None);
// 创建插件本体
IPlugin plugin = factoryProxy.CreatePlugin(string.Empty);
Console.WriteLine(plugin.GetPluginName());
//显示UI,Markup方式
plugin.ShowUI();
//释放插件
NativeLibrary.Free(modelHaldle);
public unsafe int CreatePluginFactory(nint export, out nint factoryPointer)
{
// 调用插件内实现的CreatePluginFactory入口方法,注意此方法仅为工厂类的实现,可换其他实现
delegate* unmanaged<nint*, int> createPluginFactory = (delegate* unmanaged<nint*, int>)export;
nint localFactoryPointer = 0;
int hresult = createPluginFactory(&localFactoryPointer);
factoryPointer = localFactoryPointer;
return hresult;
}
···
插件定义示例
``` C#
[GeneratedComClass]
public partial class NowTimerPlugin : IPlugin
{
private const string DiagnosticLogPath = @"D:\WorkSpace\ComPlugin\TestPlugin.ShowUI.log";
[return: MarshalAs(UnmanagedType.BStr)]
public string GetPluginName()
{
return "获取现在时间的插件";
}
[return: MarshalAs(UnmanagedType.BStr)]
public string GetPluginVersion()
{
return "获取现在插件的版本";
}
[return: MarshalAs(UnmanagedType.Bool)]
public bool IsPluginRun()
{
return true;
}
public void ShowUI()
{
try
{
Window win = new Window
{
Content = new TextBlock
{
Text = $"现在时间: {DateTime.Now}",
FontSize = 24,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
};
win.Activate();
}
catch (Exception ex)
{
File.AppendAllText(DiagnosticLogPath, "Outer catch:" + Environment.NewLine + ex + Environment.NewLine);
Debug.WriteLine(ex);
throw;
}
}
}
以上方法来自于目前的测试结果,此方法目前未能测试到使用Resource加载资源对象,目前来看,可以作为一些小的插件进行开发。
原理
使用COM机制来加载调用插件,并在宿主程序中对插件进行加载与释放。
方法
接口定义
COM Server声明
COM Client 声明
COM之间传递指针
加载插件
以上方法来自于目前的测试结果,此方法目前未能测试到使用Resource加载资源对象,目前来看,可以作为一些小的插件进行开发。