This commit is contained in:
Namhyeon Go 2025-11-19 09:52:37 +09:00
commit 0e40d890bf
3 changed files with 231 additions and 27 deletions

View File

@ -20,6 +20,7 @@ namespace WelsonJS.Launcher
private readonly string _dateTimeFormat;
private readonly ICompatibleLogger _logger;
private string _filePath;
private string _workingDirectory;
private string _instanceId;
private string _scriptName;
@ -122,6 +123,17 @@ namespace WelsonJS.Launcher
private void btnRunFromZipFile_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(_filePath))
{
string fileExtension = Path.GetExtension(_filePath);
if (fileExtension != null && fileExtension.Equals(".zip", StringComparison.OrdinalIgnoreCase))
{
DisableUI();
Task.Run(() => RunAppPackageFile());
return;
}
}
using (var openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "zip files (*.zip)|*.zip|All files (*.*)|*.*";
@ -130,15 +142,15 @@ namespace WelsonJS.Launcher
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
string filePath = openFileDialog.FileName;
_filePath = openFileDialog.FileName;
DisableUI();
Task.Run(() => RunAppPackageFile(filePath));
Task.Run(() => RunAppPackageFile());
}
}
}
private void RunAppPackageFile(string filePath)
private void RunAppPackageFile()
{
_instanceId = Guid.NewGuid().ToString();
_workingDirectory = Program.GetWorkingDirectory(_instanceId);
@ -153,7 +165,7 @@ namespace WelsonJS.Launcher
}
// try to extract ZIP file
ZipFile.ExtractToDirectory(filePath, _workingDirectory);
ZipFile.ExtractToDirectory(_filePath, _workingDirectory);
// record the first deploy time
RecordFirstDeployTime(_workingDirectory, _instanceId);
@ -339,5 +351,11 @@ namespace WelsonJS.Launcher
{
Program.OpenWebBrowser(Program.GetAppConfig("RepositoryUrl"));
}
public void RunFromZipFile(string filePath)
{
_filePath = filePath;
btnRunFromZipFile.PerformClick();
}
}
}

View File

@ -41,8 +41,22 @@ namespace WelsonJS.Launcher
}
[STAThread]
static void Main()
static void Main(string[] args)
{
// if set the target file path
string targetFilePath = GetTargetFilePath(args);
if (!string.IsNullOrEmpty(targetFilePath))
{
try {
HandleTargetFilePath(targetFilePath);
}
catch (Exception e)
{
_logger.Error($"Initialization failed: {e}");
}
return;
}
// create the mutex
_mutex = new Mutex(true, "WelsonJS.Launcher", out bool createdNew);
if (!createdNew)
@ -56,14 +70,170 @@ namespace WelsonJS.Launcher
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(_logger));
// destory the mutex
try {
// release the mutex
try
{
_mutex.ReleaseMutex();
} catch { /* ignore if not owned */ }
}
catch { /* ignore if not owned */ }
_mutex.Dispose();
}
public static void RunCommandPrompt(string workingDirectory, string entryFileName, string scriptName, bool isConsoleApplication = false, bool isInteractiveServiceAapplication = false)
private static string GetTargetFilePath(string[] args)
{
if (args == null || args.Length == 0) return null;
for (int i = 0; i < args.Length; i++)
{
string token = args[i];
if (string.Equals(token, "--file", StringComparison.OrdinalIgnoreCase) ||
string.Equals(token, "/file", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < args.Length)
{
return args[i + 1];
}
}
if (token.StartsWith("--file=", StringComparison.OrdinalIgnoreCase))
{
return token.Substring("--file=".Length).Trim('"');
}
}
return null;
}
private static void HandleTargetFilePath(string filePath)
{
string fileExtension = Path.GetExtension(filePath);
if (String.IsNullOrEmpty(fileExtension))
{
throw new ArgumentException("The file extension is null or empty");
}
if (fileExtension.Equals(".zip", StringComparison.OrdinalIgnoreCase))
{
var mainForm = new MainForm(_logger);
mainForm.Show();
mainForm.RunFromZipFile(filePath);
return;
}
if (fileExtension.Equals(".js", StringComparison.OrdinalIgnoreCase))
{
string workingDirectory = CreateInstanceDirectory();
string appRoot = GetAppRootDirectory();
string appBaseSource = Path.Combine(appRoot, "app.js");
if (!File.Exists(appBaseSource))
{
throw new FileNotFoundException("app.js not found in application root.", appBaseSource);
}
string appBaseDestination = Path.Combine(workingDirectory, "app.js");
File.Copy(appBaseSource, appBaseDestination, overwrite: true);
string assetsSource = Path.Combine(appRoot, "app", "assets", "js");
string assetsDestination = Path.Combine(workingDirectory, "app", "assets", "js");
CopyDirectoryRecursive(assetsSource, assetsDestination);
string entrypointDestination = Path.Combine(workingDirectory, "bootstrap.js");
File.Copy(filePath, entrypointDestination, overwrite: true);
RunCommandPrompt(
workingDirectory: workingDirectory,
entryFileName: "app.js",
scriptName: "bootstrap",
isConsoleApplication: true,
isInteractiveServiceApplication: false
);
return;
}
throw new NotSupportedException($"Unsupported file type: {fileExtension}");
}
private static string GetAppRootDirectory()
{
string[] candidates = new[]
{
GetAppConfig("AppRootDirectory"),
AppDomain.CurrentDomain.BaseDirectory,
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "WelsonJS"),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WelsonJS"),
};
foreach (string dir in candidates)
{
if (string.IsNullOrEmpty(dir))
continue;
string appJs = Path.Combine(dir, "app.js");
if (File.Exists(appJs))
return dir;
}
throw new FileNotFoundException("Could not locate app.js in any known application root directory.");
}
private static string CreateInstanceDirectory()
{
string instanceId = Guid.NewGuid().ToString();
string workingDirectory = GetWorkingDirectory(instanceId);
try
{
// check if the working directory exists
if (Directory.Exists(workingDirectory))
{
throw new InvalidOperationException("GUID validation failed. Directory already exists.");
}
Directory.CreateDirectory(workingDirectory);
}
catch (IOException ex)
{
throw new Exception("Instance Initialization failed due to an IO error.", ex);
}
catch (UnauthorizedAccessException ex)
{
throw new Exception("Instance Initialization failed due to insufficient permissions.", ex);
}
catch (InvalidOperationException)
{
// Let InvalidOperationException bubble up as it is thrown intentionally above
throw;
}
var sourceDirInfo = new DirectoryInfo(sourceDir);
// Create all subdirectories
foreach (DirectoryInfo dir in sourceDirInfo.GetDirectories("*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(Path.Combine(destDir, dir.FullName.Substring(sourceDirInfo.FullName.Length + 1)));
}
// Copy all files
foreach (FileInfo file in sourceDirInfo.GetFiles("*", SearchOption.AllDirectories))
{
string targetPath = Path.Combine(destDir, file.FullName.Substring(sourceDirInfo.FullName.Length + 1));
file.CopyTo(targetPath, true);
}
string targetPath = Path.Combine(destDir, relativePath);
string targetDir = Path.GetDirectoryName(targetPath);
if (!Directory.Exists(targetDir))
{
Directory.CreateDirectory(targetDir);
}
File.Copy(file, targetPath, overwrite: true);
}
}
public static void RunCommandPrompt(string workingDirectory, string entryFileName, string scriptName, bool isConsoleApplication = false, bool isInteractiveServiceApplication = false)
{
if (!isConsoleApplication)
{
@ -93,33 +263,37 @@ namespace WelsonJS.Launcher
};
process.Start();
process.StandardInput.WriteLine("pushd " + workingDirectory);
process.StandardInput.WriteLine();
process.StandardInput.Flush();
process.StandardOutput.ReadLine();
StreamWriter input = process.StandardInput;
StreamReader output = process.StandardOutput;
if (isInteractiveServiceAapplication)
input.WriteLine("pushd " + workingDirectory);
input.WriteLine();
input.Flush();
output.ReadLine();
if (isInteractiveServiceApplication)
{
process.StandardInput.WriteLine($"start cmd /c startInteractiveService.bat");
process.StandardInput.WriteLine();
process.StandardInput.Flush();
process.StandardOutput.ReadLine();
input.WriteLine($"start cmd /c startInteractiveService.bat");
input.WriteLine();
input.Flush();
output.ReadLine();
}
else if (!isConsoleApplication)
{
process.StandardInput.WriteLine(entryFileName);
process.StandardInput.WriteLine();
process.StandardInput.Flush();
process.StandardOutput.ReadLine();
input.WriteLine(entryFileName);
input.WriteLine();
input.Flush();
output.ReadLine();
}
else
{
process.StandardInput.WriteLine($"start cmd /c cscript app.js {scriptName}");
process.StandardInput.WriteLine();
process.StandardInput.Flush();
process.StandardOutput.ReadLine();
input.WriteLine($"start cmd /c cscript app.js {scriptName}");
input.WriteLine();
input.Flush();
output.ReadLine();
}
process.StandardInput.Close();
input.Close();
process.WaitForExit();
}

View File

@ -25,12 +25,24 @@ DisableWelcomePage=yes
DisableDirPage=yes
DisableProgramGroupPage=yes
LicenseFile=SECURITY.MD
ChangesAssociations=yes
; [Registry]
; Root: HKCR; Subkey: "welsonjs"; ValueType: "string"; ValueData: "URL:{cm:AppName}"; Flags: uninsdeletekey
; Root: HKCR; Subkey: "welsonjs"; ValueType: "string"; ValueName: "URL Protocol"; ValueData: ""
; Root: HKCR; Subkey: "welsonjs\DefaultIcon"; ValueType: "string"; ValueData: "{app}\app\favicon.ico,0"
; Root: HKCR; Subkey: "welsonjs\shell\open\command"; ValueType: "string"; ValueData: "cscript ""{app}\app.js"" uriloader ""%1"""
Root: HKCR; Subkey: "WelsonJS.Script"; ValueType: string; ValueData: "WelsonJS Script"; Flags: uninsdeletekey
Root: HKCR; Subkey: "WelsonJS.Script\DefaultIcon"; ValueType: string; ValueData: "{app}\app\favicon.ico,0"; Flags: uninsdeletekey
Root: HKCR; Subkey: "WelsonJS.Script\shell"; ValueType: string; ValueData: "open"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "WelsonJS.Script\shell\open"; ValueType: string; ValueData: "Run with WelsonJS"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "WelsonJS.Script\shell\open\command"; ValueType: string; ValueData: """{app}\bin\x86\WelsonJS.Launcher.exe"" --file ""%1"""; Flags: uninsdeletevalue
Root: HKCR; Subkey: ".js"; ValueType: string; ValueData: "WelsonJS.Script"; Flags: uninsdeletevalue
Root: HKCR; Subkey: ".ts"; ValueType: string; ValueData: "WelsonJS.Script"; Flags: uninsdeletevalue
Root: HKCR; Subkey: ".re"; ValueType: string; ValueData: "WelsonJS.Script"; Flags: uninsdeletevalue
Root: HKCR; Subkey: ".res"; ValueType: string; ValueData: "WelsonJS.Script"; Flags: uninsdeletevalue
Root: HKCR; Subkey: ".ls"; ValueType: string; ValueData: "WelsonJS.Script"; Flags: uninsdeletevalue
Root: HKCR; Subkey: ".coffee"; ValueType: string; ValueData: "WelsonJS.Script"; Flags: uninsdeletevalue
[Files]
Source: "app.js"; DestDir: "{app}";