diff --git a/zig/shim.zig b/zig/shim.zig index e7d56f2..13b5697 100644 --- a/zig/shim.zig +++ b/zig/shim.zig @@ -5,27 +5,27 @@ const std = @import("std"); const windows = std.os.windows; const kernel32 = windows.kernel32; +const FILE_CREATION_DISPOSITION = enum(u32) { + CREATE_NEW = 1, + CREATE_ALWAYS = 2, + OPEN_EXISTING = 3, + OPEN_ALWAYS = 4, + TRUNCATE_EXISTING = 5, +}; + const DWORD = windows.DWORD; const BOOL = windows.BOOL; const HANDLE = windows.HANDLE; -const HMODULE = *opaque {}; -const CHAR = u8; -const WCHAR = u16; +const HMODULE = windows.HMODULE; +const CHAR = windows.CHAR; +const WCHAR = windows.WCHAR; +const INFINITE: windows.DWORD = std.math.maxInt(windows.DWORD); const INVALID_HANDLE_VALUE = windows.INVALID_HANDLE_VALUE; -const TRUE = @as(BOOL, @enumFromInt(1)); -const FALSE = @as(BOOL, @enumFromInt(0)); const STD_ERROR_HANDLE = @as(DWORD, @bitCast(@as(i32, -12))); -const GENERIC_READ = 0x80000000; -const GENERIC_WRITE = 0x40000000; -const FILE_SHARE_READ = 0x00000001; -const FILE_SHARE_WRITE = 0x00000002; -const OPEN_EXISTING = 3; - const CREATE_SUSPENDED = 0x00000004; -const STARTF_USESHOWWINDOW = 0x00000001; const SW_SHOW = 5; const IMAGE_DOS_SIGNATURE = 0x5A4D; @@ -49,33 +49,53 @@ const CTRL_LOGOFF_EVENT = 5; const CTRL_SHUTDOWN_EVENT = 6; /// Compile-time UTF-8 → WCHAR literal. -fn w(comptime s: []const u8) [:0]const WCHAR { - const result = comptime blk: { - var buf: [s.len + 1]WCHAR = undefined; - for (s, 0..) |c, i| { - buf[i] = @intCast(c); - } - buf[s.len] = 0; - break :blk buf; - }; - return result[0..s.len :0]; -} +const w = std.unicode.utf8ToUtf16LeStringLiteral; extern "kernel32" fn GetStdHandle(nStdHandle: DWORD) callconv(.winapi) ?HANDLE; -extern "kernel32" fn WriteFile(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpNumberOfBytesWritten: ?*DWORD, lpOverlapped: ?*anyopaque) callconv(.winapi) BOOL; -extern "kernel32" fn ReadFile(hFile: HANDLE, lpBuffer: [*]u8, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: ?*DWORD, lpOverlapped: ?*anyopaque) callconv(.winapi) BOOL; -extern "kernel32" fn CloseHandle(hObject: HANDLE) callconv(.winapi) BOOL; +extern "kernel32" fn WriteFile( + hFile: HANDLE, + lpBuffer: [*]const u8, + nNumberOfBytesToWrite: DWORD, + lpNumberOfBytesWritten: ?*DWORD, + lpOverlapped: ?*anyopaque, +) callconv(.winapi) BOOL; +extern "kernel32" fn ReadFile( + hFile: HANDLE, + lpBuffer: [*]u8, + nNumberOfBytesToRead: DWORD, + lpNumberOfBytesRead: ?*DWORD, + lpOverlapped: ?*anyopaque, +) callconv(.winapi) BOOL; extern "kernel32" fn GetLastError() callconv(.winapi) DWORD; extern "kernel32" fn GetFileSize(hFile: HANDLE, lpFileSizeHigh: ?*DWORD) callconv(.winapi) DWORD; -extern "kernel32" fn WriteConsoleW(hConsoleOutput: HANDLE, lpBuffer: [*]const WCHAR, nNumberOfCharsToWrite: DWORD, lpNumberOfCharsWritten: ?*DWORD, lpReserved: ?*anyopaque) callconv(.winapi) BOOL; +extern "kernel32" fn WriteConsoleW( + hConsoleOutput: HANDLE, + lpBuffer: [*]const WCHAR, + nNumberOfCharsToWrite: DWORD, + lpNumberOfCharsWritten: ?*DWORD, + lpReserved: ?*anyopaque, +) callconv(.winapi) BOOL; extern "kernel32" fn GetModuleHandleW(lpModuleName: ?[*:0]const WCHAR) callconv(.winapi) ?HMODULE; extern "kernel32" fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*:0]WCHAR, nSize: DWORD) callconv(.winapi) DWORD; -extern "kernel32" fn CreateFileW(lpFileName: [*:0]const WCHAR, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: ?*anyopaque, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) callconv(.winapi) HANDLE; +extern "kernel32" fn CreateFileW( + lpFileName: [*:0]const WCHAR, + dwDesiredAccess: windows.ACCESS_MASK, + dwShareMode: windows.FILE.SHARE, + lpSecurityAttributes: ?*anyopaque, + dwCreationDisposition: FILE_CREATION_DISPOSITION, + dwFlagsAndAttributes: DWORD, + hTemplateFile: ?HANDLE, +) callconv(.winapi) HANDLE; extern "kernel32" fn FreeConsole() callconv(.winapi) BOOL; extern "kernel32" fn AttachConsole(dwProcessId: i32) callconv(.winapi) BOOL; extern "kernel32" fn GetCommandLineW() callconv(.winapi) [*:0]const WCHAR; extern "kernel32" fn CreateJobObjectW(lpJobAttributes: ?*anyopaque, lpName: ?[*:0]const WCHAR) callconv(.winapi) ?HANDLE; -extern "kernel32" fn SetInformationJobObject(hJob: HANDLE, JobObjectInformationClass: i32, lpJobObjectInformation: *const anyopaque, cbJobObjectInformationLength: DWORD) callconv(.winapi) BOOL; +extern "kernel32" fn SetInformationJobObject( + hJob: HANDLE, + JobObjectInformationClass: i32, + lpJobObjectInformation: *const anyopaque, + cbJobObjectInformationLength: DWORD, +) callconv(.winapi) BOOL; extern "kernel32" fn AssignProcessToJobObject(hJob: HANDLE, hProcess: HANDLE) callconv(.winapi) BOOL; extern "kernel32" fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) callconv(.winapi) DWORD; extern "kernel32" fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) callconv(.winapi) BOOL; @@ -87,34 +107,6 @@ extern "kernel32" fn SetEnvironmentVariableW(lpName: [*:0]const WCHAR, lpValue: extern "shell32" fn CommandLineToArgvW(lpCmdLine: [*:0]const WCHAR, pNumArgs: *i32) callconv(.winapi) ?[*]const [*:0]const WCHAR; extern "kernel32" fn LocalFree(hMem: ?*anyopaque) callconv(.winapi) ?*anyopaque; -const STARTUPINFOW = extern struct { - cb: DWORD, - lpReserved: ?[*:0]const WCHAR, - lpDesktop: ?[*:0]const WCHAR, - lpTitle: ?[*:0]const WCHAR, - dwX: DWORD, - dwY: DWORD, - dwXSize: DWORD, - dwYSize: DWORD, - dwXCountChars: DWORD, - dwYCountChars: DWORD, - dwFillAttribute: DWORD, - dwFlags: DWORD, - wShowWindow: u16, - cbReserved2: u16, - lpReserved2: ?*u8, - hStdInput: ?HANDLE, - hStdOutput: ?HANDLE, - hStdError: ?HANDLE, -}; - -const PROCESS_INFORMATION = extern struct { - hProcess: HANDLE, - hThread: HANDLE, - dwProcessId: DWORD, - dwThreadId: DWORD, -}; - const SHELLEXECUTEINFOW = extern struct { cbSize: DWORD, fMask: DWORD, @@ -167,7 +159,7 @@ const HandlerRoutine = *const fn (DWORD) callconv(.winapi) BOOL; extern "kernel32" fn GetFullPathNameW(lpFileName: [*:0]const WCHAR, nBufferLength: DWORD, lpBuffer: [*:0]WCHAR, lpFilePart: ?*?*WCHAR) callconv(.winapi) DWORD; extern "kernel32" fn ExpandEnvironmentStringsW(lpSrc: [*:0]const WCHAR, lpDst: ?[*]WCHAR, nSize: DWORD) callconv(.winapi) DWORD; extern "kernel32" fn SetConsoleCtrlHandler(HandlerRoutine: HandlerRoutine, Add: BOOL) callconv(.winapi) BOOL; -extern "kernel32" fn GetStartupInfoW(lpStartupInfo: *STARTUPINFOW) callconv(.winapi) void; +extern "kernel32" fn GetStartupInfoW(lpStartupInfo: *windows.STARTUPINFOW) callconv(.winapi) void; extern "kernel32" fn CreateProcessW( lpApplicationName: ?[*:0]const WCHAR, @@ -178,8 +170,8 @@ extern "kernel32" fn CreateProcessW( dwCreationFlags: DWORD, lpEnvironment: ?*anyopaque, lpCurrentDirectory: ?[*:0]const WCHAR, - lpStartupInfo: *STARTUPINFOW, - lpProcessInformation: *PROCESS_INFORMATION, + lpStartupInfo: *windows.STARTUPINFOW, + lpProcessInformation: *windows.PROCESS.INFORMATION, ) callconv(.winapi) BOOL; /// Write byte message to stderr. @@ -197,17 +189,17 @@ fn writeErrorW(msg: []const WCHAR) void { } /// Open CONIN$/CONOUT$ to ensure stdin/stdout/stderr are valid handles. -fn ensureStandardHandles(si: *STARTUPINFOW) void { +fn ensureStandardHandles(si: *windows.STARTUPINFOW) void { if (si.hStdInput == null or si.hStdInput == INVALID_HANDLE_VALUE) { - const h = CreateFileW(w("CONIN$"), GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, 0, null); + const h = CreateFileW(w("CONIN$"), .{ .GENERIC = .{ .READ = true } }, .{ .READ = true }, null, .OPEN_EXISTING, 0, null); si.hStdInput = if (h != INVALID_HANDLE_VALUE) h else null; } if (si.hStdOutput == null or si.hStdOutput == INVALID_HANDLE_VALUE) { - const h = CreateFileW(w("CONOUT$"), GENERIC_WRITE, FILE_SHARE_WRITE, null, OPEN_EXISTING, 0, null); + const h = CreateFileW(w("CONOUT$"), .{ .GENERIC = .{ .WRITE = true } }, .{ .WRITE = true }, null, .OPEN_EXISTING, 0, null); si.hStdOutput = if (h != INVALID_HANDLE_VALUE) h else null; } if (si.hStdError == null or si.hStdError == INVALID_HANDLE_VALUE) { - const h = CreateFileW(w("CONOUT$"), GENERIC_WRITE, FILE_SHARE_WRITE, null, OPEN_EXISTING, 0, null); + const h = CreateFileW(w("CONOUT$"), .{ .GENERIC = .{ .WRITE = true } }, .{ .WRITE = true }, null, .OPEN_EXISTING, 0, null); si.hStdError = if (h != INVALID_HANDLE_VALUE) h else null; } } @@ -365,7 +357,6 @@ fn parseArgsFromCmdLine(allocator: std.mem.Allocator, cmdline: [:0]const WCHAR) return result; } - /// Detect GUI subsystem via PE header of the current module. fn isGuiSubsystem() bool { const hModule = GetModuleHandleW(null) orelse return false; @@ -545,10 +536,10 @@ fn getShimInfo(allocator: std.mem.Allocator) !ShimInfo { const file_handle = CreateFileW( filename[0 .. filename_size + 1 :0].ptr, - GENERIC_READ, - FILE_SHARE_READ, + .{ .GENERIC = .{ .READ = true } }, + .{ .READ = true }, null, - OPEN_EXISTING, + .OPEN_EXISTING, 0, null, ); @@ -556,12 +547,12 @@ fn getShimInfo(allocator: std.mem.Allocator) !ShimInfo { writeError("Cannot open shim file for read.\n"); return error.FileNotFound; } - defer _ = CloseHandle(file_handle); + defer windows.CloseHandle(file_handle); const cur_dir = getDirectory(filename[0..filename_size]); const file_size = GetFileSize(file_handle, null); - if (file_size == 0xFFFFFFFF) { + if (file_size == INFINITE) { writeError("Cannot get shim file size.\n"); return error.FileReadError; } @@ -570,7 +561,7 @@ fn getShimInfo(allocator: std.mem.Allocator) !ShimInfo { defer allocator.free(file_buf); var bytes_read: DWORD = 0; - if (@intFromEnum(ReadFile(file_handle, file_buf.ptr, file_size, &bytes_read, null)) == 0 or bytes_read != file_size) { + if (!ReadFile(file_handle, file_buf.ptr, file_size, &bytes_read, null).toBool() or bytes_read != file_size) { writeError("Cannot read shim file.\n"); return error.FileReadError; } @@ -700,8 +691,8 @@ fn getShimInfo(allocator: std.mem.Allocator) !ShimInfo { /// Ctrl-C / Ctrl-Break handler — swallow the event so the child process handles it. fn ctrlHandler(ctrl_type: DWORD) callconv(.winapi) BOOL { switch (ctrl_type) { - CTRL_C_EVENT, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT => return TRUE, - else => return FALSE, + CTRL_C_EVENT, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT => return .TRUE, + else => return .FALSE, } } @@ -729,7 +720,7 @@ fn makeProcess(allocator: std.mem.Allocator, info: *const ShimInfo, job_handle: @memcpy(value_z[0..ev.value.len], ev.value); value_z[ev.value.len] = 0; - if (SetEnvironmentVariableW(name_z[0..ev.name.len :0].ptr, value_z[0..ev.value.len :0].ptr) == FALSE) { + if (!SetEnvironmentVariableW(name_z[0..ev.name.len :0].ptr, value_z[0..ev.value.len :0].ptr).toBool()) { writeError("Shim: Could not set environment variable.\n"); } } @@ -737,8 +728,8 @@ fn makeProcess(allocator: std.mem.Allocator, info: *const ShimInfo, job_handle: const cmd = try buildCmdLine(allocator, path, args); defer allocator.free(cmd); - var si: STARTUPINFOW = std.mem.zeroes(STARTUPINFOW); - si.cb = @sizeOf(STARTUPINFOW); + var si: windows.STARTUPINFOW = std.mem.zeroes(windows.STARTUPINFOW); + si.cb = @sizeOf(windows.STARTUPINFOW); GetStartupInfoW(&si); ensureStandardHandles(&si); @@ -774,7 +765,7 @@ fn makeProcess(allocator: std.mem.Allocator, info: *const ShimInfo, job_handle: sei.lpVerb = w("runas"); sei.nShow = SW_SHOW; - if (@intFromEnum(ShellExecuteExW(&sei)) == 0) { + if (!ShellExecuteExW(&sei).toBool()) { writeError("Shim: Unable to create elevated process.\n"); return result; } @@ -784,12 +775,12 @@ fn makeProcess(allocator: std.mem.Allocator, info: *const ShimInfo, job_handle: _ = AssignProcessToJobObject(jh, ph); } } - _ = SetConsoleCtrlHandler(ctrlHandler, TRUE); + _ = SetConsoleCtrlHandler(ctrlHandler, .TRUE); return result; } - var pi: PROCESS_INFORMATION = undefined; - if (CreateProcessW(null, @ptrCast(@constCast(cmd.ptr)), null, null, TRUE, CREATE_SUSPENDED, null, if (cwd) |c| @ptrCast(c.ptr) else null, &si, &pi) == TRUE) { + var pi: windows.PROCESS.INFORMATION = undefined; + if (CreateProcessW(null, @ptrCast(@constCast(cmd.ptr)), null, null, .TRUE, CREATE_SUSPENDED, null, if (cwd) |c| @ptrCast(c.ptr) else null, &si, &pi).toBool()) { result.thread = pi.hThread; result.process = pi.hProcess; @@ -809,7 +800,7 @@ fn makeProcess(allocator: std.mem.Allocator, info: *const ShimInfo, job_handle: sei.lpVerb = w("runas"); sei.nShow = SW_SHOW; - if (@intFromEnum(ShellExecuteExW(&sei)) == 0) { + if (!ShellExecuteExW(&sei).toBool()) { writeError("Shim: Unable to create elevated process.\n"); return result; } @@ -827,7 +818,7 @@ fn makeProcess(allocator: std.mem.Allocator, info: *const ShimInfo, job_handle: } } - _ = SetConsoleCtrlHandler(ctrlHandler, TRUE); + _ = SetConsoleCtrlHandler(ctrlHandler, .TRUE); return result; } @@ -894,7 +885,7 @@ fn shimMain() !u8 { const process_handle = proc_result.process orelse return 1; // Wait for process - _ = WaitForSingleObject(process_handle, 0xFFFFFFFF); + _ = WaitForSingleObject(process_handle, INFINITE); var exit_code: DWORD = 1; _ = GetExitCodeProcess(process_handle, &exit_code); diff --git a/zig/version b/zig/version index 6c6aa7c..17e51c3 100644 --- a/zig/version +++ b/zig/version @@ -1 +1 @@ -0.1.0 \ No newline at end of file +0.1.1