diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs index f55da4b..1ccdd06 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/MainForm.cs @@ -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,16 @@ 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()); + } + } + using (var openFileDialog = new OpenFileDialog()) { openFileDialog.Filter = "zip files (*.zip)|*.zip|All files (*.*)|*.*"; @@ -130,15 +141,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 +164,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 +350,11 @@ namespace WelsonJS.Launcher { Program.OpenWebBrowser(Program.GetAppConfig("RepositoryUrl")); } + + public void RunFromZipFile(string filePath) + { + _filePath = filePath; + btnRunFromZipFile.PerformClick(); + } } } diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/Program.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/Program.cs index 616e1a5..fb6a04b 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/Program.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/Program.cs @@ -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,165 @@ 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 + { + throw new Exception("Instance Initialization failed"); + } + + return workingDirectory; + } + + private static void CopyDirectoryRecursive(string sourceDir, string destDir) + { + if (!Directory.Exists(sourceDir)) + { + throw new DirectoryNotFoundException("Source directory not found: " + sourceDir); + } + + Directory.CreateDirectory(destDir); + + foreach (var file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories)) + { + string relativePath = file.Substring(sourceDir.Length).TrimStart( + Path.DirectorySeparatorChar, + Path.AltDirectorySeparatorChar + ); + + 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 +258,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(); } diff --git a/setup.iss b/setup.iss index 3469c46..205ea96 100644 --- a/setup.iss +++ b/setup.iss @@ -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}";