//string _defaultExtractLocation; //string _postExtractCmdLine; // string _SetDefaultLocationCode = // "namespace OfficeOpenXml.Packaging.Ionic.Zip { internal partial class WinFormsSelfExtractorStub { partial void _SetDefaultExtractLocation() {" + // " txtExtractDirectory.Text = \"@@VALUE\"; } }}"; /// <summary> /// Saves the ZipFile instance to a self-extracting zip archive. /// </summary> /// /// <remarks> /// /// <para> /// The generated exe image will execute on any machine that has the .NET /// Framework 2.0 installed on it. The generated exe image is also a /// valid ZIP file, readable with DotNetZip or another Zip library or tool /// such as WinZip. /// </para> /// /// <para> /// There are two "flavors" of self-extracting archive. The /// <c>WinFormsApplication</c> version will pop up a GUI and allow the /// user to select a target directory into which to extract. There's also /// a checkbox allowing the user to specify to overwrite existing files, /// and another checkbox to allow the user to request that Explorer be /// opened to see the extracted files after extraction. The other flavor /// is <c>ConsoleApplication</c>. A self-extractor generated with that /// flavor setting will run from the command line. It accepts command-line /// options to set the overwrite behavior, and to specify the target /// extraction directory. /// </para> /// /// <para> /// There are a few temporary files created during the saving to a /// self-extracting zip. These files are created in the directory pointed /// to by <see cref="ZipFile.TempFileFolder"/>, which defaults to <see /// cref="System.IO.Path.GetTempPath"/>. These temporary files are /// removed upon successful completion of this method. /// </para> /// /// <para> /// When a user runs the WinForms SFX, the user's personal directory (<see /// cref="Environment.SpecialFolder.Personal">Environment.SpecialFolder.Personal</see>) /// will be used as the default extract location. If you want to set the /// default extract location, you should use the other overload of /// <c>SaveSelfExtractor()</c>/ The user who runs the SFX will have the /// opportunity to change the extract directory before extracting. When /// the user runs the Command-Line SFX, the user must explicitly specify /// the directory to which to extract. The .NET Framework 2.0 is required /// on the computer when the self-extracting archive is run. /// </para> /// /// <para> /// NB: This method is not available in the version of DotNetZip build for /// the .NET Compact Framework, nor in the "Reduced" DotNetZip library. /// </para> /// /// </remarks> /// /// <example> /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication) /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate"> /// a pathname, possibly fully qualified, to be created. Typically it /// will end in an .exe extension.</param> /// <param name="flavor"> /// Indicates whether a Winforms or Console self-extractor is /// desired. </param> internal void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor) { SelfExtractorSaveOptions options = new SelfExtractorSaveOptions(); options.Flavor = flavor; SaveSelfExtractor(exeToGenerate, options); }
/// <summary> /// Saves the ZipFile instance to a self-extracting zip archive, using the specified /// default extract directory, post-extract command, and icon. /// </summary> /// <remarks> /// <para> /// This method saves a self extracting archive, with a specified default /// extracting location, a command to run after extraction, and application /// icon. Actually, the default extract directory applies only if the flavor is /// <see cref="SelfExtractorFlavor.WinFormsApplication"/>. See the /// documentation for <see cref="SaveSelfExtractor(string , /// SelfExtractorFlavor)"/> for more details. /// </para> /// /// <para> /// The user who runs the SFX will have the opportunity to change the extract directory /// before extracting. If at the time of extraction, the specified directory does not /// exist, the SFX will create the directory before extracting the files. /// </para> /// </remarks> /// /// <example> /// This example saves a self-extracting archive that will use c:\ExtractHere as the default /// extract location. /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication, "c:\\ExtractHere"); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication, "c:\ExtractHere"); /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">The name of the EXE to generate.</param> /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param> /// <param name="defaultExtractDirectory"> /// The default extract directory the user will see when running the self-extracting /// archive. Passing null (or Nothing in VB) here, if flavor is /// <c>SelfExtractorFlavor.WinFormsApplication</c>, will cause the Self Extractor to /// use the the user's personal directory (<see /// cref="Environment.SpecialFolder.Personal"/>) for the default extract location. /// Passing null when flavor is <c>SelfExtractorFlavor.ConsoleApplication</c> will /// cause the self-extractor to use the current directory for the default extract /// location; it will also be settable on the command line when the SFX is executed. /// </param> /// <param name ="postExtractCommandToExecute"> /// The command to execute on the user's machine, after unpacking the archive. If the /// flavor is <c>SelfExtractorFlavor.ConsoleApplication</c>, then the SFX changes the /// current directory to the extract directory, and starts the post-extract command /// and waits for it to exit. The exit code of the post-extract command line is /// returned as the exit code of the self-extractor exe. A non-zero exit code is /// typically used to indicated a failure by the program. In the case of an SFX, a /// non-zero exit code may indicate a failure during extraction, OR, it may indicate a /// failure of the run-on-extract program if specified. There is no way to distinguish /// these conditions from the calling shell, aside from parsing output. The GUI self /// extractor simply starts the post-extract command and exits; it does not wait for /// the command to exit first. /// </param> /// <param name ="iconFile"> /// the name of a .ico file in the filesystem to use for the application icon /// </param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor, string defaultExtractDirectory, string postExtractCommandToExecute, string iconFile) { // Save an SFX that is both an EXE and a ZIP. // Check for the case where we are re-saving a zip archive // that was originally instantiated with a stream. In that case, // the _name will be null. If so, we set _writestream to null, // which insures that we'll cons up a new WriteStream (with a filesystem // file backing it) in the Save() method. if (_name == null) { _writestream = null; } _SavingSfx = true; _name = exeToGenerate; if (Directory.Exists(_name)) { throw new ZipException("Bad Directory", new System.ArgumentException("That name specifies an existing directory. Please specify a filename.", "exeToGenerate")); } _contentsChanged = true; _fileAlreadyExists = File.Exists(_name); _SaveSfxStub(exeToGenerate, flavor, defaultExtractDirectory, postExtractCommandToExecute, iconFile); Save(); _SavingSfx = false; }
/// <summary>フォルダ以下を圧縮</summary> /// <param name="zipFileToCreate">圧縮ファイル名(zip、exe)</param> /// <param name="directoryToZip">圧縮対象フォルダ</param> /// <param name="selectionDlgt">ファイル選択デリゲード</param> /// <param name="selectionCriteriaInfo">ファイル選択基準情報</param> /// <param name="rootPathInArchive">書庫内ルートフォルダ</param> /// <param name="enc">エンコーディング</param> /// <param name="cyp">暗号化</param> /// <param name="zipPassword">パスワード</param> /// <param name="cmpLv">圧縮レベル</param> /// <param name="selfEx">書庫形式(zip形式はnullを指定)</param> public void CreateZipFromFolder( string zipFileToCreate, string directoryToZip, SelectionDelegate selectionDlgt, object selectionCriteriaInfo, string rootPathInArchive, Encoding enc, EncryptionAlgorithm cyp, string zipPassword, CompressionLevel cmpLv, SelfExtractorFlavor? selfEx) { #region ファイル選択基準 // ファイル選択デリゲード if (selectionDlgt != null) { // 指定のデリゲード this._selectionDlgt = selectionDlgt; // ファイル選択基準情報 this._selectionCriteriaInfo = selectionCriteriaInfo; } #endregion // ZipFileを取得 ZipFile zip = this.GetZipFile( enc, cyp, zipPassword, cmpLv, selfEx); using (zip) // 使い終ったら「zip.Dispose」する。 { //// ● フォルダのアーカイブ //zip.AddDirectory(directoryToZip, rootPathInArchive); // ● ファイルを個別に追加する(UOCにて実装)。 this.CreateZipFromFolderRecursive( zip, directoryToZip, rootPathInArchive); if (selfEx == null) { // ZIPファイル zip.Save(zipFileToCreate + ".zip"); } else { // 自動解凍書庫 zip.SaveSelfExtractor( zipFileToCreate + ".exe", (SelfExtractorFlavor)selfEx); } } }
/// <summary>ZipFileを取得</summary> /// <param name="enc">エンコーディング</param> /// <param name="cyp">暗号化</param> /// <param name="zipPassword">パスワード</param> /// <param name="cmpLv">圧縮レベル</param> /// <param name="selfEx">書庫形式(zip形式はnullを指定)</param> /// <returns>ZipFile</returns> protected ZipFile GetZipFile( Encoding enc, EncryptionAlgorithm cyp, string zipPassword, CompressionLevel cmpLv, SelfExtractorFlavor? selfEx) { // ZipFileの初期化 ZipFile zip = null; if (enc == null) { zip = new ZipFile(); } else { zip = new ZipFile(enc); } zip = base.SetZipFile(zip, selfEx); // 圧縮方法の指定 // 暗号化 if (cyp != EncryptionAlgorithm.Unsupported) { // EncryptionAlgorithm.Unsupportedは設定不可能。 zip.Encryption = cyp; // 解凍パスワード if (zipPassword == null || zipPassword == "") { // null、空文字なので設定しない。 if (cyp != EncryptionAlgorithm.None) { throw new ArgumentException(PublicExceptionMessage.ZIP_PASSWORD, "zipPassword"); } } else { // null、空文字で無いので設定する。 zip.Password = zipPassword; } } // 圧縮レベル zip.CompressionLevel = cmpLv; return zip; }
/// <summary>フォルダ以下を圧縮</summary> /// <param name="zipFileToCreate">圧縮ファイル名(zip、exe)</param> /// <param name="directoryToZip">圧縮対象フォルダ</param> /// <param name="selectionCriteriaString">ファイル選択基準文字列</param> /// <param name="rootPathInArchive">書庫内ルートフォルダ</param> /// <param name="enc">エンコーディング</param> /// <param name="cyp">暗号化</param> /// <param name="zipPassword">パスワード</param> /// <param name="cmpLv">圧縮レベル</param> /// <param name="selfEx">書庫形式(zip形式はnullを指定)</param> public void CreateZipFromFolder( string zipFileToCreate, string directoryToZip, string selectionCriteriaString, string rootPathInArchive, Encoding enc, EncryptionAlgorithm cyp, string zipPassword, CompressionLevel cmpLv, SelfExtractorFlavor? selfEx) { // ZipFileを取得 ZipFile zip = this.GetZipFile( enc, cyp, zipPassword, cmpLv, selfEx); using (zip) { // フィルタ条件を確認 if (selectionCriteriaString == null || selectionCriteriaString == "") { // ● フォルダのアーカイブ zip.AddDirectory(directoryToZip, rootPathInArchive); } else { // ● フォルダのアーカイブ // ファイルをフィルタして追加 // selectionCriteriaがファイル選択基準文字列 zip.AddSelectedFiles(selectionCriteriaString, directoryToZip, rootPathInArchive, true); } if (selfEx == null) { // ZIPファイル zip.Save(zipFileToCreate + ".zip"); } else { // 自動解凍書庫 zip.SaveSelfExtractor( zipFileToCreate + ".exe", (SelfExtractorFlavor)selfEx); } } }
/// <summary>ZipFileを取得</summary> /// <param name="zip">ZipFile</param> /// <param name="selfEx">書庫形式(zip形式はnullを指定)</param> /// <returns>ZipFile</returns> protected ZipFile SetZipFile(ZipFile zip, SelfExtractorFlavor? selfEx) { // 状態ライタの指定 this._statusMSGWriter = new StringWriter(); zip.StatusMessageTextWriter = this._statusMSGWriter; // 各種ハンドラ指定 zip.AddProgress += this._addProgress; zip.ExtractProgress += this._extractProgress; zip.ReadProgress += this._readProgress; zip.SaveProgress += this._saveProgress; zip.ZipError += this._zipError; // 4G以上のファイルがある時には、ZIP64を使用 zip.UseZip64WhenSaving = Zip64Option.AsNecessary; // 必要に応じてUnicodeを使用 // (自動解凍書庫の文字化け対策) if (selfEx != null) { zip.UseUnicodeAsNecessary = true; } // コメント付与 zip.Comment = "Zipper @ Powered by DotNetZip"; return zip; }
/// <summary> /// Saves the ZipFile instance to a self-extracting zip archive. /// </summary> /// /// <remarks> /// /// <para> /// The generated exe image will execute on any machine that has the .NET Framework 2.0 /// installed on it. /// </para> /// /// <para> /// There are two "flavors" of self-extracting archive. The <c>WinFormsApplication</c> /// version will pop up a GUI and allow the user to select a target directory into which /// to extract. There's also a checkbox allowing the user to specify to overwrite /// existing files, and another checkbox to allow the user to request that Explorer be /// opened to see the extracted files after extraction. The other flavor is /// <c>ConsoleApplication</c>. A self-extractor generated with that flavor setting will /// run from the command line. It accepts command-line options to set the overwrite /// behavior, and to specify the target extraction directory. /// /// </para> /// <para> /// There are a few temporary files created during the saving to a self-extracting zip. /// These files are normally stored in the directory pointed to by the TEMP environment /// variable, and they are removed upon successful completion of this method. /// </para> /// </remarks> /// /// <example> /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication) /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">a pathname, possibly fully qualified, to be created. Typically it will end in an .exe extension.</param> /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor) { if (File.Exists(exeToGenerate)) { if (Verbose) StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate); } if (!exeToGenerate.EndsWith(".exe")) { if (Verbose) StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension."); } string TempZipFile = SfxSaveTemporary(); if (TempZipFile == null) return; // cancelled // look for myself (ZipFile will be present in the Zip assembly) Assembly a1 = typeof(ZipFile).Assembly; //Console.WriteLine("DotNetZip assembly loc: {0}", a1.Location); Microsoft.CSharp.CSharpCodeProvider csharp = new Microsoft.CSharp.CSharpCodeProvider(); // I'd like to do linq query, but the resulting image has to run on .NET 2.0!! // var settings = (from x in SettingsList // where x.Flavor == flavor // select x).First(); ExtractorSettings settings = null; foreach (var x in SettingsList) { if (x.Flavor == flavor) { settings = x; break; } } if (settings == null) throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", flavor)); // This is the list of referenced assemblies. Zip is needed here. // Also if it is the winforms (gui) extractor, we need other referenced assemblies. System.CodeDom.Compiler.CompilerParameters cp = new System.CodeDom.Compiler.CompilerParameters(); cp.ReferencedAssemblies.Add(a1.Location); if (settings.ReferencedAssemblies != null) foreach (string ra in settings.ReferencedAssemblies) cp.ReferencedAssemblies.Add(ra); cp.GenerateInMemory = false; cp.GenerateExecutable = true; cp.IncludeDebugInformation = false; cp.OutputAssembly = exeToGenerate; Assembly a2 = Assembly.GetExecutingAssembly(); string TempDir = GenerateUniquePathname("tmp", null); if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0)) { System.IO.Directory.CreateDirectory(TempDir); int n = 0; byte[] bytes = new byte[1024]; foreach (string re in settings.CopyThroughResources) { string filename = Path.Combine(TempDir, re); using (Stream instream = a2.GetManifestResourceStream(re)) { using (FileStream outstream = File.OpenWrite(filename)) { do { n = instream.Read(bytes, 0, bytes.Length); outstream.Write(bytes, 0, n); } while (n > 0); } } // add the embedded resource in our own assembly into the target assembly as an embedded resource cp.EmbeddedResources.Add(filename); } } // add the zip file as an embedded resource cp.EmbeddedResources.Add(TempZipFile); // add the Zip DLL as an embedded resource cp.EmbeddedResources.Add(a1.Location); //Console.WriteLine("Resources in this assembly:"); //foreach (string rsrc in a2.GetManifestResourceNames()) //{ // Console.WriteLine(rsrc); //} //Console.WriteLine(); //Console.WriteLine("reading source code resources:"); // concatenate all the source code resources into a single module var sb = new System.Text.StringBuilder(); foreach (string rc in settings.ResourcesToCompile) { //Console.WriteLine(" trying to read stream: ({0})", rc); Stream s = a2.GetManifestResourceStream(rc); using (StreamReader sr = new StreamReader(s)) { while (sr.Peek() >= 0) sb.Append(sr.ReadLine()).Append("\n"); } sb.Append("\n\n"); } string LiteralSource = sb.ToString(); System.CodeDom.Compiler.CompilerResults cr = csharp.CompileAssemblyFromSource(cp, LiteralSource); if (cr == null) throw new SfxGenerationException("Errors compiling the extraction logic!"); foreach (string output in cr.Output) if (Verbose) StatusMessageTextWriter.WriteLine(output); if (cr.Errors.Count != 0) throw new SfxGenerationException("Errors compiling the extraction logic!"); try { if (Directory.Exists(TempDir)) { try { Directory.Delete(TempDir, true); } catch { } } if (File.Exists(TempZipFile)) { try { File.Delete(TempZipFile);} catch { } } } catch { } if (Verbose) StatusMessageTextWriter.WriteLine("Created self-extracting zip file {0}.", cr.PathToAssembly); return; // catch (Exception e1) // { // StatusMessageTextWriter.WriteLine("****Exception: " + e1); // throw; // } // return; }
private void SFX_Update(SelfExtractorFlavor flavor) { string sfxFileToCreate = Path.Combine(TopLevelDir, String.Format("SFX_Update{0}.exe", flavor.ToString())); string unpackDir = Path.Combine(TopLevelDir, "unpack"); if (Directory.Exists(unpackDir)) Directory.Delete(unpackDir, true); string readmeString = "Hey there! This zipfile entry was created directly from a string in application code."; // create a file and compute the checksum string Subdir = Path.Combine(TopLevelDir, "files"); Directory.CreateDirectory(Subdir); var checksums = new Dictionary<string, string>(); string filename = Path.Combine(Subdir, "file1.txt"); TestUtilities.CreateAndFillFileText(filename, _rnd.Next(34000) + 5000); var chk = TestUtilities.ComputeChecksum(filename); checksums.Add(filename.Replace(TopLevelDir + "\\", "").Replace('\\', '/'), TestUtilities.CheckSumToString(chk)); // create the SFX using (ZipFile zip1 = new ZipFile()) { zip1.AddFile(filename, Path.GetFileName(Subdir)); zip1.Comment = "This will be embedded into a self-extracting exe"; MemoryStream ms1 = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(readmeString)); zip1.AddEntry("Readme.txt", ms1); var sfxOptions = new SelfExtractorSaveOptions { Flavor = flavor, Quiet = true, DefaultExtractDirectory = unpackDir }; zip1.SaveSelfExtractor(sfxFileToCreate, sfxOptions); } // verify count Assert.AreEqual<int>(TestUtilities.CountEntries(sfxFileToCreate), 2, "The Zip file has the wrong number of entries."); // create another file filename = Path.Combine(Subdir, "file2.txt"); TestUtilities.CreateAndFillFileText(filename, _rnd.Next(34000) + 5000); chk = TestUtilities.ComputeChecksum(filename); checksums.Add(filename.Replace(TopLevelDir + "\\", "").Replace('\\', '/'), TestUtilities.CheckSumToString(chk)); string password = "******"; // update the SFX using (ZipFile zip1 = ZipFile.Read(sfxFileToCreate)) { zip1.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip1.Encryption = EncryptionAlgorithm.WinZipAes256; zip1.Comment = "The password is: " + password; zip1.Password = password; zip1.AddFile(filename, Path.GetFileName(Subdir)); var sfxOptions = new SelfExtractorSaveOptions { Flavor = flavor, Quiet = true, DefaultExtractDirectory = unpackDir }; zip1.SaveSelfExtractor(sfxFileToCreate, sfxOptions); } // verify count Assert.AreEqual<int>(TestUtilities.CountEntries(sfxFileToCreate), 3, "The Zip file has the wrong number of entries."); // read the SFX TestContext.WriteLine("---------------Reading {0}...", sfxFileToCreate); using (ZipFile zip2 = ZipFile.Read(sfxFileToCreate)) { zip2.Password = password; //string extractDir = String.Format("extract{0}", j); foreach (var e in zip2) { TestContext.WriteLine(" Entry: {0} c({1}) u({2})", e.FileName, e.CompressedSize, e.UncompressedSize); e.Extract(unpackDir); if (!e.IsDirectory) { if (checksums.ContainsKey(e.FileName)) { filename = Path.Combine(unpackDir, e.FileName); string actualCheckString = TestUtilities.CheckSumToString(TestUtilities.ComputeChecksum(filename)); Assert.AreEqual<string>(checksums[e.FileName], actualCheckString, "Checksums for ({1}) do not match.", e.FileName); //TestContext.WriteLine(" Checksums match ({0}).\n", actualCheckString); } else { Assert.AreEqual<string>("Readme.txt", e.FileName); } } } } int N = (flavor == SelfExtractorFlavor.ConsoleApplication) ? 2 : 1; for (int j = 0; j < N; j++) { // run the SFX TestContext.WriteLine("Running the SFX... "); var psi = new System.Diagnostics.ProcessStartInfo(sfxFileToCreate); if (flavor == SelfExtractorFlavor.ConsoleApplication) { if (j == 0) psi.Arguments = "-o -p " + password; // overwrite else psi.Arguments = "-p " + password; } psi.WorkingDirectory = TopLevelDir; psi.UseShellExecute = false; psi.CreateNoWindow = true; System.Diagnostics.Process process = System.Diagnostics.Process.Start(psi); process.WaitForExit(); int rc = process.ExitCode; TestContext.WriteLine("SFX exit code: ({0})", rc); if (j == 0) { Assert.AreEqual<Int32>(0, rc, "The exit code from the SFX was nonzero ({0}).", rc); } else { Assert.AreNotEqual<Int32>(0, rc, "The exit code from the SFX was zero ({0})."); } } // verify the unpacked files? }
/// <summary> /// Saves the ZipFile instance to a self-extracting zip archive. /// </summary> /// /// <remarks> /// /// <para> /// The generated exe image will execute on any machine that has the .NET Framework 2.0 /// installed on it. /// </para> /// /// <para> /// There are two "flavors" of self-extracting archive. The <c>WinFormsApplication</c> /// version will pop up a GUI and allow the user to select a target directory into which /// to extract. There's also a checkbox allowing the user to specify to overwrite /// existing files, and another checkbox to allow the user to request that Explorer be /// opened to see the extracted files after extraction. The other flavor is /// <c>ConsoleApplication</c>. A self-extractor generated with that flavor setting will /// run from the command line. It accepts command-line options to set the overwrite /// behavior, and to specify the target extraction directory. /// </para> /// /// <para> /// There are a few temporary files created during the saving to a self-extracting zip. /// These files are created in the directory pointed to by /// <see cref="ZipFile.TempFileFolder"/>, which defaults to <see cref="System.IO.Path.GetTempPath"/>. /// These temporary files are removed upon successful completion of this method. /// </para> /// /// <para> /// When a user runs the SFX, the user's personal directory /// (<see cref="Environment.SpecialFolder.Personal"/>) /// will be used as the default extract location. /// The user who runs the SFX will have the opportunity to change the extract /// directory before extracting. /// </para> /// /// <para> /// NB: This method is not available in the version of DotNetZip /// build for the .NET Compact Framework, nor in the "Reduced" DotNEtZip library. /// </para> /// /// </remarks> /// /// <example> /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication) /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">a pathname, possibly fully qualified, to be created. Typically it will end in an .exe extension.</param> /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor) { if (File.Exists(exeToGenerate)) { if (Verbose) { StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate); } } if (!exeToGenerate.EndsWith(".exe")) { if (Verbose) { StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension."); } } string TempZipFile = SfxSaveTemporary(); OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive); if (TempZipFile == null) { return; // cancelled } // look for myself (ZipFile will be present in the Ionic.Utils.Zip assembly) Assembly a1 = typeof(ZipFile).Assembly; //Console.WriteLine("DotNetZip assembly loc: {0}", a1.Location); Microsoft.CSharp.CSharpCodeProvider csharp = new Microsoft.CSharp.CSharpCodeProvider(); // Perfect opportunity for a linq query, but I cannot use it. // The DotNetZip library can compile into 2.0, but needs to run on .NET 2.0. // Using LINQ would break that. Here's what it would look like: // // var settings = (from x in SettingsList // where x.Flavor == flavor // select x).First(); ExtractorSettings settings = null; foreach (var x in SettingsList) { if (x.Flavor == flavor) { settings = x; break; } } if (settings == null) { throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", flavor)); } // This is the list of referenced assemblies. Ionic.Utils.Zip is needed here. // Also if it is the winforms (gui) extractor, we need other referenced assemblies. System.CodeDom.Compiler.CompilerParameters cp = new System.CodeDom.Compiler.CompilerParameters(); cp.ReferencedAssemblies.Add(a1.Location); if (settings.ReferencedAssemblies != null) { foreach (string ra in settings.ReferencedAssemblies) { cp.ReferencedAssemblies.Add(ra); } } cp.GenerateInMemory = false; cp.GenerateExecutable = true; cp.IncludeDebugInformation = false; cp.OutputAssembly = exeToGenerate; Assembly a2 = Assembly.GetExecutingAssembly(); string TempDir = GenerateUniquePathname("tmp", null); if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0)) { System.IO.Directory.CreateDirectory(TempDir); int n = 0; byte[] bytes = new byte[1024]; foreach (string re in settings.CopyThroughResources) { string filename = Path.Combine(TempDir, re); using (Stream instream = a2.GetManifestResourceStream(re)) { using (FileStream outstream = File.OpenWrite(filename)) { do { n = instream.Read(bytes, 0, bytes.Length); outstream.Write(bytes, 0, n); } while (n > 0); } } // add the embedded resource in our own assembly into the target assembly as an embedded resource cp.EmbeddedResources.Add(filename); } } // add the zip file as an embedded resource cp.EmbeddedResources.Add(TempZipFile); // add the Ionic.Utils.Zip DLL as an embedded resource cp.EmbeddedResources.Add(a1.Location); //Console.WriteLine("Resources in this assembly:"); //foreach (string rsrc in a2.GetManifestResourceNames()) //{ // Console.WriteLine(rsrc); //} //Console.WriteLine(); //Console.WriteLine("reading source code resources:"); // concatenate all the source code resources into a single module var sb = new System.Text.StringBuilder(); // set the default extract location if it is available bool wantCodeReplace = (flavor == SelfExtractorFlavor.WinFormsApplication && _defaultExtractLocation != null); if (wantCodeReplace) { _defaultExtractLocation = _defaultExtractLocation.Replace("\"", ""); } foreach (string rc in settings.ResourcesToCompile) { //Console.WriteLine(" trying to read stream: ({0})", rc); Stream s = a2.GetManifestResourceStream(rc); using (StreamReader sr = new StreamReader(s)) { while (sr.Peek() >= 0) { string line = sr.ReadLine(); if (wantCodeReplace) { line = line.Replace("@@VALUE", _defaultExtractLocation); } sb.Append(line).Append("\n"); } } sb.Append("\n\n"); } string LiteralSource = sb.ToString(); System.CodeDom.Compiler.CompilerResults cr = csharp.CompileAssemblyFromSource(cp, LiteralSource); if (cr == null) { throw new SfxGenerationException("Cannot compile the extraction logic!"); } if (Verbose) { foreach (string output in cr.Output) { StatusMessageTextWriter.WriteLine(output); } } if (cr.Errors.Count != 0) { throw new SfxGenerationException("Errors compiling the extraction logic!"); } OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor); try { if (Directory.Exists(TempDir)) { try { Directory.Delete(TempDir, true); } catch { } } if (File.Exists(TempZipFile)) { try { File.Delete(TempZipFile); } catch { } } } catch { } OnSaveCompleted(); if (Verbose) { StatusMessageTextWriter.WriteLine("Created self-extracting zip file {0}.", cr.PathToAssembly); } return; // catch (Exception e1) // { // StatusMessageTextWriter.WriteLine("****Exception: " + e1); // throw; // } // return; }
/// <summary> /// Saves the ZipFile instance to a self-extracting zip archive, using the specified /// default extract directory, and a post-extract command to run. /// </summary> /// <remarks> /// <para> /// This method saves a self extracting archive, with a specified default /// extracting location, and a command to run after extraction. Actually, the /// default extract directory applies only if the flavor is <see /// cref="SelfExtractorFlavor.WinFormsApplication"/>. See the documentation for /// <see cref="SaveSelfExtractor(string , SelfExtractorFlavor)"/> for more /// details. /// </para> /// /// <para> /// The user who runs the SFX will have the opportunity to change the extract directory /// before extracting. If at the time of extraction, the specified directory does not /// exist, the SFX will create the directory before extracting the files. /// </para> /// </remarks> /// /// <example> /// This example saves a self-extracting archive that will use c:\ExtractHere as the default /// extract location. /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication, "c:\\ExtractHere"); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication, "c:\ExtractHere"); /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">The name of the EXE to generate.</param> /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param> /// <param name="defaultExtractDirectory"> /// The default extract directory the user will see when running the self-extracting /// archive. Passing null (or Nothing in VB) here, if flavor is /// <c>SelfExtractorFlavor.WinFormsApplication</c>, will cause the Self Extractor to /// use the the user's personal directory (<see /// cref="Environment.SpecialFolder.Personal"/>) for the default extract location. /// Passing null when flavor is <c>SelfExtractorFlavor.ConsoleApplication</c> will /// cause the self-extractor to use the current directory for the default extract /// location; it will also be settable on the command line when the SFX is executed. /// </param> /// <param name ="postExtractCommandToExecute"> /// The command to execute on the user's machine, after unpacking the archive. If the /// flavor is <c>SelfExtractorFlavor.ConsoleApplication</c>, then the SFX changes the /// current directory to the extract directory, and starts the post-extract command /// and waits for it to exit. The exit code of the post-extract command line is /// returned as the exit code of the self-extractor exe. A non-zero exit code is /// typically used to indicated a failure by the program. In the case of an SFX, a /// non-zero exit code may indicate a failure during extraction, OR, it may indicate a /// failure of the run-on-extract program if specified. There is no way to distinguish /// these conditions from the calling shell, aside from parsing output. The GUI self /// extractor simply starts the post-extract command and exits; it does not wait for /// the command to exit first. /// </param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor, string defaultExtractDirectory, string postExtractCommandToExecute) { SaveSelfExtractor(exeToGenerate, flavor, defaultExtractDirectory, postExtractCommandToExecute, null); }
/// <summary> /// Saves the ZipFile instance to a self-extracting zip archive, using the specified /// default extract directory. /// </summary> /// <remarks> /// <para> /// This method saves a self extracting archive, with a specified default extracting /// location. Actually, the default extract directory applies only if the flavor is <see /// cref="SelfExtractorFlavor.WinFormsApplication"/>. See the documentation for <see /// cref="SaveSelfExtractor(string , SelfExtractorFlavor)"/> for more details. /// </para> /// /// <para> /// The user who runs the SFX will have the opportunity to change the extract directory /// before extracting. If at the time of extraction, the specified directory does not /// exist, the SFX will create the directory before extracting the files. /// </para> /// </remarks> /// /// <example> /// This example saves a self-extracting archive that will use c:\ExtractHere as the default /// extract location. /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication, "c:\\ExtractHere"); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication, "c:\ExtractHere"); /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">The name of the EXE to generate.</param> /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param> /// <param name="defaultExtractDirectory"> /// The default extract directory the user will see when running the self-extracting /// archive. Passing null (or Nothing in VB) here will cause the Self Extractor to /// use the the user's personal directory /// (<see cref="Environment.SpecialFolder.Personal"/>) for the default extract /// location. /// </param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor, string defaultExtractDirectory) { SaveSelfExtractor(exeToGenerate, flavor, defaultExtractDirectory, null, null); }
//string _defaultExtractLocation; //string _postExtractCmdLine; // string _SetDefaultLocationCode = // "namespace Ionic.Zip { public partial class WinFormsSelfExtractorStub { partial void _SetDefaultExtractLocation() {" + // " txtExtractDirectory.Text = \"@@VALUE\"; } }}"; /// <summary> /// Saves the ZipFile instance to a self-extracting zip archive. /// </summary> /// /// <remarks> /// /// <para> /// The generated exe image will execute on any machine that has the .NET Framework 2.0 /// installed on it. The generated exe image is also a valid ZIP file, readable with DotNetZip /// or another Zip library or tool such as WinZip. /// </para> /// /// <para> /// There are two "flavors" of self-extracting archive. The <c>WinFormsApplication</c> /// version will pop up a GUI and allow the user to select a target directory into which /// to extract. There's also a checkbox allowing the user to specify to overwrite /// existing files, and another checkbox to allow the user to request that Explorer be /// opened to see the extracted files after extraction. The other flavor is /// <c>ConsoleApplication</c>. A self-extractor generated with that flavor setting will /// run from the command line. It accepts command-line options to set the overwrite /// behavior, and to specify the target extraction directory. /// </para> /// /// <para> /// There are a few temporary files created during the saving to a self-extracting zip. /// These files are created in the directory pointed to by <see /// cref="ZipFile.TempFileFolder"/>, which defaults to <see /// cref="System.IO.Path.GetTempPath"/>. These temporary files are removed upon /// successful completion of this method. /// </para> /// /// <para> /// When a user runs the WinForms SFX, the user's personal directory (<see /// cref="Environment.SpecialFolder.Personal"/>) will be used as the default extract /// location. The user who runs the SFX will have the opportunity to change the extract /// directory before extracting. When the user runs the Command-Line SFX, the user must /// explicitly specify the directory to which to extract. The .NET Framework 2.0 is /// required on the computer when the self-extracting archive is run. /// </para> /// /// <para> /// NB: This method is not available in the version of DotNetZip /// build for the .NET Compact Framework, nor in the "Reduced" DotNetZip library. /// </para> /// /// </remarks> /// /// <example> /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication) /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">a pathname, possibly fully qualified, to be created. Typically it will end in an .exe extension.</param> /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor) { SaveSelfExtractor(exeToGenerate, flavor, null, null, null); }
public void _Internal_SelfExtractor_Command(string cmdFormat, SelfExtractorFlavor flavor, bool runPostExtract, bool quiet, bool forceNoninteractive, bool wantArgs) { TestContext.WriteLine("=============================="); TestContext.WriteLine("SelfExtractor_RunOnExit({0})", flavor.ToString()); int entriesAdded = 0; String filename = null; string postExtractExe = String.Format(cmdFormat, _rnd.Next(3000)); // If WinForms and want forceNoninteractive, have the post-extract-exe return 0, // else, select a random number. int expectedReturnCode = (forceNoninteractive && flavor == SelfExtractorFlavor.WinFormsApplication) ? 0 : _rnd.Next(1024) + 20; TestContext.WriteLine("The post-extract command ({0}) will return {1}", postExtractExe, expectedReturnCode); string Subdir = Path.Combine(TopLevelDir, "A"); Directory.CreateDirectory(Subdir); var checksums = new Dictionary <string, string>(); int fileCount = _rnd.Next(10) + 10; for (int j = 0; j < fileCount; j++) { filename = Path.Combine(Subdir, String.Format("file{0:D3}.txt", j)); TestUtilities.CreateAndFillFileText(filename, _rnd.Next(34000) + 5000); entriesAdded++; var chk = TestUtilities.ComputeChecksum(filename); checksums.Add(filename, TestUtilities.CheckSumToString(chk)); TestContext.WriteLine("checksum({0})= ({1})", filename, checksums[filename]); } Directory.SetCurrentDirectory(TopLevelDir); for (int k = 0; k < 2; k++) { string ReadmeString = String.Format("Hey there! This zipfile entry was created directly " + "from a string in application code. Flavor ({0}) Trial({1})", flavor.ToString(), k); string exeFileToCreate = Path.Combine(TopLevelDir, String.Format("SelfExtractor_Command.{0}.{1}.exe", flavor.ToString(), k)); TestContext.WriteLine("----------------------"); TestContext.WriteLine("Trial {0}", k); string UnpackDirectory = String.Format("unpack.{0}", k); if (k != 0) { CompileApp(expectedReturnCode, postExtractExe); } var sw = new System.IO.StringWriter(); using (ZipFile zip = new ZipFile()) { zip.StatusMessageTextWriter = sw; zip.AddDirectory(Subdir, Path.GetFileName(Subdir)); zip.Comment = String.Format("Trial options: flavor({0}) command: ({3})\r\n" + "actuallyRun({1})\r\nquiet({2})\r\n" + "exists? {4}\r\nexpected rc={5}", flavor, runPostExtract, quiet, postExtractExe, k != 0, expectedReturnCode ); MemoryStream ms1 = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(ReadmeString)); zip.AddEntry("Readme.txt", ms1); if (k != 0) { zip.AddFile(postExtractExe); } SelfExtractorSaveOptions sfxOptions = new SelfExtractorSaveOptions(); sfxOptions.Flavor = flavor; sfxOptions.DefaultExtractDirectory = UnpackDirectory; sfxOptions.Quiet = quiet; // In the case of k==0, this exe does not exist. It will result in // a return code of 5. In k == 1, the exe exists and will succeed. if (postExtractExe.Contains(' ')) { sfxOptions.PostExtractCommandLine = "\"" + postExtractExe + "\""; } else { sfxOptions.PostExtractCommandLine = postExtractExe; } if (wantArgs) { sfxOptions.PostExtractCommandLine += " arg1 arg2"; } zip.SaveSelfExtractor(exeFileToCreate, sfxOptions); } TestContext.WriteLine("status output: " + sw.ToString()); if (k != 0) { File.Delete(postExtractExe); } // Run the post-extract-exe, conditionally. // We always run, unless specifically asked not to, OR // if it's a winforms app and we want it to be noninteractive and there's no EXE to run. // If we try running a non-existent app, it will pop an error message, hence user interaction, // which we need to avoid for the automated test. if (runPostExtract && (k != 0 || !forceNoninteractive || flavor != SelfExtractorFlavor.WinFormsApplication)) { TestContext.WriteLine("Running the SFX... "); System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(exeFileToCreate); psi.WorkingDirectory = TopLevelDir; psi.UseShellExecute = false; psi.CreateNoWindow = true; // false; System.Diagnostics.Process process = System.Diagnostics.Process.Start(psi); process.WaitForExit(); int rc = process.ExitCode; TestContext.WriteLine("SFX exit code: ({0})", rc); // The exit code is returned only if it's a console SFX. if (flavor == SelfExtractorFlavor.ConsoleApplication) { // The program actually runs if k != 0 if (k != 0) { // The file to execute should have returned a specific code. Assert.AreEqual <Int32>(expectedReturnCode, rc, "In trial {0}, the exit code did not match.", k); } else { // The file to execute should not have been found, hence rc==5. Assert.AreEqual <Int32>(5, rc, "In trial {0}, the exit code was unexpected.", k); } } else { Assert.AreEqual <Int32>(0, rc, "In trial {0}, the exit code did not match.", k); } // now, compare the output in UnpackDirectory with the original string DirToCheck = Path.Combine(TopLevelDir, Path.Combine(UnpackDirectory, "A")); // verify the checksum of each file matches with its brother foreach (string fname in Directory.GetFiles(DirToCheck)) { string originalName = fname.Replace("\\" + UnpackDirectory, ""); if (checksums.ContainsKey(originalName)) { string expectedCheckString = checksums[originalName]; string actualCheckString = TestUtilities.CheckSumToString(TestUtilities.ComputeChecksum(fname)); Assert.AreEqual <String>(expectedCheckString, actualCheckString, "Unexpected checksum on extracted filesystem file ({0}).", fname); } else { Assert.AreEqual <string>("Readme.txt", originalName); } } } } }
private void SelfExtractor_Update(SelfExtractorFlavor flavor) { string SfxFileToCreate = Path.Combine(TopLevelDir, String.Format("SelfExtractor_Update{0}.exe", flavor.ToString())); string UnpackDirectory = Path.Combine(TopLevelDir, "unpack"); if (Directory.Exists(UnpackDirectory)) { Directory.Delete(UnpackDirectory, true); } string ReadmeString = "Hey there! This zipfile entry was created directly from a string in application code."; // create a file and compute the checksum string Subdir = Path.Combine(TopLevelDir, "files"); Directory.CreateDirectory(Subdir); var checksums = new Dictionary <string, string>(); string filename = Path.Combine(Subdir, "file1.txt"); TestUtilities.CreateAndFillFileText(filename, _rnd.Next(34000) + 5000); var chk = TestUtilities.ComputeChecksum(filename); checksums.Add(filename.Replace(TopLevelDir + "\\", "").Replace('\\', '/'), TestUtilities.CheckSumToString(chk)); // create the SFX using (ZipFile zip1 = new ZipFile()) { zip1.AddFile(filename, Path.GetFileName(Subdir)); zip1.Comment = "This will be embedded into a self-extracting exe"; MemoryStream ms1 = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(ReadmeString)); zip1.AddEntry("Readme.txt", ms1); SelfExtractorSaveOptions sfxOptions = new SelfExtractorSaveOptions(); sfxOptions.Flavor = flavor; sfxOptions.Quiet = true; sfxOptions.DefaultExtractDirectory = UnpackDirectory; zip1.SaveSelfExtractor(SfxFileToCreate, sfxOptions); } // verify count Assert.AreEqual <int>(TestUtilities.CountEntries(SfxFileToCreate), 2, "The Zip file has the wrong number of entries."); // create another file filename = Path.Combine(Subdir, "file2.txt"); TestUtilities.CreateAndFillFileText(filename, _rnd.Next(34000) + 5000); chk = TestUtilities.ComputeChecksum(filename); checksums.Add(filename.Replace(TopLevelDir + "\\", "").Replace('\\', '/'), TestUtilities.CheckSumToString(chk)); string password = "******"; // update the SFX using (ZipFile zip1 = ZipFile.Read(SfxFileToCreate)) { zip1.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip1.Encryption = EncryptionAlgorithm.WinZipAes256; zip1.Comment = "The password is: " + password; zip1.Password = password; zip1.AddFile(filename, Path.GetFileName(Subdir)); SelfExtractorSaveOptions sfxOptions = new SelfExtractorSaveOptions(); sfxOptions.Flavor = flavor; sfxOptions.Quiet = true; sfxOptions.DefaultExtractDirectory = UnpackDirectory; zip1.SaveSelfExtractor(SfxFileToCreate, sfxOptions); } // verify count Assert.AreEqual <int>(TestUtilities.CountEntries(SfxFileToCreate), 3, "The Zip file has the wrong number of entries."); // read the SFX TestContext.WriteLine("---------------Reading {0}...", SfxFileToCreate); using (ZipFile zip2 = ZipFile.Read(SfxFileToCreate)) { zip2.Password = password; //string extractDir = String.Format("extract{0}", j); foreach (var e in zip2) { TestContext.WriteLine(" Entry: {0} c({1}) u({2})", e.FileName, e.CompressedSize, e.UncompressedSize); e.Extract(UnpackDirectory); if (!e.IsDirectory) { if (checksums.ContainsKey(e.FileName)) { filename = Path.Combine(UnpackDirectory, e.FileName); string actualCheckString = TestUtilities.CheckSumToString(TestUtilities.ComputeChecksum(filename)); Assert.AreEqual <string>(checksums[e.FileName], actualCheckString, "Checksums for ({1}) do not match.", e.FileName); //TestContext.WriteLine(" Checksums match ({0}).\n", actualCheckString); } else { Assert.AreEqual <string>("Readme.txt", e.FileName); } } } } int N = (flavor == SelfExtractorFlavor.ConsoleApplication) ? 2 : 1; for (int j = 0; j < N; j++) { // run the SFX TestContext.WriteLine("Running the SFX... "); var psi = new System.Diagnostics.ProcessStartInfo(SfxFileToCreate); if (flavor == SelfExtractorFlavor.ConsoleApplication) { if (j == 0) { psi.Arguments = "-o -p " + password; // overwrite } else { psi.Arguments = "-p " + password; } } psi.WorkingDirectory = TopLevelDir; psi.UseShellExecute = false; psi.CreateNoWindow = true; System.Diagnostics.Process process = System.Diagnostics.Process.Start(psi); process.WaitForExit(); int rc = process.ExitCode; TestContext.WriteLine("SFX exit code: ({0})", rc); if (j == 0) { Assert.AreEqual <Int32>(0, rc, "The exit code from the SFX was nonzero ({0}).", rc); } else { Assert.AreNotEqual <Int32>(0, rc, "The exit code from the SFX was zero ({0})."); } } // verify the unpacked files? }
public void _Internal_SelfExtractor_Command(string cmdFormat, SelfExtractorFlavor flavor, bool runSfx, bool quiet, bool forceNoninteractive, bool wantArgs) { TestContext.WriteLine("=============================="); TestContext.WriteLine("SFX_RunOnExit({0})", flavor.ToString()); //int entriesAdded = 0; //String filename = null; string postExtractExe = String.Format(cmdFormat, _rnd.Next(3000)); // If WinForms and want forceNoninteractive, have the post-extract-exe return 0, // else, select a random number. int expectedReturnCode = (forceNoninteractive && flavor == SelfExtractorFlavor.WinFormsApplication) ? 0 : _rnd.Next(1024) + 20; TestContext.WriteLine("The post-extract command ({0}) will return {1}", postExtractExe, expectedReturnCode); string subdir = "A"; string[] filesToZip; Dictionary<string, byte[]> checksums; CreateFilesAndChecksums(subdir, out filesToZip, out checksums); for (int k = 0; k < 2; k++) { string readmeString = String.Format("Hey! This zipfile entry was created directly from " + "a string in application code. Flavor ({0}) Trial({1})", flavor.ToString(), k); string exeFileToCreate = String.Format("SFX_Command.{0}.{1}.exe", flavor.ToString(), k); TestContext.WriteLine("----------------------"); TestContext.WriteLine("Trial {0}", k); string unpackDir = String.Format("unpack.{0}", k); var sw = new System.IO.StringWriter(); using (ZipFile zip = new ZipFile()) { zip.StatusMessageTextWriter = sw; zip.AddDirectory(subdir, subdir); // Path.GetFileName(subdir)); zip.Comment = String.Format("Trial options: fl({0}) cmd ({3})\r\n"+ "actuallyRun({1})\r\nquiet({2})\r\n"+ "exists? {4}\r\nexpected rc={5}", flavor, runSfx, quiet, postExtractExe, k!=0, expectedReturnCode ); var ms1 = new MemoryStream(Encoding.UTF8.GetBytes(readmeString)); zip.AddEntry("Readme.txt", ms1); if (k != 0) { CompileApp(expectedReturnCode, postExtractExe); zip.AddFile(postExtractExe); } var sfxOptions = new SelfExtractorSaveOptions { Flavor = flavor, DefaultExtractDirectory = unpackDir, SfxExeWindowTitle = "Custom SFX Title " + DateTime.Now.ToString("G"), Quiet = quiet }; // In the case of k==0, this exe does not exist. It will result in // a return code of 5. In k == 1, the exe exists and will succeed. if (postExtractExe.Contains(' ')) sfxOptions.PostExtractCommandLine= "\"" + postExtractExe + "\""; else sfxOptions.PostExtractCommandLine= postExtractExe; if (wantArgs) sfxOptions.PostExtractCommandLine += " arg1 arg2"; zip.SaveSelfExtractor(exeFileToCreate, sfxOptions); } TestContext.WriteLine("status output: " + sw.ToString()); if (k != 0) File.Delete(postExtractExe); // Run the generated Self-extractor, conditionally. // // We always run, unless specifically asked not to, OR if it's a // winforms app and we want it to be noninteractive and there's no // EXE to run. If we try running a non-existent app, it will pop an // error message, hence user interaction, which we need to avoid for // the automated test. if (runSfx && (k != 0 || !forceNoninteractive || flavor != SelfExtractorFlavor.WinFormsApplication)) { TestContext.WriteLine("Running the SFX... "); System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(exeFileToCreate); psi.WorkingDirectory = TopLevelDir; psi.UseShellExecute = false; psi.CreateNoWindow = true; // false; System.Diagnostics.Process process = System.Diagnostics.Process.Start(psi); process.WaitForExit(); int rc = process.ExitCode; TestContext.WriteLine("SFX exit code: ({0})", rc); // The exit code is returned only if it's a console SFX. if (flavor == SelfExtractorFlavor.ConsoleApplication) { // The program actually runs if k != 0 if (k == 0) { // The file to execute should not have been found, hence rc==5. Assert.AreEqual<Int32> (5, rc, "In trial {0}, the exit code was unexpected.", k); } else { // The file to execute should have returned a specific code. Assert.AreEqual<Int32> (expectedReturnCode, rc, "In trial {0}, the exit code did not match.", k); } } else Assert.AreEqual<Int32>(0, rc, "In trial {0}, the exit code did not match.", k); VerifyChecksums(Path.Combine(unpackDir, "A"), filesToZip, checksums); } } }
//string _defaultExtractLocation; //string _postExtractCmdLine; // string _SetDefaultLocationCode = // "namespace Ionic.Zip { public partial class WinFormsSelfExtractorStub { partial void _SetDefaultExtractLocation() {" + // " txtExtractDirectory.Text = \"@@VALUE\"; } }}"; /// <summary> /// Saves the ZipFile instance to a self-extracting zip archive. /// </summary> /// /// <remarks> /// /// <para> /// The generated exe image will execute on any machine that has the .NET /// Framework 2.0 installed on it. The generated exe image is also a /// valid ZIP file, readable with DotNetZip or another Zip library or tool /// such as WinZip. /// </para> /// /// <para> /// There are two "flavors" of self-extracting archive. The /// <c>WinFormsApplication</c> version will pop up a GUI and allow the /// user to select a target directory into which to extract. There's also /// a checkbox allowing the user to specify to overwrite existing files, /// and another checkbox to allow the user to request that Explorer be /// opened to see the extracted files after extraction. The other flavor /// is <c>ConsoleApplication</c>. A self-extractor generated with that /// flavor setting will run from the command line. It accepts command-line /// options to set the overwrite behavior, and to specify the target /// extraction directory. /// </para> /// /// <para> /// There are a few temporary files created during the saving to a /// self-extracting zip. These files are created in the directory pointed /// to by <see cref="ZipFile.TempFileFolder"/>, which defaults to <see /// cref="System.IO.Path.GetTempPath"/>. These temporary files are /// removed upon successful completion of this method. /// </para> /// /// <para> /// When a user runs the WinForms SFX, the user's personal directory (<see /// cref="Environment.SpecialFolder.Personal">Environment.SpecialFolder.Personal</see>) /// will be used as the default extract location. If you want to set the /// default extract location, you should use the other overload of /// <c>SaveSelfExtractor()</c>/ The user who runs the SFX will have the /// opportunity to change the extract directory before extracting. When /// the user runs the Command-Line SFX, the user must explicitly specify /// the directory to which to extract. The .NET Framework 2.0 is required /// on the computer when the self-extracting archive is run. /// </para> /// /// <para> /// NB: This method is not available in the version of DotNetZip build for /// the .NET Compact Framework, nor in the "Reduced" DotNetZip library. /// </para> /// /// </remarks> /// /// <example> /// <code> /// string DirectoryPath = "c:\\Documents\\Project7"; /// using (ZipFile zip = new ZipFile()) /// { /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication); /// } /// </code> /// <code lang="VB"> /// Dim DirectoryPath As String = "c:\Documents\Project7" /// Using zip As New ZipFile() /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) /// zip.Comment = "This will be embedded into a self-extracting console-based exe" /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication) /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate"> /// a pathname, possibly fully qualified, to be created. Typically it /// will end in an .exe extension.</param> /// <param name="flavor"> /// Indicates whether a Winforms or Console self-extractor is /// desired. </param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor) { SelfExtractorSaveOptions options = new SelfExtractorSaveOptions(); options.Flavor = flavor; SaveSelfExtractor(exeToGenerate, options); }
// string _SetDefaultLocationCode = // "namespace Ionic.Zip { public partial class WinFormsSelfExtractorStub { partial void _SetDefaultExtractLocation() {" + // " txtExtractDirectory.Text = \"@@VALUE\"; } }}"; /// <summary> /// Saves the ZipFile instance to a self-extracting zip archive, using the specified /// default extract directory. /// </summary> /// <remarks> /// <para> /// This method saves a self extracting archive, /// with a specified default extracting location. Actually, the /// default extract directory applies only if the flavor is /// <see cref="SelfExtractorFlavor.WinFormsApplication"/>. /// See the documentation for /// <see cref="SaveSelfExtractor(string , SelfExtractorFlavor)"/> /// for more details. /// </para> /// <para> /// The user who runs the SFX will have the opportunity to change the extract /// directory before extracting. /// If at the time of extraction, the specified directory does not exist, /// the SFX will create the directory before extracting the files. /// </para> /// </remarks> /// <param name="exeToGenerate">The name of the EXE to generate.</param> /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param> /// <param name="defaultExtractDirectory"> /// The default extract directory the user will see when running the self-extracting /// archive. Passing null (or Nothing in VB) here will cause the Self Extractor to /// use the the user's personal directory /// (<see cref="Environment.SpecialFolder.Personal"/>) for the default extract /// location. /// </param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor, string defaultExtractDirectory) { this._defaultExtractLocation = defaultExtractDirectory; SaveSelfExtractor(exeToGenerate, flavor); this._defaultExtractLocation = null; }
private void _SaveSfxStub(string exeToGenerate, SelfExtractorFlavor flavor, string defaultExtractLocation, string postExtractCmdLine, string nameOfIconFile) { bool removeIconFile = false; string StubExe = null; string TempDir = null; try { if (File.Exists(exeToGenerate)) { if (Verbose) { StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate); } } if (!exeToGenerate.EndsWith(".exe")) { if (Verbose) { StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension."); } } StubExe = GenerateTempPathname("exe", null); // get the Ionic.Zip assembly Assembly a1 = typeof(ZipFile).Assembly; Microsoft.CSharp.CSharpCodeProvider csharp = new Microsoft.CSharp.CSharpCodeProvider(); // Perfect opportunity for a linq query, but I cannot use it. // The DotNetZip library can compile into 2.0, but needs to run on .NET 2.0. // Using LINQ would break that. Here's what it would look like: // // var settings = (from x in SettingsList // where x.Flavor == flavor // select x).First(); ExtractorSettings settings = null; foreach (var x in SettingsList) { if (x.Flavor == flavor) { settings = x; break; } } if (settings == null) { throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", flavor)); } // This is the list of referenced assemblies. Ionic.Zip is needed here. // Also if it is the winforms (gui) extractor, we need other referenced assemblies, // like System.Windows.Forms.dll, etc. System.CodeDom.Compiler.CompilerParameters cp = new System.CodeDom.Compiler.CompilerParameters(); cp.ReferencedAssemblies.Add(a1.Location); if (settings.ReferencedAssemblies != null) { foreach (string ra in settings.ReferencedAssemblies) { cp.ReferencedAssemblies.Add(ra); } } cp.GenerateInMemory = false; cp.GenerateExecutable = true; cp.IncludeDebugInformation = false; cp.CompilerOptions = ""; Assembly a2 = Assembly.GetExecutingAssembly(); if (nameOfIconFile == null) { removeIconFile = true; nameOfIconFile = GenerateTempPathname("ico", null); ExtractResourceToFile(a2, "Ionic.Zip.Resources.zippedFile.ico", nameOfIconFile); cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile); } else if (nameOfIconFile != "") { cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile); } //cp.IncludeDebugInformation = true; cp.OutputAssembly = StubExe; if (flavor == SelfExtractorFlavor.WinFormsApplication) { cp.CompilerOptions += " /target:winexe"; } if (cp.CompilerOptions == "") { cp.CompilerOptions = null; } TempDir = GenerateTempPathname("tmp", null); if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0)) { System.IO.Directory.CreateDirectory(TempDir); foreach (string re in settings.CopyThroughResources) { string filename = Path.Combine(TempDir, re); ExtractResourceToFile(a2, re, filename); // add the file into the target assembly as an embedded resource cp.EmbeddedResources.Add(filename); } } // add the Ionic.Utils.Zip DLL as an embedded resource cp.EmbeddedResources.Add(a1.Location); //Console.WriteLine("Resources in this assembly:"); //foreach (string rsrc in a2.GetManifestResourceNames()) //{ // Console.WriteLine(rsrc); //} //Console.WriteLine(); //Console.WriteLine("reading source code resources:"); // concatenate all the source code resources into a single module var sb = new System.Text.StringBuilder(); // assembly attributes sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n"); sb.Append("[assembly: System.Reflection.AssemblyProduct(\"ZipLibrary\")]\n"); sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"Copyright © Dino Chiesa 2008, 2009\")]\n"); sb.Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n\n", ZipFile.LibraryVersion.ToString())); // Set the default extract location if it is available, and if supported. bool haveLocation = (defaultExtractLocation != null); if (haveLocation) { defaultExtractLocation = defaultExtractLocation.Replace("\"", "").Replace("\\", "\\\\"); } foreach (string rc in settings.ResourcesToCompile) { //Console.WriteLine(" trying to read stream: ({0})", rc); Stream s = a2.GetManifestResourceStream(rc); if (s == null) { throw new ZipException(String.Format("missing resource '{0}'", rc)); } using (StreamReader sr = new StreamReader(s)) { while (sr.Peek() >= 0) { string line = sr.ReadLine(); if (haveLocation) { line = line.Replace("@@EXTRACTLOCATION", defaultExtractLocation); } if (postExtractCmdLine != null) { line = line.Replace("@@POST_UNPACK_CMD_LINE", postExtractCmdLine.Replace("\\", "\\\\")); } sb.Append(line).Append("\n"); } } sb.Append("\n\n"); } string LiteralSource = sb.ToString(); #if DEBUGSFX // for debugging only string sourceModule = GenerateTempPathname("cs", null); using (StreamWriter sw = File.CreateText(sourceModule)) { sw.Write(LiteralSource); } Console.WriteLine("source: {0}", sourceModule); #endif System.CodeDom.Compiler.CompilerResults cr = csharp.CompileAssemblyFromSource(cp, LiteralSource); if (cr == null) { throw new SfxGenerationException("Cannot compile the extraction logic!"); } if (Verbose) { foreach (string output in cr.Output) { StatusMessageTextWriter.WriteLine(output); } } if (cr.Errors.Count != 0) { //Console.ReadLine(); string sourcefile = GenerateTempPathname("cs", null); using (TextWriter tw = new StreamWriter(sourcefile)) { tw.Write(LiteralSource); } throw new SfxGenerationException(String.Format("Errors compiling the extraction logic! {0}", sourcefile)); } OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor); // Now, copy the resulting EXE image to the _writestream. // Because this stub exe is being saved first, the effect will be to // concatenate the exe and the zip data together. using (System.IO.Stream input = System.IO.File.OpenRead(StubExe)) { byte[] buffer = new byte[4000]; int n = 1; while (n != 0) { n = input.Read(buffer, 0, buffer.Length); if (n != 0) { WriteStream.Write(buffer, 0, n); } } } OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive); } finally { try { if (Directory.Exists(TempDir)) { try { Directory.Delete(TempDir, true); } catch { } } if (File.Exists(StubExe)) { try { File.Delete(StubExe); } catch { } } if (removeIconFile && File.Exists(nameOfIconFile)) { try { File.Delete(nameOfIconFile); } catch { } } } catch { } } return; }
public ConvertZipToSfx(string[] args) { const string parameterDuplicated = "The parameter {0} is duplicated."; for (int i = 0; i < args.Length; i++) { switch (args[i].ToLower()) { case "-extractdir": if (i >= args.Length - 1 || ExtractDir != null) { Console.Error.WriteLine(parameterDuplicated, "'extractdir"); Usage(); return; } ExtractDir = args[++i]; break; case "-cmdline": flavor = SelfExtractorFlavor.ConsoleApplication; break; case "-comment": if (i >= args.Length - 1 || ZipComment != null) { Console.Error.WriteLine(parameterDuplicated, "comment"); Usage(); return; } ZipComment = args[++i]; break; case "-exeonunpack": if (i >= args.Length - 1 || ExeOnUnpack != null) { Console.Error.WriteLine(parameterDuplicated, "exeonunpack"); Usage(); return; } ExeOnUnpack = args[++i]; break; case "-description": if (i >= args.Length - 1 || Description != null) { Console.Error.WriteLine(parameterDuplicated, "description"); Usage(); return; } Description = args[++i]; break; case "-fileversion": if (i >= args.Length - 1 || FileVersion != null) { Console.Error.WriteLine(parameterDuplicated, "fileversion"); Usage(); return; } FileVersion = new Version(args[++i]); break; case "-title": if (i >= args.Length - 1 || Title != null) { Console.Error.WriteLine(parameterDuplicated, "title"); Usage(); return; } Title = args[++i]; break; case "-iconfile": if (i >= args.Length - 1 || IconFile != null) { Console.Error.WriteLine(parameterDuplicated, "iconfile"); Usage(); return; } IconFile = args[++i]; break; case "-name": if (i >= args.Length - 1 || Name != null) { Console.Error.WriteLine(parameterDuplicated, "name"); Usage(); return; } Name = args[++i]; break; case "-version": if (i >= args.Length - 1 || Version != null) { Console.Error.WriteLine(parameterDuplicated, "version"); Usage(); return; } Version = args[++i]; break; case "-copyright": if (i >= args.Length - 1 || Copyright != null) { Console.Error.WriteLine(parameterDuplicated, "copyright"); Usage(); return; } Copyright = args[++i]; break; case "-compiler": if (i >= args.Length - 1 || Compiler != null) { Console.Error.WriteLine(parameterDuplicated, "compiler"); Usage(); return; } Compiler = args[++i]; break; case "-quiet": if (i >= args.Length - 1 || Quiet == true) { Console.Error.WriteLine(parameterDuplicated, "Quiet"); Usage(); return; } Quiet = true; break; case "-remove": if (i >= args.Length - 1 || Remove == true) { Console.Error.WriteLine(parameterDuplicated, "remove"); Usage(); return; } Remove = true; break; case "-?": case "-help": Usage(); return; default: // positional args if (ZipFileToConvert == null) { ZipFileToConvert = args[i]; targetName = ZipFileToConvert.Replace(".zip", ".exe"); } else { Usage(); return; } break; } } }
public void _Internal_SelfExtractor_Command(string cmdFormat, SelfExtractorFlavor flavor, bool runSfx, bool quiet, bool forceNoninteractive, bool wantArgs) { TestContext.WriteLine("=============================="); TestContext.WriteLine("SFX_RunOnExit({0})", flavor.ToString()); //int entriesAdded = 0; //String filename = null; string postExtractExe = String.Format(cmdFormat, _rnd.Next(3000)); // If WinForms and want forceNoninteractive, have the post-extract-exe return 0, // else, select a random number. int expectedReturnCode = (forceNoninteractive && flavor == SelfExtractorFlavor.WinFormsApplication) ? 0 : _rnd.Next(1024) + 20; TestContext.WriteLine("The post-extract command ({0}) will return {1}", postExtractExe, expectedReturnCode); string subdir = "A"; string[] filesToZip; Dictionary <string, byte[]> checksums; CreateFilesAndChecksums(subdir, out filesToZip, out checksums); for (int k = 0; k < 2; k++) { string readmeString = String.Format("Hey! This zipfile entry was created directly from " + "a string in application code. Flavor ({0}) Trial({1})", flavor.ToString(), k); string exeFileToCreate = String.Format("SFX_Command.{0}.{1}.exe", flavor.ToString(), k); TestContext.WriteLine("----------------------"); TestContext.WriteLine("Trial {0}", k); string unpackDir = String.Format("unpack.{0}", k); var sw = new System.IO.StringWriter(); using (ZipFile zip = new ZipFile()) { zip.StatusMessageTextWriter = sw; zip.AddDirectory(subdir, subdir); // Path.GetFileName(subdir)); zip.Comment = String.Format("Trial options: fl({0}) cmd ({3})\r\n" + "actuallyRun({1})\r\nquiet({2})\r\n" + "exists? {4}\r\nexpected rc={5}", flavor, runSfx, quiet, postExtractExe, k != 0, expectedReturnCode ); var ms1 = new MemoryStream(Encoding.UTF8.GetBytes(readmeString)); zip.AddEntry("Readme.txt", ms1); if (k != 0) { CompileApp(expectedReturnCode, postExtractExe); zip.AddFile(postExtractExe); } var sfxOptions = new SelfExtractorSaveOptions { Flavor = flavor, DefaultExtractDirectory = unpackDir, SfxExeWindowTitle = "Custom SFX Title " + DateTime.Now.ToString("G"), Quiet = quiet }; // In the case of k==0, this exe does not exist. It will result in // a return code of 5. In k == 1, the exe exists and will succeed. if (postExtractExe.Contains(' ')) { sfxOptions.PostExtractCommandLine = "\"" + postExtractExe + "\""; } else { sfxOptions.PostExtractCommandLine = postExtractExe; } if (wantArgs) { sfxOptions.PostExtractCommandLine += " arg1 arg2"; } zip.SaveSelfExtractor(exeFileToCreate, sfxOptions); } TestContext.WriteLine("status output: " + sw.ToString()); if (k != 0) { File.Delete(postExtractExe); } // Run the generated Self-extractor, conditionally. // // We always run, unless specifically asked not to, OR if it's a // winforms app and we want it to be noninteractive and there's no // EXE to run. If we try running a non-existent app, it will pop an // error message, hence user interaction, which we need to avoid for // the automated test. if (runSfx && (k != 0 || !forceNoninteractive || flavor != SelfExtractorFlavor.WinFormsApplication)) { TestContext.WriteLine("Running the SFX... "); System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(exeFileToCreate); psi.WorkingDirectory = TopLevelDir; psi.UseShellExecute = false; psi.CreateNoWindow = true; // false; System.Diagnostics.Process process = System.Diagnostics.Process.Start(psi); process.WaitForExit(); int rc = process.ExitCode; TestContext.WriteLine("SFX exit code: ({0})", rc); // The exit code is returned only if it's a console SFX. if (flavor == SelfExtractorFlavor.ConsoleApplication) { // The program actually runs if k != 0 if (k == 0) { // The file to execute should not have been found, hence rc==5. Assert.AreEqual <Int32> (5, rc, "In trial {0}, the exit code was unexpected.", k); } else { // The file to execute should have returned a specific code. Assert.AreEqual <Int32> (expectedReturnCode, rc, "In trial {0}, the exit code did not match.", k); } } else { Assert.AreEqual <Int32>(0, rc, "In trial {0}, the exit code did not match.", k); } VerifyChecksums(Path.Combine(unpackDir, "A"), filesToZip, checksums); } } }