/// <summary> /// Saves the ZipFile instance to a self-extracting zip archive, using the specified /// save options. /// </summary> /// /// <remarks> /// <para> /// This method saves a self extracting archive, using the specified save /// options. These options include the flavor of the SFX, the default extract /// directory, the icon file, and so on. 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"; /// SelfExtractorOptions options = new SelfExtractorOptions(); /// options.Flavor = SelfExtractorFlavor.ConsoleApplication; /// options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere"; /// options.PostExtractCommandLine = ExeToRunAfterExtract; /// options.RemoveUnpackedFilesAfterExecute = true; /// zip.SaveSelfExtractor("archive.exe", options); /// } /// </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" /// Dim options As New SelfExtractorOptions() /// options.Flavor = SelfExtractorFlavor.ConsoleApplication /// options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere" /// options.PostExtractCommandLine = ExeToRunAfterExtract /// options.RemoveUnpackedFilesAfterExecute = True /// zip.SaveSelfExtractor("archive.exe", options) /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">The name of the EXE to generate.</param> /// <param name="options">provides the options for how to save the Self-extracting archive.</param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorSaveOptions options) { // 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, options); Save(); _SavingSfx = false; }
//string _defaultExtractLocation; //string _postExtractCmdLine; // string _SetDefaultLocationCode = // "namespace PMDCP.Compression.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) { SelfExtractorSaveOptions options = new SelfExtractorSaveOptions(); options.Flavor = flavor; SaveSelfExtractor(exeToGenerate, options); }
private void _SaveSfxStub(string exeToGenerate, SelfExtractorSaveOptions options) { string nameOfIconFile = null; 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"); // get the PMDCP.Compression.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 == options.Flavor) { settings = x; break; } } if (settings == null) { throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", options.Flavor)); } // This is the list of referenced assemblies. PMDCP.Compression.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(); // Use this to concatenate all the source code resources into a single module var sb = new System.Text.StringBuilder(); // In case there are compiler errors later, we allocate a // source file name now. string sourceFile = GenerateTempPathname("cs"); // // debugging: enumerate the resources in this assembly // Console.WriteLine("Resources in this assembly:"); // foreach (string rsrc in a2.GetManifestResourceNames()) // { // Console.WriteLine(rsrc); // } // Console.WriteLine(); // all the source code is embedded in the DLL as a zip file. using (ZipFile zip = ZipFile.Read(a2.GetManifestResourceStream("PMDCP.Compression.Zip.Resources.ZippedResources.zip"))) { // // debugging: enumerate the files in the embedded zip // Console.WriteLine("Entries in the embbedded zip:"); // foreach (ZipEntry entry in zip) // { // Console.WriteLine(entry.FileName); // } // Console.WriteLine(); TempDir = GenerateTempPathname("tmp"); if (String.IsNullOrEmpty(options.IconFile)) { // Use the embedded ico file. But we must unpack it to the // filesystem, in order to specify it on the cmdline of csc.exe. We // will remove this file later. System.IO.Directory.CreateDirectory(TempDir); ZipEntry e = zip["zippedFile.ico"]; // Must not extract a readonly file - it will be impossible to // delete later. if ((e.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { e.Attributes ^= FileAttributes.ReadOnly; } e.Extract(TempDir); nameOfIconFile = Path.Combine(TempDir, "zippedFile.ico"); cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile); } else { cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", options.IconFile); } cp.OutputAssembly = StubExe; if (options.Flavor == SelfExtractorFlavor.WinFormsApplication) { cp.CompilerOptions += " /target:winexe"; } if (cp.CompilerOptions == "") { cp.CompilerOptions = null; } if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0)) { if (!Directory.Exists(TempDir)) { 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); // file header sb.Append("// " + Path.GetFileName(sourceFile) + "\n") .Append("// --------------------------------------------\n//\n") .Append("// This SFX source file was generated by DotNetZip ") .Append(ZipFile.LibraryVersion.ToString()) .Append("\n// at ") .Append(System.DateTime.Now.ToString("yyyy MMMM dd HH:mm:ss")) .Append("\n//\n// --------------------------------------------\n\n\n"); // assembly attributes if (!String.IsNullOrEmpty(options.Description)) { sb.Append("[assembly: System.Reflection.AssemblyTitle(\"" + options.Description.Replace("\"", "") + "\")]\n"); } else { sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n"); } if (!String.IsNullOrEmpty(options.ProductVersion)) { sb.Append("[assembly: System.Reflection.AssemblyInformationalVersion(\"" + options.ProductVersion.Replace("\"", "") + "\")]\n"); } string copyright = "Extractor: Copyright © Dino Chiesa 2008, 2009"; if (!String.IsNullOrEmpty(options.Copyright)) { copyright += "Contents: " + options.Copyright.Replace("\"", ""); } if (!String.IsNullOrEmpty(options.ProductName)) { sb.Append("[assembly: System.Reflection.AssemblyProduct(\"") .Append(options.ProductName.Replace("\"", "")) .Append("\")]\n"); } else { sb.Append("[assembly: System.Reflection.AssemblyProduct(\"DotNetZip\")]\n"); } sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"" + copyright + "\")]\n") .Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n", ZipFile.LibraryVersion.ToString())); if (options.FileVersion != null) { sb.Append(String.Format("[assembly: System.Reflection.AssemblyFileVersion(\"{0}\")]\n", options.FileVersion.ToString())); } sb.Append("\n\n\n"); // Set the default extract location if it is available string extractLoc = options.DefaultExtractDirectory; if (extractLoc != null) { // remove double-quotes and replace slash with double-slash. // This, because the value is going to be embedded into a // cs file as a quoted string, and it needs to be escaped. extractLoc = extractLoc.Replace("\"", "").Replace("\\", "\\\\"); } string postExCmdLine = options.PostExtractCommandLine; if (postExCmdLine != null) { postExCmdLine = postExCmdLine.Replace("\\", "\\\\"); postExCmdLine = postExCmdLine.Replace("\"", "\\\""); } foreach (string rc in settings.ResourcesToCompile) { // Console.WriteLine(" trying to read entry: ({0})", rc); using (Stream s = zip[rc].OpenReader()) { 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 (extractLoc != null) { line = line.Replace("@@EXTRACTLOCATION", extractLoc); } line = line.Replace("@@REMOVE_AFTER_EXECUTE", options.RemoveUnpackedFilesAfterExecute.ToString()); line = line.Replace("@@QUIET", options.Quiet.ToString()); line = line.Replace("@@EXTRACT_EXISTING_FILE", ((int)options.ExtractExistingFile).ToString()); if (postExCmdLine != null) { line = line.Replace("@@POST_UNPACK_CMD_LINE", postExCmdLine); } sb.Append(line).Append("\n"); } } sb.Append("\n\n"); } } } string LiteralSource = sb.ToString(); #if DEBUGSFX // for debugging only string sourceModule = GenerateTempPathname("cs"); 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) { using (TextWriter tw = new StreamWriter(sourceFile)) { // first, the source we compiled tw.Write(LiteralSource); // now, append the compile errors tw.Write("\n\n\n// ------------------------------------------------------------------\n"); tw.Write("// Errors during compilation: \n//\n"); string p = Path.GetFileName(sourceFile); foreach (System.CodeDom.Compiler.CompilerError error in cr.Errors) { tw.Write(String.Format("// {0}({1},{2}): {3} {4}: {5}\n//\n", p, // 0 error.Line, // 1 error.Column, // 2 error.IsWarning?"Warning":"error", // 3 error.ErrorNumber, // 4 error.ErrorText)); // 5 } } 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 (Exception exc1) { Console.WriteLine("Exception: {0}", exc1.ToString()); } } if (File.Exists(StubExe)) { try { File.Delete(StubExe); } catch { } } } catch { } } return; }
private void _SaveSfxStub(string exeToGenerate, SelfExtractorSaveOptions options) { string nameOfIconFile= null; 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"); // get the PMDCP.Compression.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 == options.Flavor) { settings = x; break; } } if (settings == null) throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", options.Flavor)); // This is the list of referenced assemblies. PMDCP.Compression.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(); // Use this to concatenate all the source code resources into a single module var sb = new System.Text.StringBuilder(); // In case there are compiler errors later, we allocate a // source file name now. string sourceFile = GenerateTempPathname("cs"); // // debugging: enumerate the resources in this assembly // Console.WriteLine("Resources in this assembly:"); // foreach (string rsrc in a2.GetManifestResourceNames()) // { // Console.WriteLine(rsrc); // } // Console.WriteLine(); // all the source code is embedded in the DLL as a zip file. using (ZipFile zip = ZipFile.Read(a2.GetManifestResourceStream("PMDCP.Compression.Zip.Resources.ZippedResources.zip"))) { // // debugging: enumerate the files in the embedded zip // Console.WriteLine("Entries in the embbedded zip:"); // foreach (ZipEntry entry in zip) // { // Console.WriteLine(entry.FileName); // } // Console.WriteLine(); TempDir = GenerateTempPathname("tmp"); if (String.IsNullOrEmpty(options.IconFile)) { // Use the embedded ico file. But we must unpack it to the // filesystem, in order to specify it on the cmdline of csc.exe. We // will remove this file later. System.IO.Directory.CreateDirectory(TempDir); ZipEntry e = zip["zippedFile.ico"]; // Must not extract a readonly file - it will be impossible to // delete later. if ((e.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) e.Attributes ^= FileAttributes.ReadOnly; e.Extract(TempDir); nameOfIconFile = Path.Combine(TempDir, "zippedFile.ico"); cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile); } else cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", options.IconFile); cp.OutputAssembly = StubExe; if (options.Flavor == SelfExtractorFlavor.WinFormsApplication) cp.CompilerOptions += " /target:winexe"; if (cp.CompilerOptions == "") cp.CompilerOptions = null; if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0)) { if (!Directory.Exists(TempDir)) 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); // file header sb.Append("// " + Path.GetFileName(sourceFile) + "\n") .Append("// --------------------------------------------\n//\n") .Append("// This SFX source file was generated by DotNetZip ") .Append(ZipFile.LibraryVersion.ToString()) .Append("\n// at ") .Append(System.DateTime.Now.ToString("yyyy MMMM dd HH:mm:ss")) .Append("\n//\n// --------------------------------------------\n\n\n"); // assembly attributes if (!String.IsNullOrEmpty(options.Description)) sb.Append("[assembly: System.Reflection.AssemblyTitle(\"" + options.Description.Replace("\"", "") + "\")]\n"); else sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n"); if (!String.IsNullOrEmpty(options.ProductVersion)) sb.Append("[assembly: System.Reflection.AssemblyInformationalVersion(\"" + options.ProductVersion.Replace("\"", "") + "\")]\n"); string copyright = "Extractor: Copyright © Dino Chiesa 2008, 2009"; if (!String.IsNullOrEmpty(options.Copyright)) copyright += "Contents: " + options.Copyright.Replace("\"", ""); if (!String.IsNullOrEmpty(options.ProductName)) sb.Append("[assembly: System.Reflection.AssemblyProduct(\"") .Append(options.ProductName.Replace("\"", "")) .Append("\")]\n"); else sb.Append("[assembly: System.Reflection.AssemblyProduct(\"DotNetZip\")]\n"); sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"" + copyright + "\")]\n") .Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n", ZipFile.LibraryVersion.ToString())); if (options.FileVersion != null) sb.Append(String.Format("[assembly: System.Reflection.AssemblyFileVersion(\"{0}\")]\n", options.FileVersion.ToString())); sb.Append("\n\n\n"); // Set the default extract location if it is available string extractLoc = options.DefaultExtractDirectory; if (extractLoc != null) { // remove double-quotes and replace slash with double-slash. // This, because the value is going to be embedded into a // cs file as a quoted string, and it needs to be escaped. extractLoc = extractLoc.Replace("\"", "").Replace("\\", "\\\\"); } string postExCmdLine = options.PostExtractCommandLine; if (postExCmdLine != null) { postExCmdLine = postExCmdLine.Replace("\\","\\\\"); postExCmdLine = postExCmdLine.Replace("\"","\\\""); } foreach (string rc in settings.ResourcesToCompile) { // Console.WriteLine(" trying to read entry: ({0})", rc); using (Stream s = zip[rc].OpenReader()) { 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 (extractLoc != null) line = line.Replace("@@EXTRACTLOCATION", extractLoc); line = line.Replace("@@REMOVE_AFTER_EXECUTE", options.RemoveUnpackedFilesAfterExecute.ToString()); line = line.Replace("@@QUIET", options.Quiet.ToString()); line = line.Replace("@@EXTRACT_EXISTING_FILE", ((int)options.ExtractExistingFile).ToString()); if (postExCmdLine != null) line = line.Replace("@@POST_UNPACK_CMD_LINE", postExCmdLine); sb.Append(line).Append("\n"); } } sb.Append("\n\n"); } } } string LiteralSource = sb.ToString(); #if DEBUGSFX // for debugging only string sourceModule = GenerateTempPathname("cs"); 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) { using (TextWriter tw = new StreamWriter(sourceFile)) { // first, the source we compiled tw.Write(LiteralSource); // now, append the compile errors tw.Write("\n\n\n// ------------------------------------------------------------------\n"); tw.Write("// Errors during compilation: \n//\n"); string p = Path.GetFileName(sourceFile); foreach( System.CodeDom.Compiler.CompilerError error in cr.Errors) { tw.Write(String.Format("// {0}({1},{2}): {3} {4}: {5}\n//\n", p, // 0 error.Line, // 1 error.Column, // 2 error.IsWarning?"Warning":"error", // 3 error.ErrorNumber, // 4 error.ErrorText )); // 5 } } 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 (Exception exc1) { Console.WriteLine("Exception: {0}", exc1.ToString()); } } if (File.Exists(StubExe)) { try { File.Delete(StubExe); } catch { } } } catch { } } return; }
/// <summary> /// Saves the ZipFile instance to a self-extracting zip archive, using the specified /// save options. /// </summary> /// /// <remarks> /// <para> /// This method saves a self extracting archive, using the specified save /// options. These options include the flavor of the SFX, the default extract /// directory, the icon file, and so on. 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"; /// SelfExtractorOptions options = new SelfExtractorOptions(); /// options.Flavor = SelfExtractorFlavor.ConsoleApplication; /// options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere"; /// options.PostExtractCommandLine = ExeToRunAfterExtract; /// options.RemoveUnpackedFilesAfterExecute = true; /// zip.SaveSelfExtractor("archive.exe", options); /// } /// </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" /// Dim options As New SelfExtractorOptions() /// options.Flavor = SelfExtractorFlavor.ConsoleApplication /// options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere" /// options.PostExtractCommandLine = ExeToRunAfterExtract /// options.RemoveUnpackedFilesAfterExecute = True /// zip.SaveSelfExtractor("archive.exe", options) /// End Using /// </code> /// </example> /// /// <param name="exeToGenerate">The name of the EXE to generate.</param> /// <param name="options">provides the options for how to save the Self-extracting archive.</param> public void SaveSelfExtractor(string exeToGenerate, SelfExtractorSaveOptions options) { // 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, options); Save(); _SavingSfx = false; }