diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs
index dcb3ce2..d403103 100644
--- a/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs
+++ b/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs
@@ -1,7 +1,6 @@
using System;
using System.Diagnostics;
using System.IO;
-using System.IO.Compression;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -12,11 +11,14 @@ namespace WelsonJS.Launcher
{
private string workingDirectory;
private string instanceId;
- private string entryFileName;
+ private readonly string entryFileName;
private string scriptName;
+ private readonly ZipExtractor zipExtractor;
public MainForm()
{
+ zipExtractor = new ZipExtractor();
+
entryFileName = "bootstrap.bat";
InitializeComponent();
@@ -130,17 +132,18 @@ namespace WelsonJS.Launcher
Directory.Delete(workingDirectory, true);
}
- // try to extact ZIP file
- ZipFile.ExtractToDirectory(filePath, workingDirectory);
+ // try to extact ZIP compressed file
+ if (zipExtractor.Extract(filePath, workingDirectory))
+ {
+ // record the first deploy time
+ RecordFirstDeployTime(workingDirectory);
- // record the first deploy time
- RecordFirstDeployTime(workingDirectory);
+ // follow the sub-directory
+ workingDirectory = Program.GetWorkingDirectory(instanceId, true);
- // follow the sub-directory
- workingDirectory = Program.GetWorkingDirectory(instanceId, true);
-
- // Run the appliction
- Program.RunCommandPrompt(workingDirectory, entryFileName, scriptName, cbUseSpecificScript.Checked, cbInteractiveServiceApp.Checked);
+ // Run the application
+ Program.RunCommandPrompt(workingDirectory, entryFileName, scriptName, cbUseSpecificScript.Checked, cbInteractiveServiceApp.Checked);
+ }
}
catch (Exception ex)
{
diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj b/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj
index 21be0a4..9c69d5f 100644
--- a/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj
+++ b/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj
@@ -56,17 +56,12 @@
favicon.ico
+
-
-
- ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
- True
- True
-
@@ -107,6 +102,7 @@
GlobalSettingsForm.cs
+
EnvForm.cs
@@ -125,7 +121,6 @@
GlobalSettingsForm.cs
-
SettingsSingleFileGenerator
Settings.Designer.cs
diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ZipExtractor.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ZipExtractor.cs
new file mode 100644
index 0000000..2a838bb
--- /dev/null
+++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ZipExtractor.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace WelsonJS.Launcher
+{
+ public class ZipExtractor
+ {
+ class Extractor
+ {
+ public string Name { get; set; }
+ public string FileName { get; set; }
+ public string Path { get; set; }
+ public Func ExtractCommand { get; set; }
+ }
+
+ private readonly List AvailableExtractors;
+
+ public ZipExtractor()
+ {
+ AvailableExtractors = new List{
+ new Extractor
+ {
+ Name = "7z",
+ FileName = "7z.exe",
+ ExtractCommand = (src, dest) => $"x \"{src}\" -o\"{dest}\" -y"
+ },
+ new Extractor
+ {
+ Name = "WinRAR",
+ FileName = "rar.exe",
+ ExtractCommand = (src, dest) => $"x -o+ \"{src}\" \"{dest}\\\""
+ },
+ new Extractor
+ {
+ Name = "PeaZip",
+ FileName = "peazip.exe",
+ ExtractCommand = (src, dest) => $"-ext2simple \"{src}\" \"{dest}\""
+ },
+ new Extractor
+ {
+ Name = "tar (Windows)",
+ FileName = "tar.exe",
+ ExtractCommand = (src, dest) => $"-xf \"{src}\" -C \"{dest}\""
+ },
+ new Extractor
+ {
+ Name = "WinZip",
+ FileName = "wzunzip.exe",
+ ExtractCommand = (src, dest) => $"-d \"{dest}\" \"{src}\""
+ },
+ new Extractor
+ {
+ Name = "ALZip",
+ FileName = "ALZipcon.exe",
+ ExtractCommand = (src, dest) => $"-x \"{src}\" \"{dest}\""
+ },
+ new Extractor
+ {
+ Name = "Bandizip",
+ FileName = "Bandizip.exe",
+ ExtractCommand = (src, dest) => $"x -o:\"{dest}\" \"{src}\" -y"
+ }
+ };
+
+ Task.Run(() => CheckAvailableExtractors());
+ }
+
+ public bool Extract(string filePath, string workingDirectory)
+ {
+ if (!File.Exists(filePath))
+ throw new FileNotFoundException("The specified file does not exist.", filePath);
+
+ if (!filePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
+ throw new ArgumentException("The specified file is not a ZIP archive.");
+
+ if (!IsValidFile(filePath))
+ throw new InvalidDataException("The specified file is not a valid ZIP archive.");
+
+ Directory.CreateDirectory(workingDirectory);
+
+ foreach (var extractor in AvailableExtractors.Where(e => e.Path != null))
+ {
+ if (RunProcess(extractor.Path, extractor.ExtractCommand(filePath, workingDirectory)))
+ {
+ return true;
+ }
+ }
+
+ return ExtractUsingShell(filePath, workingDirectory);
+ }
+
+ private bool IsValidFile(string filePath)
+ {
+ byte[] signature = new byte[4];
+
+ using (var fs = File.OpenRead(filePath))
+ {
+ if (fs.Length < 4)
+ return false;
+
+ int bytesRead = fs.Read(signature, 0, 4);
+ if (bytesRead < 4)
+ return false;
+ }
+
+ return signature.SequenceEqual(new byte[] { 0x50, 0x4B, 0x03, 0x04 });
+ }
+
+ private void CheckAvailableExtractors()
+ {
+ var fileNames = AvailableExtractors.Select(e => e.FileName).ToList();
+
+ // Check PATH environment variable
+ var paths = (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator);
+ foreach (var dir in paths)
+ {
+ foreach (var fileName in fileNames)
+ {
+ var path = Path.Combine(dir.Trim(), fileName);
+ if (File.Exists(path))
+ {
+ var index = fileNames.IndexOf(fileName);
+ var extractor = AvailableExtractors[index];
+ extractor.Path = path;
+ }
+ }
+ }
+
+ // Check common install locations
+ var programDirs = new[]
+ {
+ Path.Combine(Program.GetAppDataPath(), "bin"), // find an extractor from APPDATA directory
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
+ };
+
+ foreach (var rootDir in programDirs)
+ {
+ if (!Directory.Exists(rootDir))
+ continue;
+
+ try
+ {
+ foreach (var file in Directory.EnumerateFiles(rootDir, "*", SearchOption.AllDirectories))
+ {
+ var fileName = Path.GetFileName(file);
+ if (fileNames.Contains(fileName))
+ {
+ var index = fileNames.IndexOf(fileName);
+ var extractor = AvailableExtractors[index];
+ extractor.Path = file;
+ }
+ }
+ }
+ catch { }
+ }
+ }
+
+ private bool RunProcess(string executableFilePath, string arguments)
+ {
+ var psi = new ProcessStartInfo
+ {
+ FileName = executableFilePath,
+ Arguments = arguments,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+
+ using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
+ {
+ process.Start();
+ process.WaitForExit();
+ return process.ExitCode == 0;
+ }
+ }
+
+ private bool ExtractUsingShell(string filePath, string workingDirectory)
+ {
+ var shellAppType = Type.GetTypeFromProgID("Shell.Application");
+ if (shellAppType == null)
+ return false;
+
+ dynamic shell = Activator.CreateInstance(shellAppType);
+ if (shell == null)
+ return false;
+
+ dynamic zip = shell.NameSpace(filePath);
+ dynamic dest = shell.NameSpace(workingDirectory);
+ if (zip == null || dest == null)
+ return false;
+
+ int expected = zip.Items().Count;
+ dest.CopyHere(zip.Items(), 16);
+
+ // wait (max 30 s) until all files appear
+ var sw = Stopwatch.StartNew();
+ while (dest.Items().Count < expected && sw.Elapsed < TimeSpan.FromSeconds(30))
+ System.Threading.Thread.Sleep(200);
+
+ Marshal.ReleaseComObject(zip);
+ Marshal.ReleaseComObject(dest);
+ Marshal.ReleaseComObject(shell);
+
+ return dest.Items().Count == expected;
+ }
+ }
+}
diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/packages.config b/WelsonJS.Toolkit/WelsonJS.Launcher/packages.config
deleted file mode 100644
index 888dd5e..0000000
--- a/WelsonJS.Toolkit/WelsonJS.Launcher/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file