Skip to content

Plugin System

abbaye edited this page Mar 19, 2026 · 2 revisions

Plugin System

The WpfHexEditor Plugin System lets you extend the IDE with new panels, commands, editor features, build backends, solution loaders, and services — packaged as .whxplugin files.


Architecture

flowchart TD
    App["WpfHexEditor.App\nMainWindow.PluginSystem.cs"]

    subgraph SDK["WpfHexEditor.SDK (public contracts)"]
        IPlugin["IWpfHexEditorPluginV2\n+ LoadPriority"]
        ICtx["IIDEHostContext"]
        Services["14+ service interfaces\nIHexEditorService · IOutputService\nITerminalService · IUIRegistry\nISolutionLoader · IBuildAdapter\nIEditorToolbarContributor · IQuickInfoProvider\nIGrammarProvider · IIDEEventBus · ..."]
    end

    subgraph Host["WpfHexEditor.PluginHost (runtime)"]
        WpfHost["WpfPluginHost\ndiscovery + sort by priority + load + watchdog"]
        ALC["PluginLoadContext\ncollectible AssemblyLoadContext"]
        Crash["PluginCrashHandler"]
        Watch["PluginWatchdog\nWrapAsync → Task<TimeSpan>"]
        PM["PluginManagerControl"]
        Monitor["PluginMonitoringViewModel\n1s polling · CPU% · Memory MB"]
    end

    Sandbox["WpfHexEditor.PluginSandbox\nout-of-process via named-pipe IPC"]
    Tools["PackagingTool (whxpack)\nPluginInstaller (WPF dialog)"]

    App --> SDK
    App --> Host
    SDK --> ICtx
    ICtx --> Services
    Host --> ALC
    Host --> Crash
    Host --> Watch
    Host --> Sandbox
Loading

Plugin Contract

public interface IWpfHexEditorPluginV2
{
    string  Id          { get; }   // "com.example.myplugin"
    string  Name        { get; }
    string  Version     { get; }
    string? Description { get; }
    int     LoadPriority { get; }  // 0-100; lower = loads first (default: 50)

    void Init(IIDEHostContext context);
    void Activate();
    void Deactivate();
    void Dispose();
}

// Optional — auto-registers an Options page under "Plugins"
public interface IPluginWithOptions
{
    string           OptionsPageTitle { get; }
    FrameworkElement CreateOptionsPage();
}

Minimal Example

public class MyPlugin : IWpfHexEditorPluginV2, IPluginWithOptions
{
    private IIDEHostContext? _ctx;

    public string  Id           => "com.example.myplugin";
    public string  Name         => "My Plugin";
    public string  Version      => "1.0.0";
    public string? Description  => "Does something awesome";
    public int     LoadPriority => 50;
    public string  OptionsPageTitle => "My Plugin";

    public void Init(IIDEHostContext context)
    {
        _ctx = context;
        context.UIRegistry.RegisterPanel("my-panel", "My Panel", () => new MyPanelView());
        context.EventBus.Subscribe<FileOpenedEvent>(e =>
            context.OutputService.WriteLine($"Opened: {e.FilePath}"));
    }

    public void Activate()   => _ctx?.UIRegistry.ShowDockablePanel("my-panel");
    public void Deactivate() => _ctx?.UIRegistry.HideDockablePanel("my-panel");
    public void Dispose()    { }

    public FrameworkElement CreateOptionsPage() => new MyOptionsPage();
}

IIDEHostContext — Available Services

Property Contract Description
HexEditorService IHexEditorService Read bytes, search, go to offset
CodeEditorService ICodeEditorService Active code editor content
OutputService IOutputService Write to Output panel
ErrorPanelService IErrorPanelService Post diagnostics (Info/Warning/Error)
SolutionExplorerService ISolutionExplorerService Open files, query project tree
ParsedFieldService IParsedFieldService Read parsed fields from HexEditor
TerminalService ITerminalService Execute commands, write output, history
UIRegistry IUIRegistry Show/Hide/Toggle/Focus panels
EventBus IPluginEventBus Subscribe/publish IDE-wide events
IDEEventBus IIDEEventBus Subscribe to typed domain events (21 event records)
FocusContextService IFocusContextService Active editor focus
PermissionService IPermissionService Check/request permissions
ThemeService IThemeService Current theme, change notifications
BuildSystem IBuildSystem Trigger builds, register IBuildAdapter

Specialized Plugin Interfaces

ISolutionLoader — Custom solution format

public interface ISolutionLoader : IWpfHexEditorPluginV2
{
    int     Priority { get; }           // Higher = tried first
    bool    CanLoad(string path);       // Called for each dropped/opened file
    Task<ISolution> LoadAsync(string path, CancellationToken ct);
    Task    SaveAsync(ISolution solution, CancellationToken ct);
}

Built-in loaders and their priorities:

Loader Priority Handles
SolutionLoader.WH 95 .whsln / .whproj
SolutionLoader.VS 90 .sln / .csproj (4 templates)
SolutionLoader.Folder 85 .whfolder JSON marker, gitignore-aware

IBuildAdapter — Custom build backend

public interface IBuildAdapter : IWpfHexEditorPluginV2
{
    bool    CanBuild(IProject project);
    Task    BuildAsync(IProject project, IBuildConfiguration config,
                       IProgress<BuildProgress> progress, CancellationToken ct);
    Task    CleanAsync(IProject project, CancellationToken ct);
}

Build.MSBuild (priority 85) uses dotnet build CLI and streams output to the Output panel.

IEditorToolbarContributor — Add toolbar items

public interface IEditorToolbarContributor
{
    string TargetEditorId { get; }  // e.g. "code-editor"
    IReadOnlyList<EditorToolbarItem> GetItems();
}

IQuickInfoProvider — Hover Quick Info

public interface IQuickInfoProvider
{
    int Priority { get; }
    Task<QuickInfoResult?> GetQuickInfoAsync(QuickInfoRequest request, CancellationToken ct);
}

IGrammarProvider — Binary grammar overlay

public interface IGrammarProvider
{
    string GrammarId { get; }
    bool   CanApply(string filePath);
    Task<GrammarParseResult> ParseAsync(IByteProvider bytes, CancellationToken ct);
}

SynalysisGrammar (priority 45) implements IGrammarProvider for UFWB .grammar files.


Plugin Lifecycle

sequenceDiagram
    participant Host as WpfPluginHost
    participant ALC as PluginLoadContext
    participant Plugin as IWpfHexEditorPluginV2
    participant IDE as IIDEHostContext

    Host->>ALC: Load assembly (collectible)
    ALC->>Plugin: new PluginImpl()
    Host->>Plugin: Init(context)
    Plugin->>IDE: Register panels, commands, options, solution loaders
    Host->>Plugin: Activate()
    Note over Plugin: Plugin running
    Host->>Plugin: Deactivate()
    Host->>Plugin: Dispose()
    Host->>ALC: Unload() — clears ALC reference
    Note over ALC: GC collects all plugin assemblies (no restart)
Loading

Manifest Format

{
  "id":             "com.example.myplugin",
  "name":           "My Plugin",
  "version":        "1.0.0",
  "description":    "Does something awesome",
  "author":         "Jane Dev",
  "entryPoint":     "MyPlugin.dll",
  "minHostVersion": "0.6.0",
  "loadPriority":   50,
  "capabilities":   ["HexEditor", "Terminal"],
  "permissions":    ["ReadFile", "WriteOutput"]
}

Auto-generate from .csproj with PackagingTool:

<PropertyGroup>
  <PluginId>com.example.myplugin</PluginId>
  <PluginVersion>1.0.0</PluginVersion>
  <PluginEntryPoint>MyPlugin.dll</PluginEntryPoint>
  <PluginAuthor>Jane Dev</PluginAuthor>
  <PluginLoadPriority>50</PluginLoadPriority>
</PropertyGroup>

Packaging

# Build
dotnet build MyPlugin.csproj -c Release

# Package → .whxplugin (ZIP with manifest + DLL)
whxpack --input bin/Release/net8.0-windows --output MyPlugin.whxplugin

# Install (silent for CI)
WpfHexEditor.PluginInstaller.exe --file MyPlugin.whxplugin --silent

First-Party Plugins

Plugin Priority Purpose Options Page
SolutionLoader.WH 95 Native .whsln/.whproj format
SolutionLoader.VS 90 Visual Studio .sln/.csproj (4 templates)
SolutionLoader.Folder 85 Open-folder mode (gitignore-aware)
Build.MSBuild 85 dotnet build/dotnet run CLI adapter Build configs
DataInspector 50 40+ type interpretations of selected bytes Display format, endianness
ParsedFields 50 Binary structure overlay in HexEditor
AssemblyExplorer 40 .NET PE inspection + C#/IL decompilation Show non-public, recent files
SynalysisGrammar 45 UFWB binary grammar overlay (IGrammarProvider) Grammar paths
XamlDesigner 50 9 VS-like panels for the XAML Designer

IDE Event Bus

Plugins can subscribe to all 21 typed IDE domain events via IIDEEventBus:

context.IDEEventBus.Subscribe<FileOpenedEvent>(e =>
    context.OutputService.WriteLine($"File opened: {e.FilePath}"));

context.IDEEventBus.Subscribe<BuildCompletedEvent>(e =>
    context.OutputService.WriteLine($"Build {(e.Success ? "succeeded" : "FAILED")}"));

context.IDEEventBus.Subscribe<GrammarAppliedEvent>(e =>
    context.OutputService.WriteLine($"Grammar {e.GrammarId} applied: {e.FieldCount} fields"));

Key events: FileOpenedEvent, FileSavedEvent, FileClosedEvent, SolutionOpenedEvent, SolutionClosedEvent, BuildStartedEvent, BuildCompletedEvent, ThemeChangedEvent, DiagnosticRaisedEvent, GrammarAppliedEvent, ActiveEditorChangedEvent.


See Also

Navigation

Getting Started

IDE Documentation

HexEditor Control

Advanced

Development


v0.6.0 Highlights

  • WpfHexEditor.Shell (renamed from Docking.Wpf)
  • Code Editor ~90% (NavBar, Inline Hints, Quick Info)
  • XAML Visual Designer ~70%
  • Shared UndoEngine (coalescing + transactions)
  • VS .sln/.csproj + Open-Folder mode
  • Build System (IBuildAdapter + MSBuild plugin)
  • Assembly Explorer + NuGet Solution Manager
  • Synalysis Grammar Support (IGrammarProvider)

Links

Clone this wiki locally