mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-11-28 18:41:04 +00:00
Launcher now accepts a --file argument to run .js or .zip files directly from the command line. MainForm and Program.cs were refactored to support this, including new registry entries in setup.iss for file associations with WelsonJS Script extensions.
397 lines
13 KiB
C#
397 lines
13 KiB
C#
// Program.cs
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2025 Catswords OSS and WelsonJS Contributors
|
|
// https://github.com/gnh1201/welsonjs
|
|
//
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using System.Configuration;
|
|
|
|
namespace WelsonJS.Launcher
|
|
{
|
|
internal static class Program
|
|
{
|
|
private static readonly ICompatibleLogger _logger;
|
|
|
|
public static Mutex _mutex;
|
|
public static ResourceServer _resourceServer;
|
|
|
|
static Program()
|
|
{
|
|
// set up logger
|
|
_logger = new TraceLogger();
|
|
|
|
// load native libraries
|
|
string appDataSubDirectory = "WelsonJS";
|
|
bool requireSigned = string.Equals(
|
|
GetAppConfig("NativeRequireSigned"),
|
|
"true",
|
|
StringComparison.OrdinalIgnoreCase);
|
|
|
|
NativeBootstrap.Init(
|
|
dllNames: new[] { "ChakraCore.dll" },
|
|
appDataSubdirectory: appDataSubDirectory,
|
|
logger: _logger,
|
|
requireSigned: requireSigned
|
|
);
|
|
}
|
|
|
|
[STAThread]
|
|
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)
|
|
{
|
|
_logger.Info("WelsonJS Launcher already running.");
|
|
return;
|
|
}
|
|
|
|
// draw the main form
|
|
Application.EnableVisualStyles();
|
|
Application.SetCompatibleTextRenderingDefault(false);
|
|
Application.Run(new MainForm(_logger));
|
|
|
|
// release the mutex
|
|
try
|
|
{
|
|
_mutex.ReleaseMutex();
|
|
}
|
|
catch { /* ignore if not owned */ }
|
|
_mutex.Dispose();
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!File.Exists(Path.Combine(workingDirectory, entryFileName)))
|
|
{
|
|
throw new Exception("Not Found: " + entryFileName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!Directory.EnumerateFiles(workingDirectory, scriptName + ".*").Any())
|
|
{
|
|
throw new Exception("Not found matches file: " + scriptName);
|
|
}
|
|
}
|
|
|
|
Process process = new Process
|
|
{
|
|
StartInfo = new ProcessStartInfo("cmd")
|
|
{
|
|
UseShellExecute = false,
|
|
RedirectStandardInput = true,
|
|
RedirectStandardOutput = true,
|
|
CreateNoWindow = true,
|
|
Arguments = "/k",
|
|
}
|
|
};
|
|
process.Start();
|
|
|
|
StreamWriter input = process.StandardInput;
|
|
StreamReader output = process.StandardOutput;
|
|
|
|
input.WriteLine("pushd " + workingDirectory);
|
|
input.WriteLine();
|
|
input.Flush();
|
|
output.ReadLine();
|
|
|
|
if (isInteractiveServiceApplication)
|
|
{
|
|
input.WriteLine($"start cmd /c startInteractiveService.bat");
|
|
input.WriteLine();
|
|
input.Flush();
|
|
output.ReadLine();
|
|
}
|
|
else if (!isConsoleApplication)
|
|
{
|
|
input.WriteLine(entryFileName);
|
|
input.WriteLine();
|
|
input.Flush();
|
|
output.ReadLine();
|
|
}
|
|
else
|
|
{
|
|
input.WriteLine($"start cmd /c cscript app.js {scriptName}");
|
|
input.WriteLine();
|
|
input.Flush();
|
|
output.ReadLine();
|
|
}
|
|
input.Close();
|
|
|
|
process.WaitForExit();
|
|
}
|
|
|
|
public static string GetFinalDirectory(string path)
|
|
{
|
|
string[] directories = Directory.GetDirectories(path);
|
|
|
|
while (directories.Length == 1)
|
|
{
|
|
path = directories[0];
|
|
directories = Directory.GetDirectories(path);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
public static string GetAppDataPath()
|
|
{
|
|
string path = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"WelsonJS"
|
|
);
|
|
|
|
Directory.CreateDirectory(path);
|
|
|
|
if (!Directory.Exists(path))
|
|
{
|
|
throw new IOException("Failed to create directory: " + path);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
public static string GetWorkingDirectory(string instanceId, bool followSubDirectory = false)
|
|
{
|
|
string workingDirectory = Path.Combine(GetAppDataPath(), instanceId);
|
|
|
|
if (followSubDirectory)
|
|
{
|
|
if (!Directory.Exists(workingDirectory))
|
|
{
|
|
workingDirectory = Path.Combine(Path.GetTempPath(), instanceId);
|
|
}
|
|
|
|
workingDirectory = GetFinalDirectory(workingDirectory);
|
|
}
|
|
|
|
return workingDirectory;
|
|
}
|
|
|
|
public static void InitializeResourceServer()
|
|
{
|
|
lock(typeof(Program))
|
|
{
|
|
if (_resourceServer == null)
|
|
{
|
|
_resourceServer = new ResourceServer(GetAppConfig("ResourceServerPrefix"), "editor.html", _logger);
|
|
}
|
|
}
|
|
}
|
|
public static void OpenWebBrowser(string url)
|
|
{
|
|
Uri resourceServerUri = new Uri(GetAppConfig("ResourceServerPrefix"));
|
|
Uri devToolsUri = new Uri(GetAppConfig("ChromiumDevToolsPrefix"));
|
|
|
|
string userDataDir = Path.Combine(GetAppDataPath(), "EdgeUserProfile");
|
|
string remoteAllowOrigins = $"{resourceServerUri.Scheme}://{resourceServerUri.Host}:{resourceServerUri.Port}";
|
|
int remoteDebuggingPort = devToolsUri.Port;
|
|
bool isAppMode = string.Equals(
|
|
GetAppConfig("ChromiumAppMode"),
|
|
"true",
|
|
StringComparison.OrdinalIgnoreCase);
|
|
string[] arguments = {
|
|
isAppMode ? $"\"--app={url}\"" : $"\"{url}\"",
|
|
$"--remote-debugging-port={remoteDebuggingPort}",
|
|
$"--remote-allow-origins={remoteAllowOrigins}", // for security reason
|
|
$"--user-data-dir=\"{userDataDir}\""
|
|
};
|
|
|
|
Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = Program.GetAppConfig("ChromiumExecutablePath"),
|
|
Arguments = string.Join(" ", arguments),
|
|
UseShellExecute = true
|
|
});
|
|
}
|
|
|
|
public static string GetAppConfig(string key)
|
|
{
|
|
string value = ConfigurationManager.AppSettings[key];
|
|
if (!string.IsNullOrEmpty(value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
value = Properties.Resources.ResourceManager.GetString(key);
|
|
if (!string.IsNullOrEmpty(value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|