Merge pull request #255 from gnh1201/dev

Remove the dependency `System.IO.Compression.ZipFile` #254
This commit is contained in:
Namhyeon Go 2025-05-17 03:35:58 +09:00 committed by GitHub
commit 4d37a3eb4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 230 additions and 22 deletions

View File

@ -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)
{

View File

@ -56,17 +56,12 @@
<ApplicationIcon>favicon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
@ -107,6 +102,7 @@
<DependentUpon>GlobalSettingsForm.cs</DependentUpon>
</Compile>
<Compile Include="ResourceServer.cs" />
<Compile Include="ZipExtractor.cs" />
<EmbeddedResource Include="EnvForm.resx">
<DependentUpon>EnvForm.cs</DependentUpon>
</EmbeddedResource>
@ -125,7 +121,6 @@
<DependentUpon>GlobalSettingsForm.cs</DependentUpon>
</EmbeddedResource>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>

View File

@ -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<string, string, string> ExtractCommand { get; set; }
}
private readonly List<Extractor> AvailableExtractors;
public ZipExtractor()
{
AvailableExtractors = new List<Extractor>{
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;
}
}
}

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net472" />
</packages>