also looking at this
fix(windows): initialise COM before creating the WebView2 environment
#342
1 file
+39
-0
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -109,6 +109,9 @@ pub const EventRegistrationToken = extern struct { | ||
| 109 | 109 | |
| 110 | 110 | // Constants |
| 111 | 111 | pub const S_OK: HRESULT = 0; |
| 112 | /// Another apartment model was already chosen on this thread. | |
| 113 | pub const RPC_E_CHANGED_MODE: HRESULT = @as(i32, @bitCast(@as(u32, 0x8001_0106))); | |
| 114 | pub const COINIT_APARTMENTTHREADED: DWORD = 0x2; | |
| 112 | 115 | pub const WS_OVERLAPPEDWINDOW: DWORD = 0x00CF0000; |
| 113 | 116 | pub const WS_VISIBLE: DWORD = 0x10000000; |
| 114 | 117 | pub const WS_POPUP: DWORD = 0x80000000; |
| @@ -194,6 +197,7 @@ pub extern "user32" fn GetMonitorInfoW(hMonitor: *anyopaque, lpmi: *MONITORINFO) | ||
| 194 | 197 | pub extern "kernel32" fn GetModuleHandleW(lpModuleName: ?LPCWSTR) callconv(.c) ?HINSTANCE; |
| 195 | 198 | pub extern "kernel32" fn Sleep(dwMilliseconds: DWORD) callconv(.c) void; |
| 196 | 199 | pub extern "ole32" fn CoTaskMemFree(pv: ?*anyopaque) callconv(.c) void; |
| 200 | pub extern "ole32" fn CoInitializeEx(pvReserved: ?*anyopaque, dwCoInit: DWORD) callconv(.c) HRESULT; | |
| 197 | 201 | |
| 198 | 202 | // ============================================================================ |
| 199 | 203 | // WebView2 COM vtable interfaces |
| @@ -1072,6 +1076,36 @@ const CreateCoreWebView2EnvironmentWithOptionsFn = *const fn ( | ||
| 1072 | 1076 | |
| 1073 | 1077 | var webview2_create_fn: ?CreateCoreWebView2EnvironmentWithOptionsFn = null; |
| 1074 | 1078 | |
| 1079 | /// Whether the call below has already put this thread in an apartment. | |
| 1080 | threadlocal 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. | |
| 1099 | fn 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 | ||
| 1075 | 1109 | /// Dynamically load WebView2Loader.dll and resolve CreateCoreWebView2EnvironmentWithOptions. |
| 1076 | 1110 | /// Returns null if WebView2 runtime is not installed. |
| 1077 | 1111 | fn loadWebView2() ?CreateCoreWebView2EnvironmentWithOptionsFn { |
| @@ -1762,6 +1796,11 @@ pub const Window = struct { | ||
| 1762 | 1796 | y: i32, |
| 1763 | 1797 | |
| 1764 | 1798 | 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 | ||
| 1765 | 1804 | const hInstance = GetModuleHandleW(null) orelse return error.WindowCreationFailed; |
| 1766 | 1805 | |
| 1767 | 1806 | // Register window class if not already done |