Tutorial: Hello World Add-in

Dieses Tutorial zeigt, wie ein minimales Add-in von Grund auf erstellt wird. Am Ende steht ein funktionierendes Add-in, das einen Command registriert, einen Menüpunkt hinzufügt und eine Benachrichtigung anzeigt.

Zurück zur Übersicht.

Voraussetzungen

  • Free Pascal Compiler (FPC) in der Version, die der Host verwendet
  • Lazarus IDE (optional, aber empfohlen für die Formular-Entwicklung)
  • WvdS Add-in SDK (die Unit WvdS.Document.Host.Api)

Projektstruktur

hello-world/
├── plugin.json              ← Manifest
├── package.nls.json         ← Standard-Übersetzung
├── bin/
│   └── HelloWorld.dll       ← Wird vom Compiler erzeugt
├── media/
│   └── hello.svg            ← Command-Icon
└── src/
    ├── HelloWorld.lpr       ← Library-Hauptdatei
    └── HelloWorldPlugin.pas ← Plugin-Implementierung

Schritt 1: Manifest erstellen

Die Datei plugin.json beschreibt das Add-in vollständig. Der Host liest sie, bevor die DLL geladen wird.

{
  "id": "demo.hello-world",
  "name": "hello-world",
  "displayName": "Hello World",
  "description": "Ein minimales Beispiel-Add-in",
  "version": "1.0.0",
  "publisher": "demo",
  "kind": "native",
  "main": "bin/HelloWorld.dll",
  "engineVersion": "^1.0.0",
 
  "activationEvents": [
    "onCommand:hello.sayHello"
  ],
 
  "permissions": [],
 
  "contributes": {
    "commands": [
      {
        "command": "hello.sayHello",
        "title": "Hallo sagen",
        "category": "Hello"
      }
    ],
    "menus": {
      "menuBar/help": [
        { "command": "hello.sayHello", "group": "demo", "order": 1 }
      ],
      "commandPalette": [
        { "command": "hello.sayHello" }
      ]
    }
  }
}

Die Datei deklariert einen einzigen Command (hello.sayHello), der im Hilfe-Menü erscheint und über die CommandPalette erreichbar ist. Das activationEvents-Array sagt dem Host, dass die DLL erst geladen werden soll, wenn dieser Command zum ersten Mal aufgerufen wird.

Schritt 2: Library-Hauptdatei

Die Datei HelloWorld.lpr ist der Einstiegspunkt der DLL. Sie exportiert die beiden Funktionen, die der Host erwartet.

library HelloWorld;
 
{$mode objfpc}{$H+}
 
uses
  WvdS.Document.Host.Api,
  HelloWorldPlugin;
 
const
  WVDS_MAGIC   = $57564453;
  ABI_VERSION  = 1;
  FPC_VERSION  = {$I %FPCVERSION%};
 
function GetAbiInfo: TPluginAbiInfo; stdcall;
begin
  Result.Magic := WVDS_MAGIC;
  Result.AbiVersion := ABI_VERSION;
  Result.FpcVersion := FPC_VERSION;
end;
 
function CreatePlugin(const AHost: IHost): IPlugin; stdcall;
begin
  Result := THelloWorldPlugin.Create(AHost);
end;
 
exports
  GetAbiInfo,
  CreatePlugin;
 
end.

GetAbiInfo liefert die Kompatibilitätsinformationen. Der Host prüft Magic-Number, ABI-Version und FPC-Version, bevor er CreatePlugin aufruft. CreatePlugin erzeugt das Plugin-Objekt und gibt es als IPlugin zurück.

Schritt 3: Plugin-Implementierung

Die Datei HelloWorldPlugin.pas enthält das Plugin und seinen Command-Handler.

unit HelloWorldPlugin;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  WvdS.Document.Host.Api;
 
type
  THelloWorldPlugin = class(TInterfacedObject, IPlugin)
  private
    FHost: IHost;
  public
    constructor Create(const AHost: IHost);
    procedure Activate(const AContext: IExtensionContext);
    procedure Deactivate;
  end;
 
implementation
 
type
  TSayHelloHandler = class(TInterfacedObject, ICommandHandler)
  private
    FHost: IHost;
  public
    constructor Create(const AHost: IHost);
    procedure Execute;
  end;
 
{ THelloWorldPlugin }
 
constructor THelloWorldPlugin.Create(const AHost: IHost);
begin
  inherited Create;
  FHost := AHost;
end;
 
procedure THelloWorldPlugin.Activate(const AContext: IExtensionContext);
begin
  AContext.Subscribe(
    FHost.Commands.RegisterCommand('hello.sayHello', 'Hallo sagen',
      TSayHelloHandler.Create(FHost))
  );
end;
 
procedure THelloWorldPlugin.Deactivate;
begin
  // Nichts zu tun — der Host räumt alle Registrierungen automatisch auf.
end;
 
{ TSayHelloHandler }
 
constructor TSayHelloHandler.Create(const AHost: IHost);
begin
  inherited Create;
  FHost := AHost;
end;
 
procedure TSayHelloHandler.Execute;
begin
  FHost.Notifications.ShowInfo('Hallo von Hello World!');
end;
 
end.

Der TSayHelloHandler ist der eigentliche Code hinter dem Command. Wenn der Benutzer „Hallo sagen“ auswählt, ruft der Host Execute auf, und das Add-in zeigt eine Benachrichtigung an.

Wichtig: Die Deactivate-Methode ist leer. Das ist der Normalfall, weil der Host alle über AContext.Subscribe registrierten Disposables automatisch aufräumt. Nur wenn das Add-in eigene Ressourcen verwaltet (Threads, Datenbankverbindungen), muss es in Deactivate selbst aufräumen.

Schritt 4: Kompilieren

fpc -FUbin -obin/HelloWorld.dll src/HelloWorld.lpr

Die DLL wird im bin/-Verzeichnis erzeugt, wo das Manifest sie erwartet („main“: „bin/HelloWorld.dll“).

Schritt 5: Paketieren

Das fertige Add-in wird als .wvdsx-Paket ausgeliefert:

cd hello-world
zip -r ../demo.hello-world-1.0.0.wvdsx plugin.json package.nls.json bin/ media/

Schritt 6: Installieren und Testen

wdochost --install-extension demo.hello-world-1.0.0.wvdsx

Nach dem Neustart der Anwendung erscheint „Hallo sagen“ im Hilfe-Menü und in der CommandPalette. Beim ersten Aufruf wird die DLL geladen und der Handler aktiviert.

Was passiert unter der Haube

  1. Start — Der Host scannt das Erweiterungsverzeichnis, findet plugin.json und liest es.
  2. Contributions — Der Host registriert den Command hello.sayHello als Lazy-Stub und fügt den Menüpunkt ins Hilfe-Menü ein.
  3. Benutzerklick — Der Benutzer wählt „Hallo sagen“ im Menü.
  4. Lazy-Aktivierung — Der Stub fängt den Aufruf ab, lädt HelloWorld.dll, ruft GetAbiInfo und CreatePlugin auf.
  5. ActivateTHelloWorldPlugin.Activate registriert den echten Handler.
  6. Execute — Der Host führt TSayHelloHandler.Execute aus. Die Benachrichtigung erscheint.

Ab diesem Punkt ist das Add-in aktiv. Jeder weitere Aufruf des Commands geht direkt an den Handler, ohne erneute Aktivierung.

Weiter zum Document-View Tutorial oder zurück zur Übersicht.

Zuletzt geändert: den 15.03.2026 um 02:32