Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a click-once application determine its application identity?

Tags:

c#

clickonce

I have a click-once application, which is correctly signed, correctly configured and installs itself without any problem.

It is set to run offline, but install from a specific URL, and if I download and run the setup.exe, it installs updates.

So, it's basically all working... except I cannot print out the version number, or trigger an update from in code. If I try, I get the dreaded: 'Application identity is not set.'

2017-01-10 13:43:14.8367 ERROR System.Deployment.Application.InvalidDeploymentException: Application identity is not set.
   at System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment()
   at LibDataAgent.Internal.Services.UpdateService.Deployment() System.Deployment.Application.InvalidDeploymentException: Application identity is not set.
   at System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment()
   at LibDataAgent.Internal.Services.UpdateService.Deployment()

I am not running in debug mode, or using a debug build.

So here's my actual question:

How, does the click-once code in System.Deployment.Application, at runtime, determine what the application identity is?

So, there are whole lot other questions around this, but please don't close this as a duplicate, as far as I can tell it is not one.

Here's a list of things I do not want answers for:

  • How to sign a click-one application.
  • How to set the application identity as you build.
  • How to find where the click-once application is installed.
  • How to make a click-once application work while debugging.
  • How to check for updates using ApplicationDeployment.

Just very plainly, exactly what does a click-once application do, at runtime that lets it determine the application identity.

Help!

Notes

My (thus far fruitless) attempts to solve this have yielded these notes:

I'm certain this has something to do with how the application is launched, because executing applications from the command line has never worked with click-once; but executing the same application from the start menu will correctly return IsNetworkDeployed as true.

However, I've not been able to determine what the technical difference is, or why one detects the install correctly and one doesn't. (or indeed, why this specific application doesn't work from the start menu, when others with no obvious difference do).

Things I've tried that make no difference include:

  • the working directory for the application.
  • launching the application .exe directly or via a shell
  • launching the application from a new shortcut

There is some kind magic to the 'MyApplication.appref-ms' that goes into the start menu; the appref-ms is just a url to the install path:

http://s3-ap-southeast-1.amazonaws.com/blahblah/Dev/MyApplication.application#MyApplication.application, Culture=neutral, PublicKeyToken=fdasdfsafads, processorArchitecture=x86

...which somehow launches a 'click once aware' instance of the application. But how?

like image 491
Doug Avatar asked Jan 10 '17 06:01

Doug


1 Answers

I'll still happily accept an answer that explains how the guts of launching the application actually sets the application context up with an identity, but for now here's my best stab at what's going on for anyone else who finds this question later:

  • ClickOnce applications are launched by hitting the install url, or by using the .appref-ms file on the start menu, which contains the url.

  • The application/x-ms-application MIME type handler is invoked for the downloaded file, which launches the 'ClickOnce aware' instance of the application.

  • At runtime, the ApplicationContext.Identity is used to determine what the ClickOnce details are, and setup the CurrentDeployment object.

  • As far as anyone knows, directly launching the deployed executable for a ClickOnce application (in C:\Users\Administrator\AppData\Local\Apps\2.0\b107ee1... or whatever the install folder is) will always return IsNetworkDeployed as false, and will not be able to self update.

Practically speaking, this means:

  • You're looking for the install folder and path to your .exe? Don't bother. Even when you know where it is, it won't have the correct ApplicationContext when you run it.

  • To spawn a new 'ClickOnce aware' instance of your application launch internet explorer at its install url. You cannot pass command line arguments to it.

eg.

var url = "http://s3-ap-southeast-1.amazonaws.com/blahblah/Dev/MyApplication.application#MyApplication.application, Culture=neutral, PublicKeyToken=fdasdfsafads, processorArchitecture=x86";
var psi = new ProcessStartInfo
{
    FileName = @"iexplore",
    Arguments = $"\"{url}\"",
};
Process.Start(psi);

(if you want to find the URL, hunt through the start menu for the appref-ms file for the application; the url is contained in it)

...and how does the executable get launched with an identity?

No idea; but this is as far as anyone seems to ever have got with understanding it:

(tldr; magic. Probably something to do with how CreateProcess is invoked to spawn the appliciation using the ApplicationContext api, in some combination of DFsvc.exe. DFshim.dll and DFdll.dll)

(The rest of this information is taken from this old blog post by Ian Picknel: http://ianpicknell.blogspot.com.au/2010/03/launching-clickonce-application.html)

Once the deployment manifest has been stored in the Temporary Internet Files folder, Internet Explorer then attempts to establish how it should handle the file with the (assumed and actual) .application extension. It checks the user-specific file types at HKCU\Software\Classes and, if it fails to find an .application sub-key there, checks for machine-specific file types at HKCR. Via this means it establishes that the .application extension denotes an Application.Manifest file. It then uses this information to check the user-specific HKCU\Software\Classes\Application.Manifest and machine-specific HKCR\Application.Manifest keys to establish the CLSID of a library which handles Application.Manifest files and yields the result {98af66e4-aa41-4226-b80f-0b1a8f34eeb4}. Finally, it looks up this CLSID in the user-specific HKCU\Software\Classes\CLSID{98af66e4-aa41-4226-b80f-0b1a8f34eeb4} and machine-specific HKCR\CLSID{98af66e4-aa41-4226-b80f-0b1a8f34eeb4} paths to establish that .application files are handled by C:\WINDOWS\system32\DFshim.dll.

This is where things start to get a little complicated. I said earlier that no 'magic' was happening behind the scenes. Well, whilst that was true for the process of retrieving the deployment manifest it most certainly is not true of the process of actually launching the ClickOnce application once the deployment manifest has been retrieved and the handler, DFshim.dll, has been identified.

DFshim.dll is described in the registry as the 'Manifest mime handler' although its file properties describe it as the 'Application Deployment Support Library'. It implements the Internet Explorer MIME handler COM interface. It is a native 32-bit DLL, written in Microsoft Visual C++ 2005, and was installed into C:\Windows\system32 when the .NET Framework 2.0 was installed.

DFshim.dll has a hard-coded reference to DFdll.dll, which it locates by checking the values of HKLM\SOFTWARE\Microsoft.NETFramework\InstallRoot (typically C:\Windows\Microsoft.NET\Framework) and the keys beneath HKLM\SOFTWARE\Microsoft.NETFramework\Policy\AppPatch (typically v2.0.50727, even if a later version of the Framework is installed). DFdll.dll too is a native 32-bit DLL written in Microsoft Visual C++ 2005.

DFdll.dll uses COM services exposed by DFsvc.exe, which is also located in the .NET Framework folder. DFsvc.exe is a standard .NET MSIL assembly. DFsvc contains a single Main method (within the System.Deployment.Application namespace) which simply calls the internal System.Deployment.Application.DFServiceEntryPoint.Initialize method within System.Deployment.dll. The Initialize method registers System.Deployment.Application.DeploymentServiceCom with COM via System.Runtime.InteropServices.RegistrationServices (i.e. it performs the equivalent of CoRegisterClassObject in COM) using the CLSID {33246f92-d56f-4e34-837a-9a49bfc91df3}. This is the means by which its services are made available to DFdll.dll.

The COM service exposed by System.Deployment.Application.DeploymentServiceCom delegates the majority of its methods to other non-ComVisible classes within the System.Deployment.Application namespace. For example, the public ActivateDeployment method calls ActivateDeployment on a new System.Deployment.Application.ApplicationActivator instance, the public CheckForDeploymentUpdate method calls CheckForDeploymentUpdate on System.Deployment.Application.SubscriptionStore, etc.

It is clear that the vast majority of the work surrounding the actual installation and launch of the ClickOnce application is undertaken by classes within the System.Deployment namespace, hosted within DFsvc.exe. DFshim.dll and DFdll.dll appear to primarily be responsible for arbitrating between the COM-based world of Internet Explorer and the .NET-based world of ClickOnce.

like image 61
Doug Avatar answered Oct 13 '22 01:10

Doug