using System.Reflection; using System.Text.Json; using Microsoft.Win32; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; namespace MyStart; static class Program { [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); bool minimized = args.Any(a => a.Equals("--minimized", StringComparison.OrdinalIgnoreCase) || a.Equals("--min", StringComparison.OrdinalIgnoreCase) || a.Equals("--autostart", StringComparison.OrdinalIgnoreCase)); Application.Run(new MainForm(minimized)); } } class MainForm : Form { private readonly WebView2 _web = new() { Dock = DockStyle.Fill }; private const string Host = "mystart.local"; private bool _pendingMaximize; public MainForm(bool startMinimized = false) { Text = "MyStart"; StartPosition = FormStartPosition.CenterScreen; Width = 1280; Height = 800; ShowInTaskbar = true; BackColor = System.Drawing.Color.Black; // No title bar / border. Close with Alt+F4, minimize via the taskbar button. FormBorderStyle = FormBorderStyle.None; if (startMinimized) { // Autostart: sit minimized on the taskbar; maximize on first open. WindowState = FormWindowState.Minimized; _pendingMaximize = true; Resize += (_, _) => { if (_pendingMaximize && WindowState != FormWindowState.Minimized) { _pendingMaximize = false; WindowState = FormWindowState.Maximized; } }; } else { WindowState = FormWindowState.Maximized; } try { var asm = Assembly.GetExecutingAssembly(); var icoName = asm.GetManifestResourceNames() .FirstOrDefault(n => n.EndsWith("app.ico", StringComparison.OrdinalIgnoreCase)); if (icoName != null) { using var s = asm.GetManifestResourceStream(icoName); if (s != null) Icon = new Icon(s); } } catch { /* icon optional */ } Controls.Add(_web); Shown += async (_, _) => await InitAsync(); } // Keep a borderless maximized window from covering the taskbar. protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); MaximizedBounds = Screen.FromHandle(Handle).WorkingArea; } private async Task InitAsync() { // Persistent data dir -> localStorage (settings/bookmarks) survive across runs // and independent of where the .exe lives. string dataDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyStart"); Directory.CreateDirectory(dataDir); var env = await CoreWebView2Environment.CreateAsync(null, dataDir); await _web.EnsureCoreWebView2Async(env); // Extract the embedded app to a fixed folder, served via a stable origin. string appDir = Path.Combine(dataDir, "app"); Directory.CreateDirectory(appDir); string htmlPath = Path.Combine(appDir, "index.html"); var asm = Assembly.GetExecutingAssembly(); string resName = asm.GetManifestResourceNames() .First(n => n.EndsWith("app-index.html", StringComparison.OrdinalIgnoreCase)); await using (var res = asm.GetManifestResourceStream(resName)!) await using (var file = File.Create(htmlPath)) await res.CopyToAsync(file); var core = _web.CoreWebView2; core.SetVirtualHostNameToFolderMapping(Host, appDir, CoreWebView2HostResourceAccessKind.Allow); core.Settings.IsStatusBarEnabled = false; core.Settings.AreDefaultContextMenusEnabled = true; core.Settings.IsWebMessageEnabled = true; // Autostart toggle from the in-app "MyStart" info page. core.WebMessageReceived += (_, e) => { try { using var doc = JsonDocument.Parse(e.WebMessageAsJson); var root = doc.RootElement; if (!root.TryGetProperty("type", out var t)) return; switch (t.GetString()) { case "autostart-get": PostAutostartState(); break; case "autostart-set": bool on = root.TryGetProperty("value", out var v) && v.ValueKind == JsonValueKind.True; SetAutostart(on); PostAutostartState(); break; case "window-drag": StartWindowDrag(); break; case "window-min": WindowState = FormWindowState.Minimized; break; case "window-max": WindowState = WindowState == FormWindowState.Maximized ? FormWindowState.Normal : FormWindowState.Maximized; break; case "window-close": Close(); break; } } catch { /* ignore malformed messages */ } }; // Bookmark links that open a new window -> user's default browser. core.NewWindowRequested += (_, e) => { e.Handled = true; OpenExternal(e.Uri); }; // Same-tab clicks to external sites -> default browser, keep the app loaded. core.NavigationStarting += (_, e) => { var uri = e.Uri ?? string.Empty; if (uri.StartsWith("https://" + Host, StringComparison.OrdinalIgnoreCase)) return; // internal if (uri.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || uri.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { e.Cancel = true; OpenExternal(uri); } }; core.Navigate("https://" + Host + "/index.html"); } // --- Autostart (per-user HKCU Run key, points at THIS exe with --minimized) --- private const string RunKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Run"; private const string RunValueName = "MyStart"; private static string ExePath => Environment.ProcessPath ?? Application.ExecutablePath; private static bool IsAutostartEnabled() { try { using var k = Registry.CurrentUser.OpenSubKey(RunKeyPath, false); return k?.GetValue(RunValueName) is string v && v.Length > 0; } catch { return false; } } private static void SetAutostart(bool on) { try { using var k = Registry.CurrentUser.OpenSubKey(RunKeyPath, true) ?? Registry.CurrentUser.CreateSubKey(RunKeyPath, true); if (k == null) return; if (on) k.SetValue(RunValueName, "\"" + ExePath + "\" --minimized"); else if (k.GetValue(RunValueName) != null) k.DeleteValue(RunValueName, false); } catch { /* ignore */ } } private void PostAutostartState() { var json = "{\"type\":\"autostart\",\"enabled\":" + (IsAutostartEnabled() ? "true" : "false") + "}"; try { _web.CoreWebView2.PostWebMessageAsJson(json); } catch { } } // --- Free window drag for the borderless window (page sends "window-drag" // on grip mousedown / Alt+drag; we hand off to the native move loop). --- [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern bool ReleaseCapture(); [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); private const int WM_NCLBUTTONDOWN = 0xA1; private const int HTCAPTION = 0x2; private void StartWindowDrag() { // A maximized window can't be moved -> restore first, centred under cursor-ish. if (WindowState == FormWindowState.Maximized) WindowState = FormWindowState.Normal; ReleaseCapture(); SendMessage(Handle, WM_NCLBUTTONDOWN, (IntPtr)HTCAPTION, IntPtr.Zero); } private static void OpenExternal(string uri) { try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = uri, UseShellExecute = true }); } catch { /* ignore */ } } }