Dieser Artikel entstand aus einem lockeren Gespräch darüber, wie weit man unter Windows überhaupt von einer Single Binary sprechen darf. Ausgangspunkt war der folgende Beitrag. Sie können diesen Abschnitt überspringen – der Rest des Artikels lässt sich auch eigenständig lesen.
Unter Windows ist „wenn möglich als eine einzige Datei verteilen“ ein ganz gewöhnlicher Wunsch. Für interne Tools, Anbindungswerkzeuge für Geräte, Überwachungsterminals, Offline-Umgebungen und Umgebungen, die Installationsprogramme möglichst vermeiden wollen, ist die Umstellung auf eine Single Binary durchaus attraktiv.
Allerdings passt das Gespräch meist nicht mehr zusammen, wenn man es nicht vorher entwirrt. Denn hinter „wir wollen eine Single Binary“ unter Windows verstecken sich in der Praxis vier verschiedene Anliegen.
- Wir wollen ein einziges Verteilungsartefakt
- Wir wollen die .NET- oder Visual-C++-Laufzeit nicht vorab installieren müssen
- Wir wollen ohne Installationsprogramm und ohne Administratorrechte einfach nur ablegen und starten können
- Wir wollen nicht von Unterschieden zwischen Ziel-Windows-Versionen abhängen
Diese vier Anliegen sind nicht dasselbe. In der Praxis führt diese Denkweise am wenigsten in die Irre:
Ein Verteilungspaket auf 1 EXE zu reduzieren, ist gut möglich. Die Abhängigkeit vom Ziel-Windows auf null zu bringen, ist aber unmöglich.
Dieser Artikel arbeitet diese Grenze für die praktische Arbeit an Windows-Apps heraus.
1. Zuerst das Fazit
Zusammengefasst vorab:
- Bei einer gewöhnlichen Desktop-EXE lässt sich die Single-Binary-Umstellung ziemlich weit treiben
- Allerdings sind die Fähigkeit, auf 1 EXE zu reduzieren und die Unabhängigkeit vom Ziel-Windows zwei verschiedene Dinge
- Bei Shell-Erweiterungen, Windows-Diensten, Treibern, WebView2 und Teilen von WinUI 3 geht es weniger um die Dateianzahl als vielmehr darum, was beim OS registriert wird und was vorausgesetzt wird
- In der Praxis ist es am wichtigsten, getrennt zu entscheiden, ob Sie eine Single Binary wollen, ob Sie kein Installationsprogramm wollen oder ob Sie die OS-Abhängigkeit verringern wollen
Anders ausgedrückt sieht die Grenzziehung unter Windows so aus:
- Verteilung auf ein einziges Artefakt reduzieren: gut möglich
- Zusätzliche Laufzeiten mitbringen: gut möglich
- Auf xcopy-Verteilung setzen: hängt vom App-Typ ab
- Abhängigkeit vom Ziel-Windows beseitigen: unmöglich
1.1 Begriffe, die in diesem Artikel verwendet werden
Zuerst eine Übersicht über die Begriffe, die im Folgenden immer wieder auftauchen.
| Begriff | Aussprache/Ausschreibung | Bedeutung in diesem Artikel |
|---|---|---|
| UCRT | Universal C Runtime | Der Teil der Standard-C-Bibliothek, der entstand, als die C-Laufzeit in Visual Studio 2015 aufgeteilt wurde. Ab Windows 10 ist er als Bestandteil des Betriebssystems enthalten |
| VC++-Redistributable-Paket | Visual C++ Redistributable | Ein Installationsprogramm, das die Visual-C++-Laufzeit-DLLs außer der UCRT auf dem Zielrechner installiert |
| framework-dependent | Abhängig vom .NET der Zielumgebung | Eine Verteilungsform, die voraussetzt, dass auf dem Zielrechner bereits eine .NET-Laufzeit installiert ist |
| self-contained | .NET-Laufzeit inklusive | Eine Form, bei der die App die komplette .NET-Laufzeit selbst mitbringt |
| single-file | Einzeldatei-Veröffentlichung | Eine .NET-Veröffentlichungsoption, die das Verteilungspaket zu einer einzigen EXE-Datei zusammenfasst |
| Native AOT | Native Ahead-Of-Time | Eine Verteilungsform, bei der IL beim Veröffentlichen vorab in nativen Code kompiliert wird. Zur Laufzeit kommt kein JIT zum Einsatz |
| app-local-Verteilung | App-nahe Verteilung | Eine Verteilungsart, bei der DLLs im selben Ordner wie die EXE liegen und nur von dieser App genutzt werden |
| xcopy-Verteilung | Verteilung durch reines Kopieren | Eine Verteilungsart, die ohne Installationsprogramm und ohne Administratorrechte auskommt – es reicht, den ganzen Ordner abzulegen |
| SCM | Service Control Manager, Dienststeuerungs-Manager | Der Mechanismus des Betriebssystems zur Registrierung, zum Starten und Stoppen von Windows-Diensten |
| UAC | User Account Control, Benutzerkontensteuerung | Der Windows-Sicherheitsmechanismus, der die Erhöhung auf Administratorrechte steuert |
| Shell-Erweiterung | Shell-Erweiterung | Eine COM-Komponente, die in Prozesse wie den Explorer geladen wird und dort läuft |
| Evergreen | Dauerhaft aktualisiertes Verfahren | Ein Verteilungsmodus, bei dem die WebView2 Runtime als eine gemeinsam genutzte, automatisch aktualisierte Instanz bereitgestellt wird |
| Fixed Version | Versionsfestes Verfahren | Ein Verteilungsmodus, bei dem eine bestimmte Version der WebView2 Runtime der eigenen App beigelegt wird |
| arch | architecture, CPU-Architektur | x86 / x64 / Arm64 |
2. „Single Binary“ in vier Stufen denken
Stellen wir die Stufen zuerst als Diagramm dar. Von unten nach oben wird es schwieriger, und die oberste Stufe ist unter Windows nicht erreichbar.
flowchart BT
A["Stufe A: Verteilung besteht aus 1 Datei<br/>gut möglich"]
B["Stufe B: Keine vorherige Installation der Sprachlaufzeit nötig<br/>gut möglich"]
C["Stufe C: Keine Installation oder Registrierung nötig<br/>je nach App-Typ"]
D["Stufe D: Keine Abhängigkeit vom Ziel-Windows<br/>unter Windows nicht erreichbar"]
A --> B
B --> C
C --> D
Was mit „wir wollen eine Single Binary“ genau gemeint ist, hängt davon ab, auf welche dieser vier Stufen sich der Wunsch bezieht – und das entscheidet, welche Arbeit tatsächlich nötig ist.
2.1 Stufe A: Verteilung besteht aus 1 Datei
Das ist die oberflächlichste Stufe.
- Man kann eine einzige Datei per E-Mail verschicken
- Man muss nur eine Datei auf einen USB-Stick legen
- Am Zielort liegt nur
app.exe
Das betrifft die sichtbare Verteilungseinheit. Auch wenn die App beim Start temporär Dateien entpackt oder von OS-seitigen DLLs abhängt, kann diese Bedingung allein erfüllt sein.
2.2 Stufe B: Keine vorherige Installation der Sprachlaufzeit nötig
Als Nächstes: Der Zustand, in dem die App läuft, ohne dass vorab eine .NET-Laufzeit oder das VC++-Redistributable-Paket auf dem Zielrechner installiert werden muss.
- Statisches Linken bei C/C++
- .NET self-contained
- .NET single-file
- .NET Native AOT
Auf dieser Stufe wird das Gefühl „das lässt sich eigenständig mitnehmen“ deutlich stärker.
2.3 Stufe C: Keine Installation oder Registrierung nötig
Ab hier wird es plötzlich schwierig.
Eine einfache EXE funktioniert manchmal schon, wenn man sie nur ablegt. Diese Dinge hier sind jedoch etwas anderes.
- Shell-Erweiterungen
- Windows-Dienste
- Benutzerdefinierte URL-Schemas oder Dateizuordnungen
- Treiber
- Komponenten, die in andere Prozesse wie Explorer oder Office geladen werden
In diesem Bereich reicht das reine Ablegen der Datei nicht aus. Es braucht Registrierung auf OS-Seite oder eine Verdrahtung mit dem Host.
2.4 Stufe D: Keine Abhängigkeit vom Ziel-Windows
Das ist unter Windows unmöglich.
Windows-Apps laufen letztlich immer auf den APIs, dem Loader, dem Sicherheitsmodell und dem Gerätestack von Windows. Die Single-Binary-Umstellung deckt nur den Verantwortungsbereich der App selbst ab. Das Betriebssystem selbst wird dabei nicht mitgenommen.
3. Bereiche, in denen sich 1 EXE gut erreichen lässt
Auch unter Windows gibt es Apps, die sich vergleichsweise gut auf 1 EXE reduzieren lassen.
- Eigenständig gestartete Desktop-Tools
- Business-Apps, bei denen die EXE selbst UI und Verarbeitung übernimmt
- Werkzeuge für Kommunikation, Dateiverarbeitung, Log-Sammlung, Überwachung oder Gerätesteuerung
- Anwendungen, die keine Host-Integration mit Explorer oder Office benötigen
- UIs, die keine Web-Laufzeit voraussetzen
Bei diesem Typ lässt sich vieles problemlos in die App selbst integrieren.
- Eigener Code
- Ressourcen
- Manifest
- Standardeinstellungen
- Vorlagendaten
- Manche Drittanbieter-Bibliotheken
- Die Sprachlaufzeit selbst
Auch ohne DLLs vollständig in die EXE einzubetten, ist die app-local-Verteilung, bei der DLLs neben der EXE liegen, unter Windows eine ganz gewöhnliche, starke Option. In der Praxis heißt das:
- eine einzelne
app.exe - oder
app.exeplus ein paar benachbarte DLLs - dabei kein Installationsprogramm, keine Administratorrechte, per xcopy verteilbar
Diese Form ist nicht selten leichter zu pflegen, als alles gewaltsam in eine einzige EXE zu pressen.
4. Windows-Abhängigkeiten, die auch bei 1 EXE bestehen bleiben
Wer davon ausgeht, „eine EXE bedeutet keine Abhängigkeit vom Ziel-Windows mehr“, gerät genau hier in Schwierigkeiten. Tatsächlich bleiben auch bei einer einzigen EXE Abhängigkeiten bestehen.
4.1 Abhängigkeit von der OS-Version
Jede Windows-API hat ein minimal unterstütztes OS. Auch die Unterschiede zwischen x64 und Arm64 spielen eine Rolle. Selbst bei einer einzigen EXE muss also von Anfang an feststehen:
- Läuft sie noch unter Windows 10?
- Wird Windows 11 vorausgesetzt?
- Muss sie auch unter Windows Server laufen?
- Welche der Architekturen x86 / x64 / Arm64 werden anvisiert?
4.2 Abhängigkeit von Systembibliotheken
Auch wenn man meint, eine einzige EXE zu haben, nutzt sie zur Laufzeit selbstverständlich vom OS bereitgestellte Komponenten.
kernel32.dlluser32.dlladvapi32.dll- Die COM-Infrastruktur
- Die Infrastruktur der Dienststeuerung
Das liegt im Verantwortungsbereich von Windows.
4.3 Abhängigkeit vom Sicherheitsmodell
- UAC
- Datei-ACLs
- Der Service Control Manager
- Die Registry
- Die Richtlinie zur Treibersignierung
Solche Dinge kann die App nicht allein in sich tragen.
4.4 Abhängigkeit von Host und Laufzeit
Handelt es sich nicht um eine eigenständig gestartete EXE, sondern um ein Design, das auf einem Host aufsetzt, steigen die Abhängigkeiten schlagartig.
- Wird WebView2 verwendet: Die WebView2 Runtime wird benötigt
- Wird WinUI 3 / Windows App SDK verwendet: Der Verteilungsmodus muss geklärt werden
- Wird eine Shell-Erweiterung gebaut: Die Registrierung auf Explorer-Seite ist erforderlich
Mit anderen Worten: Die Wahl der UI oder der Integration wird häufig direkt zur Schwierigkeit der Verteilung.
5. Realistische Kompromisse nach Technologie
5.1 Natives C/C++
Natives C/C++ steht auf der Seite mit der größten Freiheit bei der Single-Binary-Umstellung. Statisches Linken ist eine Option, und eine eigenständig gestartete EXE lässt sich ziemlich weit reduzieren.
Wichtiger als alles in eine einzige Datei zu pressen ist in der Praxis aber:
- Wie mit UCRT und der VC++-Laufzeit umgegangen wird
- Ob Drittanbieter-DLLs app-local abgelegt werden
- Wie eng die Ziel-CPU / das Ziel-OS eingegrenzt wird
Einstieg: Der Unterschied zwischen /MT und /MD
Bei MSVC entscheidet im Wesentlichen diese Compiler-Option darüber, ob die Laufzeit mitgebracht wird.
| Option | Was gelinkt wird | Was auf dem Zielrechner benötigt wird |
|---|---|---|
/MD |
ucrt.lib und vcruntime.lib, die Import-Bibliotheken für die DLLs. Zur Laufzeit werden ucrtbase.dll und vcruntime<Version>.dll verwendet |
UCRT und VC++-Laufzeit |
/MT |
libucrt.lib, libvcruntime.lib und libcmt.lib werden statisch gelinkt |
Keine zusätzliche Laufzeit |
Am einfachsten probiert man das in der Developer Command Prompt aus:
cl /std:c++17 /EHsc /O2 /MT app.cpp /Fe:app.exe
Dabei sind drei Punkte zu beachten.
- Die UCRT wurde mit der Aufteilung der C-Laufzeit in Visual Studio 2015 zu einem Bestandteil von Windows und ist ab Windows 10 als Teil des Betriebssystems enthalten. Soll ein älteres Windows als Ziel dienen, ist eine erneute Verteilung über
vcredisterforderlich. - Eine DLL mit
/MTzu bauen, wird nicht empfohlen. Da der statisch gelinkte CRT-Zustand nur innerhalb dieser DLL gekapselt ist, weichen Speicherzuweisung, Locale-Verhalten und die Wirkung von_set_se_translatorzwischen EXE und DLL voneinander ab. „EXE mit/MT, mitgelieferte DLL ebenfalls mit/MT“ nach Gefühl zu vereinheitlichen, führt zu Problemen bei Zuweisung und Freigabe über die Grenze hinweg. /clrund/MTlassen sich nicht kombinieren. Kommt C++/CLI ins Spiel, ist/MDdie richtige Wahl.
5.2 .NET
.NET bietet single-file, self-contained und Native AOT, sodass sich die sichtbare Verteilungseinheit ziemlich klein halten lässt.
Die Unterscheidung ist aber notwendig.
- framework-dependent: Hängt vom .NET der Zielumgebung ab
- self-contained: Bringt die .NET-Laufzeit mit
- single-file: Reduziert die Verteilung auf ein Artefakt
- Native AOT: Reduziert die Startzeit-Abhängigkeiten weiter, bringt aber auch Funktionseinschränkungen mit sich
„Single-file, also weniger OS-Abhängigkeit“ stimmt nicht. Was dadurch vor allem reduziert wird, ist, wie zusammenhängend das Verteilungspaket der App ist.
Einstieg: Die 3 Muster von dotnet publish
Um die Unterschiede erst einmal auszuprobieren, reichen diese drei Befehle.
rem 1. framework-dependent + single-file: Auf dem Zielrechner wird eine .NET-Laufzeit benötigt
dotnet publish -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true
rem 2. self-contained + single-file: Die .NET-Laufzeit wird mitgeliefert
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
rem 3. Native AOT: PublishAot muss vorher in der csproj gesetzt sein, dann veröffentlichen
dotnet publish -c Release -r win-x64
PublishSingleFile und PublishAot besser in der csproj als auf der Kommandozeile anzugeben, ist die offizielle Empfehlung. Dadurch wird die Kompatibilitätsanalyse während des Builds aktiv, was Überraschungen nach der Veröffentlichung reduziert.
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
<PublishAot>true</PublishAot>
</PropertyGroup>
Wird RuntimeIdentifier angegeben, ist SelfContained standardmäßig true. Soll es framework-dependent sein, muss --self-contained false explizit gesetzt werden. Um Native AOT unter Windows zu veröffentlichen, wird die Visual-Studio-Workload Desktopentwicklung mit C++ benötigt.
Kompromiss: „Auf eins reduzieren“ ist nicht umsonst
Es reicht nicht zu schreiben, was „geht“ – hier stehen auch die Kosten.
Startzeit-Entpacken bei single-file
- Standardmäßig werden nur die managed DLLs gebündelt. Sie werden beim Start in den Speicher geladen, nicht in einen Ordner entpackt. Die nativen Binärdateien der Laufzeit selbst bleiben als eigene Dateien bestehen.
- Sollen auch die nativen Binärdateien in einer einzigen Datei enthalten sein, kommt
IncludeNativeLibrariesForSelfExtractzum Einsatz; soll vor dem Start alles entpackt werden,IncludeAllContentForSelfExtract. Beide bewirken „erst entpacken, dann starten“, und unter Windows wird dabei nach%TEMP%\.netentpackt. MitDOTNET_BUNDLE_EXTRACT_BASE_DIRlässt sich der Ort ändern – wählen Sie aber keinen Ort, der von Benutzern oder Diensten mit anderen Rechten beschreibbar ist. - Wird
EnableCompressionInSingleFileaktiviert, wird die EXE deutlich kleiner, aber der Start dauert länger, weil beim Start im Speicher entpackt werden muss. Auch die offizielle Dokumentation rät, vor dem Einsatz sowohl Größe als auch Startkosten zu messen. Die Auswirkung schwankt stark von App zu App.
API-Inkompatibilitäten bei single-file
Wird die Verteilung auf eine Datei reduziert, bricht Code, der von Dateipfaden ausgeht, still und leise.
| API | Verhalten bei single-file |
|---|---|
Assembly.Location |
Gibt einen leeren String zurück |
Assembly.CodeBase |
PlatformNotSupportedException |
Assembly.GetFile |
IOException |
Module.Name |
Gibt den String <Unknown> zurück |
Wollen Sie auf Dateien neben der EXE zugreifen, verwenden Sie AppContext.BaseDirectory; brauchen Sie den Pfad der ausführbaren Datei, Environment.ProcessPath. Auch Drittanbieter-Bibliotheken nutzen diese APIs mitunter intern, weshalb nach der Umstellung auf single-file mindestens einmal ein Starttest auf echter Hardware nötig ist.
Funktionseinschränkungen bei Native AOT
Als Gegenleistung für „schnellerer Start, keine Laufzeit nötig“ entfällt Folgendes.
- Dynamisches Laden wie bei
Assembly.LoadFile - Codeerzeugung zur Laufzeit wie bei
System.Reflection.Emit - C++/CLI
- Unter Windows eingebettetes COM
- Trimming ist Pflicht, daher gelten auch dessen Einschränkungen
- Da es faktisch single-file ist, gelten auch die oben genannten API-Inkompatibilitäten
System.Linq.Expressionsläuft immer interpretiert und ist damit langsamer als zur Laufzeit erzeugter Code
Auch die Zielplattform ist fest vorgegeben. Unter .NET 8 werden für Windows x64 und Arm64 unterstützt, ab .NET 9 kommt x86 hinzu. Der Gedanke „AnyCPU als eine einzige Datei“ funktioniert von vornherein nicht.
5.3 WebView2
Mit WebView2 ändert sich die Schwierigkeit der Single-Binary-Umstellung grundlegend. Hier geht es nicht um die Anzahl der EXE-Dateien, sondern um den Umgang mit der WebView2 Runtime.
Noch vor der Frage „lässt sich das auf 1 EXE reduzieren?“ stehen andere Fragen im Raum.
- Wird die Runtime als in der Zielumgebung bereits vorhanden vorausgesetzt?
- Wird Evergreen verwendet?
- Wird eine Fixed Version mitgeliefert?
- Wie weit trägt man selbst die Verantwortung für die Offline-Verteilung?
Der Unterschied zwischen Evergreen und Fixed Version lässt sich grob so zusammenfassen:
| Evergreen | Fixed Version | |
|---|---|---|
| Wer aktualisiert | Microsoft. Automatische Aktualisierung | Sie selbst. Ausgetauscht passend zu App-Updates |
| Instanz auf dem Gerät | Eine gemeinsame Instanz für alle Apps | Für jede App separat mitgeliefert |
| Verteilungsgröße | Wächst kaum | Überschreitet 250 MB |
| Installationsart | Der Bootstrapper ist rund 2 MB groß und lädt nur das Nötige herunter. Für Offline-Verteilung wird der Standalone Installer mitgeliefert | Das entpackte Binärpaket wird komplett zusammen mit der App verteilt |
| Bestehende Einschränkung | Muss vor dem Start geprüft werden, ob sie auf dem Gerät vorhanden ist | Kann nicht über einen Netzwerkpfad oder UNC-Pfad ausgeführt werden |
Die Evergreen Runtime ist auf Windows 11 bereits als Bestandteil des Betriebssystems vorinstalliert. Allerdings gibt es unter Windows 10 nach wie vor Geräte, auf denen sie fehlt, weshalb Microsoft selbst schreibt, dass die Runtime auch bei der Wahl von Evergreen mitverteilt werden sollte. Mit anderen Worten: Das Verfahren, auf App-Seite selbst zu prüfen, ob sie vorhanden ist, und sie bei Bedarf nachzuinstallieren, ist so oder so nötig. Fixed Version erlaubt es, den Zeitpunkt der Aktualisierung selbst in der Hand zu behalten, wächst dafür aber um mehrere hundert MB. Losgelöst von der Frage nach der 1-EXE-Lösung muss das vorher geklärt werden.
5.4 WinUI 3 / Windows App SDK
Auch WinUI 3 ändert allein durch seine Wahl die Verteilungsanforderungen. Die Wahl der UI-Technologie ist unmittelbar die Wahl der Verteilungsmethode.
Konkret gibt es zwei Achsen, die zu entscheiden sind.
- packaging: packaged (MSIX), packaged mit Verweis auf einen externen Ort, oder unpackaged
- runtime: framework-dependent oder self-contained
Und aus Sicht der Single Binary wirkt sich diese Kombinationseinschränkung aus:
PublishSingleFilefunktioniert nur bei WinUI-3-Apps, die unpackaged und self-contained sind, und setzt Windows App SDK 1.5 oder neuer voraus.- Bei packaged Apps und bei packaged Apps mit Verweis auf einen externen Ort funktioniert
PublishSingleFilenicht. - Bei unpackaged geht die package identity verloren, wodurch Windows-Funktionen, die diese voraussetzen – Benachrichtigungen, Hintergrundaufgaben, Dateizuordnungen, Erweiterungen des Kontextmenüs –, nicht mehr genutzt werden können.
Bei WinUI 3 stoßen „auf 1 EXE reduzieren“ und „Windows-Erweiterungsfunktionen nutzen“ also direkt aufeinander. Wer die Single Binary in den Vordergrund stellen will, kommt oft schneller ans Ziel, wenn er zuerst die Grundannahme der UI-Technologie überdenkt.
6. Bereiche, die grundsätzlich Registrierung und Abhängigkeiten erfordern
6.1 Shell-Erweiterungen
Eine in den Explorer geladene Shell-Erweiterung ist etwas ganz anderes als eine „einfach abgelegte EXE“. Hier geht es weniger um die Dateianzahl als vielmehr darum, wie beim Explorer registriert wird.
6.2 Windows-Dienste
Auch wenn sich die eigentliche exe des Dienstes auf eine Datei reduzieren lässt, ist die Verteilung ein eigenes Thema.
- Registrierung bei der SCM
- Rechte
- Startkonto
- Wiederherstellungseinstellungen
müssen bedacht werden. Bei Diensten geht es also weniger darum, „auf 1 EXE zu reduzieren“, als vielmehr darum, „wie installiert wird“.
6.3 Treiber
Bei Treibern ist das noch eindeutiger. Sie stehen erst mit INF, Signierung und Installationsverfahren zusammen, weshalb sie von vornherein kaum auf dem Spielfeld der Single Binary landen.
7. Entscheidungstabelle für die Praxis
Für eine grobe Einschätzung ist diese Tabelle hilfreich.
| Was gebaut werden soll | Realistischer Grad für 1 EXE | Was zuerst zu klären ist |
|---|---|---|
| Eigenständig gestartetes Win32-/C++-Tool | Hoch | Statisches Linken, Ziel-OS / -Architektur |
| Eigenständig gestartetes WinForms-/WPF-Tool | Hoch | Eignung von self-contained, single-file, Native AOT |
| WinUI 3 / Windows App SDK-App | Mittel | Verteilungsmodus, zusätzliche Abhängigkeiten |
| WebView2-basierte Desktop-UI | Niedrig bis mittel | Verteilungsart der Runtime |
| Explorer-Kontextmenü-Erweiterung oder Vorschau-Handler | Niedrig | COM-/Registry-Registrierung |
| Windows-Dienst | Mittel | SCM-Registrierung, Rechte, Aktualisierungsverfahren |
| App mit gebündeltem Treiber | Niedrig | INF, Signierung, Installation |
Das Wichtigste an dieser Tabelle ist die Erkenntnis, dass „Anzahl der Binärdateien“ und „Umfang der Verteilungsverantwortung“ zwei verschiedene Dinge sind.
8. Was beim Verteilungsdesign zuerst zu entscheiden ist
Wer die Single-Binary-Umstellung zum Erfolg führen will, sollte vor der Implementierung einige Dinge festlegen.
8.1 Klären, was genau auf eins reduziert werden soll
- Wollen Sie ein einziges Verteilungsartefakt?
- Wollen Sie die vorherige Runtime-Installation loswerden?
- Wollen Sie das Installationsprogramm loswerden?
- Wollen Sie Offline-Updates vereinfachen?
Die Antwort bestimmt, welche Technologie gewählt wird.
8.2 Das minimal unterstützte Windows und die Architektur zuerst festlegen
Sowohl single-file als auch Native AOT sind grundsätzlich OS- und architekturspezifisch. Bleibt das unklar und man geht einfach nach dem Motto „irgendwie als eine Datei“ vor, gerät man am Ende wegen fehlender APIs oder inkompatibler Laufzeiten ins Stocken.
8.3 Festhalten, was mitgeliefert wird und was Windows überlassen bleibt
In der Praxis lassen sich viele Probleme allein dadurch vermeiden, dass man diese Tabelle einmal aufschreibt.
- Was die App mitliefert
- Die eigentliche exe
- Eigene DLLs
- Konfigurationsvorlagen
- Die self-contained-Laufzeit
- Was Windows überlassen bleibt
- Systembibliotheken
- OS-APIs
- SCM / Registry / Explorer
- Die Treiberinfrastruktur
- Was separat als Voraussetzung angenommen wird
- WebView2 Runtime
- VC++ Redistributable
- Office / Excel
- Dedizierte Treiber
8.4 Wer die Single Binary priorisiert, sollte die Host-Integration verringern
Das wirkt ziemlich stark.
- Auf die Shell-Erweiterung verzichten und eine normale EXE daraus machen
- Nicht als Dienst laufen, sondern Aufgabenplanung oder expliziten Start nutzen
- Statt WebView2 eine native UI verwenden
- COM innerhalb des eigenen Prozesses kapseln
Kurz gesagt: Je mehr man Designs vermeidet, bei denen das OS etwas „lädt“ oder „registriert“, desto näher kommt man der Single Binary.
9. Zusammenfassung
Die Single-Binary-Umstellung unter Windows ist bis zu einem gewissen Grad durchaus möglich. Am Ende läuft aber alles auf diesen einen Satz hinaus:
Eine App auf 1 EXE zu reduzieren, ist möglich. Aber das Windows, von dem diese App abhängt, lässt sich nicht auf 1 EXE reduzieren.
Fünf Punkte, die besonders im Gedächtnis bleiben sollten:
- Bei einer gewöhnlichen, eigenständig gestarteten EXE lässt sich die Verteilung als Einzeldatei ziemlich weit vorantreiben
- Statisches Linken bei C/C++, .NET single-file und Native AOT sind starke Optionen
- Allerdings verschwinden die Abhängigkeiten von OS-Version, Architektur, Systembibliotheken und Sicherheitsmodell nicht
- Bei Shell-Erweiterungen, Diensten, Treibern, WebView2 und Teilen von WinUI 3 werden OS-Registrierung und zusätzliche Laufzeiten zum eigentlichen Thema
- Ob die Single Binary gelingt, entscheidet sich daran, ob man von Anfang an klar trennt, was genau auf eins reduziert werden soll
Wer die Single Binary stark priorisiert, hat weit bessere Erfolgschancen, wenn er bereits bei der Technologieauswahl darauf achtet, die Kopplung mit dem OS zu verringern.
10. Quellen
- Microsoft Learn, Create a single file for application deployment
- Microsoft Learn, Native AOT deployment overview
- Microsoft Learn, C runtime (CRT) and C++ standard library (STL) lib files
- Microsoft Learn, Dynamic-link library search order
- Microsoft Learn, Targeting your application for Windows
- Microsoft Learn, Creating Registration-Free COM Objects
- Microsoft Learn, Registering Shell Extension Handlers
- Microsoft Learn, CreateServiceW function
- Microsoft Learn, Overview of INF Files
- Microsoft Learn, Windows driver signing tutorial
- Microsoft Learn, Distribute your app and the WebView2 Runtime
- Microsoft Learn, Windows App SDK deployment guide for self-contained apps
- Microsoft Learn, Package and deploy Windows apps overview
- Microsoft Learn, Packaging overview - Windows apps
- Microsoft Learn, Evergreen vs. fixed version of the WebView2 Runtime
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Ist WebView2 der richtige Nachfolger für den IE-Modus? ── Die ActiveX-Einschränkung und ein realistisches Migrationsdesign
Wir behandeln die Grundstruktur von WebView2, die Verteilungsstrategien Evergreen und Fixed Version, die Falle der Benutzerdatenordner, d...
Windows-App-Outsourcing und Auftragsentwicklung: Was Sie vor der Beauftragung klären sollten
Bevor Sie die Entwicklung einer Windows-App outsourcen oder in Auftrag geben, sollten Sie folgende Punkte klären: Überarbeitung bestehend...
Was ist ClickOnce? - Funktionsweise, Updates und wo es passt oder nicht, aus praktischer Sicht
Ein Überblick über ClickOnce, die Verteilungstechnologie für .NET-Windows-Desktopanwendungen: Manifeste, Updates, Cache, Signierung sowie...
Eine Checkliste für den sicheren Umgang mit Kindprozessen in Windows-Apps
Beim sicheren Umgang mit Kindprozessen in Windows-Apps zählt weniger die Start-API als der Besitz des Prozessbaums und das Design der Bee...
Praktische Multithreading-Best-Practices: C++-Edition — Unfälle mit RAII und jthread strukturell beseitigen
In C++ ist Multithreading eine Welt, in der eine Datenrace zu undefiniertem Verhalten wird. Dieser Artikel behandelt die Falle des std::t...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Bei der Verteilung von Windows-Apps lässt sich Nacharbeit vermeiden, wenn Single-File-Packaging, Runtime-Bundling, die Entscheidung für WebView2 oder WinUI sowie die Frage, ob eine Dienstanwendung sinnvoll ist, zusammen entworfen werden.
Technische Beratung und Design-Review
Der Wunsch „wir wollen eine einzige EXE“ lässt sich leichter entscheiden, wenn man Verteilungseinheit, OS-Abhängigkeit, Registrierungspflicht und Update-Verantwortung getrennt betrachtet.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Kann man eine Windows-App als eine einzige EXE-Datei verteilen?
- Bei einem eigenständig gestarteten Desktop-Tool geht das ziemlich weit. Mit statischem Linken bei C/C++, mit self-contained oder single-file bei .NET oder mit Native AOT lässt sich das Verteilungspaket auf eine einzige EXE reduzieren. Allerdings sind „auf 1 EXE reduzierbar“ und „unabhängig vom Ziel-Windows“ zwei verschiedene Dinge – die Abhängigkeit von OS-Version, Architektur, Systembibliotheken und Sicherheitsmodell verschwindet dadurch nicht.
- Entfällt bei .NET single-file die OS-Abhängigkeit?
- Nein. Was single-file vor allem reduziert, ist, wie zusammenhängend das Verteilungspaket der App ist – nicht die OS-Abhängigkeit. Framework-dependent hängt vom .NET der Zielumgebung ab, self-contained bringt die .NET-Laufzeit mit, und Native AOT reduziert die Startzeit-Abhängigkeiten weiter, hat aber auch Funktionseinschränkungen. Sowohl single-file als auch Native AOT sind grundsätzlich OS- und architekturspezifisch, weshalb das minimal unterstützte Windows und die Ziel-Architektur von Anfang an feststehen müssen.
- Bei welchen Apps ist eine einzige EXE schwer zu erreichen?
- Bei Shell-Erweiterungen, Windows-Diensten, Treibern, WebView2-basierten UIs und Teilen von WinUI 3. Hier geht es weniger um die Anzahl der Dateien als um die Registrierung beim Betriebssystem und den Umgang mit zusätzlichen Laufzeiten. Eine Shell-Erweiterung muss etwa beim Explorer registriert werden, ein Dienst erfordert die Registrierung bei der SCM sowie den Entwurf von Rechten und Startkonto, und ein Treiber steht erst mit INF und Signierung – reines Ablegen der Dateien funktioniert hier nicht. Bei WebView2 muss zuerst geklärt werden, wie die WebView2 Runtime bereitgestellt wird.
- Gibt es bessere Verteilungswege, als eine App gewaltsam in 1 EXE zu pressen?
- Die app-local-Verteilung, bei der DLLs neben der EXE liegen, ist eine starke Option. Wenn `app.exe` mit ein paar benachbarten DLLs ohne Installationsprogramm und ohne Administratorrechte per xcopy verteilbar ist, ist das oft leichter zu pflegen, als alles gewaltsam in eine EXE zu pressen. Wichtig ist, zuerst zu trennen, ob Sie ein einziges Verteilungsartefakt, den Wegfall der vorherigen Runtime-Installation oder den Wegfall des Installationsprogramms wollen.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.