using System.Runtime.InteropServices; using System.Text; namespace Zcbot.WindowsNode; internal static class Program { [STAThread] private static int Main(string[] args) { if (args.Length > 0) { NativeConsole.AttachToParent(); return RunCommandAsync(args).GetAwaiter().GetResult(); } ApplicationConfiguration.Initialize(); using var singleInstance = new Mutex( initiallyOwned: true, "Local\\Zcbot.WindowsNode", out var isFirstInstance); if (!isFirstInstance) { MessageBox.Show( "zcbot Windows Node 已在运行。请查看系统托盘。", "zcbot Windows Node", MessageBoxButtons.OK, MessageBoxIcon.Information); return 0; } var store = new NodeConfigStore(NodePaths.ForCurrentMachine()); Application.Run(new TrayApplicationContext(store)); return 0; } private static async Task RunCommandAsync(string[] args) { try { var store = new NodeConfigStore(NodePaths.ForCurrentMachine()); if (string.Equals(args[0], "enroll", StringComparison.OrdinalIgnoreCase)) { var options = EnrollOptions.Parse(args[1..]); await EnrollmentClient.EnrollAsync(options, store, CancellationToken.None); Console.WriteLine("[OK] Node registration saved."); return 0; } if (!string.Equals(args[0], "run", StringComparison.OrdinalIgnoreCase) || !args.Skip(1).Contains("--headless", StringComparer.OrdinalIgnoreCase)) { throw new NodeConfigurationException( "Usage: Zcbot.WindowsNode enroll ... | run --headless"); } using var shutdown = new CancellationTokenSource(); Console.CancelKeyPress += (_, eventArgs) => { eventArgs.Cancel = true; shutdown.Cancel(); }; var config = store.Load(); await new NodeConnectionLoop(config).RunAsync(shutdown.Token); return 0; } catch (OperationCanceledException) { return 0; } catch (NodeConfigurationException exception) { Console.Error.WriteLine($"[ERR] {exception.Message}"); return 2; } catch (Exception exception) { Console.Error.WriteLine($"[ERR] {exception.GetType().Name}: {exception.Message}"); return 1; } } } internal static class NativeConsole { private const uint AttachParentProcess = 0xFFFFFFFF; [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool AttachConsole(uint processId); internal static void AttachToParent() { if (!AttachConsole(AttachParentProcess)) { return; } Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), new UTF8Encoding(false)) { AutoFlush = true, }); Console.SetError(new StreamWriter(Console.OpenStandardError(), new UTF8Encoding(false)) { AutoFlush = true, }); } }