fix(startup): pin the WebView2 profile, and retry without the GPU after a hang
His log reaches 'entering wails.Run' and stops: WebView2 is installed (120.0.2210.91) and never hands control back. A hang, not a failure — there is no error to report, so the two remaining causes have to be addressed rather than diagnosed. The profile folder is now named explicitly, on the LOCAL disk. Wails defaults it into the ROAMING profile, which on a managed account can be redirected to a share; a WebView2 profile on a share that is slow or gone does not fail, it hangs. Naming it also gives someone a folder they can be told to delete, which is the fix when the profile itself is corrupt. And a marker is written before the window is attempted, removed when it paints. Finding it at the next launch means the last one never got there, so that launch runs with GPU acceleration off: a WebView2 that cannot get on with the graphics driver hangs exactly like one that cannot start, and this is the single lever that separates them — at no cost on machines where it was never the problem.
This commit is contained in:
+51
@@ -109,3 +109,54 @@ const startupStuckMessage = "OpsLog started but its window never opened.\n\n" +
|
||||
"• The Microsoft Edge WebView2 Runtime is missing — install it from Microsoft, then start OpsLog again.\n" +
|
||||
"• An antivirus is blocking msedgewebview2.exe — add OpsLog's folder to its exceptions.\n\n" +
|
||||
"The details are in the OpsLog folder under LOCALAPPDATA (startup.log)."
|
||||
|
||||
// webviewDataPath is where WebView2 keeps its profile.
|
||||
//
|
||||
// Named rather than left to the default, which lands in the ROAMING profile: on
|
||||
// a managed account that folder can be redirected to a network share, and a
|
||||
// WebView2 profile on a share that is slow or unreachable does not fail — it
|
||||
// hangs, which is indistinguishable from a runtime that will not start.
|
||||
//
|
||||
// It also gives the folder a name someone can be told to delete: a corrupt
|
||||
// profile is a real cause of this, and "delete this folder and try again" is
|
||||
// only advice if the folder can be pointed at.
|
||||
func webviewDataPath() string {
|
||||
dir := os.Getenv("LOCALAPPDATA")
|
||||
if strings.TrimSpace(dir) == "" {
|
||||
return "" // let Wails decide; there is nothing better to offer
|
||||
}
|
||||
p := filepath.Join(dir, "OpsLog", "WebView2")
|
||||
if err := os.MkdirAll(p, 0o755); err != nil {
|
||||
bootLog("WebView2 profile folder %s could not be created: %v — leaving it to Wails", p, err)
|
||||
return ""
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// stuckMarkerPath is written before the window is attempted and removed once it
|
||||
// opens, so the NEXT launch can tell that the last one never got there.
|
||||
func stuckMarkerPath() string {
|
||||
dir := os.Getenv("LOCALAPPDATA")
|
||||
if strings.TrimSpace(dir) == "" {
|
||||
dir = os.TempDir()
|
||||
}
|
||||
return filepath.Join(dir, "OpsLog", ".launching")
|
||||
}
|
||||
|
||||
// lastLaunchHung is set at startup from the marker left by the previous run.
|
||||
var lastLaunchHung bool
|
||||
|
||||
// noteLaunchAttempt records that a window is about to be attempted, and reports
|
||||
// whether the previous attempt ever finished.
|
||||
func noteLaunchAttempt() {
|
||||
p := stuckMarkerPath()
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
lastLaunchHung = true
|
||||
bootLog("the previous launch never opened its window — trying again with GPU acceleration off")
|
||||
}
|
||||
_ = os.MkdirAll(filepath.Dir(p), 0o755)
|
||||
_ = os.WriteFile(p, []byte(time.Now().Format(time.RFC3339)), 0o644)
|
||||
}
|
||||
|
||||
// clearLaunchMarker is called once the window is up: the attempt finished.
|
||||
func clearLaunchMarker() { _ = os.Remove(stuckMarkerPath()) }
|
||||
|
||||
Reference in New Issue
Block a user