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)); } } }