Skip to content

Debugging Security Compatibility

Ling edited this page Jul 7, 2026 · 4 revisions

调试、安全与兼容性

插件运行在启动器进程内。调试时既要关注插件自身问题,也要避免影响宿主稳定性。

C# 插件调试

推荐流程:

  1. 单独构建插件项目。
  2. 复制插件包到 PCL 插件目录。
  3. 启动 PCL。
  4. 在 IDE 中附加到 PCL 进程。
  5. 在插件入口或注册的回调中打断点。

日志:

var logger = context.Host.Core.GetLogger("main");
logger.Debug("Debug message");
logger.Info("Plugin loaded");
logger.Warn("Something looks suspicious");
logger.Error("Operation failed", exception);

提示:

context.Host.Core.Hint("Saved", PluginHintType.Success);
context.Host.Core.Hint("Failed", PluginHintType.Error);

JS 插件调试

JS 插件没有 Node.js 调试器集成,优先使用日志和 toast:

function load(ctx) {
  ctx.log("loading");
  ctx.toast("loaded");
}

JS 运行时异常会被宿主记录。修改 main.js 后通常需要重启 PCL 重新加载插件。

常见错误

Host.Ui 为 null

可能原因:

  • manifest 没声明 ContributeToolsContributeSettings 等 UI 能力。
  • 插件加载时机太早,UI 尚未就绪。

解决:

"capabilities": ["ContributeTools"]

并使用:

LoadTiming = PluginLoadTiming.WindowCreated

Host.Extensions 为 null

manifest 没声明:

"capabilities": ["RegisterExtension"]

插件无法识别入口

检查:

  • DLL 中是否有类型实现 IPclPlugin
  • 入口类型是否 public。
  • 是否存在公共无参构造函数。
  • 插件包是否误带了 PCL.Plugin.Abstractions.dll
  • plugin.jsonentry 是否指向正确 DLL。

NuGet 包能安装但编译报框架不兼容

SDK 包目标框架是 net8.0-windows。插件项目也应使用:

<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<EnableWindowsTargeting>true</EnableWindowsTargeting>

普通 net8.0 项目不能引用该 WPF/Windows SDK 包。

安全边界

DLL 插件不是安全沙箱。它运行在宿主进程内,理论上可以访问当前进程权限下的文件、网络和系统 API。

因此:

  • 只安装可信来源 DLL 插件。
  • 插件作者不要读取与插件功能无关的用户数据。
  • 插件不要修改启动器内部文件。
  • 后台任务应支持取消。
  • 网络请求应有超时。

JS 插件受 Jint runtime 限制,但如果通过 dotnet facade 加载 .NET DLL 或调用 .NET 类型,也需要同样谨慎。

能力声明不是系统权限沙箱

capabilities 用于控制宿主主动暴露哪些 API。它不是操作系统级权限隔离。

例如未声明 ContributeTools 时不能注册工具页,但 DLL 插件本身仍运行在宿主进程内。因此能力声明用于插件生态约束和宿主 API 管理,不应被理解成完整沙箱。

兼容性建议

  • 使用 SDK 新能力时提高 minApiVersion
  • 插件发布后保持插件 ID 不变。
  • 对可选 API 做 null 检查。
  • 保存配置时做好默认值兼容。
  • 反序列化旧配置时容忍字段缺失。
  • 插件卸载应允许重复调用或半初始化状态。

UI 线程

WPF 控件必须在 UI 线程操作。C# 插件使用:

var ui = context.Host.Ui;
if (ui is not null)
{
    ui.InvokeOnUi(() =>
    {
        // update WPF control
    });
}

JS 插件使用:

ctx.ui.run(function () {
  // update UI
});

后台任务

长时间任务应监听取消令牌:

private CancellationTokenSource? _cts;

public override Task LoadAsync(IPluginContext context, CancellationToken cancellationToken = default)
{
    _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, context.HostStopping);
    _ = Task.Run(() => BackgroundLoopAsync(_cts.Token), _cts.Token);
    return Task.CompletedTask;
}

public override Task UnloadAsync(CancellationToken cancellationToken = default)
{
    _cts?.Cancel();
    _cts?.Dispose();
    _cts = null;
    return Task.CompletedTask;
}

Clone this wiki locally