| // ZipFile.saveSelfExtractor.cs | |
| // ------------------------------------------------------------------ | |
| // | |
| // Copyright (c) 2008-2011 Dino Chiesa. | |
| // All rights reserved. | |
| // | |
| // This code module is part of DotNetZip, a zipfile class library. | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // This code is licensed under the Microsoft Public License. | |
| // See the file License.txt for the license details. | |
| // More info on: http://dotnetzip.codeplex.com | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // last saved (in emacs): | |
| // Time-stamp: <2011-August-10 19:22:46> | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // This is a the source module that implements the stuff for saving to a | |
| // self-extracting Zip archive. | |
| // | |
| // ZipFile is set up as a "partial class" - defined in multiple .cs source modules. | |
| // This is one of the source modules for the ZipFile class. | |
| // | |
| // Here's the design: The self-extracting zip file is just a regular managed EXE | |
| // file, with embedded resources. The managed code logic instantiates a ZipFile, and | |
| // then extracts each entry. The embedded resources include the zip archive content, | |
| // as well as the Zip library itself. The latter is required so that self-extracting | |
| // can work on any machine, whether or not it has the DotNetZip library installed on | |
| // it. | |
| // | |
| // What we need to do is create the animal I just described, within a method on the | |
| // ZipFile class. This source module provides that capability. The method is | |
| // SaveSelfExtractor(). | |
| // | |
| // The way the method works: it uses the programmatic interface to the csc.exe | |
| // compiler, Microsoft.CSharp.CSharpCodeProvider, to compile "boilerplate" | |
| // extraction logic into a new assembly. As part of that compile, we embed within | |
| // that assembly the zip archive itself, as well as the Zip library. | |
| // | |
| // Therefore we need to first save to a temporary zip file, then produce the exe. | |
| // | |
| // There are a few twists. | |
| // | |
| // The Visual Studio Project structure is a little weird. There are code files | |
| // that ARE NOT compiled during a normal build of the VS Solution. They are | |
| // marked as embedded resources. These are the various "boilerplate" modules that | |
| // are used in the self-extractor. These modules are: WinFormsSelfExtractorStub.cs | |
| // WinFormsSelfExtractorStub.Designer.cs CommandLineSelfExtractorStub.cs | |
| // PasswordDialog.cs PasswordDialog.Designer.cs | |
| // | |
| // At design time, if you want to modify the way the GUI looks, you have to | |
| // mark those modules to have a "compile" build action. Then tweak em, test, | |
| // etc. Then again mark them as "Embedded resource". | |
| // | |
| // ------------------------------------------------------------------ | |
| using System; | |
| using System.Reflection; | |
| using System.IO; | |
| using System.Collections.Generic; | |
| namespace OfficeOpenXml.Packaging.Ionic.Zip | |
| { | |
| #if !NO_SFX | |
| /// <summary> | |
| /// An enum that provides the different self-extractor flavors | |
| /// </summary> | |
| internal enum SelfExtractorFlavor | |
| { | |
| /// <summary> | |
| /// A self-extracting zip archive that runs from the console or | |
| /// command line. | |
| /// </summary> | |
| ConsoleApplication = 0, | |
| /// <summary> | |
| /// A self-extracting zip archive that presents a graphical user | |
| /// interface when it is executed. | |
| /// </summary> | |
| WinFormsApplication, | |
| } | |
| /// <summary> | |
| /// The options for generating a self-extracting archive. | |
| /// </summary> | |
| internal class SelfExtractorSaveOptions | |
| { | |
| /// <summary> | |
| /// The type of SFX to create. | |
| /// </summary> | |
| public SelfExtractorFlavor Flavor | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The command to run after extraction. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// This is optional. Leave it empty (<c>null</c> in C# or <c>Nothing</c> in | |
| /// VB) to run no command after extraction. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If it is non-empty, the SFX will execute the command specified in this | |
| /// string on the user's machine, and using the extract directory as the | |
| /// working directory for the process, after unpacking the archive. The | |
| /// program to execute can include a path, if you like. If you want to execute | |
| /// a program that accepts arguments, specify the program name, followed by a | |
| /// space, and then the arguments for the program, each separated by a space, | |
| /// just as you would on a normal command line. Example: <c>program.exe arg1 | |
| /// arg2</c>. The string prior to the first space will be taken as the | |
| /// program name, and the string following the first space specifies the | |
| /// arguments to the program. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If you want to execute a program that has a space in the name or path of | |
| /// the file, surround the program name in double-quotes. The first character | |
| /// of the command line should be a double-quote character, and there must be | |
| /// a matching double-quote following the end of the program file name. Any | |
| /// optional arguments to the program follow that, separated by | |
| /// spaces. Example: <c>"c:\project files\program name.exe" arg1 arg2</c>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If the flavor of the SFX is <c>SelfExtractorFlavor.ConsoleApplication</c>, | |
| /// then the SFX starts a new process, using this string as the post-extract | |
| /// command line. The SFX waits for the process to exit. The exit code of | |
| /// the post-extract command line is returned as the exit code of the | |
| /// command-line 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-after-extract program if specified, OR, it may indicate | |
| /// the run-after-extract program could not be fuond. There is no way to | |
| /// distinguish these conditions from the calling shell, aside from parsing | |
| /// the output of the SFX. If you have Quiet set to <c>true</c>, you may not | |
| /// see error messages, if a problem occurs. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If the flavor of the SFX is | |
| /// <c>SelfExtractorFlavor.WinFormsApplication</c>, then the SFX starts a new | |
| /// process, using this string as the post-extract command line, and using the | |
| /// extract directory as the working directory for the process. The SFX does | |
| /// not wait for the command to complete, and does not check the exit code of | |
| /// the program. If the run-after-extract program cannot be fuond, a message | |
| /// box is displayed indicating that fact. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// You can specify environment variables within this string, with a format like | |
| /// <c>%NAME%</c>. The value of these variables will be expanded at the time | |
| /// the SFX is run. Example: <c>%WINDIR%\system32\xcopy.exe</c> may expand at | |
| /// runtime to <c>c:\Windows\System32\xcopy.exe</c>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// By combining this with the <c>RemoveUnpackedFilesAfterExecute</c> | |
| /// flag, you can create an SFX that extracts itself, runs a file that | |
| /// was extracted, then deletes all the files that were extracted. If | |
| /// you want it to run "invisibly" then set <c>Flavor</c> to | |
| /// <c>SelfExtractorFlavor.ConsoleApplication</c>, and set <c>Quiet</c> | |
| /// to true. The user running such an EXE will see a console window | |
| /// appear, then disappear quickly. You may also want to specify the | |
| /// default extract location, with <c>DefaultExtractDirectory</c>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If you set <c>Flavor</c> to | |
| /// <c>SelfExtractorFlavor.WinFormsApplication</c>, and set <c>Quiet</c> to | |
| /// true, then a GUI with progressbars is displayed, but it is | |
| /// "non-interactive" - it accepts no input from the user. Instead the SFX | |
| /// just automatically unpacks and exits. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| public String PostExtractCommandLine | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The default extract directory the user will see when | |
| /// running the self-extracting archive. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// 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. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// This is only a default location. The actual extract location will be | |
| /// settable on the command line when the SFX is executed. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// You can specify environment variables within this string, | |
| /// with <c>%NAME%</c>. The value of these variables will be | |
| /// expanded at the time the SFX is run. Example: | |
| /// <c>%USERPROFILE%\Documents\unpack</c> may expand at runtime to | |
| /// <c>c:\users\melvin\Documents\unpack</c>. | |
| /// </para> | |
| /// </remarks> | |
| public String DefaultExtractDirectory | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The name of an .ico file in the filesystem to use for the application icon | |
| /// for the generated SFX. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// Normally, DotNetZip will embed an "zipped folder" icon into the generated | |
| /// SFX. If you prefer to use a different icon, you can specify it here. It | |
| /// should be a .ico file. This file is passed as the <c>/win32icon</c> | |
| /// option to the csc.exe compiler when constructing the SFX file. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| public string IconFile | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// Whether the ConsoleApplication SFX will be quiet during extraction. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// This option affects the way the generated SFX runs. By default it is | |
| /// false. When you set it to true,... | |
| /// </para> | |
| /// | |
| /// <list type="table"> | |
| /// <listheader> | |
| /// <term>Flavor</term> | |
| /// <description>Behavior</description> | |
| /// </listheader> | |
| /// | |
| /// <item> | |
| /// <term><c>ConsoleApplication</c></term> | |
| /// <description><para>no messages will be emitted during successful | |
| /// operation.</para> <para> Double-clicking the SFX in Windows | |
| /// Explorer or as an attachment in an email will cause a console | |
| /// window to appear briefly, before it disappears. If you run the | |
| /// ConsoleApplication SFX from the cmd.exe prompt, it runs as a | |
| /// normal console app; by default, because it is quiet, it displays | |
| /// no messages to the console. If you pass the -v+ command line | |
| /// argument to the Console SFX when you run it, you will get verbose | |
| /// messages to the console. </para> | |
| /// </description> | |
| /// </item> | |
| /// | |
| /// <item> | |
| /// <term><c>WinFormsApplication</c></term> | |
| /// <description>the SFX extracts automatically when the application | |
| /// is launched, with no additional user input. | |
| /// </description> | |
| /// </item> | |
| /// | |
| /// </list> | |
| /// | |
| /// <para> | |
| /// When you set it to false,... | |
| /// </para> | |
| /// | |
| /// <list type="table"> | |
| /// <listheader> | |
| /// <term>Flavor</term> | |
| /// <description>Behavior</description> | |
| /// </listheader> | |
| /// | |
| /// <item> | |
| /// <term><c>ConsoleApplication</c></term> | |
| /// <description><para>the extractor will emit a | |
| /// message to the console for each entry extracted.</para> | |
| /// <para> | |
| /// When double-clicking to launch the SFX, the console window will | |
| /// remain, and the SFX will emit a message for each file as it | |
| /// extracts. The messages fly by quickly, they won't be easily | |
| /// readable, unless the extracted files are fairly large. | |
| /// </para> | |
| /// </description> | |
| /// </item> | |
| /// | |
| /// <item> | |
| /// <term><c>WinFormsApplication</c></term> | |
| /// <description>the SFX presents a forms UI and allows the user to select | |
| /// options before extracting. | |
| /// </description> | |
| /// </item> | |
| /// | |
| /// </list> | |
| /// | |
| /// </remarks> | |
| public bool Quiet | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// Specify what the self-extractor will do when extracting an entry | |
| /// would overwrite an existing file. | |
| /// </summary> | |
| /// <remarks> | |
| /// <para> | |
| /// The default behavvior is to Throw. | |
| /// </para> | |
| /// </remarks> | |
| public Ionic.Zip.ExtractExistingFileAction ExtractExistingFile | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// Whether to remove the files that have been unpacked, after executing the | |
| /// PostExtractCommandLine. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// If true, and if there is a <see | |
| /// cref="SelfExtractorSaveOptions.PostExtractCommandLine"> | |
| /// PostExtractCommandLine</see>, and if the command runs successfully, | |
| /// then the files that the SFX unpacked will be removed, afterwards. If | |
| /// the command does not complete successfully (non-zero return code), | |
| /// that is interpreted as a failure, and the extracted files will not be | |
| /// removed. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// Setting this flag, and setting <c>Flavor</c> to | |
| /// <c>SelfExtractorFlavor.ConsoleApplication</c>, and setting <c>Quiet</c> to | |
| /// true, results in an SFX that extracts itself, runs a file that was | |
| /// extracted, then deletes all the files that were extracted, with no | |
| /// intervention by the user. You may also want to specify the default | |
| /// extract location, with <c>DefaultExtractDirectory</c>. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| public bool RemoveUnpackedFilesAfterExecute | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The file version number to embed into the generated EXE. It will show up, for | |
| /// example, during a mouseover in Windows Explorer. | |
| /// </summary> | |
| /// | |
| public Version FileVersion | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The product version to embed into the generated EXE. It will show up, for | |
| /// example, during a mouseover in Windows Explorer. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// You can use any arbitrary string, but a human-readable version number is | |
| /// recommended. For example "v1.2 alpha" or "v4.2 RC2". If you specify nothing, | |
| /// then there is no product version embedded into the EXE. | |
| /// </remarks> | |
| /// | |
| public String ProductVersion | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The copyright notice, if any, to embed into the generated EXE. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// It will show up, for example, while viewing properties of the file in | |
| /// Windows Explorer. You can use any arbitrary string, but typically you | |
| /// want something like "Copyright � Dino Chiesa 2011". | |
| /// </remarks> | |
| /// | |
| public String Copyright | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The description to embed into the generated EXE. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// Use any arbitrary string. This text will be displayed during a | |
| /// mouseover in Windows Explorer. If you specify nothing, then the string | |
| /// "DotNetZip SFX Archive" is embedded into the EXE as the description. | |
| /// </remarks> | |
| /// | |
| public String Description | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The product name to embed into the generated EXE. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// Use any arbitrary string. This text will be displayed | |
| /// while viewing properties of the EXE file in | |
| /// Windows Explorer. | |
| /// </remarks> | |
| /// | |
| public String ProductName | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// The title to display in the Window of a GUI SFX, while it extracts. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// By default the title show in the GUI window of a self-extractor | |
| /// is "DotNetZip Self-extractor (http://DotNetZip.codeplex.com/)". | |
| /// You can change that by setting this property before saving the SFX. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// This property has an effect only when producing a Self-extractor | |
| /// of flavor <c>SelfExtractorFlavor.WinFormsApplication</c>. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| public String SfxExeWindowTitle | |
| { | |
| // workitem 12608 | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// Additional options for the csc.exe compiler, when producing the SFX | |
| /// EXE. | |
| /// </summary> | |
| /// <exclude/> | |
| public string AdditionalCompilerSwitches | |
| { | |
| get; set; | |
| } | |
| } | |
| partial class ZipFile | |
| { | |
| class ExtractorSettings | |
| { | |
| public SelfExtractorFlavor Flavor; | |
| public List<string> ReferencedAssemblies; | |
| public List<string> CopyThroughResources; | |
| public List<string> ResourcesToCompile; | |
| } | |
| private static ExtractorSettings[] SettingsList = { | |
| new ExtractorSettings() { | |
| Flavor = SelfExtractorFlavor.WinFormsApplication, | |
| ReferencedAssemblies= new List<string>{ | |
| "System.dll", "System.Windows.Forms.dll", "System.Drawing.dll"}, | |
| CopyThroughResources = new List<string>{ | |
| "Ionic.Zip.WinFormsSelfExtractorStub.resources", | |
| "Ionic.Zip.Forms.PasswordDialog.resources", | |
| "Ionic.Zip.Forms.ZipContentsDialog.resources"}, | |
| ResourcesToCompile = new List<string>{ | |
| "WinFormsSelfExtractorStub.cs", | |
| "WinFormsSelfExtractorStub.Designer.cs", // .Designer.cs? | |
| "PasswordDialog.cs", | |
| "PasswordDialog.Designer.cs", //.Designer.cs" | |
| "ZipContentsDialog.cs", | |
| "ZipContentsDialog.Designer.cs", //.Designer.cs" | |
| "FolderBrowserDialogEx.cs", | |
| } | |
| }, | |
| new ExtractorSettings() { | |
| Flavor = SelfExtractorFlavor.ConsoleApplication, | |
| ReferencedAssemblies= new List<string> { "System.dll", }, | |
| CopyThroughResources = null, | |
| ResourcesToCompile = new List<string>{"CommandLineSelfExtractorStub.cs"} | |
| } | |
| }; | |
| //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 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 WinForms-based self-extracting archive EXE that | |
| /// will use c:\ExtractHere as the default extract location. The C# code | |
| /// shows syntax for .NET 3.0, which uses an object initializer for | |
| /// the SelfExtractorOptions object. | |
| /// <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 WinForms-based exe"; | |
| /// var options = new SelfExtractorOptions | |
| /// { | |
| /// Flavor = SelfExtractorFlavor.WinFormsApplication, | |
| /// DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere", | |
| /// PostExtractCommandLine = ExeToRunAfterExtract, | |
| /// SfxExeWindowTitle = "My Custom Window Title", | |
| /// 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.WinFormsApplication | |
| /// options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere" | |
| /// options.PostExtractCommandLine = ExeToRunAfterExtract | |
| /// options.SfxExeWindowTitle = "My Custom Window Title" | |
| /// 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 creating the | |
| /// Self-extracting archive.</param> | |
| internal 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; | |
| } | |
| private static void ExtractResourceToFile(Assembly a, string resourceName, string filename) | |
| { | |
| int n = 0; | |
| byte[] bytes = new byte[1024]; | |
| using (Stream instream = a.GetManifestResourceStream(resourceName)) | |
| { | |
| if (instream == null) | |
| throw new ZipException(String.Format("missing resource '{0}'", resourceName)); | |
| using (FileStream outstream = File.OpenWrite(filename)) | |
| { | |
| do | |
| { | |
| n = instream.Read(bytes, 0, bytes.Length); | |
| outstream.Write(bytes, 0, n); | |
| } while (n > 0); | |
| } | |
| } | |
| } | |
| private void _SaveSfxStub(string exeToGenerate, SelfExtractorSaveOptions options) | |
| { | |
| string nameOfIconFile = null; | |
| string stubExe = null; | |
| string unpackedResourceDir = null; | |
| string tmpDir = 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."); | |
| } | |
| // workitem 10553 | |
| tmpDir = TempFileFolder ?? Path.GetDirectoryName(exeToGenerate); | |
| stubExe = GenerateTempPathname(tmpDir, "exe"); | |
| // get the Ionic.Zip assembly | |
| Assembly a1 = typeof(ZipFile).Assembly; | |
| using (var csharp = new Microsoft.CSharp.CSharpCodeProvider | |
| (new Dictionary<string,string>() { { "CompilerVersion", "v2.0" } })) { | |
| // The following is a perfect opportunity for a linq query, but | |
| // I cannot use it. DotNetZip needs to run on .NET 2.0, | |
| // and 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; | |
| } | |
| } | |
| // sanity check; should never happen | |
| 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. Ionic.Zip is | |
| // needed here. Also if it is the winforms (gui) extractor, we | |
| // need other referenced assemblies, like | |
| // System.Windows.Forms.dll, etc. | |
| var 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. If errors are detected, we'll spool the source | |
| // code as well as the errors (in comments) into that filename, | |
| // and throw an exception with the filename. Makes it easier to | |
| // diagnose. This should be rare; most errors happen only | |
| // during devlpmt of DotNetZip itself, but there are rare | |
| // occasions when they occur in other cases. | |
| string sourceFile = GenerateTempPathname(tmpDir, "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("Ionic.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(); | |
| unpackedResourceDir = GenerateTempPathname(tmpDir, "tmp"); | |
| if (String.IsNullOrEmpty(options.IconFile)) | |
| { | |
| // Use the ico file that is embedded into the Ionic.Zip | |
| // DLL itself. To do this we must unpack the icon to | |
| // the filesystem, in order to specify it on the cmdline | |
| // of csc.exe. This method will remove the unpacked | |
| // file later. | |
| System.IO.Directory.CreateDirectory(unpackedResourceDir); | |
| 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(unpackedResourceDir); | |
| nameOfIconFile = Path.Combine(unpackedResourceDir, "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 (!String.IsNullOrEmpty(options.AdditionalCompilerSwitches)) | |
| cp.CompilerOptions += " " + options.AdditionalCompilerSwitches; | |
| if (String.IsNullOrEmpty(cp.CompilerOptions)) | |
| cp.CompilerOptions = null; | |
| if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0)) | |
| { | |
| if (!Directory.Exists(unpackedResourceDir)) System.IO.Directory.CreateDirectory(unpackedResourceDir); | |
| foreach (string re in settings.CopyThroughResources) | |
| { | |
| string filename = Path.Combine(unpackedResourceDir, 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"); | |
| // workitem | |
| string copyright = | |
| (String.IsNullOrEmpty(options.Copyright)) | |
| ? "Extractor: Copyright � Dino Chiesa 2008-2011" | |
| : 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) | |
| { | |
| 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()); | |
| if (!String.IsNullOrEmpty(options.SfxExeWindowTitle)) | |
| line = line.Replace("@@SFX_EXE_WINDOW_TITLE", options.SfxExeWindowTitle); | |
| 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(tmpDir, "cs"); | |
| using (StreamWriter sw = File.CreateText(sourceModule)) | |
| { | |
| sw.Write(LiteralSource); | |
| } | |
| Console.WriteLine("source: {0}", sourceModule); | |
| #endif | |
| var 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(unpackedResourceDir)) | |
| { | |
| try { Directory.Delete(unpackedResourceDir, true); } | |
| catch (System.IO.IOException exc1) | |
| { | |
| StatusMessageTextWriter.WriteLine("Warning: Exception: {0}", exc1); | |
| } | |
| } | |
| if (File.Exists(stubExe)) | |
| { | |
| try { File.Delete(stubExe); } | |
| catch (System.IO.IOException exc1) | |
| { | |
| StatusMessageTextWriter.WriteLine("Warning: Exception: {0}", exc1); | |
| } | |
| } | |
| } | |
| catch (System.IO.IOException) { } | |
| } | |
| return; | |
| } | |
| internal static string GenerateTempPathname(string dir, string extension) | |
| { | |
| string candidate = null; | |
| String AppName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; | |
| do | |
| { | |
| // workitem 13475 | |
| string uuid = System.Guid.NewGuid().ToString(); | |
| string Name = String.Format("{0}-{1}-{2}.{3}", | |
| AppName, System.DateTime.Now.ToString("yyyyMMMdd-HHmmss"), | |
| uuid, extension); | |
| candidate = System.IO.Path.Combine(dir, Name); | |
| } while (System.IO.File.Exists(candidate) || System.IO.Directory.Exists(candidate)); | |
| // The candidate path does not exist as a file or directory. | |
| // It can now be created, as a file or directory. | |
| return candidate; | |
| } | |
| } | |
| #endif | |
| } |