Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 74 additions & 83 deletions zig/shim.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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.
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -545,23 +536,23 @@ 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,
);
if (file_handle == INVALID_HANDLE_VALUE) {
writeError("Cannot open shim file for read.\n");
return error.FileNotFound;
}
defer _ = CloseHandle(file_handle);
defer windows.CloseHandle(file_handle);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -729,16 +720,16 @@ 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");
}
}

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);

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;

Expand All @@ -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;
}
Expand All @@ -827,7 +818,7 @@ fn makeProcess(allocator: std.mem.Allocator, info: *const ShimInfo, job_handle:
}
}

_ = SetConsoleCtrlHandler(ctrlHandler, TRUE);
_ = SetConsoleCtrlHandler(ctrlHandler, .TRUE);
return result;
}

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion zig/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.0
0.1.1
Loading