ReviewOS

also looking at this

craft-native/craft

fix(windows): initialise COM before creating the WebView2 environment

#348
Closed glennmichael123 wants to merge fix/windows-com-init into main
1 file +99 -0
9 days ago

Reopens the change from #342 on a branch in this repository. That PR closed itself when the fork it came from was deleted — nothing to do with its contents, and its comments are still the record of how this was measured.

What is wrong

On Windows, CreateCoreWebView2EnvironmentWithOptions returns CO_E_NOTINITIALIZED (0x800401f0) when the calling thread is in no COM apartment, and craft never puts it in one: ole32 is linked for CoTaskMemFree alone, with no CoInitializeEx, CoInitialize or OleInitialize anywhere in the tree. So craft depends on something else in the process having created an apartment.

That dependency is satisfied when craft is spawned with a console, which is why windows-multi-window passes. It is not satisfied under CREATE_NO_WINDOW, which is what an embedder passes so Windows does not put a console window beside the app — and then no window opens at all.

The change

Window.create calls CoInitializeEx(null, COINIT_APARTMENTTHREADED) before anything touches WebView2. It is the single funnel every window goes through, and a thread-local guard keeps later windows from repeating it. S_FALSE (an apartment already existed) and RPC_E_CHANGED_MODE (an embedder chose an MTA first) are both accepted rather than treated as failures. There is no matching CoUninitialize: the apartment has to outlive every webview on the thread, and the only point at which that holds is process exit. No new link dependency — build.zig already links ole32.

Measured, not argued

Three rounds on windows-latest, WebView2 runtime 153.0.4234.48, against the shipped craft-windows-x64.zip and then against builds from this source.

The invocation is innocent. Six argument lists, each with a page that fetches a URL when it loads, so a hit proves the environment was created. All six produced a webview on v0.0.100 and v0.0.142 alike — including --persistent-storage and a positional URL.

CREATE_NO_WINDOW is the whole difference. Same binary, arguments held fixed, varying only the spawn:

spawnresult
piped stdio, no windowsHideloaded in 490ms
piped stdio + windowsHideno webview — 0x800401f0
inherit stdio + windowsHideno webview — 0x800401f0
inherit stdio, no windowsHideloaded in 467ms
GUI-subsystem parent + windowsHideno webview — 0x800401f0

stdio makes no difference, the parent's subsystem makes no difference, and renaming the binary reproduces the table exactly.

This patch clears it. Two builds on one runner from commits one apart — this change and its parent — same Zig, same -Doptimize=ReleaseSafe -Dcpu=baseline, the pinned zig-js/zig-regex/zig-gc revisions, and the WebView2Loader.dll from the release archive. git diff --stat between them is 1 file changed, 39 insertions(+); the parent's craft.exe does not reference CoInitializeEx and this one does.

spawnparent committhis branch
piped stdio, no windowsHideloaded in 2184msloaded in 812ms
piped stdio + windowsHideno webview — 0x800401f0loaded in 1163ms
inherit stdio + windowsHideno webview — exit 1loaded in 654ms
inherit stdio, no windowsHideloaded in 514msloaded in 466ms
GUI-subsystem parent + windowsHideno webview — 0x800401f0loaded in 519ms

Every CREATE_NO_WINDOW failure becomes a load, the three spawns that already worked still work and got no slower, and the last row is the real case: a GUI-subsystem launcher spawning craft with inherited stdio and CREATE_NO_WINDOW, which is what stacksjs/system-cleaner's desktop bundle does and what reported WebView2InitFailed every time.

Harness and runs: spawn probe, workflow, A/B run. The first two are permalinks into a deleted experiment branch; the spawn probe now lives on main as a CI regression guard.

One thing worth fixing separately

The Zig this repository's Windows CI pins cannot be installed any more:

HTTP 404 downloading https://ziglang.org/builds/zig-x86_64-windows-0.17.0-dev.1963+e00c6c439.zip

ziglang.org keeps only the current master build, and pantry's registry mirrors no windows-x86-64 dev build to outlive that pruning. windows-multi-window is presumably green on a cached toolchain; a fresh runner would hit this. The A/B above built with tagged 0.17.0 from ziglang.org, which compiled craft without changes and satisfies minimum_zig_version.

🤖 Generated with Claude Code

1 changed file on the files tab, with 0 review threads.

Sign in to comment on this pull request.