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