welsonjs/WelsonJS.Toolkit/WelsonJS.Launcher/Program.cs
Namhyeon, Go daea458f64
Update WelsonJS.Toolkit/WelsonJS.Launcher/Program.cs
Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com>
2025-11-16 18:58:48 +09:00

402 lines
14 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 (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)
{
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;
}
}
}