Commands

Ein Command ist eine benannte Aktion. Jeder Menüpunkt, jeder Toolbar-Button und jedes Tastenkürzel verweist auf einen Command. Der Command selbst definiert keine Sichtbarkeit und keinen Ort — das übernehmen die anderen Contribution-Typen (Menüs, Toolbar, Keybindings). Der Command stellt lediglich einen Namen, einen Titel und einen Handler bereit.

Diese Trennung hat einen konkreten Vorteil: Ein und derselbe Command kann gleichzeitig in einem Menü, in der Toolbar und über ein Tastenkürzel erreichbar sein, ohne dass die Logik dupliziert werden muss. Wenn sich der Handler ändert, ändert er sich an einer einzigen Stelle.

Zurück zur Contributions-Übersicht oder zur Hauptübersicht.

Manifest-Deklaration

"contributes": {
  "commands": [
    {
      "command": "assets.open",
      "title": "Anlage öffnen",
      "category": "Assets",
      "icon": "media/asset-open.svg"
    },
    {
      "command": "assets.list",
      "title": "Anlageliste",
      "category": "Assets",
      "icon": "media/asset-list.svg"
    }
  ]
}
Feld Pflicht Beschreibung
command ja Eindeutiger Bezeichner. Konvention: addin-name.aktion (z.B. assets.open). Dieser Name wird überall referenziert — in Menüs, Toolbar, Keybindings und bei ExecuteCommand.
title ja Anzeigename des Commands. Erscheint in der CommandPalette und als Standard-Label in Menüs. Kann über NLS übersetzt werden.
category nein Gruppierung in der CommandPalette. Wird dem Titel als Prefix vorangestellt (Assets: Anlage öffnen). Erleichtert das Filtern in langen Command-Listen.
icon nein Relativer Pfad zu einer SVG- oder PNG-Datei. Wird in Toolbar-Buttons und Menüpunkten angezeigt.

Lazy-Stubs und Aktivierung

Wenn der Host die Manifeste verarbeitet, registriert er für jeden deklarierten Command einen sogenannten Lazy-Stub. Das ist ein Platzhalter-Handler, der beim ersten Aufruf die Aktivierung des zugehörigen Add-ins auslöst.

Der Ablauf im Detail:

  1. Der Host liest das Manifest und findet den Command assets.open mit dem Aktivierungsereignis onCommand:assets.open.
  2. Der Host registriert einen TLazyCommandHandler unter dem Namen assets.open. Dieser Handler macht nichts außer: beim ersten Execute das Add-in aktivieren.
  3. Der Benutzer klickt auf den Menüpunkt „Anlage öffnen“.
  4. Der Lazy-Stub fängt den Aufruf ab, entfernt sich selbst und aktiviert das Add-in.
  5. Das Add-in wird geladen (ABI-Handshake, CreatePlugin, Activate).
  6. In Activate registriert das Add-in den echten Handler für assets.open.
  7. Der Host führt den Command erneut aus, diesmal mit dem echten Handler.

Für den Benutzer ist dieser Vorgang transparent — er sieht nur eine kurze Verzögerung beim ersten Aufruf.

Dieses Vorgehen hat einen messbaren Vorteil: Wenn zehn Add-ins installiert sind, aber der Benutzer nur zwei davon tatsächlich verwendet, werden nur zwei DLLs geladen. Die Oberfläche zeigt trotzdem alle Commands aller zehn Add-ins, weil die Metadaten aus den Manifesten stammen.

Handler-Registrierung

Der echte Command-Handler wird in der Activate-Methode des Add-ins registriert:

type
  TOpenAssetHandler = class(TInterfacedObject, ICommandHandler)
  private
    FHost: IHost;
  public
    constructor Create(const AHost: IHost);
    procedure Execute;
  end;
 
procedure TOpenAssetHandler.Execute;
begin
  FHost.Documents.OpenDocument('assets.editor');
end;
 
procedure TAssetPlugin.Activate(const AContext: IExtensionContext);
begin
  AContext.Subscribe(
    FHost.Commands.RegisterCommand('assets.open', 'Anlage öffnen',
      TOpenAssetHandler.Create(FHost))
  );
end;

Der ICommandHandler hat eine einzige Methode Execute, die vom Host aufgerufen wird. Der Rückgabewert von RegisterCommand ist ein IDisposable, das an den IExtensionContext übergeben wird. Beim Deaktivieren des Add-ins wird der Handler automatisch abgemeldet.

CommandPalette

Die CommandPalette ist eine schnelle Suchleiste, über die der Benutzer jeden registrierten Command finden und ausführen kann. Sie funktioniert ähnlich wie in VSCode: Der Benutzer drückt ein Tastenkürzel, tippt einen Suchbegriff, und die Palette filtert die Liste der verfügbaren Commands.

Jeder Command, der im Manifest deklariert ist, erscheint automatisch in der CommandPalette. Die category wird als Prefix angezeigt, was das Filtern erleichtert:

Assets: Anlage öffnen
Assets: Anlageliste
Berichte: Monatsbericht erstellen
Einstellungen: Sprache ändern

Commands können über den commandPalette-Ort im Menü-Merge gezielt aus der Palette ausgeschlossen werden, wenn sie nur über andere Wege erreichbar sein sollen:

"menus": {
  "commandPalette": [
    { "command": "assets.internal.refresh", "when": "false" }
  ]
}

Script-Commands

Neben nativen Commands, deren Handler in einer DLL implementiert ist, unterstützt der Host auch Script-Commands. Ein Script-Command verweist auf ein PowerShell-Script (.ps1), das der Host als pwsh-Kindprozess ausführt. Script-Commands erscheinen in der CommandPalette und können über Menüs, Toolbar und Keybindings ausgelöst werden — identisch zu nativen Commands.

Ziel dieser Erweiterung ist es, leichtgewichtige Automatisierungen ohne DLL-Kompilierung zu ermöglichen. Im Unterschied zu nativen Commands benötigt ein Script-Command keinen ABI-Handshake und keine Aktivierungsphase — der Host startet das Script direkt. Der Vorteil besteht darin, dass Entwickler PowerShell-Scripts als vollwertige Commands registrieren können, ohne den DLL-Entwicklungszyklus durchlaufen zu müssen.

Manifest-Deklaration

"contributes": {
  "commands": [
    {
      "command": "export.run-csv",
      "title": "CSV-Export ausführen",
      "category": "Export",
      "icon": "media/export-csv.svg",
      "kind": "script",
      "script": "scripts/export-csv.ps1"
    }
  ]
}
Feld Pflicht Beschreibung
kind nein „native“ (Standard) oder „script“. Bestimmt, ob der Command einen DLL-Handler oder ein PowerShell-Script ausführt.
script bei kind: „script“ Relativer Pfad zur .ps1-Datei innerhalb des Add-in-Pakets. Der Host löst den Pfad beim Laden auf und validiert, dass die Datei existiert.

Die Felder command, title, category und icon funktionieren identisch zu nativen Commands.

Sicherheit

Bei der ersten Ausführung eines Script-Commands zeigt der Host einen Bestätigungsdialog. Der Benutzer sieht den vollständigen Pfad zum Script und muss die Ausführung explizit genehmigen. Einmal genehmigte Scripts werden in einer internen Liste gespeichert und bei künftigen Aufrufen ohne Dialog gestartet.

Host-Kommunikation

Scripts können über das WvdSHostContract-PowerShell-Modul mit dem Host kommunizieren. Das Modul stellt Cmdlets bereit, die strukturierte Nachrichten über stdout senden. Normaler Text landet direkt im PowerShell-Panel des BottomPanels. Ausführliche Dokumentation: ScriptHost & Host-Contract.

Programmatischer Aufruf

Ein Add-in kann Commands anderer Add-ins programmatisch auslösen:

FHost.Commands.ExecuteCommand('anderes-addin.command-name');

Dieser Aufruf funktioniert wie ein Benutzerklick — er löst die Aktivierung des Ziel-Add-ins aus, falls es noch nicht geladen ist, und führt dann den Handler aus.

Weiter zu Menü-Merge oder zurück zur Contributions-Übersicht.

Zuletzt geändert: den 15.03.2026 um 18:13