Skip to content

Windows: Create msi installers and a .NET bootstrapper on Release builds - #3194

Open
cwensley wants to merge 7 commits into
picoe:developfrom
cwensley:curtis/windows-msi-installer
Open

cwensley wants to merge 7 commits into
picoe:developfrom
cwensley:curtis/windows-msi-installer

Conversation

@cwensley

@cwensley cwensley commented Nov 5, 2026

Copy link
Copy Markdown
Member

Windows: Create msi installers and a .NET bootstrapper on Release builds

Adds native Windows installers for Eto apps, like LinuxPackageFormats on Linux and EnableDmgBuild on Mac. There's no WiX project or bootstrapper project to maintain, and no tools to install: the msi is written with msi.dll and makecab.exe, and Setup.exe is compiled with the .NET Framework csc.exe. All three come with Windows.

<WindowsPackageFormats>msi;setup</WindowsPackageFormats>
  • msi: installs with a progress bar only, no wizard. Adds a Start menu shortcut and an Installed apps entry, and replaces older versions. Installs per-user to %LocalAppData%\Programs by default, with no admin prompt; set WindowsPackageScope=perMachine for Program Files. If the .NET runtime the app needs is missing, it refuses to install and links to the download.
  • setup: a small .NET Framework exe that downloads and installs the missing .NET runtime from Microsoft (only after checking the download is signed by Microsoft), then runs the msi it contains. Arguments are passed on to msiexec, e.g. /quiet or INSTALLFOLDER=....
  • .NET apps get one installer per architecture (WindowsPackageRuntimeIdentifiers, defaulting to the build machine's). .NET Framework apps get one. WindowsPackageSelfContained includes .NET instead, so no bootstrapper is needed.
  • Code signing: set WindowsSignCertificateFile/WindowsSignCertificatePassword, WindowsSignCertificateThumbprint, or WindowsSignCommand (e.g. Azure Trusted Signing), plus WindowsSignTimestampUrl. The app's exe and dll are signed before packaging, then the msi, then setup.

All options are documented at the top of src/Shared/build/WindowsPackage.targets. The targets ship in the Wpf, WinForms and Direct2D packages and are imported once. Eto.Test.Wpf now builds installers, and CI uploads them as test-win-packages.

The upgrade code defaults to a GUID derived from the assembly name, so apps should set WindowsPackageUpgradeCode before ever renaming the assembly.

Tested on Windows 11 Arm64: per-user install, launch, upgrade (x64 over Arm64) and uninstall for net10.0 and net48; Setup.exe /quiet with a custom install folder; runtime detection, including x64 runtimes on Arm64; and signing with a pfx, a thumbprint and a custom command. Not tested: a per-machine install, and the bootstrapper actually installing .NET, since both need an admin prompt.

@Descolada

Copy link
Copy Markdown
Contributor

What does an install mean for Eto? 🧐

@cwensley

cwensley commented Nov 5, 2026

Copy link
Copy Markdown
Member Author

Hey @Descolada, I don't know if I understand the question? This creates an installer so users can install the Eto application you've created, upgrade it, etc. Similar to how you can create packages for Linux, or a .dmg for Mac.

These are meant to be a quick way to deploy your app without having to roll your own. Of course it's opt-in so if you need more control, you would use WiX, custom scripts to package, etc.

@Descolada

Copy link
Copy Markdown
Contributor

@cwensley ah I see, my confusion stemmed from thinking Eto more like a library, not as a complete framework.

cwensley and others added 4 commits November 12, 2026 13:38
Set WindowsPackageFormats to msi;setup, similar to LinuxPackageFormats and EnableDmgBuild. The msi is written with msi.dll and makecab, and Setup.exe is compiled with the .NET Framework csc, so nothing beyond Windows is needed. Setup.exe installs the .NET runtime the app needs when it's missing, then runs the msi. Set a certificate or WindowsSignCommand to sign the app, msi and setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he msi

WindowsPackageLicense shows a license to accept (.rtf, .md, .txt or a link), and the msi and Setup.exe text comes in Windows' display language, set with WindowsPackageLanguages and WindowsPackageStrings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WindowsPackageImage sets a square .ico or .png, otherwise the app's icon is used when it has a 48 pixel size. It's shown small at the top of each page, and large on the welcome and finished pages when it's 128 pixels or larger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rmats is set

Projects choose the configurations by setting WindowsPackageFormats conditionally, as Eto.Test.Wpf now does for Release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cwensley
cwensley force-pushed the curtis/windows-msi-installer branch from 49c00e3 to 3555bfe Compare November 13, 2026 10:17
cwensley and others added 3 commits November 13, 2026 21:26
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hitecture

Built with appxpackaging.dll, which comes with Windows. The publisher is read from the
signing certificate, and without one the package is unsigned for testing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cwensley
cwensley force-pushed the curtis/windows-msi-installer branch from 3555bfe to f97a7d9 Compare November 14, 2026 04:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants