I disabled update checking in Visual Studio by unchecking the publish property The application should check for updates
.
My app checks for updates and the user has to option to decline to update.
The issue is when the user skips the update, the next time he starts the app the default ClickOnce update screen is presented again.
How do I make sure it never shows the default ClickOnce update dialog?
My update code:
private void CheckForUpdates()
{
if (!ApplicationDeployment.IsNetworkDeployed)
{
return;
}
var currentDeployment = ApplicationDeployment.CurrentDeployment;
UpdateCheckInfo info;
try
{
info = currentDeployment.CheckForDetailedUpdate();
}
catch (Exception e)
{
return;
}
if (!info.UpdateAvailable)
{
return;
}
var changelogDialog = new Changelog();
if (changelogDialog.ShowDialog() != true)
{
return;
}
currentDeployment.Update();
Exit();
}
This is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="test.ccpl.Desktop.application" version="1.0.0.89" publicKeyToken="7613da056444d824" language="en-CA" processorArchitecture="x86" xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="test ccpl" co.v1:suiteName="test" asmv2:product="test ccpl" xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="true" mapFileExtensions="true" co.v1:createDesktopShortcut="true">
<deploymentProvider codebase="https://test-test.test.ca/Installers/test.ccpl.Desktop.application" />
</deployment>
<dependency>
<dependentAssembly dependencyType="install" codebase="Application Files\test.ccpl.Desktop_1_0_0_89\test.ccpl.Desktop.exe.manifest" size="58997">
<assemblyIdentity name="test.ccpl.Desktop.exe" version="1.0.0.89" publicKeyToken="7613da056444d824" language="en-CA" processorArchitecture="x86" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<dsig:DigestValue>u36JKY4n1mmu2LZC3Ea5uRLheiM=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
<framework targetVersion="4.7.2" profile="Full" supportedRuntime="4.0.30319" />
</compatibleFrameworks>
<publisherIdentity ...>
I think that everything you are seeing is by design.
As soon as you call currentDeployment.CheckForDetailedUpdate()
, clickonce will store that update metadata in the registry. On next startup, clickonce will always look at this information to see if there is a pending deployment and if so, it will also determine if the user has skipped this newer version or not. If you want or not, this is by design.
However, if you really want to get rid of the clickonce Update dialog on startup, then there is an ugly solution available :-) Update: See below for a second workaround.
First, lets have a look at the registry and how everything works:
Navigate to this location:HKEY_CURRENT_USER\Software\Classes\Software\Microsoft\Windows\CurrentVersion\Deployment\SideBySide\2.0\PackageMetadata\{2ec93463-b0c3-45e1-8364-327e96aea856}_{3f471841-eef2-47d6-89c0-d028f03a4ad5}\
You will see 3 sub keys for every clickonce application you have installed.
In my case the application is called WindowsApplication and the public key token is a619d47505395849. So the important key to look for starts with wind..tion_a619d47505395849_
You should see something similar to test..tion_7613da056444d824_xxxxxxxxxx on your side. Just iterate over the keys, look for the public key token and choose the shortest key.
Now comes the important part. Look at the value which name ends with !PendingDeployment
. After calling the CheckForDetailedUpdate
method, it should look like this:
This is why the Update Dialog is showing up.
And then just replace the value with this and you are done:
The Update Dialog will then not appear anymore. The user can manually check for an update within your application, accept or ignore it again and again.
You can test these steps manually to see if everything works as expected. Putting it in code should become something similar to this:
var changelogDialog = new Changelog();
if (changelogDialog.ShowDialog() != true)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Classes\Software\Microsoft\Windows\CurrentVersion\Deployment\SideBySide\2.0\PackageMetadata\{2ec93463-b0c3-45e1-8364-327e96aea856}_{3f471841-eef2-47d6-89c0-d028f03a4ad5}");
var subkeyName = key.GetSubKeyNames().Where(x => x.Contains("7613da056444d824")).OrderBy(x => x.Length).First();
var subkey = key.OpenSubKey(subkeyName, true);
subkey.SetValue("{2ad613da-6fdb-4671-af9e-18ab2e4df4d8}!PendingDeployment", new byte[] { 00, 00 }, RegistryValueKind.Binary);
return;
}
Another Workaround:
Instead of calling the built-in function CheckForDetailedUpdate()
, you can create your own "CheckForUpdate" method. That is not be a big deal:
private CustomUpdateCheckInfo CheckForUpdate()
{
var info = new CustomUpdateCheckInfo();
var currentVersion = ApplicationDeployment.CurrentDeployment.CurrentVersion;
var manifestUri = ApplicationDeployment.CurrentDeployment.UpdateLocation;
using (XmlTextReader reader = new XmlTextReader(manifestUri.AbsoluteUri))
{
var doc = XDocument.Load(reader);
var version = doc.Descendants().FirstOrDefault(n => n.Name.LocalName == "assemblyIdentity").Attribute("version").Value;
info.NewestVersion = version;
info.IsUpdateAvailable = currentVersion.ToString() != version;
}
return info;
}
It will compare the currently deployed version with the newest version in the manifest file and returns an instance of CustomUpdateCheckInfo
:
public class CustomUpdateCheckInfo
{
public bool IsUpdateAvailable { get; set; }
public string NewestVersion { get; set; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With