From 14bb92fac028225b05e2b0b9e6dbd7f387ce0f45 Mon Sep 17 00:00:00 2001 From: rouggy Date: Tue, 25 Aug 2026 21:19:37 +0200 Subject: [PATCH] feat(startup): name an Edge blocker instead of hanging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Confirmed on the machine that started this: an 'Edge blocker' utility was in the way, and unblocking Edge fixed the launch. Those tools work by registering a Debugger entry under Image File Execution Options for msedge.exe and msedgewebview2.exe, so Windows refuses to run the process. The runtime is installed and registers its version quite happily — which is why the logs showed WebView2 present — and then never starts. OpsLog draws its whole interface with that engine, so the window never opened: no error, no crash, a process sitting there doing nothing. That entry is read at startup now and said plainly, in the log and on screen, with the one thing that helps: unblock Edge in the tool that blocked it. Reinstalling OpsLog cannot fix a machine in this state, and that is where an unexplained silence sends people. Also logs the Windows build and which Edge policies are set — value names only, never their contents: a policy key holds URLs and account names that are nobody's business here. --- bootlog.go | 11 ++++++++ changelog.json | 6 ++-- main.go | 8 ++++++ webview2_other.go | 6 ++++ webview2_windows.go | 67 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 2 deletions(-) diff --git a/bootlog.go b/bootlog.go index 6c06182..c612359 100644 --- a/bootlog.go +++ b/bootlog.go @@ -213,3 +213,14 @@ func findWebView2Binary(dir string) string { } return "" } + +// edgeBlockedMessage is shown when an Edge blocker is found in the way. +// +// It names the tool rather than the symptom: OpsLog draws its whole interface +// with the WebView2 engine, which is Edge's, so a machine where Edge is blocked +// cannot run it — and no amount of reinstalling OpsLog will change that. +const edgeBlockedMessage = "OpsLog cannot start because Edge is blocked on this PC.\n\n" + + "A blocker tool (or a policy) is stopping msedgewebview2.exe from running. " + + "OpsLog draws its whole interface with that engine, so it cannot open its window.\n\n" + + "Unblock Edge — in the tool that blocked it — and start OpsLog again. " + + "The details are in the OpsLog folder under LOCALAPPDATA (startup.log)." diff --git a/changelog.json b/changelog.json index f9511f7..2054451 100644 --- a/changelog.json +++ b/changelog.json @@ -19,7 +19,8 @@ "Dropdown lists open over whatever is beneath them instead of being cut off by it. The satellite list showed one entry of eight, clipped by the panel it sat in.", "A launch that fails before the window exists now says why. OpsLog keeps its log in the data folder beside the program, so anything that went wrong BEFORE that folder existed left nothing to read anywhere — the process appeared and vanished in silence. Startup milestones are written to %LOCALAPPDATA%OpsLogstartup.log, and the three silent exits (already running, the data folder cannot be written — Program Files refuses it — and the window itself failing to open, usually a missing WebView2) now show a message saying which one it was. The log also names the installed WebView2 runtime — the one dependency OpsLog cannot carry inside itself, and whose absence is otherwise invisible — and marks each milestone up to the window painting, so a launch that produces nothing can be placed exactly.", "WebView2 is given an explicit profile folder on the local disk (LOCALAPPDATAOpsLogWebView2) instead of the roaming default, which on a redirected or managed account can hang rather than fail. And when a launch never opens its window, the next one retries with GPU acceleration off and says so — a WebView2 that cannot get on with the graphics driver hangs exactly like one that cannot start at all, and that is the one lever telling them apart.", - "OpsLog can now use a FIXED-VERSION WebView2 runtime carried beside it: drop the folder Microsoft publishes next to OpsLog.exe and it is used, with nothing to install and no administrator. It is the way in for a machine where the Evergreen installer itself will not run — a locked-down, offline or managed Windows — where until now there was none. The startup log also records the Windows build, since 'WebView2 will not install either' is a statement about the machine rather than about OpsLog." + "OpsLog can now use a FIXED-VERSION WebView2 runtime carried beside it: drop the folder Microsoft publishes next to OpsLog.exe and it is used, with nothing to install and no administrator. It is the way in for a machine where the Evergreen installer itself will not run — a locked-down, offline or managed Windows — where until now there was none. The startup log also records the Windows build, since 'WebView2 will not install either' is a statement about the machine rather than about OpsLog.", + "An 'Edge blocker' in the way is now named at startup. Those tools stop msedgewebview2.exe from running, and since OpsLog draws its whole interface with that engine, the window simply never opened — no error, no crash, a process sitting there doing nothing. It is detected and said plainly, with what to do about it." ], "fr": [ "Console Elecraft : les mesures d'émission sont lues dès que la RADIO se déclare en émission, et plus seulement quand OpsLog l'a mise en émission. Passer en émission par le PTT de façade, une pédale ou le bouton du micro laissait le panneau croire à une réception — et comme les barres de puissance et de ROS ne sont lues qu'en émission, elles ne l'étaient jamais pour qui manipule à la main.", @@ -38,7 +39,8 @@ "Les listes déroulantes s'ouvrent par-dessus ce qui les entoure au lieu d'être coupées par lui. Celle des satellites n'affichait qu'une entrée sur huit, tronquée par le panneau qui la contenait.", "Un lancement qui échoue avant l'apparition de la fenêtre dit maintenant pourquoi. OpsLog tient son journal dans le dossier data, à côté du programme : tout ce qui échouait AVANT la création de ce dossier ne laissait donc aucune trace nulle part — le processus apparaissait et disparaissait en silence. Les étapes du démarrage sont écrites dans %LOCALAPPDATA%OpsLogstartup.log, et les trois sorties silencieuses (déjà lancé, dossier data impossible à écrire — Program Files le refuse — et fenêtre qui ne s'ouvre pas, généralement un WebView2 manquant) affichent désormais un message disant laquelle c'était. Le journal indique aussi la version du runtime WebView2 installé — la seule dépendance qu'OpsLog ne peut pas embarquer, et dont l'absence est autrement invisible — et marque chaque étape jusqu'à l'affichage de la fenêtre, si bien qu'un lancement sans résultat peut être situé précisément.", "WebView2 reçoit un dossier de profil explicite sur le disque local (LOCALAPPDATAOpsLogWebView2) au lieu du dossier itinérant par défaut, qui sur un compte redirigé ou administré peut bloquer au lieu d'échouer. Et lorsqu'un lancement n'ouvre jamais sa fenêtre, le suivant réessaie sans accélération GPU et le dit — un WebView2 qui ne s'entend pas avec le pilote graphique se bloque exactement comme un WebView2 qui ne démarre pas, et c'est le seul levier qui les distingue.", - "OpsLog peut désormais utiliser un runtime WebView2 en VERSION FIXE transporté à côté de lui : il suffit de déposer le dossier publié par Microsoft à côté d'OpsLog.exe pour qu'il soit utilisé, sans rien installer et sans droits administrateur. C'est la porte d'entrée pour une machine où l'installeur Evergreen lui-même refuse de s'exécuter — Windows verrouillé, hors ligne ou administré — là où il n'y en avait aucune. Le journal de démarrage note aussi la version de Windows, « WebView2 ne s'installe pas non plus » étant une information sur la machine et non sur OpsLog." + "OpsLog peut désormais utiliser un runtime WebView2 en VERSION FIXE transporté à côté de lui : il suffit de déposer le dossier publié par Microsoft à côté d'OpsLog.exe pour qu'il soit utilisé, sans rien installer et sans droits administrateur. C'est la porte d'entrée pour une machine où l'installeur Evergreen lui-même refuse de s'exécuter — Windows verrouillé, hors ligne ou administré — là où il n'y en avait aucune. Le journal de démarrage note aussi la version de Windows, « WebView2 ne s'installe pas non plus » étant une information sur la machine et non sur OpsLog.", + "Un « bloqueur Edge » qui gêne est désormais nommé au démarrage. Ces outils empêchent msedgewebview2.exe de s'exécuter et, comme OpsLog dessine toute son interface avec ce moteur, la fenêtre ne s'ouvrait tout simplement jamais — sans erreur, sans plantage, un processus qui reste là sans rien faire. C'est détecté et dit clairement, avec la marche à suivre." ] }, { diff --git a/main.go b/main.go index e7e4b7b..f3081b1 100644 --- a/main.go +++ b/main.go @@ -165,6 +165,14 @@ func main() { }() noteLaunchAttempt() bootLog("Windows %s", windowsVersion()) + bootLog("Edge policy: %s", edgePolicyNotes()) + // The blocker utilities: they leave an IFEO entry that makes Windows refuse + // to run the WebView2 process at all. Named loudly, because a machine in + // this state looks identical to one with a broken graphics driver. + if blocked := edgeExecutionBlocked(); blocked != "" { + bootLog("EDGE EXECUTION IS BLOCKED: %s — an \"Edge blocker\" tool is stopping WebView2 from starting", blocked) + fatalBox("OpsLog", edgeBlockedMessage) + } bootLog("WebView2 profile: %q", webviewDataPath()) bootLog("entering wails.Run (window %dx%d, state %v)", width, height, startState) err := wails.Run(&options.App{ diff --git a/webview2_other.go b/webview2_other.go index 0ffff1a..3da78f5 100644 --- a/webview2_other.go +++ b/webview2_other.go @@ -8,3 +8,9 @@ func webView2Version() string { return "n/a" } // windowsVersion is a Windows question too. func windowsVersion() string { return "n/a" } + +// edgePolicyNotes is a Windows question too. +func edgePolicyNotes() string { return "n/a" } + +// edgeExecutionBlocked is a Windows question too. +func edgeExecutionBlocked() string { return "" } diff --git a/webview2_windows.go b/webview2_windows.go index 517a49b..e3bae8d 100644 --- a/webview2_windows.go +++ b/webview2_windows.go @@ -14,6 +14,7 @@ package main import ( "fmt" + "strings" "golang.org/x/sys/windows/registry" ) @@ -68,3 +69,69 @@ func windowsVersion() string { display, _, _ := k.GetStringValue("DisplayVersion") return fmt.Sprintf("%s %s build %s.%d", name, display, build, ubr) } + +// edgePolicyNotes reports the administrative policies applied to Edge, which is +// the engine WebView2 runs on. +// +// "Edge is blocked on this PC" is a sentence about group policy, and policy is +// readable: rather than have an operator guess which rule is in the way, the +// names of the values under the Edge policy key go into the startup log. It is +// evidence for whoever administers that machine, in the one file we can be sure +// they will be sent. +// +// Value NAMES only, never their contents: a policy key can hold URLs, account +// names and site lists that are nobody's business here. +func edgePolicyNotes() string { + var found []string + for _, hive := range []registry.Key{registry.LOCAL_MACHINE, registry.CURRENT_USER} { + k, err := registry.OpenKey(hive, `SOFTWARE\Policies\Microsoft\Edge`, registry.QUERY_VALUE|registry.ENUMERATE_SUB_KEYS) + if err != nil { + continue + } + names, _ := k.ReadValueNames(0) + subs, _ := k.ReadSubKeyNames(0) + k.Close() + where := "HKLM" + if hive == registry.CURRENT_USER { + where = "HKCU" + } + if len(names) > 0 || len(subs) > 0 { + found = append(found, fmt.Sprintf("%s: %d policy values, %d sub-keys %v", where, len(names), len(subs), names)) + } + } + if len(found) == 0 { + return "no Edge policies set" + } + return strings.Join(found, " | ") +} + +// edgeExecutionBlocked reports an Image File Execution Options entry that stops +// the WebView2 process from running. +// +// This is what the "Edge blocker" utilities do: they register a Debugger value +// under IFEO for msedge.exe and msedgewebview2.exe, which makes Windows launch +// something else — usually nothing — in its place. The WebView2 runtime is +// installed, it registers its version, and it never starts, which is exactly +// the hang OpsLog was showing. Confirmed on a real machine: an Edge blocker was +// in the way, and unblocking it fixed the launch. +// +// One line in the startup log instead of an afternoon. +func edgeExecutionBlocked() string { + const ifeo = `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\` + var hits []string + for _, exe := range []string{"msedge.exe", "msedgewebview2.exe"} { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, ifeo+exe, registry.QUERY_VALUE) + if err != nil { + continue + } + dbg, _, err := k.GetStringValue("Debugger") + k.Close() + if err == nil && strings.TrimSpace(dbg) != "" { + hits = append(hits, fmt.Sprintf("%s → Debugger=%q", exe, dbg)) + } + } + if len(hits) == 0 { + return "" + } + return strings.Join(hits, " | ") +}