Inhaltsverzeichnis
Lifecycle
Der Lifecycle beschreibt den vollständigen Lebenszyklus eines Add-ins, von der Entdeckung über die Aktivierung bis zur Deaktivierung. Ziel dieser Seite ist es, Add-in-Entwicklern das Zusammenspiel der vier Phasen nachvollziehbar zu machen, damit sie verstehen, wann welcher Teil ihres Codes ausgeführt wird und wie sie Probleme bei der Aktivierung diagnostizieren können.
Im Unterschied zur Architektur-Seite, die das Modell abstrakt beschreibt, zeigt diese Seite den konkreten Ablauf mit Log-Ausgaben und Sequenzen. Der Vorteil besteht darin, dass ein Entwickler beim Debugging sofort erkennen kann, in welcher Phase sich sein Add-in befindet und was als Nächstes passieren müsste.
Zurück zur Übersicht.
Übersicht der Phasen
Phase 1: SCAN Phase 2: RESOLVE Phase 3: REGISTER Phase 4: ACTIVATE
───────────────── ───────────────── ───────────────────── ──────────────────
Plugin-Verzeichnisse Abhängigkeitsgraph Lazy-Command-Stubs DLL laden
↓ aufbauen Menüpunkte GetAbiInfo
plugin.json lesen ↓ Toolbar-Buttons CreatePlugin
↓ Topologisch sortieren Sidebar-Views ↓
NLS-Dateien laden ↓ Keybindings IPlugin.Activate
↓ Engine-Version Konfiguration ↓
Manifest validieren prüfen ↓ Stubs entfernen
↓ ↓ UI steht vollständig Echte Handler
psDiscovered psResolved registrieren
↓
psActive
┌──── Kein Code geladen ────┐ ┌── UI vollständig ──┐ ┌── DLL aktiv ──┐
Phase 1: Scan
Der Host durchsucht zwei Verzeichnisse nach plugin.json-Dateien:
| Scope | Pfad | Zweck |
| Machine | <exe>/plugins/ | Vom Administrator gebundelte Add-ins (MSI/GPO) |
| User | ~/.wvdsx/extensions/ | Vom Benutzer installierte Add-ins (Roaming-Profile-kompatibel) |
Jedes Unterverzeichnis, das eine plugin.json enthält, wird als Kandidat behandelt. Der Host lädt die NLS-Dateien (package.nls.json + sprachspezifische Variante) und ersetzt %platzhalter%-Tokens im Manifest durch die übersetzten Texte.
Danach wird das Manifest validiert. Pflichtfelder wie id, name, version und main müssen vorhanden sein. Fehlende Felder führen dazu, dass das Add-in übersprungen wird — der Host zeigt eine Warnung, bricht aber nicht ab.
Typische Log-Ausgabe:
[INFO] Plugin scanning 2 search path(s) [DEBUG] Plugin discovered: "wvds.amed-wis" v36.01.15.001 (AMED Prüfungsverwaltung (WIS)) [INFO] Plugin found 1 plugin(s)
Fehlerbehandlung beim Scan
- Manifest fehlt — Das Unterverzeichnis wird still übersprungen.
- JSON-Syntaxfehler — Error-Log und Benutzer-Benachrichtigung. Das Add-in wird nicht registriert.
- Doppelte Id — Das zweite Add-in mit derselben Id wird übersprungen. Warning-Log.
- Fehlende Pflichtfelder — Warning-Log mit den konkreten fehlenden Feldern.
- NLS-Datei fehlt — Kein Fehler. Platzhalter bleiben als
%schlüssel%stehen.
Phase 2: Resolve
Der Host baut einen Abhängigkeitsgraphen auf. Quellen für Abhängigkeiten sind:
dependencies— Explizite Add-in-Abhängigkeiten mit Id und Version.servicesConsumed— Der Host sucht das Add-in, das den entsprechenden Vertragsnamen inservicesProvideddeklariert.
Der Graph wird topologisch sortiert (Kahn-Algorithmus). Das Ergebnis ist die Aktivierungsreihenfolge: Abhängigkeiten werden vor ihren Konsumenten aktiviert.
Typische Log-Ausgabe:
[INFO] Plugin resolving dependencies [DEBUG] Plugin activation order: 2 plugin(s) [DEBUG] 1. wvds.core-services [DEBUG] 2. wvds.amed-wis
Fehlerbehandlung bei Resolve
- Zirkuläre Abhängigkeit — Beide betroffenen Add-ins werden als
psErrormarkiert. Error-Log mit den Ids. - Fehlende Abhängigkeit — Das konsumierende Add-in wird übersprungen. Warning-Log.
- Inkompatible Engine-Version — Das Add-in wird übersprungen. Der Host zeigt eine Benachrichtigung mit der erwarteten vs. vorhandenen Version.
Phase 3: Register Contributions
Noch bevor eine einzige DLL geladen wird, registriert der Host alle statischen Contributions aus den Manifesten. Für jeden deklarierten Command wird ein Lazy-Stub angelegt — ein Platzhalter-Handler, der bei der ersten Ausführung die Aktivierung des Add-ins auslöst.
Gleichzeitig fließen die Manifest-Contributions in die zentrale Contribution-Sammlung (TWvdSShellContributions), die der TWvdSContributionRouter anschließend an die UI-Komponenten verteilt:
- Commands →
TWvdSCommandRouter→ CommandPalette - Menüs →
TWvdSMainMenu(Menüleiste) - Submenüs → Top-Level-Menüs in der MenuBar
- Toolbar →
TWvdSShellToolbar(Haupt-Toolbar) - Views →
TWvdSActivityBar+TWvdSSideBar(Seitenleiste) - Keybindings →
TWvdSCommandRouter(Tastenkürzel)
Typische Log-Ausgabe:
[INFO] Plugin registering contributions for 1 plugin(s) [DEBUG] Plugin "wvds.amed-wis": 10 commands, 11 menus, 2 keybindings [INFO] Plugin contributions fed: 10 commands, 11 menus, 3 views [INFO] Routed 10 commands [DEBUG] TWvdSSideBar.RegisterView: id="wis.orgTree" title="ORGANISATIONSSTRUKTUR" [DEBUG] TWvdSSideBar.RegisterView: id="wis.filter" title="FILTER" [INFO] Routed 5 toolbar items
Beachtenswert: Die Oberfläche steht jetzt vollständig. Der Benutzer sieht alle Menüpunkte, Toolbar-Buttons, Sidebar-Panels und Tastenkürzel — obwohl keine einzige DLL geladen wurde. Das ist der zentrale Vorteil des deklarativen Contribution-Modells.
Phase 4: Activate
Die Aktivierung verläuft in zwei Varianten:
Eager-Aktivierung (onStartup)
Add-ins mit activationEvents: [„onStartup“] werden sofort nach Phase 3 aktiviert. Das ist typisch für Sidebar-Panels, die beim Start sichtbar sein sollen.
Lazy-Aktivierung (onCommand)
Add-ins mit activationEvents: [„onCommand:mein.command“] werden erst beim ersten Aufruf des Commands aktiviert. Der Lazy-Stub fängt den Aufruf ab und löst folgende Schritte aus:
- Lazy-Stubs entfernen — Alle Stubs des Add-ins werden disposed, damit die Command-Namen für die echten Handler frei sind.
- DLL laden —
LoadLibrarylädt die DLL in den Prozess. - ABI-Handshake —
GetAbiInfowird aufgerufen. Der Host prüft Magic ($57564453), ABI-Version und FPC-Version. - Plugin erzeugen —
CreatePlugin(IHost)wird aufgerufen. Das Add-in erhält Zugriff auf alle Host-Services. - Activate —
IPlugin.Activate(IExtensionContext)wird aufgerufen. Das Add-in registriert echte Command-Handler, Document-Factories und Event-Abonnements. - Command erneut ausführen — Der ursprünglich aufgerufene Command wird erneut dispatcht — diesmal an den echten Handler.
Typische Log-Ausgabe:
[INFO] Plugin lazy activation "wvds.amed-wis" by command "wis.proofExec.open" [DEBUG] Plugin DLL loaded: bin/WIS.Plugin.dll [DEBUG] Plugin ABI handshake: magic=$57564453, abi=1, fpc=30301 [INFO] Plugin "wvds.amed-wis" activated successfully
Fehlerbehandlung bei der Aktivierung
- DLL nicht gefunden — Error-Log. Add-in wird als
psErrormarkiert. - ABI-Mismatch — DLL wird sofort entladen. Error-Log mit erwarteten vs. gefundenen Werten. Typische Ursache: Add-in mit einer anderen FPC-Version kompiliert als der Host.
- CreatePlugin wirft Exception — Exception wird gefangen, geloggt, Add-in als
psErrormarkiert. - Activate wirft Exception — Exception wird gefangen, Fehlerzähler inkrementiert. Nach drei aufeinanderfolgenden Fehlern wird das Add-in automatisch deaktiviert.
Deaktivierung
Beim Herunterfahren der Anwendung durchläuft jedes aktive Add-in die Deaktivierung:
- IPlugin.Deactivate — Das Add-in kann eigene Aufräumarbeiten durchführen. Die meisten Add-ins lassen diese Methode leer.
- IExtensionContext.DisposeAll — Alle über
SubscribegesammeltenIDisposable-Tokens werden in umgekehrter Reihenfolge (LIFO) aufgelöst. Command-Handler, Menüpunkte, Factories und Event-Abonnements werden entfernt. - FreeLibrary — Die DLL wird entladen.
Die LIFO-Reihenfolge ist wichtig: Wenn ein Menüpunkt auf einen Command verweist, wird der Menüpunkt vor dem Command entfernt. Dadurch gibt es keine Dangling References.
Troubleshooting
Add-in erscheint nicht
| Symptom | Mögliche Ursache | Diagnose |
| Kein Log-Eintrag | Plugin-Verzeichnis nicht im Suchpfad | Prüfe, ob plugin.json in <exe>/plugins/<name>/ oder ~/.wvdsx/extensions/<name>/ liegt |
Plugin found 0 | Verzeichnis existiert nicht | Erstelle das Verzeichnis und kopiere plugin.json hinein |
invalid manifest | Pflichtfeld fehlt | Prüfe Log auf fehlende Felder (id, name, version, main) |
%displayName% in der UI | NLS-Datei fehlt oder package.nls.json enthält den Schlüssel nicht | Prüfe, ob package.nls.json alle %platzhalter% abdeckt |
Command in Palette nicht sichtbar
| Symptom | Mögliche Ursache | Diagnose |
Kein Eintrag trotz Routed N commands | Command wurde nicht in contributes.commands deklariert | Prüfe plugin.json auf Tippfehler im Command-Namen |
Routed 0 commands | Contributions wurden nicht in die Routing-Pipeline gespeist | Prüfe Log auf Plugin contributions fed: N commands |
DLL wird nicht geladen
| Symptom | Mögliche Ursache | Diagnose |
DLL not found | main-Pfad im Manifest stimmt nicht mit der Datei überein | Prüfe main: „bin/MyPlugin.dll“ gegen die reale Dateistruktur |
ABI mismatch | DLL mit anderer FPC-Version kompiliert | Kompiliere mit derselben FPC-Version wie der Host |
CreatePlugin failed | Exception im Constructor | Starte im Debug-Modus: wdochost –debug-extension=mein.addin |
Weiter zur Architektur oder zurück zur Übersicht.