Demystifying ClickOnce

As we rebuild Microsoft Edge atop the Chromium open-source platform, we are working through various scenarios that behave differently in the new browser. In most cases, such scenarios also worked differently between 2018’s Edge (aka “Spartan”) and Chrome, but users either weren’t aware of the difference (because they used Trident-derived browsers inside their enterprise) or were aware and simply switched to a Microsoft-browser for certain tasks.

One example of a behavioral gap is related to running ClickOnce apps. ClickOnce is a Microsoft application deployment framework that aims to allow installation of native-code applications from the web in (around) one click.

Chrome and Firefox can successfully install and launch ClickOnce’s .application files if the .application file specifies a deploymentProvider element with a codebase attribute (example):

InstallPrompt

Installation prompt when opening an .application file.

However, it’s also possible to author and deploy an .application that doesn’t specify a deploymentProvider element (example). Such files launch correctly from Internet Explorer and pre-Chromium Edge, but fail in Firefox and Chrome with an error message:

ApplicationCannotBeStarted

ClickOnce fails for a downloaded .application file.

So, what gives? Why does this scenario magically work in Edge Spartan but not Firefox or Chrome?

The secret can be found in the EditFlags for the Application.Manifest ProgId (to which the .application filename extension and application/x-ms-application MIME type are mapped):

ApplicationManifestRegistry

Registry settings for the Application.Manifest ProgId.

The EditFlags contain the FTA_AlwaysUseDirectInvoke flag, which is documented on MSDN as 

FTA_AlwaysUseDirectInvoke 0x00400000
Introduced in Windows 8. Ensures that the verbs for the file type are invoked with a URL instead of a downloaded version of the file. Use this flag only if you’ve registered the file type’s verb to support DirectInvoke through the SupportedProtocols or UseUrl registration.

If you peek in the Application.Manifest’s Shell\Open\Command value, you’ll find that it calls for running the ShOpenVerbApplication function inside dfshim.dll, passing along the .application file’s path or URL in a parameter (%1):

“C:\Windows\System32\rundll32.exe” “C:\Windows\System32\dfshim.dll”,ShOpenVerbApplication %1

And therein lies the source of the behavioral difference.

When you download and open an Application.Manifest file from Edge Spartan, it passes the source URL for the .application to the handler. When you download the file in Firefox or Chrome, it passes the local file path of the downloaded .application file. With only the local file path, the ShOpenVerbApplication function doesn’t know how to resolve the relative references in the Application Manifest’s XML and the function bails out with the Cannot Start Application error message.

Setting FTA_AlwaysUseDirectInvoke also has the side-effect of removing the “Save” button from Edge’s download manager:

NoSave

…helping prevent the user from accidentally downloading an .application file that won’t work if opened outside of the browser from the Downloads folder (since the file’s original URL isn’t readily available to Windows Explorer).

Advice to Publishers

If you’re planning to distribute your ClickOnce application from a website, specify the URL in Visual Studio’s ClickOnce Publish Wizard:

Manifest

Specify “From a Web site” in the ClickOnce Publish Wizard.

This will ensure that even if DirectInvoke isn’t used, ShOpenVerbApplication can still find the files needed to install your application.

Workarounds

A company called Meta4 offers a Chrome browser extension that aims to add fuller support for ClickOnce to Chrome. The extension comes in two pieces– a traditional JavaScript extension and a trivial “native” executable (written in C#) that simply invokes the ShOpenVerbApplication call with the URL. The JavaScript extension launches and communicates with the native executable running outside of the Chrome sandbox using Native Messaging.

Unfortunately, the extension is a bit hacky– it installs a blocking onBeforeRequest handler which watches all requests (not just downloads), and if the target URL’s path component ends in .application, it invokes the native executable. Alas, it’s not really safe to make any assumptions about extensions in URLs (the web is based on MIME types, rather than filenames).

Next Steps

For the Edge team– TBD.

Do you use ClickOnce to deploy your applications? If so, are you specifying the deployment URL in the manifest file?

-Eric

PS: Notably, Internet Explorer doesn’t rely upon the DirectInvoke mechanism; removing the EditFlags value entirely causes IE to show an additional prompt but the install still succeeds. That’s because IE activates the file using a MIME handler (see the CLSID subkey of Application.Manifest) much like it does for .ZIP files. The DirectInvoke mechanism was invented, in part, to replace the legacy MIME handler mechanism.

22 thoughts on “Demystifying ClickOnce

    1. Indeed, it’s still live, but only gets triggered in certain scenarios (IE on Win7, perhaps). The Chrome . application file, alas, does not specify the deployment codebase attribute.

  1. My Click Once app stopped working all of a sudden last week. I have been using the ClickOnce for Google extension without problems for quite some time.

    Last week, when I click to launch the app, it now tries to download it (.application file), instead of launching it.

  2. Hi Eric,

    Version of Chrome is Version 72.0.3626.119 (Official Build) (64-bit)

    The URL of the application is internal. Its actually an app that is launched from a .NET application to handle document management.

    Were you asking for the URL meaning to see it for yourself?

    1. I asked about the URL for two reasons: 1) If public, I could look myself, and 2) Because most of these extensions work by looking for URLs that have the string “.application” in them in a particular place.

      Other things to check are that the extension is enabled (E.g. you’re not browsing Incognito without extensions). You could also check the links at the very top of my test page: https://bayden.com/test/clickonce/

  3. Well that is very nice of you. I am baffled, and all our developers state they have changed nothing. All I can assume is that something changed in Chrome. If you want to take a look:{elided for privacy}

    This is the app that has been working fine, and now it tries to download.

    1. I’ve taken a look at this, and I understand why it’s happening. If your developers want to workaround this in the web application, they can do so by editing their |window.downloadFile| function call in the JavaScript source code.

      The problem is that the common ClickOnce extensions for Chrome (e.g. the one above, and https://chrome.google.com/webstore/detail/clickonce-for-google-chro/kekahkplibinaibelipdcikofmedafmb) only install an onBeforeRequest listener that watches for URLs containing “.application”.

      The problem is that Chrome isn’t firing the onBeforeRequest event when the link to the .application contains a DOWNLOAD attribute (which is what informs Chrome that the target should go into the download manager).

      I’ve added a test case at the top of https://bayden.com/test/clickonce/ for this scenario.

      I will now attempt to figure out whether this is a change in Chrome.

      1. Eric, that is amazing thank you.

        Yes, there must have been something that changed in Chrome in the past week to have this start failing all of a sudden.

        Thanks for the view! I am going to share this now

      2. This problem turns out to be a bug in Chrome; it’s not specific to a version, but instead the “Network Service” experiment. You should see the problem go away if you change chrome://flags/#network-service setting to Disabled.

        I’ve reported this issue to Chrome: https://crbug.com/935567

  4. Hi Eric,
    Kon Tantos again, just want to give you an update.
    The click once application is ‘online only’. IE users can only start it via the web site. It also has a deployment provider via our code signing certificate.

    We tried a couple of other apps which allowed offline and online access. They all install without any issues.

    Using one of the clickonce extensions (Meta4, Remix) is problematic. They work in some PCs but not in others.

  5. Hi Eric,
    We are using ClickOnce for deploy some applications from a .net website of our company, without any problem until now.
    But now, with Chrome version 73, when users click on the link, the clickonce installation works fine, but the browser shows ERR_UNSAFE_REDIRECT error.

    Any ideas to solve this?
    Thanks!

  6. Hi Eric, great post, and really informative. If I need to deploy my Click Once application from multiple sites, would I have to republish for each site?
    For example, would I need a publish for our QA site, and another for the Production site?

    Thanks!

    1. I /assume/ that this is how that works, but to be honest my understanding of ClickOnce is limited to just what I’ve written above. Probably straightforward to test this?

    2. ClickOnce just sets up an online installer for your application. It has no bearing on the how that application behaves.

      Typically you have a single install site (URL) for each type of application.
      EG if QA v Production is specific to the application (IE not determined by login credentials) you would install the QA application to one URL (may be internal access only) and Production to another.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s