Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update-Process not started launched by Desktop-Shortcut or Taskbar-Item #59

Closed
THillebrand opened this issue Sep 30, 2019 · 33 comments
Closed

Comments

@THillebrand
Copy link

THillebrand commented Sep 30, 2019

Hi @ALL!
I have a msix package with an appinstaller, setting the UpdateSettings:

OnLaunch HoursBetweenUpdateChecks="0" ShowPrompt="true" UpdateBlocksActivation="true"

Building, installing an execute this by using the Start-Menu-Entry seems to be fine. The Update-Window appears (and is closed if no update is pending) and the program starts.

But if I create and use a shortcut or a Taskbar-Item, the Update don't appear. The program starts directly with the old version.

The shortcut seems to be correct created (using the PackageFamily and the Id).

Same using the shell command:

explorer.exe shell:AppsFolder\8bbf065c-eac1-4fca-a02e-6753c5308256_ke2zapfkqzqsw!App

Does anybody know how to fix this? I want a shortcut and a taskbar item which calls the update process before starting the program.

@THillebrand THillebrand changed the title Update-Process not started launching with Desktop-Shortcut or Taskbar-Item Update-Process not started launched by Desktop-Shortcut or Taskbar-Item Sep 30, 2019
@Huios
Copy link
Contributor

Huios commented Sep 30, 2019

@THillebrand

When the update doesn't work when launching from the Taskbar, is this happening
(a)on the first run when you're expecting the update to be downloaded
(b)after the update was previously downloaded but a subsequent launch from the taskbar launches the old version?

Can you please share your AppInstallerFile and OS version?

Thanks,
Tanaka MSFT

@THillebrand
Copy link
Author

THillebrand commented Oct 1, 2019

Hi Tanaka,
I would say (a), the update isn't downloading. You can start from taskbar or shortcut several times without an update.

The Windows 10 Version is 1903 (Build 18362.356).
The AppInstaller-File is attached.
PremiumDrive.Package.appinstaller.zip

@THillebrand
Copy link
Author

Sorry, but I have a meeting tomorrow....
are there any news about that?

@dragnilar
Copy link

dragnilar commented Oct 9, 2019

@Huios We are also having this problem. For example, if myself or another user launches the app from the start menu or a live tile, the update process runs and checks for updates and lets them know they have an update for the app that they need to install before they can proceed.

This happens regardless of whether or not they are using the app installer that we have on our test environment (local network file server) or the one that we use for production (Azure blob).

This also happens regardless of whether or not it is the first run or a subsequent run after the update has been made available.

However if they open the app from either a task bar or desktop shortcut, the updater doesn't show up. Windows also doesn't seem to download and queue up the app for installation in the background. When they restart the app it is still the old version. We have to get them to either use the start menu or live tile short cut to get the app to update. It seems almost like the UWP container is getting bypassed by using traditional shortcuts.

OS version - Windows 10 1903

App installer file snippet:

<?xml version="1.0" encoding="utf-8"?> <AppInstaller Uri="UriForAppInstallerFileChangedForPrivacyReasons" Version="1.1.21.0" xmlns="http://schemas.microsoft.com/appx/appinstaller/2018"> <MainBundle Name="PIP" Version="1.1.21.0" Publisher="CN=PublisherName, O=CompanyName, C=US" Uri="PathToMyMSIBundleChangedForPrivacyReasons" /> <UpdateSettings> <OnLaunch HoursBetweenUpdateChecks="0" ShowPrompt="true" UpdateBlocksActivation="true" /> <ForceUpdateFromAnyVersion>true</ForceUpdateFromAnyVersion> </UpdateSettings> </AppInstaller>

@dragnilar
Copy link

@Huios If this isn't the proper place to raise this issue, could you please provide me with guidance as to where to bring it up? This is going to cause a lot of problems with people running on outdated versions of our software since we have a lot of users who rely upon desktop shortcuts and task bar icons for launching their apps.

@Huios
Copy link
Contributor

Huios commented Oct 14, 2019

@THillebrand & @dragnilar

Sorry for the delay here, we're still investigating the issue. I'll loop back as soon as we have an update on what's causing this and any potential workarounds.

@Huios
Copy link
Contributor

Huios commented Oct 16, 2019

@THillebrand & @dragnilar

After following up with our team, it turns out current implementation does not trigger the update from taskbar and desktop shortcuts by design. The reasoning was that many of those types of app launches are for completing a task so they were trying to reduce friction for the user. Updates were limited to launches from the Start menu or live tiles.

We've added your ask as a feature request for upcoming releases. 

@dragnilar
Copy link

dragnilar commented Oct 16, 2019

@Huios Thanks for looking into that for us. I am considering a couple of workarounds right now until this feature is supported. I find the design decision a little strange since most users still use desktop shortcuts in my experience. However, from the way you have worded it, it sounds like it may have been also due to some programming and architectural considerations. So I can understand why it's not as "simple as it seems" (nothing ever is, right? 😉)

In the meantime, I'd recommend that you should consider updating the documentation for AppInstaller to point out that traditional shortcuts are not supported at this time.

Aside from the obvious work-around of telling users to launch from the start menu or live tiles, do you know if there are any Windows 10/RT APIs that we can leverage to check for updates while the app is launching to at least have Windows queue up an update and then inform the user that the app is updating? I know there is one for the store, but it isn't clear if that API works for sideloaded apps.

I don't mind programming in something like that. We're just concerned that many users will be forgetful and launch from their desktop/taskbar shortcuts when there is an update available and the background task hasn't caught the update yet for them.

If you don't recommend using any APIs or there isn't any way to do such a thing, I can see about using KACE (which we have) to help automate deployment, but I'd rather not go that route if there are some other means.

@THillebrand
Copy link
Author

I agree dragnilar!
We have a 2-Tier-Application. It is client/server-based. The client has to be updated after the server has been updated. There is no chance to connect to an older version of the server (or DB). The client-update is absolute required.
On the other hand the launch by shortcut or taskbar is the usecase our customers prefer, so there is no alternative. By using the MSIX as a desktop-installation I think Microsoft should think about it. A little app, maybe UWP, has maybe different versions to run parallel. But a program (and not an app) does maybe not. So I think this decision is fail decision for desktop-deployment.

So, please add the feature with a high prio.

@Huios
Copy link
Contributor

Huios commented Oct 16, 2019

@dragnilar you can consider using the PackageManager.UpdatePackageAsync API

To use it you need the package management capability in your package - <rescap:Capability Name="packageManagement" />

We're working on getting this documented better. In the meantime, here's sample C# code on using the API to update your package:

`private async void CheckUpdate(object sender, TappedRoutedEventArgs e)

    {

        WebClient client = new WebClient();

        Stream stream = client.OpenRead("https://trial3.azurewebsites.net/HRApp/Version.txt");

        StreamReader reader = new StreamReader(stream);

        var newVersion = new Version(await reader.ReadToEndAsync());

        Package package = Package.Current;

        PackageVersion packageVersion = package.Id.Version;

        var currentVersion = new Version(string.Format("{0}.{1}.{2}.{3}", packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision));

        if (newVersion.CompareTo(currentVersion) > 0)

        {

            var messageDialog = new MessageDialog("Found an update.");

            messageDialog.Commands.Add(new UICommand(

                "Update",

                new UICommandInvokedHandler(this.CommandInvokedHandler)));

            messageDialog.Commands.Add(new UICommand(

                "Close",

                new UICommandInvokedHandler(this.CommandInvokedHandler)));

            messageDialog.DefaultCommandIndex = 0;

            messageDialog.CancelCommandIndex = 1;

            await messageDialog.ShowAsync();

        } else

        {

            var messageDialog = new MessageDialog("Did not find an update.");

            await messageDialog.ShowAsync();

        }

    }


    private async void CommandInvokedHandler(IUICommand command)

    {

        if (command.Label == "Update")

        {

            PackageManager packagemanager = new PackageManager();

            await packagemanager.UpdatePackageAsync(

                new Uri("https://trial3.azurewebsites.net/HRApp/HRApp.msix"),

                null,

                DeploymentOptions.ForceApplicationShutdown

            );

        }


    }`

@dragnilar
Copy link

@Huios That API is exactly what I needed. Thank you for pointing that out to me. It works well with the splash screen in our WPF app, so we're now covered on both sides (start menu and traditional shortcuts). We can definitely live with this. Thanks for the help!

@Huios
Copy link
Contributor

Huios commented Oct 21, 2019

You're welcome @dragnilar , glad I was able to help!

@dragnilar
Copy link

Hi @dianmsft,

Did this get closed because @Huios was able to provide a work around or is there an upcoming update for Windows that addresses this shortcoming? We're doing fine with the work around, but I just wanted to make sure there wasn't some news regarding this issue that I am missing.

@marosklempa
Copy link

Hi @dianmsft,

I also ask to fix this shortcoming. We invest a lot of time and resource to switch from our old install and update procedure to new MSIX installation and automatic update procedure. We estimated that new way of installation and update of apps provided by Microsoft will cover all ordinary scenarios. Now we are in situation where update process is not working using standard app launch method (desktop shortcut, taskbar button). To be a honest, our users strongly prefer desktop and taskbar over start menu. Proposed workaround to check for new version inside app is not acceptable, because we estimate and require that MSIX standard update procedure have to work in any ordinary circumstances. Run app by desktop shortcut and taskbar button is standard way how to work in Windows. Please reopen this issue and ask team to fix it.
Thanks a lot.

@biilmann-orifarm
Copy link

biilmann-orifarm commented Jan 3, 2020

@Huios : Can you please provide a link to the feature request you mentioned above has been created for this issue. So I can go and upvote. I cannot seem to find the feature request/"idea" here : https://techcommunity.microsoft.com/t5/msix/idb-p/MSIXIdeas
-- but maybe I'm looking in the wrong place(?)

@marosklempa
Copy link

marosklempa commented Jan 5, 2020

@THillebrand, @dragnilar and @biilmann-orifarm,

I created new idea in Microsoft Tech Community MSIXIdeas page which describe request to run update-process starting app by taskbar button, desktop shortcut. Please upvote for it if you feel that it is important.
https://techcommunity.microsoft.com/t5/msix/start-update-process-by-clicking-on-taskbar-button-or-desktop/idi-p/1091191

Thanks a lot.

@biilmann-orifarm
Copy link

biilmann-orifarm commented Jan 7, 2020

@dragnilar: I'm facing a similar problem here. We have a WPF app that we recently migrated to target .NET Core 3.1 - and now I'm trying to package and deploy that app using MSIX, along with an AppInstaller file for auto-updates. I just recently discovered this "by-design-feature" re. not getting updates when started from a desktop shortcut.

Did you manage to call that API which @Huios mentions above, from .NET Core code? Or how did you go about adding code that checks for updates?

UPDATE: Aha - via this blog post I just found out about Microsoft.Windows.SDK.Contracts. It seems I can use the API mentioned above this way. I'm going to give that a try and see if does the trick.

@dragnilar
Copy link

dragnilar commented Jan 7, 2020

@biilmann-orifarm

Yep, the API that @Huios provided definitely worked. And that library that is mentioned in the blog post is the one I used.

I noticed that its a little slower than the auto updater that is embedded in Windows 10 if you call it within the program/package that is being updated. I haven't really had the time to experiment with it further to see if there is a way to get it perform as quickly.

One thing I found that helps is if you use it to check for updates before running the UpdatePackagesAsync method since it can throw exceptions if there are no updates available.

For example:

        public static async Task<bool> CheckForUpdates()
        {
            var currentPackage = Package.Current;
            var status = await currentPackage.CheckUpdateAvailabilityAsync();
            return status.Availability == PackageUpdateAvailability.Required || status.Availability == PackageUpdateAvailability.Available;
        }

You can use that to determine whether or not it is "safe" to use the UpdatePackageAsync method on the PackageManager class.

Edit:

I also posted a solution about this on Stackoverflow when I asked on there if anyone else had a work around. It's effectively a recap of everything on here.

@biilmanndk
Copy link

@dragnilar : Excellent. Thanks for the info and update. I'll dig more into this now that I (finally) got my code signing certificate to work on our build server.

@SimoneBWS
Copy link

Hi @Huios
with UpdatePackageAsync api, is it possible to automatically restart app after update is completed?

@Dreiundzwanzig
Copy link

Dreiundzwanzig commented Jan 23, 2020

@SimoneBWS I was facing the same problem but couldn't find a way to restart the app after an update.

Instead i solved it this way: if an update is available download the .appinstaller, run it and exit the application.
This way the "normal" update process is started.

    var uri = Package.Current.GetAppInstallerInfo().Uri;

    internal static void DownloadAndStartAppInstaller(Uri uri)
    {
        var webClient = new System.Net.WebClient();
        var tempFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "YourProgramName.appinstaller");

        if (System.IO.File.Exists(tempFile))
            System.IO.File.Delete(tempFile);

        webClient.DownloadFile(uri, tempFile);

        Process.Start(tempFile);

        Application.Exit();
    }

Edit:
Instead of downloading the .appinstaller file you can start it directly:

    Process.Start("ms-appinstaller:?source=https://<server>/YourProgramName.appinstaller");

@marosklempa
Copy link

Hi @Dreiundzwanzig,

if you are interesting on this topic, please upvote for feature request on MSIX team pages on Microsoft Tech Community site.
https://techcommunity.microsoft.com/t5/msix/start-update-process-by-clicking-on-application-taskbar-button/idi-p/1091191

@marosklempa
Copy link

Hi @Dreiundzwanzig

will your approach to directly launch appinstaller work if user has no Administrator rights, thus user can't elevate process to admin level? In other words could be appinstaller launch without administrator rights?

@dragnilar
Copy link

dragnilar commented Jan 25, 2020

@SimoneBWS I was facing the same problem but couldn't find a way to restart the app after an update.

Instead i solved it this way: if an update is available download the .appinstaller, run it and exit the application.
This way the "normal" update process is started.

    var uri = Package.Current.GetAppInstallerInfo().Uri;

    internal static void DownloadAndStartAppInstaller(Uri uri)
    {
        var webClient = new System.Net.WebClient();
        var tempFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "YourProgramName.appinstaller");

        if (System.IO.File.Exists(tempFile))
            System.IO.File.Delete(tempFile);

        webClient.DownloadFile(uri, tempFile);

        Process.Start(tempFile);

        Application.Exit();
    }

Edit:
Instead of downloading the .appinstaller file you can start it directly:

    Process.Start("ms-appinstaller:?source=https://<server>/YourProgramName.appinstaller");

I actually like this as a better approach than the one I came up with; opening up the app installer using the string seems to be pretty much the same thing that Windows does when you launch from the start menu. 👍

The only thing I might add is that if you are using Dot Net Core, it looks like you'll need to create a process object with a process start info that specifies to use shell execute. Otherwise you'll get an exception saying "File Not Found":

var p = new Process();
p.StartInfo = new ProcessStartInfo(@"C:\Users\user2\Desktop\XXXX.reg")
{ 
    UseShellExecute = true 
};
p.Start();

@Dreiundzwanzig
Copy link

@marosklempa

will your approach to directly launch appinstaller work if user has no Administrator rights, thus user can't elevate process to admin level? In other words could be appinstaller launch without administrator rights?

Sorry for the late reply:
Yes, it works without admin rights.

@DuncanStone
Copy link

DuncanStone commented May 4, 2021

Just because it took me hours to get working, and because a lot of the versions of this written above seem to be broken (in many cases this appears to be due to bugs introduced since they were written), I thought I might just add in my version which is currently working as of this date:

private async Task CheckForUpdates()
{
	if (this.Version == null || this.Version.CompareTo(new Version("0.0.0.0")) == 0)
	{
		return;
	}
	string installerUri = Package.Current.GetAppInstallerInfo()?.Uri?.AbsoluteUri;
	if (installerUri == null)
		this.logger.Warning("App installer info returned null"); // For some reason GetAppInstallerInfo can not be relied upon
	{
#if STAGING
		installerUri = "YOUR HARDCODED STAGING URL";
#endif
#if RELEASE
		installerUri = "YOUR HARDCODED RELEASE URL";
#endif
	}

	this.logger.Debug($"Attempting to contact update server at {installerUri}");

	using HttpClient client = new();
	HttpResponseMessage response = await client.GetAsync(installerUri);
	string installer = await response.Content.ReadAsStringAsync();
	//this.logger.Information($"Found installer file on server: \n{installer}");
	XDocument doc = XDocument.Parse(installer);
	XNamespace ns = doc.Elements().First().GetDefaultNamespace();
	string bundleUri = doc.Descendants(ns + "MainBundle").First().Attribute("Uri").Value;
	Version newVersion = new(doc.Descendants(ns + "MainBundle").First().Attribute("Version").Value);

	if (newVersion.CompareTo(this.Version) != 0)
	{
		this.logger.Information($"Server version is different, starting installer: \n{installerUri}");
		this.logger.Information($"Attempting to patch");
		DownloadAndStartAppInstaller(installerUri);
	}
}

private static void DownloadAndStartAppInstaller(string uri)
{
	WebClient webClient = new();
	string tempFile = Path.Combine(Path.GetTempPath(), "WriteInStoneInstaller.appinstaller");

	if (System.IO.File.Exists(tempFile))
	{
		System.IO.File.Delete(tempFile);
	}

	webClient.DownloadFile(uri, tempFile);

	ProcessStartInfo startInfo = new ProcessStartInfo(tempFile) { UseShellExecute = true };

	_ = Process.Start(startInfo);

	Current.Shutdown();
}

Please note that this.Version is a System.Version which can be gotten using:

Package package = Package.Current;
this.Version = new Version({ package.Id.Version.Major}.{ package.Id.Version.Minor}.{ package.Id.Version.Build}.{ package.Id.Version.Revision});

I refuse to use the PackageVersion class because it is totally redundant, and does not even have a ToString implementation.

Edit: updated the code to reflect the fact that the appinstaller protocol has been disabled
Edit: updated to reflect the fact that GetAppInstallerInfo randomly returns null on some machines for no apparent reason

@MichaelBakkerDP
Copy link

@DuncanStone Thanks for sharing your workaround.
In my environment this solution leads to the same message in AppInstaller that brought this issue to life in the first place.
... protocol disabled...

A - not very elegant - workaround is changing the Process.Start... line to:

Process.Start(new ProcessStartInfo(installerUri.ToString()) { UseShellExecute = true });

@DuncanStone
Copy link

DuncanStone commented Jan 28, 2022

@MichaelBakkerDP actually that is a new issue, this post was written long before the protocol was disabled. I do apologise however as I posted a link to this in the thread for the protocol issue and forgot to update this post to reflect my most recent changes. I will correct this immediately

@amirvenus
Copy link

Just because it took me hours to get working, and because a lot of the versions of this written above seem to be broken (in many cases this appears to be due to bugs introduced since they were written), I thought I might just add in my version which is currently working as of this date:

public async Task CheckForUpdates()
{
	if (this.Version == null || this.Version.CompareTo(new Version("0.0.0.0")) == 0)
	{
		return;
	}
	Uri installerUri = Package.Current.GetAppInstallerInfo().Uri;
	this.logger.Debug($"Attempting to contact update server at {installerUri.AbsoluteUri}");

	using HttpClient client = new();
	HttpResponseMessage response = await client.GetAsync(installerUri);
	string installer = await response.Content.ReadAsStringAsync();
	//this.logger.Information($"Found installer file on server: \n{installer}");
	XDocument doc = XDocument.Parse(installer);
	XNamespace ns = doc.Elements().First().GetDefaultNamespace();
	string bundleUri = doc.Descendants(ns + "MainBundle").First().Attribute("Uri").Value;
	Version newVersion = new(doc.Descendants(ns + "MainBundle").First().Attribute("Version").Value);

	if (newVersion.CompareTo(this.Version) > 0)
	{
		this.logger.Information($"Server version is newer, starting installer: \n{installerUri}");
		// See https://techcommunity.microsoft.com/t5/msix-deployment/the-ms-appinstaller-protocol-has-been-disabled/m-p/3076320/highlight/false#M1173
		// Process.Start(new ProcessStartInfo($"ms-appinstaller:?source={installerUri}") { UseShellExecute = true });
		// Use this until the appinstaller protocol is fixed:
		DownloadAndStartAppInstaller(installerUri);
		this.logger.Information($"Shutting down application");
		App.AppCurrent.Shutdown();
	}
}

private static void DownloadAndStartAppInstaller(Uri uri)
{
	WebClient webClient = new();
	string tempFile = Path.Combine(Path.GetTempPath(), "WriteInStoneInstaller.appinstaller");

	if (System.IO.File.Exists(tempFile))
	{
		System.IO.File.Delete(tempFile);
	}

	webClient.DownloadFile(uri, tempFile);

	_ = Process.Start(tempFile);

	Current.Shutdown();
}

Please note that this.Version is a System.Version which can be gotten using:

Package package = Package.Current;
this.Version = new Version({ package.Id.Version.Major}.{ package.Id.Version.Minor}.{ package.Id.Version.Build}.{ package.Id.Version.Revision});

I refuse to use the PackageVersion class because it is totally redundant, and does not even have a ToString implementation.

Edit: updated the code to reflect the fact that the appinstaller protocol has been disabled

For me, Package.Current.GetAppInstallerInfo() is returning null. Any idea why?

@DuncanStone
Copy link

Pacakage.Current is null if you are running the main project code in the debugger. If you want to debug this code you need to set the .wapproj project as the startup project and run that. This will deploy it as a package. Yes this can be a major pain. My solution for everyday use is simply to comment out the whole contents of the CheckForUpdates function with #if !DEBUG if you are not directly testing that function.

@rprimora-pricer
Copy link

@Dreiundzwanzig

Process.Start("ms-appinstaller:?source=https://<server>/YourProgramName.appinstaller"); will not work on some versions of Windows where this kind of activation has been disabled.

@supershopping
Copy link

A workaround is not a final solution. It's clearly a feature missing / bug for the current behavior.
This issue shouldn't be closed because there is a temporary workaround.

@dragnilar
Copy link

A workaround is not a final solution. It's clearly a feature missing / bug for the current behavior. This issue shouldn't be closed because there is a temporary workaround.

@supershopping I completely empathize. It's disappointing that they are still "working on it":

https://techcommunity.microsoft.com/t5/msix/start-update-process-by-clicking-on-application-taskbar-button/idi-p/1091191

I know the work around worked for us, but we had to ditch MSIX. This was due to the obvious lack of resources Microsoft has been dedicating to the product.

It is unfortunate that they are dedicating even less these days. Satya Nadella is starting to seem like he is not doing a respectable job as a leader.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests