Skip to content

Create a Windows installer for a .NET app (WPF, WinForms, console)

Publish a .NET desktop app with dotnet publish, choose between framework-dependent and self-contained, then wrap the publish folder in a Setup.exe with shortcuts and an uninstaller.

Updated October 31, 2026 · 3 min read

Visual Studio builds your WPF or WinForms app, and dotnet publish gives you a folder that runs anywhere. The missing piece is the installer: the Start Menu entry, the Program Files folder, the uninstall entry in Windows Settings. The old ClickOnce and Visual Studio Installer Projects still exist, but both are showing their age. Here is the straightforward path from a .NET project to a Setup.exe.

Step 1: publish the app

Publishing produces a folder with everything the app needs. Run it from the project folder:

dotnet publish -c Release -r win-x64 --self-contained true

The output lands under bin\Release\net8.0-windows\win-x64\publish\ (the target framework in the path varies with your project). That folder is what you ship.

The one decision that matters:

  • Self-contained (--self-contained true) bundles the .NET runtime. The folder is 70 to 150 MB, but it runs on any Windows machine with nothing to install first. Choose this for software you distribute to the public.
  • Framework-dependent (--self-contained false) is a few MB, but the user must have the matching .NET Desktop Runtime installed, or the app shows a download prompt on first launch. Fine for internal tools on managed machines.

Useful options:

  • -p:PublishReadyToRun=true precompiles the app for a faster start.
  • -p:PublishSingleFile=true folds the output into one .exe. It works, but an installer removes the reason to use it, and single-file apps unpack native libraries on first run. The plain folder is simpler.
  • Trimming (PublishTrimmed) is not supported for WPF and WinForms; leave it off.

Set the app icon in the project file so the published .exe carries it:

<PropertyGroup>
  <ApplicationIcon>app.ico</ApplicationIcon>
</PropertyGroup>

Run the published .exe once from the publish folder, ideally on a machine without Visual Studio, to make sure nothing is missing.

Step 2: zip the publish folder

Zip the contents of publish`: the .exe, the DLLs, .deps.json, .runtimeconfig.json and any content files. Leave out .pdb` files unless you want crash symbols on customers' machines.

Step 3: build the installer

  1. Upload the zip to ExeTup. It picks your app's .exe as the main program; confirm it, since a self-contained publish includes createdump.exe, which is not it.
  2. Fill in the name, version (keep it in step with <Version> in the project), publisher and licence.
  3. Choose all users for a classic desktop app in Program Files, or current user when customers may lack admin rights.
  4. Keep 64-bit for a win-x64 publish; choose 32-bit for win-x86.
  5. Upload the same app.ico, enable the shortcuts, and build.

Download Setup.exe and test the install and uninstall on Windows.

.NET-specific notes

  • Updates. Ship a new installer with a higher version. Installing over the old version replaces the files; the installer does not include an auto-update channel.
  • Settings and data. Write user data to %APPDATA% or %LOCALAPPDATA%, not next to the .exe. Program Files is read-only for normal users, and the uninstaller only removes what it installed.
  • Prerequisites. Self-contained publishes need nothing else. If your app needs the Visual C++ runtime or a database driver, document it; the installer doesn't chain other installers.
  • Code signing. Sign the published .exe before zipping, then sign the downloaded Setup.exe. See the SmartScreen guide.

Alternatives

  • Visual Studio Installer Projects produce MSI files from inside Visual Studio. Still available as an extension, limited and awkward with self-contained publishes.
  • ClickOnce gives per-user installs with auto-update, tied to a web server you maintain.
  • WiX Toolset builds MSI files from XML and is the standard for enterprise-grade MSI packaging. Powerful, with a learning curve to match.
  • MSIX is Microsoft's newer package format, best when you target the Microsoft Store.

See MSI vs EXE installers for when the format matters.

FAQ

Should I publish framework-dependent or self-contained?

Self-contained for anything you give to the public, so the app runs without a runtime download. Framework-dependent for internal tools on machines where IT manages the .NET runtime.

Does the installer include the .NET runtime?

If you publish self-contained, yes, because it is part of the publish folder. The installer ships whatever is in the zip.

Can I produce an MSI for Group Policy?

ExeTup builds a Setup.exe. It installs silently with /S, which covers most scripted deployment. For MSI-only policies, WiX is the right tool.

Which .NET versions does this work with?

Any that produce a publish folder: .NET 6, 7, 8, 9 and later, and .NET Framework apps built with Visual Studio (use the bin\Release output).

Related guides