// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie // SPDX-License-Identifier: MIT //! Opening a URI with `ShellExecuteExW`, the shell's own "open" verb: //! https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecuteexw //! //! `SEE_MASK_NOASYNC` makes the call finish its work before returning, which //! a program about to exit needs, and `SEE_MASK_FLAG_NO_UI` keeps the shell //! from showing its own error dialogs, so that failures come back as errors. //! Some protocol handlers need COM initialized on the calling thread; the //! library leaves that to the application. const std = @import("std"); const Io = std.Io; const Allocator = std.mem.Allocator; const windows = std.os.windows; const root = @import("root.zig"); const Error = root.Error; const Options = root.Options; const Outcome = root.Outcome; const SEE_MASK_NOASYNC: windows.ULONG = 0x00000100; const SEE_MASK_FLAG_NO_UI: windows.ULONG = 0x00000400; const SW_SHOWNORMAL: windows.INT = 1; const SHELLEXECUTEINFOW = extern struct { cbSize: windows.DWORD = @sizeOf(SHELLEXECUTEINFOW), fMask: windows.ULONG = 0, hwnd: ?windows.HWND = null, lpVerb: ?windows.LPCWSTR = null, lpFile: ?windows.LPCWSTR = null, lpParameters: ?windows.LPCWSTR = null, lpDirectory: ?windows.LPCWSTR = null, nShow: windows.INT = 0, hInstApp: ?windows.HINSTANCE = null, lpIDList: ?windows.LPVOID = null, lpClass: ?windows.LPCWSTR = null, hkeyClass: ?windows.HKEY = null, dwHotKey: windows.DWORD = 0, hIconOrMonitor: ?windows.HANDLE = null, hProcess: ?windows.HANDLE = null, }; extern "shell32" fn ShellExecuteExW(info: *SHELLEXECUTEINFOW) callconv(.winapi) windows.BOOL; pub fn open( io: Io, gpa: Allocator, environ: std.process.Environ, uri: []const u8, is_file: bool, options: Options, ) Error!Outcome { _ = io; _ = environ; _ = is_file; _ = options; const wide = std.unicode.utf8ToUtf16LeAllocZ(gpa, uri) catch |err| return switch (err) { error.OutOfMemory => error.OutOfMemory, error.InvalidUtf8 => error.InvalidUri, }; defer gpa.free(wide); var info: SHELLEXECUTEINFOW = .{ .fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI, .lpVerb = std.unicode.utf8ToUtf16LeStringLiteral("open"), .lpFile = wide.ptr, .nShow = SW_SHOWNORMAL, }; if (ShellExecuteExW(&info).toBool()) return .opened; return switch (windows.GetLastError()) { // The user said no to an elevation prompt the handler needed. .CANCELLED => .cancelled, .FILE_NOT_FOUND, .PATH_NOT_FOUND => error.FileNotFound, .NO_ASSOCIATION => error.NoHandler, .ACCESS_DENIED => error.AccessDenied, .NOT_ENOUGH_MEMORY, .OUTOFMEMORY => error.OutOfMemory, else => error.Unexpected, }; }