ReviewOS

also looking at this

craft-native/craft

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

#342
Closed glennmichael123 wants to merge fix/windows-com-init into main
1 file +39 -0
packages/zig/src/windows.zigmodified+39-0
Changes to packages/zig/src/windows.zig
@@ -109,6 +109,9 @@ pub const EventRegistrationToken = extern struct {
109109
110110// Constants
111111pub const S_OK: HRESULT = 0;
112/// Another apartment model was already chosen on this thread.
113pub const RPC_E_CHANGED_MODE: HRESULT = @as(i32, @bitCast(@as(u32, 0x8001_0106)));
114pub const COINIT_APARTMENTTHREADED: DWORD = 0x2;
112115pub const WS_OVERLAPPEDWINDOW: DWORD = 0x00CF0000;
113116pub const WS_VISIBLE: DWORD = 0x10000000;
114117pub const WS_POPUP: DWORD = 0x80000000;
@@ -194,6 +197,7 @@ pub extern "user32" fn GetMonitorInfoW(hMonitor: *anyopaque, lpmi: *MONITORINFO)
194197pub extern "kernel32" fn GetModuleHandleW(lpModuleName: ?LPCWSTR) callconv(.c) ?HINSTANCE;
195198pub extern "kernel32" fn Sleep(dwMilliseconds: DWORD) callconv(.c) void;
196199pub extern "ole32" fn CoTaskMemFree(pv: ?*anyopaque) callconv(.c) void;
200pub extern "ole32" fn CoInitializeEx(pvReserved: ?*anyopaque, dwCoInit: DWORD) callconv(.c) HRESULT;
197201
198202// ============================================================================
199203// WebView2 COM vtable interfaces
@@ -1072,6 +1076,36 @@ const CreateCoreWebView2EnvironmentWithOptionsFn = *const fn (
10721076
10731077var webview2_create_fn: ?CreateCoreWebView2EnvironmentWithOptionsFn = null;
10741078
1079/// Whether the call below has already put this thread in an apartment.
1080threadlocal var com_initialized = false;
1081
1082/// Put the calling thread in a COM apartment, which WebView2 requires.
1083///
1084/// `CreateCoreWebView2EnvironmentWithOptions` fails with `CO_E_NOTINITIALIZED`
1085/// (0x800401f0) when the calling thread has no apartment, and nothing in craft
1086/// used to create one: `ole32` was linked for `CoTaskMemFree` alone. So on
1087/// Windows the environment never came up, and the process exited before a
1088/// window existed.
1089///
1090/// An STA, because WebView2 hosts UI on the thread that pumps messages and
1091/// that is the apartment Edge expects. Two non-`S_OK` results are still fine:
1092/// `S_FALSE` means the thread already had an apartment, and
1093/// `RPC_E_CHANGED_MODE` means an embedder got here first and chose an MTA,
1094/// which is theirs to choose and which WebView2 can still work in.
1095///
1096/// There is deliberately no matching `CoUninitialize`. The apartment has to
1097/// outlive every webview created on the thread, and the only point at which
1098/// that is true is process exit, which tears it down regardless.
1099fn ensureComInitialized() void {
1100 if (com_initialized) return;
1101 const hr = CoInitializeEx(null, COINIT_APARTMENTTHREADED);
1102 if (!succeeded(hr) and hr != RPC_E_CHANGED_MODE) {
1103 std.debug.print("[WebView2] CoInitializeEx failed: 0x{x}\n", .{@as(u32, @bitCast(hr))});
1104 return;
1105 }
1106 com_initialized = true;
1107}
1108
10751109/// Dynamically load WebView2Loader.dll and resolve CreateCoreWebView2EnvironmentWithOptions.
10761110/// Returns null if WebView2 runtime is not installed.
10771111fn loadWebView2() ?CreateCoreWebView2EnvironmentWithOptionsFn {
@@ -1762,6 +1796,11 @@ pub const Window = struct {
17621796 y: i32,
17631797
17641798 pub fn create(options: .WindowOptions) !Window {
1799 // Before anything asks WebView2 for an environment. See
1800 // ensureComInitialized: without an apartment on this thread, creation
1801 // fails with CO_E_NOTINITIALIZED and no window is ever created.
1802 ensureComInitialized();
1803
17651804 const hInstance = GetModuleHandleW(null) orelse return error.WindowCreationFailed;
17661805
17671806 // Register window class if not already done