-
Notifications
You must be signed in to change notification settings - Fork 1
Quick Start
本页创建一个最小 C# Mixin 插件,包含插件程序集、plugin.json 和 Mixin 配置。
插件应引用你要支持的启动器版本对应的 PCL.Core.dll。公开发布版只有一个 .NET 单文件 apphost,PCL.Core.dll 被嵌入在 EXE 中,需要先提取。
- 打开 PCL2-Nex GitHub Releases,选择目标 BaseVersion。
- 下载与插件目标架构一致的
PCL2_Nex_Release_x64.exe或PCL2_Nex_Release_ARM64.exe。 - 在 EXE 所在目录创建
Extract-PclCore.ps1,内容如下:
param(
[Parameter(Mandatory)]
[string] $AppHost,
[string] $OutputPath
)
$ErrorActionPreference = 'Stop'
$AppHost = (Resolve-Path -LiteralPath $AppHost).Path
if ([string]::IsNullOrWhiteSpace($OutputPath)) {
$OutputPath = Join-Path (Split-Path -Parent $AppHost) 'PCL.Core.dll'
}
$OutputPath = [IO.Path]::GetFullPath($OutputPath)
$outputDirectory = Split-Path -Parent $OutputPath
if (-not (Test-Path -LiteralPath $outputDirectory)) {
New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null
}
# .NET apphost bundle signature. The preceding Int64 stores the bundle header offset.
$signature = [byte[]] (
0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38,
0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32,
0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18,
0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae
)
$image = [IO.File]::ReadAllBytes($AppHost)
$signatureOffset = -1
for ($i = 8; $i -le $image.Length - $signature.Length; $i++) {
if ($image[$i] -ne $signature[0]) { continue }
$matched = $true
for ($j = 1; $j -lt $signature.Length; $j++) {
if ($image[$i + $j] -ne $signature[$j]) {
$matched = $false
break
}
}
if ($matched) {
$signatureOffset = $i
break
}
}
if ($signatureOffset -lt 8) {
throw '输入文件不是受支持的 .NET 单文件 apphost。'
}
$headerOffset = [BitConverter]::ToInt64($image, $signatureOffset - 8)
$stream = [IO.File]::OpenRead($AppHost)
try {
$reader = [IO.BinaryReader]::new($stream, [Text.Encoding]::UTF8, $true)
$stream.Position = $headerOffset
$major = $reader.ReadUInt32()
$minor = $reader.ReadUInt32()
$fileCount = $reader.ReadInt32()
$null = $reader.ReadString() # Bundle ID
if ($major -ge 2) {
$null = $reader.ReadInt64() # deps.json offset
$null = $reader.ReadInt64() # deps.json size
$null = $reader.ReadInt64() # runtimeconfig.json offset
$null = $reader.ReadInt64() # runtimeconfig.json size
$null = $reader.ReadUInt64() # Bundle flags
}
$entry = $null
for ($i = 0; $i -lt $fileCount; $i++) {
$offset = $reader.ReadInt64()
$size = $reader.ReadInt64()
$compressedSize = if ($major -ge 6) { $reader.ReadInt64() } else { 0 }
$null = $reader.ReadByte() # File type
$relativePath = $reader.ReadString()
if ($relativePath -ieq 'PCL.Core.dll') {
$entry = [pscustomobject]@{
Offset = $offset
Size = $size
CompressedSize = $compressedSize
}
break
}
}
if ($null -eq $entry) {
throw 'apphost Bundle 中没有找到 PCL.Core.dll。'
}
$stream.Position = $entry.Offset
$storedSize = if ($entry.CompressedSize -gt 0) {
$entry.CompressedSize
} else {
$entry.Size
}
if ($storedSize -gt [int]::MaxValue) {
throw 'PCL.Core.dll 过大,当前脚本无法提取。'
}
$data = $reader.ReadBytes([int] $storedSize)
if ($data.Length -ne $storedSize) {
throw '读取 PCL.Core.dll 时意外到达 apphost 末尾。'
}
if ($entry.CompressedSize -gt 0) {
$source = [IO.MemoryStream]::new($data, $false)
$deflate = [IO.Compression.DeflateStream]::new(
$source,
[IO.Compression.CompressionMode]::Decompress
)
$target = [IO.File]::Create($OutputPath)
try {
$deflate.CopyTo($target)
} finally {
$target.Dispose()
$deflate.Dispose()
$source.Dispose()
}
} else {
[IO.File]::WriteAllBytes($OutputPath, $data)
}
} finally {
$stream.Dispose()
}
$assembly = [Reflection.AssemblyName]::GetAssemblyName($OutputPath)
$version = [Diagnostics.FileVersionInfo]::GetVersionInfo($OutputPath).ProductVersion
Write-Host "已提取: $OutputPath"
Write-Host "程序集: $($assembly.Name)"
Write-Host "BaseVersion: $version"在 PowerShell 中运行:
powershell -ExecutionPolicy Bypass -File .\Extract-PclCore.ps1 `
-AppHost .\PCL2_Nex_Release_x64.exe `
-OutputPath .\sdk\2026.07.1\x64\PCL.Core.dll脚本末尾必须显示 程序集: PCL.Core,BaseVersion 必须等于 Release Tag 去掉前缀 v 后的值。把这个 BaseVersion 原样写入插件的 pclCoreVersion。不要只设置 DOTNET_BUNDLE_EXTRACT_BASE_DIR:当前 Release apphost 会直接从 Bundle 加载托管程序集,不能依靠该变量取得 PCL.Core.dll。
如果你正在构建启动器源码,可以直接使用同次构建产生的 DLL,不需要从 apphost 提取。Debug 构建通常位于:
PCL.Core/bin/Debug-x64/net8.0-windows/PCL.Core.dll
Release 构建通常位于:
PCL.Core/bin/Release-x64/net8.0-windows/PCL.Core.dll
不要从另一个启动器版本随意复制 DLL。公开发布插件时优先从对应 Release apphost 提取,确保引用内容与用户实际下载的版本完全一致。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<AssemblyName>Example.Plugin</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="PCL.Core">
<HintPath>C:\path\to\PCL.Core.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
</Project>需要创建或修改 WPF 控件时再加入:
<UseWPF>true</UseWPF>Private=false 很重要。PCLX 不应携带自己的 PCL.Core.dll。
下面的示例在 PCL.FormMain 构造完成后执行一次处理器。使用字符串目标可以避免在编译时直接引用非公开主程序类型。
using System.Diagnostics;
using System.Reflection;
using PCL.Mixin;
namespace Example.Plugin.Mixins;
[Mixin("PCL.FormMain")]
internal static class FormMainMixin
{
[Inject(".ctor", At = MixinAt.Return, Priority = int.MaxValue)]
private static void AfterCreated([This] object window, MethodBase target)
{
Debug.WriteLine($"PCL.Mixin applied: {window.GetType().FullName}.{target.Name}");
}
}[Mixin(typeof(SomeType))] 适合你能直接引用的公开类型;[Mixin("Namespace.TypeName")] 适合启动器内部类型或其他插件类型。
文件名示例:mixins/example.plugin.mixins.json
{
"required": true,
"package": "Example.Plugin.Mixins",
"mixins": ["FormMainMixin"],
"priority": 1000,
"injectors": {
"defaultRequire": 1
}
}-
package是 Mixin 类型的公共命名空间前缀。 -
mixins中可以写相对类名,也可以写完整类型名。 -
required: true表示配置失败时该插件加载失败并被禁用。 -
defaultRequire是未显式声明Require时的默认最少匹配数。
{
"id": "example.plugin",
"name": "Example Plugin",
"version": "1.0.0",
"author": "Example",
"description": "Minimal PCL.Mixin plugin.",
"pclCoreVersion": "2026.07.1",
"entryAssembly": "lib/Example.Plugin.dll",
"mixinConfig": "mixins/example.plugin.mixins.json",
"logo": "assets/logo.png"
}把 pclCoreVersion 替换为实际引用的 PCL.Core BaseVersion。格式严格为 yyyy.MM.patch。
example.plugin.pclx
plugin.json
lib/
Example.Plugin.dll
Example.Plugin.pdb
mixins/
example.plugin.mixins.json
assets/
logo.png
PCLX 是 ZIP 内容使用 .pclx 扩展名的包。plugin.json 必须位于包根目录。
可以通过启动器插件页面选择 PCLX,也可以双击已关联的 .pclx 文件。用于本地 Debug 时,也可以在启动器关闭后把已解包目录放到:
<LauncherOutput>/PCL/Plugins/example.plugin/
重启启动器后查看 PCL/Log/ 中的插件和 Mixin 日志。修改插件 DLL 或配置后必须重新启动;当前不支持热卸载和热重载。
下一步阅读 Mixin API 参考 和 注入点与修改操作。