kopia lustrzana https://github.com/dl2alf/AirScout
1102 wiersze
48 KiB
C#
1102 wiersze
48 KiB
C#
// 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 Ionic.Zip
|
||
{
|
||
#if !NO_SFX
|
||
/// <summary>
|
||
/// An enum that provides the different self-extractor flavors
|
||
/// </summary>
|
||
public 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>
|
||
public 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 <20> 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 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);
|
||
}
|
||
|
||
|
||
|
||
/// <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>
|
||
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;
|
||
}
|
||
|
||
|
||
|
||
|
||
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 <20> 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
|
||
}
|