Example #1
0
        public async Task Pack(
            string directory,
            string packagePath,
            IDictionary <string, string> extraFiles,
            AppxPackerOptions options           = 0,
            CancellationToken cancellationToken = default,
            IProgress <ProgressData> progress   = null)
        {
            if (!Directory.Exists(directory))
            {
                throw new DirectoryNotFoundException($"Folder {directory} does not exist.");
            }

            var fileInfo = new FileInfo(packagePath);

            if (fileInfo.Directory == null)
            {
                throw new ArgumentException($"File path {packagePath} is not supported.", nameof(packagePath));
            }

            if (!fileInfo.Directory.Exists)
            {
                fileInfo.Directory.Create();
            }

            var tempFile             = Path.GetTempFileName();
            var tempManifestFilePath = Path.GetTempFileName();
            var tempAutoGenerated    = Path.GetTempFileName();

            try
            {
                var inputDirectory = new DirectoryInfo(directory);

                var listBuilder = new PackageFileListBuilder();

                foreach (var item in inputDirectory.EnumerateFiles("*", SearchOption.AllDirectories))
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var relativePath = Path.GetRelativePath(directory, item.FullName);

                    if (string.IsNullOrEmpty(relativePath))
                    {
                        continue;
                    }

                    // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
                    if (string.Equals(FileConstants.AppxManifestFile, relativePath, StringComparison.OrdinalIgnoreCase))
                    {
                        listBuilder.AddManifest(tempManifestFilePath);
                        item.CopyTo(tempManifestFilePath, true);
                        continue;
                    }

                    listBuilder.AddFile(item.FullName, relativePath);
                }

                if (extraFiles != null)
                {
                    foreach (var item in extraFiles)
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        if (string.Equals(Path.GetFileName(item.Key), FileConstants.AppxManifestFile, StringComparison.OrdinalIgnoreCase))
                        {
                            tempManifestFilePath = item.Value;
                            listBuilder.AddFile(item.Value, item.Key);
                        }
                        else
                        {
                            listBuilder.AddFile(item.Value, item.Key);
                        }
                    }
                }

                var allDirs = inputDirectory.EnumerateDirectories("*", SearchOption.AllDirectories);
                foreach (var emptyDir in allDirs.Where(d =>
                                                       !d.EnumerateFiles("*", SearchOption.TopDirectoryOnly).Any() &&
                                                       !d.EnumerateDirectories("*", SearchOption.TopDirectoryOnly).Any()))
                {
                    // this means we have an empty folder, which requires some special handling
                    if (new FileInfo(tempAutoGenerated).Length == 0)
                    {
                        await File.WriteAllBytesAsync(tempAutoGenerated, Array.Empty <byte>(), cancellationToken).ConfigureAwait(false);
                    }

                    var relativePath = Path.GetRelativePath(inputDirectory.FullName, emptyDir.FullName);
                    listBuilder.AddFile(tempAutoGenerated, $"{relativePath}\\GeneratedFile.txt\"");
                }

                await File.WriteAllTextAsync(tempFile, listBuilder.ToString(), Encoding.UTF8, cancellationToken).ConfigureAwait(false);

                XDocument xmlManifestDocument;
                await using (var tempManifestStream = File.OpenRead(tempManifestFilePath))
                {
                    using var tempManifestReader = new StreamReader(tempManifestStream);
                    xmlManifestDocument          = await XDocument.LoadAsync(tempManifestReader, LoadOptions.None, cancellationToken).ConfigureAwait(false);

                    var injector = new MsixHeroBrandingInjector();
                    await injector.Inject(xmlManifestDocument).ConfigureAwait(false);
                }

                cancellationToken.ThrowIfCancellationRequested();
                var appxWriter = new AppxDocumentWriter(xmlManifestDocument);
                await appxWriter.WriteAsync(tempManifestFilePath).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested();

                var compress = !options.HasFlag(AppxPackerOptions.NoCompress);
                var validate = !options.HasFlag(AppxPackerOptions.NoValidation);

                await new MakeAppxWrapper().PackPackageFiles(tempFile, packagePath, compress, validate, cancellationToken, progress).ConfigureAwait(false);
            }
            finally
            {
                if (File.Exists(tempFile))
                {
                    File.Delete(tempFile);
                }

                if (File.Exists(tempManifestFilePath))
                {
                    File.Delete(tempManifestFilePath);
                }

                if (File.Exists(tempAutoGenerated))
                {
                    File.Delete(tempAutoGenerated);
                }
            }
        }
        protected override async Task <bool> Save(CancellationToken cancellationToken, IProgress <ProgressData> progress)
        {
            var temporaryFiles = new List <string>();

            try
            {
                var fileListBuilder = new PackageFileListBuilder();
                fileListBuilder.AddDirectory(this.InputPath.CurrentValue, true, null);

                if (this.PrePackOptions != null && !this.PrePackOptions.ManifestPresent)
                {
                    if (!this.PrePackOptions.CanConvert)
                    {
                        throw new InvalidOperationException("The selected folder does not contain a manifest file and any executable files. It cannot be packed to MSIX format.");
                    }

                    if (!string.IsNullOrEmpty(this.InputPath.CurrentValue))
                    {
                        progress.Report(new ProgressData(0, "Creating manifest file..."));
                        var options = new AppxManifestCreatorOptions
                        {
                            CreateLogo         = this.PrePackOptions.CreateLogo,
                            EntryPoints        = this.PrePackOptions.EntryPoints.Where(e => e.IsChecked).Select(e => e.Value).ToArray(),
                            PackageDisplayName = Path.GetFileName(this.InputPath.CurrentValue),
                            RegistryFile       = this.PrePackOptions.SelectedRegistry?.FilePath == null ? null : new FileInfo(this.PrePackOptions.SelectedRegistry.FilePath)
                        };

                        // ReSharper disable once AssignNullToNotNullAttribute
                        await foreach (var result in this.manifestCreator.CreateManifestForDirectory(new DirectoryInfo(this.InputPath.CurrentValue), options, cancellationToken).ConfigureAwait(false))
                        {
                            temporaryFiles.Add(result.SourcePath);

                            if (result.PackageRelativePath == null)
                            {
                                continue;
                            }

                            fileListBuilder.AddFile(result.SourcePath, result.PackageRelativePath);
                        }
                    }
                }

                using var progressWrapper = new WrappedProgress(progress);
                var progress1 = progressWrapper.GetChildProgress(50);
                var progress2 = this.Sign.CurrentValue ? progressWrapper.GetChildProgress(30) : null;

                var tempFileList = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".list");
                temporaryFiles.Add(tempFileList);

                var tempManifestPath = Path.Combine(Path.GetTempPath(), "AppxManifest-" + Guid.NewGuid().ToString("N") + ".xml");
                temporaryFiles.Add(tempManifestPath);

                var srcManifest = fileListBuilder.GetManifestSourcePath();
                if (srcManifest == null || !File.Exists(srcManifest))
                {
                    throw new InvalidOperationException("The selected folder cannot be packed because it has no manifest, and MSIX Hero was unable to create one. A manifest can be only created if the selected folder contains any executable file.");
                }

                // Copy manifest to a temporary file
                var injector = new MsixHeroBrandingInjector();
                await using (var manifestStream = File.OpenRead(fileListBuilder.GetManifestSourcePath()))
                {
                    var xml = await XDocument.LoadAsync(manifestStream, LoadOptions.None, cancellationToken).ConfigureAwait(false);

                    await injector.Inject(xml).ConfigureAwait(false);

                    await File.WriteAllTextAsync(tempManifestPath, xml.ToString(SaveOptions.None), cancellationToken);

                    fileListBuilder.AddManifest(tempManifestPath);
                }

                await File.WriteAllTextAsync(tempFileList, fileListBuilder.ToString(), cancellationToken).ConfigureAwait(false);

                var sdk = new MakeAppxWrapper();
                await sdk.PackPackageFiles(tempFileList, this.OutputPath.CurrentValue, this.Compress.CurrentValue, this.Validate.CurrentValue, cancellationToken, progress1).ConfigureAwait(false);

                if (this.Sign.CurrentValue)
                {
                    var manager = await this.signingManagerFactory.GetProxyFor(SelfElevationLevel.HighestAvailable, cancellationToken).ConfigureAwait(false);

                    string timeStampUrl;
                    switch (this.SelectedCertificate.TimeStampSelectionMode.CurrentValue)
                    {
                    case TimeStampSelectionMode.None:
                        timeStampUrl = null;
                        break;

                    case TimeStampSelectionMode.Auto:
                        timeStampUrl = "auto";
                        break;

                    case TimeStampSelectionMode.Url:
                        timeStampUrl = this.SelectedCertificate.TimeStamp.CurrentValue;
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    switch (this.SelectedCertificate.Store.CurrentValue)
                    {
                    case CertificateSource.Personal:
                        await manager.SignPackageWithInstalled(this.OutputPath.CurrentValue, this.OverrideSubject.CurrentValue, this.SelectedCertificate.SelectedPersonalCertificate.CurrentValue?.Model, timeStampUrl, IncreaseVersionMethod.None, cancellationToken, progress2).ConfigureAwait(false);

                        break;

                    case CertificateSource.Pfx:
                        await manager.SignPackageWithPfx(this.OutputPath.CurrentValue, this.OverrideSubject.CurrentValue, this.SelectedCertificate.PfxPath.CurrentValue, this.SelectedCertificate.Password.CurrentValue, timeStampUrl, IncreaseVersionMethod.None, cancellationToken, progress2).ConfigureAwait(false);

                        break;

                    case CertificateSource.DeviceGuard:
                        await manager.SignPackageWithDeviceGuardFromUi(this.OutputPath.CurrentValue, this.SelectedCertificate.DeviceGuard.CurrentValue, timeStampUrl, IncreaseVersionMethod.None, cancellationToken, progress2).ConfigureAwait(false);

                        break;
                    }
                }

                if (this.RemoveDirectory.CurrentValue)
                {
                    ExceptionGuard.Guard(() => Directory.Delete(this.InputPath.CurrentValue, true));
                }

                return(true);
            }
            finally
            {
                foreach (var tempFile in temporaryFiles)
                {
                    ExceptionGuard.Guard(() => File.Delete(tempFile));
                }
            }
        }