What Is ClickOnce? - How It Works, How It Updates, and Where It Fits (and Doesn't) in Practice
· Go Komura · Windows, Deployment, ClickOnce, .NET, Windows Development
When the conversation turns to distributing .NET desktop apps on Windows, one name keeps quietly coming up in the shadow of MSI and MSIX: ClickOnce.
But if you swing carelessly to either of these positions:
- It’s old technology, so we don’t use it anymore
- Or conversely, it looks easy, so ClickOnce can handle anything
you will usually be wrong.
ClickOnce is not an all-purpose installer that can do everything. On the other hand, for distributing internal .NET line-of-business apps to standard users, with updates included, at low operational cost, it remains a very strong option even today.
This article organizes what ClickOnce is, how it works, what it does well, and where it starts to strain — from a practical standpoint, with plenty of Mermaid diagrams. The content is primarily based on Microsoft Learn information verifiable as of April 2026.
The diagrams are conceptual. In Markdown environments with Mermaid support, they render as figures.
Table of Contents
- The Conclusion First
- What Is ClickOnce?
- What Makes Up ClickOnce
- The Flow from Install to Launch
- How Updates Work
- Where ClickOnce Excels
- Where It Fits
- Where It Doesn’t Fit
- Common Pitfalls in Practice
- Summary
- Related Articles
- References
1. The Conclusion First
In one sentence, ClickOnce is a deployment technology for distributing .NET Windows desktop apps easily on a per-user basis, with automatic updates included.
It fits cases like these.
- Internal line-of-business apps in WinForms / WPF
- You want standard users to be able to install
- Per-user distribution is sufficient
- You want updates built in
- A website or file share is enough as the distribution channel
Conversely, with requirements like these, it is safer to look at other methods from the start.
- Machine-wide install for all users
- Windows services, drivers, in-process shell extensions, heavy COM registration
- Package identity is required
- You want to own update channels, staged delivery, and a custom rollback UX
- The installer carries heavy responsibilities deep inside the OS
In short, ClickOnce is a method that is strong at easy distribution and easy updates, but not suited to projects with heavy OS integration.
First, the positioning in one picture
flowchart TD
A["Want to distribute a Windows app"]
B{"Are there requirements for deep OS integration?"}
C["Start from MSI / MSIX"]
D{"Is it a .NET Windows desktop app where<br/>per-user distribution suffices?"}
E{"Do you want to distribute to standard users?"}
F{"Is the built-in update enough?"}
G["ClickOnce is a strong candidate"]
H["Also compare xcopy / custom updater"]
A --> B
B -- Yes --> C
B -- No --> D
D -- No --> H
D -- Yes --> E
E -- No --> H
E -- Yes --> F
F -- Yes --> G
F -- No --> H
2. What Is ClickOnce?
ClickOnce is Microsoft’s Windows app deployment technology. Officially, it is described as a deployment technology for Windows-based apps that can be installed and run with minimal user interaction and that are self-updating.
The important thing here is not to view ClickOnce as just “a kind of installer.”
In reality, ClickOnce is a deployment model that takes care of all of the following together.
- Which version to distribute
- Which files belong to that version
- How to detect updates
- Where to fetch updates from
- How to keep the app in a safe per-user location
- How to verify integrity at launch
The heart of ClickOnce is not setup.exe itself; it is closer to the essence to see it as a mechanism that manages distribution, updates, and cache management together, centered on manifests.
Even on current .NET, ClickOnce remains a normal candidate. In Visual Studio, the Publish tool is used for .NET Core 3.1 and .NET 5 and later, and when handling manifests manually you use dotnet-mage.exe.
The responsibilities ClickOnce takes care of
flowchart LR
CO["ClickOnce"]
V["Which version to distribute"]
U["Where to fetch updates from"]
S["Keep in a safe per-user location"]
I["Verify integrity and launch"]
CO --> V
CO --> U
CO --> S
CO --> I
3. What Makes Up ClickOnce
The shortcut to understanding how ClickOnce works is to grasp these four pieces.
| Element | Role |
|---|---|
Deployment manifest (.application) |
Describes the version to distribute now, the update location, the update method, etc. |
Application manifest (*.exe.manifest) |
Describes that version’s app binaries, dependent files, hashes, entry point, etc. |
| Application files | exe, dll, config, data files, and so on |
setup.exe (optional) |
A bootstrapper that checks for and installs prerequisites. Used when required runtimes or dependencies exist |
The core here is the two kinds of manifests.
- The deployment manifest expresses “which version is the current right answer for this app”
- The application manifest expresses “what is inside that version”
In other words, ClickOnce’s update decision starts from the deployment manifest, and the application manifest controls what actually gets downloaded.
How the four elements relate
flowchart LR
Setup["setup.exe<br/>optional<br/>check / install prerequisites"]
Deploy["Deployment manifest (.application)<br/>which version to distribute<br/>update location / update conditions"]
App["Application manifest (*.exe.manifest)<br/>contents of that version<br/>file list / hashes / entry point"]
Files["App binaries<br/>exe / dll / config / data"]
Cache["ClickOnce cache<br/>per-user / per-application"]
Setup --> Deploy
Deploy --> App
App --> Files
Files --> Cache
setup.exe is a helper, not the lead
setup.exe gets a lot of attention, but it is not the lead actor in ClickOnce.
It is a helper that checks for and installs prerequisites.
For example, when the correct .NET runtime or additional redistributable components are needed, setup.exe puts those in place first, and only then does the ClickOnce deployment proper begin.
4. The Flow from Install to Launch
Boldly simplified for practical purposes, the ClickOnce flow looks like this.
- The user opens
setup.exeor the.applicationfile on a web page or file share - In configurations using
setup.exe, prerequisites are checked and any missing ones installed - ClickOnce reads the deployment manifest
- It reads the application manifest the deployment manifest points to
- It fetches the required files and places them into the per-user ClickOnce cache
- In configurations with offline availability, it registers the app in the Start menu / app list
- From then on, the app launches under ClickOnce’s management
The key point is that this is not the mindset of placing files into Program Files like an ordinary installer.
ClickOnce apps go into a safe per-user cache area, isolated per app and per user. This is one of ClickOnce’s defining characteristics.
From install to first launch
sequenceDiagram
participant U as User
participant P as setup.exe / .application
participant D as Deployment manifest
participant A as Application manifest
participant C as ClickOnce cache
participant X as App binaries
U->>P: Open
Note over U,P: In configurations using setup.exe,<br/>prerequisite checks come first
P->>D: Fetch and read
D->>A: Reference the target version
A->>C: Fetch required files, verify integrity
C->>X: Place and launch
Online-only versus offline-available
ClickOnce offers two broad presentation modes.
- Online-only: runs from the publish location. Feels less like a permanent install
- Offline-available: installed onto the user’s machine and launchable from the Start menu
For internal line-of-business apps, in practice offline-available is the common choice.
flowchart LR
subgraph Online[Online-only]
O1["Launches from the publish location"]
O2["Feels less like a permanent install"]
O3["Tends to assume a network"]
O1 --> O2 --> O3
end
subgraph Offline[Offline-available]
F1["Installed into the user area"]
F2["Registered in the Start menu"]
F3["Launches locally"]
F4["Checks for updates at the configured timing"]
F1 --> F2 --> F3 --> F4
end
5. How Updates Work
ClickOnce’s most obvious strength is, after all, that the update model is built in.
Update decisions start from the deployment manifest
A ClickOnce app reads the deployment manifest and checks:
- Is there a newer version
- Is the update mandatory
- Where to fetch it from
Once an update begins, ClickOnce uses file patching to avoid redundant re-downloads. As a working mental model, it is easiest to think of it as comparing the new version’s application manifest against the current version and fetching only the files that changed.
For thinking about update checks, these three patterns make it easy to organize.
- Check before startup
- Check after startup
- Provide a “check for updates” UI in the app
However, the available APIs and configuration UI differ between .NET Framework and .NET 5+. Do not start implementing based only on memories of old ClickOnce articles.
The update flow
flowchart TD
Start["App launches"]
Check["Check the deployment manifest"]
New{"Is there a newer version?"}
Run["Launch as is"]
Get["Fetch the new version's application manifest"]
Compare["Compare file signatures / hashes"]
Download["Fetch what changed"]
Switch["Materialize the new version and switch"]
Restart["If needed, run the new version after restart"]
Start --> Check --> New
New -- No --> Run
New -- Yes --> Get --> Compare --> Download --> Switch --> Restart
It also helps to keep in mind that, without a network connection, the app simply runs without an update check — knowing this avoids confusion in practice.
Versions are kept in isolation
ClickOnce is less “overwrite the current files in place” and more “fully materialize the new version, then switch over.”
Furthermore, the ClickOnce cache keeps the current version and the previous version separately. This is one of the reasons it rarely pollutes the environment and easily avoids version collisions.
flowchart TB
subgraph UA[User A's ClickOnce cache]
APrev["Previous version"]
ACur["Current version"]
AData["Settings / data"]
end
subgraph UB[User B's ClickOnce cache]
BPrev["Previous version"]
BCur["Current version"]
BData["Settings / data"]
end
APrev --> ACur
AData --> ACur
BPrev --> BCur
BData --> BCur
There are two key points here.
- Hard to mix with other users
- Hard to collide with other versions
This structure is a big part of why ClickOnce easily avoids the proverbial DLL Hell.
6. Where ClickOnce Excels
ClickOnce’s virtues go beyond “easy to distribute.” What pays off in practice is roughly this set.
6.1 Easy to distribute to standard users
ClickOnce pairs well with per-user distribution, and the big win is that it is easy to install without administrator privileges.
A common situation with internal line-of-business apps is:
- The users are standard users
- You do not want to file an install request with IT every time
- But you do not want updates to stop
Under these conditions, ClickOnce is quite strong. Its premise — “install into each user’s area, safely, updates included” — matches from the start.
6.2 You do not have to implement updates yourself
Building auto-update from scratch involves more than it looks.
- New-version detection
- Download
- Integrity verification
- Switching from the old version
- Recovery on failure
- Update UI
- Handling the updater itself
ClickOnce covers a large share of this with its existing model.
flowchart LR
subgraph Custom[Responsibilities with a custom updater]
C1["New-version detection"]
C2["Download"]
C3["Integrity verification"]
C4["Switchover"]
C5["Recovery on failure"]
C1 --> C2 --> C3 --> C4 --> C5
end
subgraph Click[Responsibilities you can push onto ClickOnce]
K1["New-version detection"]
K2["Fetch and verify"]
K3["Switchover"]
K1 --> K2 --> K3
end
Of course, you cannot do absolutely anything you like. But the “good-enough updates” that internal apps need are quite easy to satisfy.
6.3 Apps rarely collide with each other
ClickOnce apps are isolated per app, per user, and per version.
This makes the classic accidents hard to trigger:
- Version conflicts in shared components
- Overwriting some DLL and breaking another app
- Polluting the environment with manual file swaps
6.4 Easy to ship from Visual Studio
ClickOnce pairs well with Visual Studio’s publish feature, and the short distance from build to distribution is another advantage.
Before diving into a separate discipline like MSI authoring, it is easy to establish the loop of:
- publish first
- distribute first
- get updates flowing first
- get field feedback first
6.5 Settings carry-over is also relatively straightforward
If you use the default application settings provider, ClickOnce has a mechanism to merge the previous version’s settings into the new version at update time.
But note this assumes the default settings provider. With custom settings storage, custom providers, or changed storage locations, it naturally will not work as is.
7. Where It Fits
The projects ClickOnce particularly suits look roughly like these.
| Situation | Why it pairs well with ClickOnce |
|---|---|
| Internal WinForms / WPF line-of-business apps | Standard-user distribution and auto-update mesh well |
| Per-user installation is sufficient | Per-user distribution is natural |
| You want to distribute via a website or UNC share | The distribution channel stays simple |
| Update cadence is roughly weekly to monthly | The built-in update keeps up comfortably |
| You do not want to craft update UI as product value | You can use the existing update model |
Concrete examples with good practical fit:
- Internal data-entry applications
- Desktop business tools for quotes, ordering, inventory, and so on
- Helper apps for configuring equipment
- Internal-only clients distributed to branch offices, factories, and back offices
- Apps that want updates but do not justify building a dedicated updater
For apps like these, keeping distribution and updates simple is itself the value. ClickOnce answers that value directly.
A decision tree for fit
flowchart TD
S["Organize the requirements"]
Q1{"Is it a .NET desktop app such as<br/>WinForms / WPF?"}
Q2{"Is per-user distribution sufficient?"}
Q3{"Do you want standard users to install it?"}
Q4{"Can you distribute via web / file share?"}
Q5{"Is it OK not to over-engineer the update UX?"}
G["ClickOnce is a very strong candidate"]
O["Compare other methods"]
S --> Q1
Q1 -- No --> O
Q1 -- Yes --> Q2
Q2 -- No --> O
Q2 -- Yes --> Q3
Q3 -- No --> O
Q3 -- Yes --> Q4
Q4 -- No --> O
Q4 -- Yes --> Q5
Q5 -- Yes --> G
Q5 -- No --> O
8. Where It Doesn’t Fit
On the other hand, the situations where you should not force ClickOnce are equally clear.
8.1 A machine-wide install is required
ClickOnce is fundamentally per-user.
- You want to install for all users
- You want to install under
Program Files - You want to deploy and manage at the machine level
In these cases, MSI and friends are more natural than ClickOnce.
8.2 Windows services / drivers / shell extensions / heavy COM registration
These are matters of touching the OS deeply.
- Windows services
- Drivers
- In-process shell extensions
- Configurations that assume systematic COM registration
Once these come in, you have left ClickOnce’s “distribute lightly” worldview.
8.3 You want package identity
One reason to choose MSIX is the requirement of obtaining package identity.
ClickOnce does not go in that direction. If what you want is modern packaging or Windows features that assume package identity, prefer MSIX.
8.4 You want to own the update UX and delivery channels as product
For example:
- stable / beta / preview channels
- Staged delivery
- Adjusting rollout percentages while watching telemetry
- Fine-grained control of background downloads
- A custom rollback strategy
- A complex lifecycle for the updater itself
Once you want all this, ClickOnce’s built-in updates stop being enough.
8.5 Tools where “just drop it in” is enough
Conversely, some cases can be even simpler.
- Drop in the folder and it runs
- Manual file swaps are fine for updates
- Hand it over on a USB stick
- In a closed network where simplicity is the top priority
Here, xcopy distribution can involve even less friction.
Which requirements push you toward other methods
flowchart LR
A1["Install for all users"] --> B1["MSI / sometimes MSIX"]
A2["service / driver / shell extension / heavy COM"] --> B2["MSI / dedicated installer"]
A3["Package identity required"] --> B3["MSIX"]
A4["Staged delivery / channels / custom UX"] --> B4["Custom updater"]
A5["Just dropping it in is enough"] --> B5["xcopy"]
ClickOnce is not all-powerful in either direction; instead, it is a method that is strong on projects of just the right complexity.
9. Common Pitfalls in Practice
ClickOnce is convenient, but going in carelessly causes some stumbles. These in particular are worth grasping in advance.
9.1 Do not view it like “an ordinary installer”
ClickOnce is not a model where files are placed at a fixed install location in a form humans manage directly.
The actual files go into a cache managed by ClickOnce, isolated per version. That makes it a poor match for:
- Operations that assume a fixed EXE path
- Operations that overwrite files directly by hand
- Operations where humans control where the actual files live
ClickOnce is not a method where people manage file placement; it is a method where the deployment state is managed by manifests.
9.2 Many old ClickOnce articles assume .NET Framework
Caution is warranted here.
Even today, search results are full of ClickOnce articles from the .NET Framework era. But on current .NET the situation differs somewhat.
- On .NET Core 3.1 / .NET 5 / .NET 6, the
ApplicationDeploymentAPI cannot be used as is - On .NET 7 and later, some deployment properties can be read via environment variables
- For manual manifest handling,
dotnet-mage.exeis the assumed tool - On the Visual Studio side too, advice assuming the old Publish Wizard may no longer apply directly
Even if you feel you “already know ClickOnce,” it is safer not to implement from old memories.
9.3 Treat prerequisites separately
ClickOnce itself is a deployment model, but the app may need prerequisites to run.
- A supported runtime
- Additional redistributable components
- Other dependencies
This is where the bootstrapper configuration using setup.exe pays off. Leave this fuzzy and you get “we distributed it with ClickOnce, but it doesn’t run.”
9.4 Settings carry-over depends on “what you store and how”
With the default settings provider, settings migration at update time is relatively straightforward. However, with:
- A custom settings provider
- Roaming assumptions
- A self-managed change of the settings storage location
- Large changes to the settings structure between versions
it naturally will not work as is.
9.5 Do not take signing and the update path lightly
ClickOnce has machinery for distribution and updates, but that does not erase your security responsibilities.
Especially in production, it is best to organize up front:
- How the signing certificate is managed
- How the publisher name is presented
- How the update source is managed
- How test self-signing is separated from production signing
flowchart TD
Cert["Code-signing certificate"]
AppMan["Application manifest"]
DepMan["Deployment manifest"]
Verify["Verified on the client"]
Run["Update / run"]
Tamper["Manifest tampering"]
Stop["Stop on verification failure"]
Cert --> AppMan
Cert --> DepMan
AppMan --> Verify
DepMan --> Verify
Verify --> Run
Tamper -. verification fails .-> Stop
“There’s auto-update, so we’re safe” is not the conclusion; what you trust and how that trust is maintained must be considered separately.
9.6 If you move the publish location, revisit deploymentProvider
This one is unglamorous but bites hard in practice.
An installed ClickOnce app looks for updates at the location pointed to by deploymentProvider in the deployment manifest.
That means even if you copy the entire publish folder to a different URL or share, without updating deploymentProvider, clients may keep looking at the original location.
And if you modify a manifest by hand, it must be re-signed.
The operational flow from publish to update pickup
flowchart TD
Build["Build"]
AppManifest["Generate the new application manifest"]
SignApp["Sign the application manifest"]
UpdateDep["Update the deployment manifest to the new version"]
SignDep["Sign the deployment manifest"]
Publish["Place at the publish location"]
Client["Clients detect the update"]
Build --> AppManifest --> SignApp --> UpdateDep --> SignDep --> Publish --> Client
In the end, what matters in operating ClickOnce is not so much whether the files were placed as whether the manifests and signatures are consistent.
10. Summary
In one sentence, ClickOnce is
a mechanism for distributing .NET Windows desktop apps easily on a per-user basis, with updates handled at low cost.
Its strengths are mainly these.
- Easy to distribute to standard users
- A built-in update model
- Easy delta-only updates
- Easy isolation of apps and versions
- Easy publishing from Visual Studio
- A good match for internal line-of-business apps
But it is not all-powerful.
- Machine-wide install
- Services / drivers / shell extensions
- Heavy COM registration
- Package identity
- Custom channels or staged delivery
- Crafting the update UX as product value
If you have requirements like these, you should think from the MSI / MSIX / custom-updater side rather than ClickOnce.
ClickOnce is not a lightweight installer that fits everything, but on the projects it suits, it remains quite strong even today.
If what you want to distribute is
- a .NET Windows line-of-business app,
- where per-user installation suffices,
- to standard users,
- without owning updates yourself,
then ClickOnce belongs high on the candidate list.
11. Related Articles
- How to Choose a Distribution Method for Windows Apps - A Decision Table for MSI / MSIX / ClickOnce / xcopy / Custom Updater
- Your Auto-Updater Is a Trust Boundary - Why HTTPS Alone Is Not Enough
- When Does Windows Actually Require Administrator Privileges - UAC, Protected Areas, and How to Tell by Design
Related Topics
These topic pages are close to this theme. Starting from this article, you can move on to related services and other articles.
Windows Technical Topics
An entry point that gathers technical topics on Windows development, bug investigation, and reusing existing assets.
Services Related to This Theme
This article connects to the following service pages. Please enter from whichever is closest.
Windows App Development
For internal line-of-business apps, equipment configuration tools, and modifications to existing software, which of ClickOnce / MSI / MSIX / xcopy has the least friction changes considerably with some organizing before implementation.
Technical Consulting & Design Review
Questions like “Is ClickOnce enough?”, “Should we lean toward MSIX or MSI?”, and “Do we settle for built-in auto-update or own it ourselves?” become much easier to decide when organized before implementation.
Author Profile
Go Komura
Representative, KomuraSoft LLC
Specializing in Windows software development, technical consulting, and bug investigation, with particular strength in projects involving existing assets and in investigating failures whose causes are hard to see.
12. References
- Microsoft Learn - ClickOnce deployment and security
- Microsoft Learn - ClickOnce for .NET on Windows
- Microsoft Learn - How ClickOnce performs application updates
- Microsoft Learn - Choosing a ClickOnce update strategy
- Microsoft Learn - ClickOnce deployment manifest
- Microsoft Learn - ClickOnce application manifest
- Microsoft Learn - ClickOnce cache overview
- Microsoft Learn - ClickOnce and application settings
- Microsoft Learn - Install prerequisites with a ClickOnce application
- Microsoft Learn - ClickOnce and Authenticode
- Microsoft Learn - Security, versioning, and manifest issues in ClickOnce deployments
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Windows App Outsourcing and Contract Development: What to Sort Out Before You Ask
Before commissioning Windows app outsourcing or contract development, here is how to sort out existing software modification, device inte...
Why Windows Shows "Windows protected your PC"
A practical look at why SmartScreen warnings appear when distributing Windows apps, covering code signing, EV/OV certificates, Azure Arti...
Why Windows Became What It Is Today: The Evolution of Windows Through a Developer's Eyes
A look at the changes from Windows 95 to Windows 11 — not as a visual timeline, but from a Windows application developer's perspective: c...
When Do You Actually Need Administrator Privileges on Windows? - UAC, Protected Areas, and How to Tell by Design
A practical look at when administrator privileges are required on Windows, from the perspectives of UAC, protected areas, services, drive...
Choosing a Windows App Distribution Method - MSI/MSIX/ClickOnce/xcopy/Custom Updater
Choosing a Windows app distribution method is not about installer-format preference - it is a choice about OS coupling and update respons...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Technical Consulting & Design Review
We help clarify design direction, architectural boundaries, lifetime ownership, and how to handle legacy Windows assets.
Author Profile
Profile page for the article author.
Go Komura
Representative of KomuraSoft LLC
Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.
Public links