private bool TryRetrieveInstalledPackages(AppPackageIdentity packageId, out List <PackageInfo> matchingPackages) { if (_verbose) { Console.Out.WriteLine("Attempting to query installed packages matching app"); } matchingPackages = null; bool successful = false; try { Task <AppPackages> packagesTask = _portal.GetInstalledAppPackagesAsync(); packagesTask.Wait(); // We want to find all packages that *loosely* match our appx in case something like // architecture or configuration changed matchingPackages = (from package in packagesTask.Result.Packages where package.AppId == packageId.LaunchId && package.Publisher == package.Publisher select package).ToList(); successful = true; } catch (Exception ex) { if (_verbose) { Console.Out.WriteLine("Failed to acquire list of installed apps from device: " + ex.Message); } } if (_verbose) { if (!successful) { Console.Out.WriteLine("Failed to retrieve installed packages from the device"); } else { Console.Out.WriteLine("Successfully retrieved installed packages matching app from the device"); } } return(successful); }
private bool TryRetrievePackageNameAndLaunchIdFromDevice(AppPackageIdentity packageId, int numAttempts, out string packageFullName, out string launchId) { // Already have the info locally and don't need to query from device if (packageId.CompleteIdentity) { packageFullName = packageId.PackageFullName; launchId = packageId.LaunchId; return(true); } if (_verbose) { Console.Out.WriteLine("Attempting to query PackageFullName and AUMID from remote device"); } packageFullName = String.Empty; launchId = String.Empty; bool successful = false; while (numAttempts > 0 && !successful) { numAttempts--; try { Task <AppPackages> packagesTask = _portal.GetInstalledAppPackagesAsync(); packagesTask.Wait(); // This basic query should provide the matching package in most cases // -PackageName must exactly match // -Publisher must exactly match // -AUMID (called "AppId" in PackageInfo class) must contain our AppId value (registered app entry point) // -Version string must exactly match var matchingPackages = (from package in packagesTask.Result.Packages where package.Name == packageId.PackageName && package.Publisher == package.Publisher && package.AppId.Contains(package.AppId) && // AppId from PackageInfo is actually full AUMID package.Version.ToString() == packageId.Version select package).ToList(); PackageInfo matchingPackage = null; if (matchingPackages.Count() == 0) { if (_verbose) { Console.Out.Write("Failed to find '" + packageId.PackageName + "' package installed on the device..."); Console.Out.WriteLine((numAttempts > 0) ? "trying again" : "giving up"); } } else if (matchingPackages.Count() > 1) { // It's technically possible for the above query to return multiple packages, in which case we need to // disambiguate using the optional package identifiers (CPU architecture and ResourceId). // Since PackageInfo doesn't provide this fields directly, need to split PackageFullName // into component its component parts => // [0] PackageName // [1] Version // [2] CPU architecture // [3] ResourceId (if present) // [4] Publisher name hash foreach (var package in matchingPackages) { var nameParts = package.FullName.Split(new char[] { '_' }, 5); if (nameParts.Length < 5) { continue; } // ResoruceId will be an empty string if not present, which should match our manifest data if (nameParts[2] == packageId.CpuArchitecture && nameParts[3] == packageId.ResourceId) { matchingPackage = package; break; } } } else { matchingPackage = matchingPackages.First(); } if (matchingPackage != null) { packageFullName = matchingPackage.FullName; launchId = matchingPackage.AppId; successful = true; } // Query attempt may failed because it a few seconds before newly installed apps are reported // So if we have more attempts then wait a bit before trying again if (numAttempts > 0 && !successful) { Thread.Sleep(2000); } } catch (Exception ex) { if (_verbose) { Console.Out.WriteLine("Failed to acquire list of installed apps from device: " + ex.Message); } } } if (_verbose) { if (!successful) { Console.Out.WriteLine("Failed to retrieve FullPackageName and AUMID from remote device"); } else { Console.Out.WriteLine("Successfully retrieved FullPackageName and AUMID from remote device"); } } return(successful); }
private void ExecuteInstallAppx(ParameterHelper parameters, string appxFile, string certificate) { if (_verbose) { Console.Out.WriteLine("Starting Appx installation..."); } var file = new FileInfo(Path.GetFullPath(appxFile)); if (!file.Exists) { throw new System.IO.FileNotFoundException("Specified appx file '" + appxFile + "' wasn't found"); } if (!String.IsNullOrWhiteSpace(certificate)) { var certFile = new FileInfo(certificate); if (!certFile.Exists) { throw new System.IO.FileNotFoundException("Specified certificate file '" + certFile + "' wasn't found"); } certificate = certFile.FullName; } else { // Must pass in null instead of empty string if certificate is omitted certificate = null; } // Parse the AppxManfest contained in the Appx file to extract the package name, dependencies, AppID, etc. AppxManifest appxData; try { appxData = AppxManifest.Get(appxFile); } catch (Exception ex) { Console.Out.WriteLine("Failed to parse Appx manifest: " + ex.Message); throw; } if (!appxData.IsValid) { throw new System.ArgumentException("Specified Appx '" + appxFile + "' contains an invalid AppxManifest"); } // Construct an "identity" object from the AppxManifest data which can be referenced later to launch the installed app var appIdentity = new AppPackageIdentity(appxData); // Query for app packages already installed on the device and uninstall them if necessary // NOTE: Check for any uninstall any package matching this appx PackageName and Publisher to // ensure a clean install of the new build List <PackageInfo> matchingPackages; if (TryRetrieveInstalledPackages(appIdentity, out matchingPackages) && matchingPackages.Count() > 0) { if (_verbose) { Console.Out.WriteLine("Uninstalling previous app.."); } foreach (var package in matchingPackages) { try { if (_verbose) { Console.Out.WriteLine("Uninstalling package: " + package.FullName); } _portal.UninstallApplicationAsync(package.FullName).Wait(); } catch (AggregateException ex) { // NOTE: We really shouldn't continue with installation if we failed to remove a previous version of the app. // If a version of the app remains on the device, the Install API will NOT replace it but still reports "success", // meaning the user could be running old code and not know it. A hard fail is the only way to ensure this doesn't happen. Console.Out.WriteLine("Uninstall of package '" + package.FullName + "' failed: " + ex.InnerException.Message); System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } } if (_verbose) { Console.Out.WriteLine("Finished uninstalling previous app packages"); } } Task installTask = _portal.InstallApplicationAsync(null, file.FullName, appxData.Dependencies, certificate, 500, 1, false); try { installTask.Wait(); Console.Out.WriteLine("Installation completed successfully"); } catch (AggregateException ex) { Console.Out.WriteLine("Installation of Appx failed!"); HandleInstallOperationException(ex); System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } // Save AppIdentity to field after successful installation _installedAppId = appIdentity; // If the app Identity is "complete" we have the FullPackageName and AUMID parameters, so we'll // add them to our parameter set to later launch the app; no need to query package info from the device if (_installedAppId.CompleteIdentity) { parameters.AddOrUpdateParameter(parameterPackage, _installedAppId.PackageFullName); parameters.AddOrUpdateParameter(ParameterAumid, _installedAppId.LaunchId); } }