master
   1//! This file contains thin wrappers around Windows-specific APIs, with these
   2//! specific goals in mind:
   3//! * Convert "errno"-style error codes into Zig errors.
   4//! * When null-terminated or WTF16LE byte buffers are required, provide APIs which accept
   5//!   slices as well as APIs which accept null-terminated WTF16LE byte buffers.
   6
   7const builtin = @import("builtin");
   8const native_arch = builtin.cpu.arch;
   9
  10const std = @import("../std.zig");
  11const Io = std.Io;
  12const mem = std.mem;
  13const assert = std.debug.assert;
  14const math = std.math;
  15const maxInt = std.math.maxInt;
  16const UnexpectedError = std.posix.UnexpectedError;
  17
  18test {
  19    if (builtin.os.tag == .windows) {
  20        _ = @import("windows/test.zig");
  21    }
  22}
  23
  24pub const advapi32 = @import("windows/advapi32.zig");
  25pub const kernel32 = @import("windows/kernel32.zig");
  26pub const ntdll = @import("windows/ntdll.zig");
  27pub const ws2_32 = @import("windows/ws2_32.zig");
  28pub const crypt32 = @import("windows/crypt32.zig");
  29pub const nls = @import("windows/nls.zig");
  30
  31pub const self_process_handle = @as(HANDLE, @ptrFromInt(maxInt(usize)));
  32
  33const Self = @This();
  34
  35pub const OpenError = error{
  36    IsDir,
  37    NotDir,
  38    FileNotFound,
  39    NoDevice,
  40    AccessDenied,
  41    PipeBusy,
  42    PathAlreadyExists,
  43    Unexpected,
  44    NameTooLong,
  45    WouldBlock,
  46    NetworkNotFound,
  47    AntivirusInterference,
  48    BadPathName,
  49};
  50
  51pub const OpenFileOptions = struct {
  52    access_mask: ACCESS_MASK,
  53    dir: ?HANDLE = null,
  54    sa: ?*SECURITY_ATTRIBUTES = null,
  55    share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
  56    creation: ULONG,
  57    /// If true, tries to open path as a directory.
  58    /// Defaults to false.
  59    filter: Filter = .file_only,
  60    /// If false, tries to open path as a reparse point without dereferencing it.
  61    /// Defaults to true.
  62    follow_symlinks: bool = true,
  63
  64    pub const Filter = enum {
  65        /// Causes `OpenFile` to return `error.IsDir` if the opened handle would be a directory.
  66        file_only,
  67        /// Causes `OpenFile` to return `error.NotDir` if the opened handle would be a file.
  68        dir_only,
  69        /// `OpenFile` does not discriminate between opening files and directories.
  70        any,
  71    };
  72};
  73
  74pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
  75    if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and options.filter == .file_only) {
  76        return error.IsDir;
  77    }
  78    if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and options.filter == .file_only) {
  79        return error.IsDir;
  80    }
  81
  82    var result: HANDLE = undefined;
  83
  84    const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
  85    var nt_name = UNICODE_STRING{
  86        .Length = path_len_bytes,
  87        .MaximumLength = path_len_bytes,
  88        .Buffer = @constCast(sub_path_w.ptr),
  89    };
  90    var attr = OBJECT_ATTRIBUTES{
  91        .Length = @sizeOf(OBJECT_ATTRIBUTES),
  92        .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
  93        .Attributes = if (options.sa) |ptr| blk: { // Note we do not use OBJ_CASE_INSENSITIVE here.
  94            const inherit: ULONG = if (ptr.bInheritHandle == TRUE) OBJ_INHERIT else 0;
  95            break :blk inherit;
  96        } else 0,
  97        .ObjectName = &nt_name,
  98        .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null,
  99        .SecurityQualityOfService = null,
 100    };
 101    var io: IO_STATUS_BLOCK = undefined;
 102    const blocking_flag: ULONG = FILE_SYNCHRONOUS_IO_NONALERT;
 103    const file_or_dir_flag: ULONG = switch (options.filter) {
 104        .file_only => FILE_NON_DIRECTORY_FILE,
 105        .dir_only => FILE_DIRECTORY_FILE,
 106        .any => 0,
 107    };
 108    // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
 109    const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;
 110
 111    while (true) {
 112        const rc = ntdll.NtCreateFile(
 113            &result,
 114            options.access_mask,
 115            &attr,
 116            &io,
 117            null,
 118            FILE_ATTRIBUTE_NORMAL,
 119            options.share_access,
 120            options.creation,
 121            flags,
 122            null,
 123            0,
 124        );
 125        switch (rc) {
 126            .SUCCESS => return result,
 127            .OBJECT_NAME_INVALID => return error.BadPathName,
 128            .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
 129            .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
 130            .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
 131            .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
 132            .NO_MEDIA_IN_DEVICE => return error.NoDevice,
 133            .INVALID_PARAMETER => unreachable,
 134            .SHARING_VIOLATION => return error.AccessDenied,
 135            .ACCESS_DENIED => return error.AccessDenied,
 136            .PIPE_BUSY => return error.PipeBusy,
 137            .PIPE_NOT_AVAILABLE => return error.NoDevice,
 138            .OBJECT_PATH_SYNTAX_BAD => unreachable,
 139            .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
 140            .FILE_IS_A_DIRECTORY => return error.IsDir,
 141            .NOT_A_DIRECTORY => return error.NotDir,
 142            .USER_MAPPED_FILE => return error.AccessDenied,
 143            .INVALID_HANDLE => unreachable,
 144            .DELETE_PENDING => {
 145                // This error means that there *was* a file in this location on
 146                // the file system, but it was deleted. However, the OS is not
 147                // finished with the deletion operation, and so this CreateFile
 148                // call has failed. There is not really a sane way to handle
 149                // this other than retrying the creation after the OS finishes
 150                // the deletion.
 151                _ = kernel32.SleepEx(1, TRUE);
 152                continue;
 153            },
 154            .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
 155            else => return unexpectedStatus(rc),
 156        }
 157    }
 158}
 159
 160pub fn GetCurrentProcess() HANDLE {
 161    const process_pseudo_handle: usize = @bitCast(@as(isize, -1));
 162    return @ptrFromInt(process_pseudo_handle);
 163}
 164
 165pub fn GetCurrentProcessId() DWORD {
 166    return @truncate(@intFromPtr(teb().ClientId.UniqueProcess));
 167}
 168
 169pub fn GetCurrentThread() HANDLE {
 170    const thread_pseudo_handle: usize = @bitCast(@as(isize, -2));
 171    return @ptrFromInt(thread_pseudo_handle);
 172}
 173
 174pub fn GetCurrentThreadId() DWORD {
 175    return @truncate(@intFromPtr(teb().ClientId.UniqueThread));
 176}
 177
 178pub fn GetLastError() Win32Error {
 179    return @enumFromInt(teb().LastErrorValue);
 180}
 181
 182pub const CreatePipeError = error{ Unexpected, SystemResources };
 183
 184var npfs: ?HANDLE = null;
 185
 186/// A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls.
 187/// It implements similar behavior to `CreatePipe` and is meant to serve
 188/// as a direct substitute for that call.
 189pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
 190    // Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to:
 191    //
 192    //      \??\pipe\Win32Pipes.{pid}.{count}
 193    //
 194    // where `pid` is the process id and count is a incrementing counter.
 195    // The implementation was changed after NT 6.0 (Vista) to open a handle to the Named Pipe File System
 196    // and use that as the root directory for `NtCreateNamedPipeFile`.
 197    // This object is visible under the NPFS but has no filename attached to it.
 198    //
 199    // This implementation replicates how `CreatePipe` works in modern Windows versions.
 200    const opt_dev_handle = @atomicLoad(?HANDLE, &npfs, .seq_cst);
 201    const dev_handle = opt_dev_handle orelse blk: {
 202        const str = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\NamedPipe\\");
 203        const len: u16 = @truncate(str.len * @sizeOf(u16));
 204        const name = UNICODE_STRING{
 205            .Length = len,
 206            .MaximumLength = len,
 207            .Buffer = @ptrCast(@constCast(str)),
 208        };
 209        const attrs = OBJECT_ATTRIBUTES{
 210            .ObjectName = @constCast(&name),
 211            .Length = @sizeOf(OBJECT_ATTRIBUTES),
 212            .RootDirectory = null,
 213            .Attributes = 0,
 214            .SecurityDescriptor = null,
 215            .SecurityQualityOfService = null,
 216        };
 217
 218        var iosb: IO_STATUS_BLOCK = undefined;
 219        var handle: HANDLE = undefined;
 220        switch (ntdll.NtCreateFile(
 221            &handle,
 222            GENERIC_READ | SYNCHRONIZE,
 223            @constCast(&attrs),
 224            &iosb,
 225            null,
 226            0,
 227            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 228            FILE_OPEN,
 229            FILE_SYNCHRONOUS_IO_NONALERT,
 230            null,
 231            0,
 232        )) {
 233            .SUCCESS => {},
 234            // Judging from the ReactOS sources this is technically possible.
 235            .INSUFFICIENT_RESOURCES => return error.SystemResources,
 236            .INVALID_PARAMETER => unreachable,
 237            else => |e| return unexpectedStatus(e),
 238        }
 239        if (@cmpxchgStrong(?HANDLE, &npfs, null, handle, .seq_cst, .seq_cst)) |xchg| {
 240            CloseHandle(handle);
 241            break :blk xchg.?;
 242        } else break :blk handle;
 243    };
 244
 245    const name = UNICODE_STRING{ .Buffer = null, .Length = 0, .MaximumLength = 0 };
 246    var attrs = OBJECT_ATTRIBUTES{
 247        .ObjectName = @constCast(&name),
 248        .Length = @sizeOf(OBJECT_ATTRIBUTES),
 249        .RootDirectory = dev_handle,
 250        .Attributes = OBJ_CASE_INSENSITIVE,
 251        .SecurityDescriptor = sattr.lpSecurityDescriptor,
 252        .SecurityQualityOfService = null,
 253    };
 254    if (sattr.bInheritHandle != 0) attrs.Attributes |= OBJ_INHERIT;
 255
 256    // 120 second relative timeout in 100ns units.
 257    const default_timeout: LARGE_INTEGER = (-120 * std.time.ns_per_s) / 100;
 258    var iosb: IO_STATUS_BLOCK = undefined;
 259    var read: HANDLE = undefined;
 260    switch (ntdll.NtCreateNamedPipeFile(
 261        &read,
 262        GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
 263        &attrs,
 264        &iosb,
 265        FILE_SHARE_READ | FILE_SHARE_WRITE,
 266        FILE_CREATE,
 267        FILE_SYNCHRONOUS_IO_NONALERT,
 268        FILE_PIPE_BYTE_STREAM_TYPE,
 269        FILE_PIPE_BYTE_STREAM_MODE,
 270        FILE_PIPE_QUEUE_OPERATION,
 271        1,
 272        4096,
 273        4096,
 274        @constCast(&default_timeout),
 275    )) {
 276        .SUCCESS => {},
 277        .INVALID_PARAMETER => unreachable,
 278        .INSUFFICIENT_RESOURCES => return error.SystemResources,
 279        else => |e| return unexpectedStatus(e),
 280    }
 281    errdefer CloseHandle(read);
 282
 283    attrs.RootDirectory = read;
 284
 285    var write: HANDLE = undefined;
 286    switch (ntdll.NtCreateFile(
 287        &write,
 288        GENERIC_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
 289        &attrs,
 290        &iosb,
 291        null,
 292        0,
 293        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 294        FILE_OPEN,
 295        FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
 296        null,
 297        0,
 298    )) {
 299        .SUCCESS => {},
 300        .INVALID_PARAMETER => unreachable,
 301        .INSUFFICIENT_RESOURCES => return error.SystemResources,
 302        else => |e| return unexpectedStatus(e),
 303    }
 304
 305    rd.* = read;
 306    wr.* = write;
 307}
 308
 309pub const DeviceIoControlError = error{
 310    AccessDenied,
 311    /// The volume does not contain a recognized file system. File system
 312    /// drivers might not be loaded, or the volume may be corrupt.
 313    UnrecognizedVolume,
 314    Unexpected,
 315};
 316
 317/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
 318/// It implements similar behavior to `DeviceIoControl` and is meant to serve
 319/// as a direct substitute for that call.
 320/// TODO work out if we need to expose other arguments to the underlying syscalls.
 321pub fn DeviceIoControl(
 322    h: HANDLE,
 323    ioControlCode: ULONG,
 324    in: ?[]const u8,
 325    out: ?[]u8,
 326) DeviceIoControlError!void {
 327    // Logic from: https://doxygen.reactos.org/d3/d74/deviceio_8c.html
 328    const is_fsctl = (ioControlCode >> 16) == FILE_DEVICE_FILE_SYSTEM;
 329
 330    var io: IO_STATUS_BLOCK = undefined;
 331    const in_ptr = if (in) |i| i.ptr else null;
 332    const in_len = if (in) |i| @as(ULONG, @intCast(i.len)) else 0;
 333    const out_ptr = if (out) |o| o.ptr else null;
 334    const out_len = if (out) |o| @as(ULONG, @intCast(o.len)) else 0;
 335
 336    const rc = blk: {
 337        if (is_fsctl) {
 338            break :blk ntdll.NtFsControlFile(
 339                h,
 340                null,
 341                null,
 342                null,
 343                &io,
 344                ioControlCode,
 345                in_ptr,
 346                in_len,
 347                out_ptr,
 348                out_len,
 349            );
 350        } else {
 351            break :blk ntdll.NtDeviceIoControlFile(
 352                h,
 353                null,
 354                null,
 355                null,
 356                &io,
 357                ioControlCode,
 358                in_ptr,
 359                in_len,
 360                out_ptr,
 361                out_len,
 362            );
 363        }
 364    };
 365    switch (rc) {
 366        .SUCCESS => {},
 367        .PRIVILEGE_NOT_HELD => return error.AccessDenied,
 368        .ACCESS_DENIED => return error.AccessDenied,
 369        .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
 370        .INVALID_PARAMETER => unreachable,
 371        .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
 372        else => return unexpectedStatus(rc),
 373    }
 374}
 375
 376pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
 377    var bytes: DWORD = undefined;
 378    if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @intFromBool(wait)) == 0) {
 379        switch (GetLastError()) {
 380            .IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable,
 381            else => |err| return unexpectedError(err),
 382        }
 383    }
 384    return bytes;
 385}
 386
 387pub const SetHandleInformationError = error{Unexpected};
 388
 389pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void {
 390    if (kernel32.SetHandleInformation(h, mask, flags) == 0) {
 391        switch (GetLastError()) {
 392            else => |err| return unexpectedError(err),
 393        }
 394    }
 395}
 396
 397pub const RtlGenRandomError = error{
 398    /// `RtlGenRandom` has been known to fail in situations where the system is under heavy load.
 399    /// Unfortunately, it does not call `SetLastError`, so it is not possible to get more specific
 400    /// error information; it could actually be due to an out-of-memory condition, for example.
 401    SystemResources,
 402};
 403
 404/// Call RtlGenRandom() instead of CryptGetRandom() on Windows
 405/// https://github.com/rust-lang-nursery/rand/issues/111
 406/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
 407pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void {
 408    var total_read: usize = 0;
 409    var buff: []u8 = output[0..];
 410    const max_read_size: ULONG = maxInt(ULONG);
 411
 412    while (total_read < output.len) {
 413        const to_read: ULONG = @min(buff.len, max_read_size);
 414
 415        if (advapi32.RtlGenRandom(buff.ptr, to_read) == 0) {
 416            return error.SystemResources;
 417        }
 418
 419        total_read += to_read;
 420        buff = buff[to_read..];
 421    }
 422}
 423
 424pub const WaitForSingleObjectError = error{
 425    WaitAbandoned,
 426    WaitTimeOut,
 427    Unexpected,
 428};
 429
 430pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void {
 431    return WaitForSingleObjectEx(handle, milliseconds, false);
 432}
 433
 434pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void {
 435    switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @intFromBool(alertable))) {
 436        WAIT_ABANDONED => return error.WaitAbandoned,
 437        WAIT_OBJECT_0 => return,
 438        WAIT_TIMEOUT => return error.WaitTimeOut,
 439        WAIT_FAILED => switch (GetLastError()) {
 440            else => |err| return unexpectedError(err),
 441        },
 442        else => return error.Unexpected,
 443    }
 444}
 445
 446pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 {
 447    assert(handles.len > 0 and handles.len <= MAXIMUM_WAIT_OBJECTS);
 448    const nCount: DWORD = @as(DWORD, @intCast(handles.len));
 449    switch (kernel32.WaitForMultipleObjectsEx(
 450        nCount,
 451        handles.ptr,
 452        @intFromBool(waitAll),
 453        milliseconds,
 454        @intFromBool(alertable),
 455    )) {
 456        WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| {
 457            const handle_index = n - WAIT_OBJECT_0;
 458            assert(handle_index < nCount);
 459            return handle_index;
 460        },
 461        WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| {
 462            const handle_index = n - WAIT_ABANDONED_0;
 463            assert(handle_index < nCount);
 464            return error.WaitAbandoned;
 465        },
 466        WAIT_TIMEOUT => return error.WaitTimeOut,
 467        WAIT_FAILED => switch (GetLastError()) {
 468            else => |err| return unexpectedError(err),
 469        },
 470        else => return error.Unexpected,
 471    }
 472}
 473
 474pub const CreateIoCompletionPortError = error{Unexpected};
 475
 476pub fn CreateIoCompletionPort(
 477    file_handle: HANDLE,
 478    existing_completion_port: ?HANDLE,
 479    completion_key: usize,
 480    concurrent_thread_count: DWORD,
 481) CreateIoCompletionPortError!HANDLE {
 482    const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
 483        switch (GetLastError()) {
 484            .INVALID_PARAMETER => unreachable,
 485            else => |err| return unexpectedError(err),
 486        }
 487    };
 488    return handle;
 489}
 490
 491pub const PostQueuedCompletionStatusError = error{Unexpected};
 492
 493pub fn PostQueuedCompletionStatus(
 494    completion_port: HANDLE,
 495    bytes_transferred_count: DWORD,
 496    completion_key: usize,
 497    lpOverlapped: ?*OVERLAPPED,
 498) PostQueuedCompletionStatusError!void {
 499    if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
 500        switch (GetLastError()) {
 501            else => |err| return unexpectedError(err),
 502        }
 503    }
 504}
 505
 506pub const GetQueuedCompletionStatusResult = enum {
 507    Normal,
 508    Aborted,
 509    Canceled,
 510    EOF,
 511    Timeout,
 512};
 513
 514pub fn GetQueuedCompletionStatus(
 515    completion_port: HANDLE,
 516    bytes_transferred_count: *DWORD,
 517    lpCompletionKey: *usize,
 518    lpOverlapped: *?*OVERLAPPED,
 519    dwMilliseconds: DWORD,
 520) GetQueuedCompletionStatusResult {
 521    if (kernel32.GetQueuedCompletionStatus(
 522        completion_port,
 523        bytes_transferred_count,
 524        lpCompletionKey,
 525        lpOverlapped,
 526        dwMilliseconds,
 527    ) == FALSE) {
 528        switch (GetLastError()) {
 529            .ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
 530            .OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Canceled,
 531            .HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
 532            .WAIT_TIMEOUT => return GetQueuedCompletionStatusResult.Timeout,
 533            else => |err| {
 534                if (std.debug.runtime_safety) {
 535                    @setEvalBranchQuota(2500);
 536                    std.debug.panic("unexpected error: {}\n", .{err});
 537                }
 538            },
 539        }
 540    }
 541    return GetQueuedCompletionStatusResult.Normal;
 542}
 543
 544pub const GetQueuedCompletionStatusError = error{
 545    Aborted,
 546    Canceled,
 547    EOF,
 548    Timeout,
 549} || UnexpectedError;
 550
 551pub fn GetQueuedCompletionStatusEx(
 552    completion_port: HANDLE,
 553    completion_port_entries: []OVERLAPPED_ENTRY,
 554    timeout_ms: ?DWORD,
 555    alertable: bool,
 556) GetQueuedCompletionStatusError!u32 {
 557    var num_entries_removed: u32 = 0;
 558
 559    const success = kernel32.GetQueuedCompletionStatusEx(
 560        completion_port,
 561        completion_port_entries.ptr,
 562        @as(ULONG, @intCast(completion_port_entries.len)),
 563        &num_entries_removed,
 564        timeout_ms orelse INFINITE,
 565        @intFromBool(alertable),
 566    );
 567
 568    if (success == FALSE) {
 569        return switch (GetLastError()) {
 570            .ABANDONED_WAIT_0 => error.Aborted,
 571            .OPERATION_ABORTED => error.Canceled,
 572            .HANDLE_EOF => error.EOF,
 573            .WAIT_TIMEOUT => error.Timeout,
 574            else => |err| unexpectedError(err),
 575        };
 576    }
 577
 578    return num_entries_removed;
 579}
 580
 581pub fn CloseHandle(hObject: HANDLE) void {
 582    assert(ntdll.NtClose(hObject) == .SUCCESS);
 583}
 584
 585pub const ReadFileError = error{
 586    BrokenPipe,
 587    /// The specified network name is no longer available.
 588    ConnectionResetByPeer,
 589    Canceled,
 590    /// Unable to read file due to lock.
 591    LockViolation,
 592    /// Known to be possible when:
 593    /// - Unable to read from disconnected virtual com port (Windows)
 594    AccessDenied,
 595    NotOpenForReading,
 596    Unexpected,
 597};
 598
 599/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
 600/// multiple non-atomic reads.
 601pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize {
 602    while (true) {
 603        const want_read_count: DWORD = @min(@as(DWORD, maxInt(DWORD)), buffer.len);
 604        var amt_read: DWORD = undefined;
 605        var overlapped_data: OVERLAPPED = undefined;
 606        const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
 607            overlapped_data = .{
 608                .Internal = 0,
 609                .InternalHigh = 0,
 610                .DUMMYUNIONNAME = .{
 611                    .DUMMYSTRUCTNAME = .{
 612                        .Offset = @as(u32, @truncate(off)),
 613                        .OffsetHigh = @as(u32, @truncate(off >> 32)),
 614                    },
 615                },
 616                .hEvent = null,
 617            };
 618            break :blk &overlapped_data;
 619        } else null;
 620        if (kernel32.ReadFile(in_hFile, buffer.ptr, want_read_count, &amt_read, overlapped) == 0) {
 621            switch (GetLastError()) {
 622                .IO_PENDING => unreachable,
 623                .OPERATION_ABORTED => continue,
 624                .BROKEN_PIPE => return 0,
 625                .HANDLE_EOF => return 0,
 626                .NETNAME_DELETED => return error.ConnectionResetByPeer,
 627                .LOCK_VIOLATION => return error.LockViolation,
 628                .ACCESS_DENIED => return error.AccessDenied,
 629                .INVALID_HANDLE => return error.NotOpenForReading,
 630                else => |err| return unexpectedError(err),
 631            }
 632        }
 633        return amt_read;
 634    }
 635}
 636
 637pub const WriteFileError = error{
 638    SystemResources,
 639    Canceled,
 640    BrokenPipe,
 641    NotOpenForWriting,
 642    /// The process cannot access the file because another process has locked
 643    /// a portion of the file.
 644    LockViolation,
 645    /// The specified network name is no longer available.
 646    ConnectionResetByPeer,
 647    /// Known to be possible when:
 648    /// - Unable to write to disconnected virtual com port (Windows)
 649    AccessDenied,
 650    Unexpected,
 651};
 652
 653pub fn WriteFile(
 654    handle: HANDLE,
 655    bytes: []const u8,
 656    offset: ?u64,
 657) WriteFileError!usize {
 658    var bytes_written: DWORD = undefined;
 659    var overlapped_data: OVERLAPPED = undefined;
 660    const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
 661        overlapped_data = .{
 662            .Internal = 0,
 663            .InternalHigh = 0,
 664            .DUMMYUNIONNAME = .{
 665                .DUMMYSTRUCTNAME = .{
 666                    .Offset = @truncate(off),
 667                    .OffsetHigh = @truncate(off >> 32),
 668                },
 669            },
 670            .hEvent = null,
 671        };
 672        break :blk &overlapped_data;
 673    } else null;
 674    const adjusted_len = math.cast(u32, bytes.len) orelse maxInt(u32);
 675    if (kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, overlapped) == 0) {
 676        switch (GetLastError()) {
 677            .INVALID_USER_BUFFER => return error.SystemResources,
 678            .NOT_ENOUGH_MEMORY => return error.SystemResources,
 679            .OPERATION_ABORTED => return error.Canceled,
 680            .NOT_ENOUGH_QUOTA => return error.SystemResources,
 681            .IO_PENDING => unreachable,
 682            .NO_DATA => return error.BrokenPipe,
 683            .INVALID_HANDLE => return error.NotOpenForWriting,
 684            .LOCK_VIOLATION => return error.LockViolation,
 685            .NETNAME_DELETED => return error.ConnectionResetByPeer,
 686            .ACCESS_DENIED => return error.AccessDenied,
 687            .WORKING_SET_QUOTA => return error.SystemResources,
 688            else => |err| return unexpectedError(err),
 689        }
 690    }
 691    return bytes_written;
 692}
 693
 694pub const SetCurrentDirectoryError = error{
 695    NameTooLong,
 696    FileNotFound,
 697    NotDir,
 698    AccessDenied,
 699    NoDevice,
 700    BadPathName,
 701    Unexpected,
 702};
 703
 704pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void {
 705    const path_len_bytes = math.cast(u16, path_name.len * 2) orelse return error.NameTooLong;
 706
 707    var nt_name = UNICODE_STRING{
 708        .Length = path_len_bytes,
 709        .MaximumLength = path_len_bytes,
 710        .Buffer = @constCast(path_name.ptr),
 711    };
 712
 713    const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);
 714    switch (rc) {
 715        .SUCCESS => {},
 716        .OBJECT_NAME_INVALID => return error.BadPathName,
 717        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
 718        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
 719        .NO_MEDIA_IN_DEVICE => return error.NoDevice,
 720        .INVALID_PARAMETER => unreachable,
 721        .ACCESS_DENIED => return error.AccessDenied,
 722        .OBJECT_PATH_SYNTAX_BAD => unreachable,
 723        .NOT_A_DIRECTORY => return error.NotDir,
 724        else => return unexpectedStatus(rc),
 725    }
 726}
 727
 728pub const GetCurrentDirectoryError = error{
 729    NameTooLong,
 730    Unexpected,
 731};
 732
 733/// The result is a slice of `buffer`, indexed from 0.
 734/// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
 735pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
 736    var wtf16le_buf: [PATH_MAX_WIDE:0]u16 = undefined;
 737    const result = kernel32.GetCurrentDirectoryW(wtf16le_buf.len + 1, &wtf16le_buf);
 738    if (result == 0) {
 739        switch (GetLastError()) {
 740            else => |err| return unexpectedError(err),
 741        }
 742    }
 743    assert(result <= wtf16le_buf.len);
 744    const wtf16le_slice = wtf16le_buf[0..result];
 745    var end_index: usize = 0;
 746    var it = std.unicode.Wtf16LeIterator.init(wtf16le_slice);
 747    while (it.nextCodepoint()) |codepoint| {
 748        const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
 749        if (end_index + seq_len >= buffer.len)
 750            return error.NameTooLong;
 751        end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable;
 752    }
 753    return buffer[0..end_index];
 754}
 755
 756pub const CreateSymbolicLinkError = error{
 757    AccessDenied,
 758    PathAlreadyExists,
 759    FileNotFound,
 760    NameTooLong,
 761    NoDevice,
 762    NetworkNotFound,
 763    BadPathName,
 764    /// The volume does not contain a recognized file system. File system
 765    /// drivers might not be loaded, or the volume may be corrupt.
 766    UnrecognizedVolume,
 767    Unexpected,
 768};
 769
 770/// Needs either:
 771/// - `SeCreateSymbolicLinkPrivilege` privilege
 772/// or
 773/// - Developer mode on Windows 10
 774/// otherwise fails with `error.AccessDenied`. In which case `sym_link_path` may still
 775/// be created on the file system but will lack reparse processing data applied to it.
 776pub fn CreateSymbolicLink(
 777    dir: ?HANDLE,
 778    sym_link_path: []const u16,
 779    target_path: [:0]const u16,
 780    is_directory: bool,
 781) CreateSymbolicLinkError!void {
 782    const SYMLINK_DATA = extern struct {
 783        ReparseTag: ULONG,
 784        ReparseDataLength: USHORT,
 785        Reserved: USHORT,
 786        SubstituteNameOffset: USHORT,
 787        SubstituteNameLength: USHORT,
 788        PrintNameOffset: USHORT,
 789        PrintNameLength: USHORT,
 790        Flags: ULONG,
 791    };
 792
 793    const symlink_handle = OpenFile(sym_link_path, .{
 794        .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
 795        .dir = dir,
 796        .creation = FILE_CREATE,
 797        .filter = if (is_directory) .dir_only else .file_only,
 798    }) catch |err| switch (err) {
 799        error.IsDir => return error.PathAlreadyExists,
 800        error.NotDir => return error.Unexpected,
 801        error.WouldBlock => return error.Unexpected,
 802        error.PipeBusy => return error.Unexpected,
 803        error.NoDevice => return error.Unexpected,
 804        error.AntivirusInterference => return error.Unexpected,
 805        else => |e| return e,
 806    };
 807    defer CloseHandle(symlink_handle);
 808
 809    // Relevant portions of the documentation:
 810    // > Relative links are specified using the following conventions:
 811    // > - Root relative—for example, "\Windows\System32" resolves to "current drive:\Windows\System32".
 812    // > - Current working directory–relative—for example, if the current working directory is
 813    // >   C:\Windows\System32, "C:File.txt" resolves to "C:\Windows\System32\File.txt".
 814    // > Note: If you specify a current working directory–relative link, it is created as an absolute
 815    // > link, due to the way the current working directory is processed based on the user and the thread.
 816    // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
 817    var is_target_absolute = false;
 818    const final_target_path = target_path: {
 819        if (hasCommonNtPrefix(u16, target_path)) {
 820            // Already an NT path, no need to do anything to it
 821            break :target_path target_path;
 822        } else {
 823            switch (getWin32PathType(u16, target_path)) {
 824                // Rooted paths need to avoid getting put through wToPrefixedFileW
 825                // (and they are treated as relative in this context)
 826                // Note: It seems that rooted paths in symbolic links are relative to
 827                //       the drive that the symbolic exists on, not to the CWD's drive.
 828                //       So, if the symlink is on C:\ and the CWD is on D:\,
 829                //       it will still resolve the path relative to the root of
 830                //       the C:\ drive.
 831                .rooted => break :target_path target_path,
 832                // Keep relative paths relative, but anything else needs to get NT-prefixed.
 833                else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path))
 834                    break :target_path target_path,
 835            }
 836        }
 837        var prefixed_target_path = try wToPrefixedFileW(dir, target_path);
 838        // We do this after prefixing to ensure that drive-relative paths are treated as absolute
 839        is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
 840        break :target_path prefixed_target_path.span();
 841    };
 842
 843    // prepare reparse data buffer
 844    var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
 845    const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
 846    const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2;
 847    const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path);
 848    const symlink_data = SYMLINK_DATA{
 849        .ReparseTag = IO_REPARSE_TAG_SYMLINK,
 850        .ReparseDataLength = @intCast(buf_len - header_len),
 851        .Reserved = 0,
 852        .SubstituteNameOffset = @intCast(final_target_path.len * 2),
 853        .SubstituteNameLength = @intCast(final_target_path.len * 2),
 854        .PrintNameOffset = 0,
 855        .PrintNameLength = @intCast(final_target_path.len * 2),
 856        .Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0,
 857    };
 858
 859    @memcpy(buffer[0..@sizeOf(SYMLINK_DATA)], std.mem.asBytes(&symlink_data));
 860    @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
 861    const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
 862    @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
 863    _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
 864}
 865
 866pub const ReadLinkError = error{
 867    FileNotFound,
 868    NetworkNotFound,
 869    AccessDenied,
 870    Unexpected,
 871    NameTooLong,
 872    BadPathName,
 873    AntivirusInterference,
 874    UnsupportedReparsePointType,
 875};
 876
 877/// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it
 878/// is safe to reuse a single buffer for both.
 879pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLinkError![]u16 {
 880    const result_handle = OpenFile(sub_path_w, .{
 881        .access_mask = FILE_READ_ATTRIBUTES | SYNCHRONIZE,
 882        .dir = dir,
 883        .creation = FILE_OPEN,
 884        .follow_symlinks = false,
 885        .filter = .any,
 886    }) catch |err| switch (err) {
 887        error.IsDir, error.NotDir => return error.Unexpected, // filter = .any
 888        error.PathAlreadyExists => return error.Unexpected, // FILE_OPEN
 889        error.WouldBlock => return error.Unexpected,
 890        error.NoDevice => return error.FileNotFound,
 891        error.PipeBusy => return error.AccessDenied,
 892        else => |e| return e,
 893    };
 894    defer CloseHandle(result_handle);
 895
 896    var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
 897    _ = DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) {
 898        error.AccessDenied => return error.Unexpected,
 899        error.UnrecognizedVolume => return error.Unexpected,
 900        else => |e| return e,
 901    };
 902
 903    const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
 904    switch (reparse_struct.ReparseTag) {
 905        IO_REPARSE_TAG_SYMLINK => {
 906            const buf: *const SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
 907            const offset = buf.SubstituteNameOffset >> 1;
 908            const len = buf.SubstituteNameLength >> 1;
 909            const path_buf = @as([*]const u16, &buf.PathBuffer);
 910            const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
 911            return parseReadLinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
 912        },
 913        IO_REPARSE_TAG_MOUNT_POINT => {
 914            const buf: *const MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
 915            const offset = buf.SubstituteNameOffset >> 1;
 916            const len = buf.SubstituteNameLength >> 1;
 917            const path_buf = @as([*]const u16, &buf.PathBuffer);
 918            return parseReadLinkPath(path_buf[offset..][0..len], false, out_buffer);
 919        },
 920        else => {
 921            return error.UnsupportedReparsePointType;
 922        },
 923    }
 924}
 925
 926fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
 927    path: {
 928        if (is_relative) break :path;
 929        return ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
 930            error.NameTooLong => |e| return e,
 931            error.NotNtPath => break :path,
 932        };
 933    }
 934    if (out_buffer.len < path.len) return error.NameTooLong;
 935    const dest = out_buffer[0..path.len];
 936    @memcpy(dest, path);
 937    return dest;
 938}
 939
 940pub const DeleteFileError = error{
 941    FileNotFound,
 942    AccessDenied,
 943    NameTooLong,
 944    /// Also known as sharing violation.
 945    FileBusy,
 946    Unexpected,
 947    NotDir,
 948    IsDir,
 949    DirNotEmpty,
 950    NetworkNotFound,
 951};
 952
 953pub const DeleteFileOptions = struct {
 954    dir: ?HANDLE,
 955    remove_dir: bool = false,
 956};
 957
 958pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
 959    const create_options_flags: ULONG = if (options.remove_dir)
 960        FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
 961    else
 962        FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
 963
 964    const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
 965    var nt_name = UNICODE_STRING{
 966        .Length = path_len_bytes,
 967        .MaximumLength = path_len_bytes,
 968        // The Windows API makes this mutable, but it will not mutate here.
 969        .Buffer = @constCast(sub_path_w.ptr),
 970    };
 971
 972    if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
 973        // Windows does not recognize this, but it does work with empty string.
 974        nt_name.Length = 0;
 975    }
 976    if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
 977        // Can't remove the parent directory with an open handle.
 978        return error.FileBusy;
 979    }
 980
 981    var attr = OBJECT_ATTRIBUTES{
 982        .Length = @sizeOf(OBJECT_ATTRIBUTES),
 983        .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
 984        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
 985        .ObjectName = &nt_name,
 986        .SecurityDescriptor = null,
 987        .SecurityQualityOfService = null,
 988    };
 989    var io: IO_STATUS_BLOCK = undefined;
 990    var tmp_handle: HANDLE = undefined;
 991    var rc = ntdll.NtCreateFile(
 992        &tmp_handle,
 993        SYNCHRONIZE | DELETE,
 994        &attr,
 995        &io,
 996        null,
 997        0,
 998        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 999        FILE_OPEN,
1000        create_options_flags,
1001        null,
1002        0,
1003    );
1004    switch (rc) {
1005        .SUCCESS => {},
1006        .OBJECT_NAME_INVALID => unreachable,
1007        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1008        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
1009        .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
1010        .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
1011        .INVALID_PARAMETER => unreachable,
1012        .FILE_IS_A_DIRECTORY => return error.IsDir,
1013        .NOT_A_DIRECTORY => return error.NotDir,
1014        .SHARING_VIOLATION => return error.FileBusy,
1015        .ACCESS_DENIED => return error.AccessDenied,
1016        .DELETE_PENDING => return,
1017        else => return unexpectedStatus(rc),
1018    }
1019    defer CloseHandle(tmp_handle);
1020
1021    // FileDispositionInformationEx has varying levels of support:
1022    // - FILE_DISPOSITION_INFORMATION_EX requires >= win10_rs1
1023    //   (INVALID_INFO_CLASS is returned if not supported)
1024    // - Requires the NTFS filesystem
1025    //   (on filesystems like FAT32, INVALID_PARAMETER is returned)
1026    // - FILE_DISPOSITION_POSIX_SEMANTICS requires >= win10_rs1
1027    // - FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
1028    //   (NOT_SUPPORTED is returned if a flag is unsupported)
1029    //
1030    // The strategy here is just to try using FileDispositionInformationEx and fall back to
1031    // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
1032    const need_fallback = need_fallback: {
1033        // Deletion with posix semantics if the filesystem supports it.
1034        var info = FILE_DISPOSITION_INFORMATION_EX{
1035            .Flags = FILE_DISPOSITION_DELETE |
1036                FILE_DISPOSITION_POSIX_SEMANTICS |
1037                FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
1038        };
1039
1040        rc = ntdll.NtSetInformationFile(
1041            tmp_handle,
1042            &io,
1043            &info,
1044            @sizeOf(FILE_DISPOSITION_INFORMATION_EX),
1045            .FileDispositionInformationEx,
1046        );
1047        switch (rc) {
1048            .SUCCESS => return,
1049            // The filesystem does not support FileDispositionInformationEx
1050            .INVALID_PARAMETER,
1051            // The operating system does not support FileDispositionInformationEx
1052            .INVALID_INFO_CLASS,
1053            // The operating system does not support one of the flags
1054            .NOT_SUPPORTED,
1055            => break :need_fallback true,
1056            // For all other statuses, fall down to the switch below to handle them.
1057            else => break :need_fallback false,
1058        }
1059    };
1060
1061    if (need_fallback) {
1062        // Deletion with file pending semantics, which requires waiting or moving
1063        // files to get them removed (from here).
1064        var file_dispo = FILE_DISPOSITION_INFORMATION{
1065            .DeleteFile = TRUE,
1066        };
1067
1068        rc = ntdll.NtSetInformationFile(
1069            tmp_handle,
1070            &io,
1071            &file_dispo,
1072            @sizeOf(FILE_DISPOSITION_INFORMATION),
1073            .FileDispositionInformation,
1074        );
1075    }
1076    switch (rc) {
1077        .SUCCESS => {},
1078        .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
1079        .INVALID_PARAMETER => unreachable,
1080        .CANNOT_DELETE => return error.AccessDenied,
1081        .MEDIA_WRITE_PROTECTED => return error.AccessDenied,
1082        .ACCESS_DENIED => return error.AccessDenied,
1083        else => return unexpectedStatus(rc),
1084    }
1085}
1086
1087pub const RenameError = error{
1088    IsDir,
1089    NotDir,
1090    FileNotFound,
1091    NoDevice,
1092    AccessDenied,
1093    PipeBusy,
1094    PathAlreadyExists,
1095    Unexpected,
1096    NameTooLong,
1097    NetworkNotFound,
1098    AntivirusInterference,
1099    BadPathName,
1100    RenameAcrossMountPoints,
1101} || UnexpectedError;
1102
1103pub fn RenameFile(
1104    /// May only be `null` if `old_path_w` is a fully-qualified absolute path.
1105    old_dir_fd: ?HANDLE,
1106    old_path_w: []const u16,
1107    /// May only be `null` if `new_path_w` is a fully-qualified absolute path,
1108    /// or if the file is not being moved to a different directory.
1109    new_dir_fd: ?HANDLE,
1110    new_path_w: []const u16,
1111    replace_if_exists: bool,
1112) RenameError!void {
1113    const src_fd = OpenFile(old_path_w, .{
1114        .dir = old_dir_fd,
1115        .access_mask = SYNCHRONIZE | GENERIC_WRITE | DELETE,
1116        .creation = FILE_OPEN,
1117        .filter = .any, // This function is supposed to rename both files and directories.
1118        .follow_symlinks = false,
1119    }) catch |err| switch (err) {
1120        error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
1121        else => |e| return e,
1122    };
1123    defer CloseHandle(src_fd);
1124
1125    var rc: NTSTATUS = undefined;
1126    // FileRenameInformationEx has varying levels of support:
1127    // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1
1128    //   (INVALID_INFO_CLASS is returned if not supported)
1129    // - Requires the NTFS filesystem
1130    //   (on filesystems like FAT32, INVALID_PARAMETER is returned)
1131    // - FILE_RENAME_POSIX_SEMANTICS requires >= win10_rs1
1132    // - FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
1133    //   (NOT_SUPPORTED is returned if a flag is unsupported)
1134    //
1135    // The strategy here is just to try using FileRenameInformationEx and fall back to
1136    // FileRenameInformation if the return value lets us know that some aspect of it is not supported.
1137    const need_fallback = need_fallback: {
1138        const struct_buf_len = @sizeOf(FILE_RENAME_INFORMATION_EX) + (PATH_MAX_WIDE * 2);
1139        var rename_info_buf: [struct_buf_len]u8 align(@alignOf(FILE_RENAME_INFORMATION_EX)) = undefined;
1140        const struct_len = @sizeOf(FILE_RENAME_INFORMATION_EX) + new_path_w.len * 2;
1141        if (struct_len > struct_buf_len) return error.NameTooLong;
1142
1143        const rename_info: *FILE_RENAME_INFORMATION_EX = @ptrCast(&rename_info_buf);
1144        var io_status_block: IO_STATUS_BLOCK = undefined;
1145
1146        var flags: ULONG = FILE_RENAME_POSIX_SEMANTICS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE;
1147        if (replace_if_exists) flags |= FILE_RENAME_REPLACE_IF_EXISTS;
1148        rename_info.* = .{
1149            .Flags = flags,
1150            .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
1151            .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
1152            .FileName = undefined,
1153        };
1154        @memcpy((&rename_info.FileName).ptr, new_path_w);
1155        rc = ntdll.NtSetInformationFile(
1156            src_fd,
1157            &io_status_block,
1158            rename_info,
1159            @intCast(struct_len), // already checked for error.NameTooLong
1160            .FileRenameInformationEx,
1161        );
1162        switch (rc) {
1163            .SUCCESS => return,
1164            // The filesystem does not support FileDispositionInformationEx
1165            .INVALID_PARAMETER,
1166            // The operating system does not support FileDispositionInformationEx
1167            .INVALID_INFO_CLASS,
1168            // The operating system does not support one of the flags
1169            .NOT_SUPPORTED,
1170            => break :need_fallback true,
1171            // For all other statuses, fall down to the switch below to handle them.
1172            else => break :need_fallback false,
1173        }
1174    };
1175
1176    if (need_fallback) {
1177        const struct_buf_len = @sizeOf(FILE_RENAME_INFORMATION) + (PATH_MAX_WIDE * 2);
1178        var rename_info_buf: [struct_buf_len]u8 align(@alignOf(FILE_RENAME_INFORMATION)) = undefined;
1179        const struct_len = @sizeOf(FILE_RENAME_INFORMATION) + new_path_w.len * 2;
1180        if (struct_len > struct_buf_len) return error.NameTooLong;
1181
1182        const rename_info: *FILE_RENAME_INFORMATION = @ptrCast(&rename_info_buf);
1183        var io_status_block: IO_STATUS_BLOCK = undefined;
1184
1185        rename_info.* = .{
1186            .Flags = @intFromBool(replace_if_exists),
1187            .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
1188            .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
1189            .FileName = undefined,
1190        };
1191        @memcpy((&rename_info.FileName).ptr, new_path_w);
1192
1193        rc = ntdll.NtSetInformationFile(
1194            src_fd,
1195            &io_status_block,
1196            rename_info,
1197            @intCast(struct_len), // already checked for error.NameTooLong
1198            .FileRenameInformation,
1199        );
1200    }
1201
1202    switch (rc) {
1203        .SUCCESS => {},
1204        .INVALID_HANDLE => unreachable,
1205        .INVALID_PARAMETER => unreachable,
1206        .OBJECT_PATH_SYNTAX_BAD => unreachable,
1207        .ACCESS_DENIED => return error.AccessDenied,
1208        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1209        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
1210        .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
1211        .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
1212        .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
1213        .FILE_IS_A_DIRECTORY => return error.IsDir,
1214        .NOT_A_DIRECTORY => return error.NotDir,
1215        else => return unexpectedStatus(rc),
1216    }
1217}
1218
1219pub const GetStdHandleError = error{
1220    NoStandardHandleAttached,
1221    Unexpected,
1222};
1223
1224pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {
1225    const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
1226    if (handle == INVALID_HANDLE_VALUE) {
1227        switch (GetLastError()) {
1228            else => |err| return unexpectedError(err),
1229        }
1230    }
1231    return handle;
1232}
1233
1234pub const SetFilePointerError = error{
1235    Unseekable,
1236    Unexpected,
1237};
1238
1239/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
1240pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
1241    // "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
1242    // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
1243    // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
1244    const ipos = @as(LARGE_INTEGER, @bitCast(offset));
1245    if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
1246        switch (GetLastError()) {
1247            .INVALID_FUNCTION => return error.Unseekable,
1248            .NEGATIVE_SEEK => return error.Unseekable,
1249            .INVALID_PARAMETER => unreachable,
1250            .INVALID_HANDLE => unreachable,
1251            else => |err| return unexpectedError(err),
1252        }
1253    }
1254}
1255
1256/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
1257pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
1258    if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
1259        switch (GetLastError()) {
1260            .INVALID_FUNCTION => return error.Unseekable,
1261            .NEGATIVE_SEEK => return error.Unseekable,
1262            .INVALID_PARAMETER => unreachable,
1263            .INVALID_HANDLE => unreachable,
1264            else => |err| return unexpectedError(err),
1265        }
1266    }
1267}
1268
1269/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
1270pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
1271    if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
1272        switch (GetLastError()) {
1273            .INVALID_FUNCTION => return error.Unseekable,
1274            .NEGATIVE_SEEK => return error.Unseekable,
1275            .INVALID_PARAMETER => unreachable,
1276            .INVALID_HANDLE => unreachable,
1277            else => |err| return unexpectedError(err),
1278        }
1279    }
1280}
1281
1282/// The SetFilePointerEx function with parameters to get the current offset.
1283pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
1284    var result: LARGE_INTEGER = undefined;
1285    if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
1286        switch (GetLastError()) {
1287            .INVALID_FUNCTION => return error.Unseekable,
1288            .NEGATIVE_SEEK => return error.Unseekable,
1289            .INVALID_PARAMETER => unreachable,
1290            .INVALID_HANDLE => unreachable,
1291            else => |err| return unexpectedError(err),
1292        }
1293    }
1294    // Based on the docs for FILE_BEGIN, it seems that the returned signed integer
1295    // should be interpreted as an unsigned integer.
1296    return @as(u64, @bitCast(result));
1297}
1298
1299pub const QueryObjectNameError = error{
1300    AccessDenied,
1301    InvalidHandle,
1302    NameTooLong,
1303    Unexpected,
1304};
1305
1306pub fn QueryObjectName(handle: HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 {
1307    const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
1308
1309    const info = @as(*OBJECT_NAME_INFORMATION, @ptrCast(out_buffer_aligned));
1310    // buffer size is specified in bytes
1311    const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(ULONG);
1312    // last argument would return the length required for full_buffer, not exposed here
1313    return switch (ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) {
1314        .SUCCESS => blk: {
1315            // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
1316            // if the object was "unnamed", not sure if this can happen for file handles
1317            if (info.Name.MaximumLength == 0) break :blk error.Unexpected;
1318            // resulting string length is specified in bytes
1319            const path_length_unterminated = @divExact(info.Name.Length, 2);
1320            break :blk info.Name.Buffer.?[0..path_length_unterminated];
1321        },
1322        .ACCESS_DENIED => error.AccessDenied,
1323        .INVALID_HANDLE => error.InvalidHandle,
1324        // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
1325        // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
1326        .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong,
1327        else => |e| unexpectedStatus(e),
1328    };
1329}
1330
1331test QueryObjectName {
1332    if (builtin.os.tag != .windows)
1333        return;
1334
1335    //any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
1336    var tmp = std.testing.tmpDir(.{});
1337    defer tmp.cleanup();
1338    const handle = tmp.dir.fd;
1339    var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
1340
1341    const result_path = try QueryObjectName(handle, &out_buffer);
1342    const required_len_in_u16 = result_path.len + @divExact(@intFromPtr(result_path.ptr) - @intFromPtr(&out_buffer), 2) + 1;
1343    //insufficient size
1344    try std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
1345    //exactly-sufficient size
1346    _ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
1347}
1348
1349pub const GetFinalPathNameByHandleError = error{
1350    AccessDenied,
1351    BadPathName,
1352    FileNotFound,
1353    NameTooLong,
1354    /// The volume does not contain a recognized file system. File system
1355    /// drivers might not be loaded, or the volume may be corrupt.
1356    UnrecognizedVolume,
1357    Unexpected,
1358};
1359
1360/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
1361/// Defaults to DOS volume names.
1362pub const GetFinalPathNameByHandleFormat = struct {
1363    volume_name: enum {
1364        /// Format as DOS volume name
1365        Dos,
1366        /// Format as NT volume name
1367        Nt,
1368    } = .Dos,
1369};
1370
1371/// Returns canonical (normalized) path of handle.
1372/// Use `GetFinalPathNameByHandleFormat` to specify whether the path is meant to include
1373/// NT or DOS volume name (e.g., `\Device\HarddiskVolume0\foo.txt` versus `C:\foo.txt`).
1374/// If DOS volume name format is selected, note that this function does *not* prepend
1375/// `\\?\` prefix to the resultant path.
1376pub fn GetFinalPathNameByHandle(
1377    hFile: HANDLE,
1378    fmt: GetFinalPathNameByHandleFormat,
1379    out_buffer: []u16,
1380) GetFinalPathNameByHandleError![]u16 {
1381    const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
1382        // we assume InvalidHandle is close enough to FileNotFound in semantics
1383        // to not further complicate the error set
1384        error.InvalidHandle => return error.FileNotFound,
1385        else => |e| return e,
1386    };
1387
1388    switch (fmt.volume_name) {
1389        .Nt => {
1390            // the returned path is already in .Nt format
1391            return final_path;
1392        },
1393        .Dos => {
1394            // parse the string to separate volume path from file path
1395            const device_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
1396
1397            // We aren't entirely sure of the structure of the path returned by
1398            // QueryObjectName in all contexts/environments.
1399            // This code is written to cover the various cases that have
1400            // been encountered and solved appropriately. But note that there's
1401            // no easy way to verify that they have all been tackled!
1402            // (Unless you, the reader knows of one then please do action that!)
1403            if (!mem.startsWith(u16, final_path, device_prefix)) {
1404                // Wine seems to return NT namespaced paths starting with \??\ from QueryObjectName
1405                // (e.g. `\??\Z:\some\path\to\a\file.txt`), in which case we can just strip the
1406                // prefix to turn it into an absolute path.
1407                // https://github.com/ziglang/zig/issues/26029
1408                // https://bugs.winehq.org/show_bug.cgi?id=39569
1409                return ntToWin32Namespace(final_path, out_buffer) catch |err| switch (err) {
1410                    error.NotNtPath => return error.Unexpected,
1411                    error.NameTooLong => |e| return e,
1412                };
1413            }
1414
1415            const file_path_begin_index = mem.indexOfPos(u16, final_path, device_prefix.len, &[_]u16{'\\'}) orelse unreachable;
1416            const volume_name_u16 = final_path[0..file_path_begin_index];
1417            const device_name_u16 = volume_name_u16[device_prefix.len..];
1418            const file_name_u16 = final_path[file_path_begin_index..];
1419
1420            // MUP is Multiple UNC Provider, and indicates that the path is a UNC
1421            // path. In this case, the canonical UNC path can be gotten by just
1422            // dropping the \Device\Mup\ and making sure the path begins with \\
1423            if (mem.eql(u16, device_name_u16, std.unicode.utf8ToUtf16LeStringLiteral("Mup"))) {
1424                out_buffer[0] = '\\';
1425                @memmove(out_buffer[1..][0..file_name_u16.len], file_name_u16);
1426                return out_buffer[0 .. 1 + file_name_u16.len];
1427            }
1428
1429            // Get DOS volume name. DOS volume names are actually symbolic link objects to the
1430            // actual NT volume. For example:
1431            // (NT) \Device\HarddiskVolume4 => (DOS) \DosDevices\C: == (DOS) C:
1432            const MIN_SIZE = @sizeOf(MOUNTMGR_MOUNT_POINT) + MAX_PATH;
1433            // We initialize the input buffer to all zeros for convenience since
1434            // `DeviceIoControl` with `IOCTL_MOUNTMGR_QUERY_POINTS` expects this.
1435            var input_buf: [MIN_SIZE]u8 align(@alignOf(MOUNTMGR_MOUNT_POINT)) = [_]u8{0} ** MIN_SIZE;
1436            var output_buf: [MIN_SIZE * 4]u8 align(@alignOf(MOUNTMGR_MOUNT_POINTS)) = undefined;
1437
1438            // This surprising path is a filesystem path to the mount manager on Windows.
1439            // Source: https://stackoverflow.com/questions/3012828/using-ioctl-mountmgr-query-points
1440            // This is the NT namespaced version of \\.\MountPointManager
1441            const mgmt_path_u16 = std.unicode.utf8ToUtf16LeStringLiteral("\\??\\MountPointManager");
1442            const mgmt_handle = OpenFile(mgmt_path_u16, .{
1443                .access_mask = SYNCHRONIZE,
1444                .share_access = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1445                .creation = FILE_OPEN,
1446            }) catch |err| switch (err) {
1447                error.IsDir => return error.Unexpected,
1448                error.NotDir => return error.Unexpected,
1449                error.NoDevice => return error.Unexpected,
1450                error.AccessDenied => return error.Unexpected,
1451                error.PipeBusy => return error.Unexpected,
1452                error.PathAlreadyExists => return error.Unexpected,
1453                error.WouldBlock => return error.Unexpected,
1454                error.NetworkNotFound => return error.Unexpected,
1455                error.AntivirusInterference => return error.Unexpected,
1456                else => |e| return e,
1457            };
1458            defer CloseHandle(mgmt_handle);
1459
1460            var input_struct: *MOUNTMGR_MOUNT_POINT = @ptrCast(&input_buf[0]);
1461            input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
1462            input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
1463            @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
1464
1465            DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {
1466                error.AccessDenied => return error.Unexpected,
1467                else => |e| return e,
1468            };
1469            const mount_points_struct: *const MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
1470
1471            const mount_points = @as(
1472                [*]const MOUNTMGR_MOUNT_POINT,
1473                @ptrCast(&mount_points_struct.MountPoints[0]),
1474            )[0..mount_points_struct.NumberOfMountPoints];
1475
1476            for (mount_points) |mount_point| {
1477                const symlink = @as(
1478                    [*]const u16,
1479                    @ptrCast(@alignCast(&output_buf[mount_point.SymbolicLinkNameOffset])),
1480                )[0 .. mount_point.SymbolicLinkNameLength / 2];
1481
1482                // Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
1483                // with traditional DOS drive letters, so pick the first one available.
1484                var prefix_buf = std.unicode.utf8ToUtf16LeStringLiteral("\\DosDevices\\");
1485                const prefix = prefix_buf[0..prefix_buf.len];
1486
1487                if (mem.startsWith(u16, symlink, prefix)) {
1488                    const drive_letter = symlink[prefix.len..];
1489
1490                    if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
1491
1492                    @memcpy(out_buffer[0..drive_letter.len], drive_letter);
1493                    @memmove(out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16);
1494                    const total_len = drive_letter.len + file_name_u16.len;
1495
1496                    // Validate that DOS does not contain any spurious nul bytes.
1497                    if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
1498                        return error.BadPathName;
1499                    }
1500
1501                    return out_buffer[0..total_len];
1502                } else if (mountmgrIsVolumeName(symlink)) {
1503                    // If the symlink is a volume GUID like \??\Volume{383da0b0-717f-41b6-8c36-00500992b58d},
1504                    // then it is a volume mounted as a path rather than a drive letter. We need to
1505                    // query the mount manager again to get the DOS path for the volume.
1506
1507                    // 49 is the maximum length accepted by mountmgrIsVolumeName
1508                    const vol_input_size = @sizeOf(MOUNTMGR_TARGET_NAME) + (49 * 2);
1509                    var vol_input_buf: [vol_input_size]u8 align(@alignOf(MOUNTMGR_TARGET_NAME)) = [_]u8{0} ** vol_input_size;
1510                    // Note: If the path exceeds MAX_PATH, the Disk Management GUI doesn't accept the full path,
1511                    // and instead if must be specified using a shortened form (e.g. C:\FOO~1\BAR~1\<...>).
1512                    // However, just to be sure we can handle any path length, we use PATH_MAX_WIDE here.
1513                    const min_output_size = @sizeOf(MOUNTMGR_VOLUME_PATHS) + (PATH_MAX_WIDE * 2);
1514                    var vol_output_buf: [min_output_size]u8 align(@alignOf(MOUNTMGR_VOLUME_PATHS)) = undefined;
1515
1516                    var vol_input_struct: *MOUNTMGR_TARGET_NAME = @ptrCast(&vol_input_buf[0]);
1517                    vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
1518                    @memcpy(@as([*]WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
1519
1520                    DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, &vol_input_buf, &vol_output_buf) catch |err| switch (err) {
1521                        error.AccessDenied => return error.Unexpected,
1522                        else => |e| return e,
1523                    };
1524                    const volume_paths_struct: *const MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
1525                    const volume_path = std.mem.sliceTo(@as(
1526                        [*]const u16,
1527                        &volume_paths_struct.MultiSz,
1528                    )[0 .. volume_paths_struct.MultiSzLength / 2], 0);
1529
1530                    if (out_buffer.len < volume_path.len + file_name_u16.len) return error.NameTooLong;
1531
1532                    // `out_buffer` currently contains the memory of `file_name_u16`, so it can overlap with where
1533                    // we want to place the filename before returning. Here are the possible overlapping cases:
1534                    //
1535                    // out_buffer:       [filename]
1536                    //       dest: [___(a)___] [___(b)___]
1537                    //
1538                    // In the case of (a), we need to copy forwards, and in the case of (b) we need
1539                    // to copy backwards. We also need to do this before copying the volume path because
1540                    // it could overwrite the file_name_u16 memory.
1541                    const file_name_dest = out_buffer[volume_path.len..][0..file_name_u16.len];
1542                    @memmove(file_name_dest, file_name_u16);
1543                    @memcpy(out_buffer[0..volume_path.len], volume_path);
1544                    const total_len = volume_path.len + file_name_u16.len;
1545
1546                    // Validate that DOS does not contain any spurious nul bytes.
1547                    if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
1548                        return error.BadPathName;
1549                    }
1550
1551                    return out_buffer[0..total_len];
1552                }
1553            }
1554
1555            // If we've ended up here, then something went wrong/is corrupted in the OS,
1556            // so error out!
1557            return error.FileNotFound;
1558        },
1559    }
1560}
1561
1562/// Equivalent to the MOUNTMGR_IS_VOLUME_NAME macro in mountmgr.h
1563fn mountmgrIsVolumeName(name: []const u16) bool {
1564    return (name.len == 48 or (name.len == 49 and name[48] == mem.nativeToLittle(u16, '\\'))) and
1565        name[0] == mem.nativeToLittle(u16, '\\') and
1566        (name[1] == mem.nativeToLittle(u16, '?') or name[1] == mem.nativeToLittle(u16, '\\')) and
1567        name[2] == mem.nativeToLittle(u16, '?') and
1568        name[3] == mem.nativeToLittle(u16, '\\') and
1569        mem.startsWith(u16, name[4..], std.unicode.utf8ToUtf16LeStringLiteral("Volume{")) and
1570        name[19] == mem.nativeToLittle(u16, '-') and
1571        name[24] == mem.nativeToLittle(u16, '-') and
1572        name[29] == mem.nativeToLittle(u16, '-') and
1573        name[34] == mem.nativeToLittle(u16, '-') and
1574        name[47] == mem.nativeToLittle(u16, '}');
1575}
1576
1577test mountmgrIsVolumeName {
1578    @setEvalBranchQuota(2000);
1579    const L = std.unicode.utf8ToUtf16LeStringLiteral;
1580    try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
1581    try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
1582    try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
1583    try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
1584    try std.testing.expect(!mountmgrIsVolumeName(L("\\\\.\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
1585    try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\foo")));
1586    try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58}")));
1587}
1588
1589test GetFinalPathNameByHandle {
1590    if (builtin.os.tag != .windows)
1591        return;
1592
1593    //any file will do
1594    var tmp = std.testing.tmpDir(.{});
1595    defer tmp.cleanup();
1596    const handle = tmp.dir.fd;
1597    var buffer: [PATH_MAX_WIDE]u16 = undefined;
1598
1599    //check with sufficient size
1600    const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
1601    _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
1602
1603    const required_len_in_u16 = nt_path.len + @divExact(@intFromPtr(nt_path.ptr) - @intFromPtr(&buffer), 2) + 1;
1604    //check with insufficient size
1605    try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
1606    try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
1607
1608    //check with exactly-sufficient size
1609    _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
1610    _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
1611}
1612
1613pub const GetFileSizeError = error{Unexpected};
1614
1615pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 {
1616    var file_size: LARGE_INTEGER = undefined;
1617    if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) {
1618        switch (GetLastError()) {
1619            else => |err| return unexpectedError(err),
1620        }
1621    }
1622    return @as(u64, @bitCast(file_size));
1623}
1624
1625pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
1626    return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen)));
1627}
1628
1629pub fn sendmsg(
1630    s: ws2_32.SOCKET,
1631    msg: *ws2_32.WSAMSG_const,
1632    flags: u32,
1633) i32 {
1634    var bytes_send: DWORD = undefined;
1635    if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
1636        return ws2_32.SOCKET_ERROR;
1637    } else {
1638        return @as(i32, @as(u31, @intCast(bytes_send)));
1639    }
1640}
1641
1642pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
1643    var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = @constCast(buf) };
1644    var bytes_send: DWORD = undefined;
1645    if (ws2_32.WSASendTo(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_send, flags, to, @as(i32, @intCast(to_len)), null, null) == ws2_32.SOCKET_ERROR) {
1646        return ws2_32.SOCKET_ERROR;
1647    } else {
1648        return @as(i32, @as(u31, @intCast(bytes_send)));
1649    }
1650}
1651
1652pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
1653    var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = buf };
1654    var bytes_received: DWORD = undefined;
1655    var flags_inout = flags;
1656    if (ws2_32.WSARecvFrom(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_received, &flags_inout, from, @as(?*i32, @ptrCast(from_len)), null, null) == ws2_32.SOCKET_ERROR) {
1657        return ws2_32.SOCKET_ERROR;
1658    } else {
1659        return @as(i32, @as(u31, @intCast(bytes_received)));
1660    }
1661}
1662
1663pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
1664    return ws2_32.WSAPoll(fds, n, timeout);
1665}
1666
1667pub fn WSAIoctl(
1668    s: ws2_32.SOCKET,
1669    dwIoControlCode: DWORD,
1670    inBuffer: ?[]const u8,
1671    outBuffer: []u8,
1672    overlapped: ?*OVERLAPPED,
1673    completionRoutine: ?ws2_32.LPWSAOVERLAPPED_COMPLETION_ROUTINE,
1674) !DWORD {
1675    var bytes: DWORD = undefined;
1676    switch (ws2_32.WSAIoctl(
1677        s,
1678        dwIoControlCode,
1679        if (inBuffer) |i| i.ptr else null,
1680        if (inBuffer) |i| @as(DWORD, @intCast(i.len)) else 0,
1681        outBuffer.ptr,
1682        @as(DWORD, @intCast(outBuffer.len)),
1683        &bytes,
1684        overlapped,
1685        completionRoutine,
1686    )) {
1687        0 => {},
1688        ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
1689            else => |err| return unexpectedWSAError(err),
1690        },
1691        else => unreachable,
1692    }
1693    return bytes;
1694}
1695
1696const GetModuleFileNameError = error{Unexpected};
1697
1698pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 {
1699    const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
1700    if (rc == 0) {
1701        switch (GetLastError()) {
1702            else => |err| return unexpectedError(err),
1703        }
1704    }
1705    return buf_ptr[0..rc :0];
1706}
1707
1708pub const TerminateProcessError = error{ AccessDenied, Unexpected };
1709
1710pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void {
1711    if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) {
1712        switch (GetLastError()) {
1713            Win32Error.ACCESS_DENIED => return error.AccessDenied,
1714            else => |err| return unexpectedError(err),
1715        }
1716    }
1717}
1718
1719pub const NtAllocateVirtualMemoryError = error{
1720    AccessDenied,
1721    InvalidParameter,
1722    NoMemory,
1723    Unexpected,
1724};
1725
1726pub fn NtAllocateVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, zero_bits: ULONG_PTR, size: ?*SIZE_T, alloc_type: ULONG, protect: ULONG) NtAllocateVirtualMemoryError!void {
1727    return switch (ntdll.NtAllocateVirtualMemory(hProcess, addr, zero_bits, size, alloc_type, protect)) {
1728        .SUCCESS => return,
1729        .ACCESS_DENIED => NtAllocateVirtualMemoryError.AccessDenied,
1730        .INVALID_PARAMETER => NtAllocateVirtualMemoryError.InvalidParameter,
1731        .NO_MEMORY => NtAllocateVirtualMemoryError.NoMemory,
1732        else => |st| unexpectedStatus(st),
1733    };
1734}
1735
1736pub const NtFreeVirtualMemoryError = error{
1737    AccessDenied,
1738    InvalidParameter,
1739    Unexpected,
1740};
1741
1742pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_type: ULONG) NtFreeVirtualMemoryError!void {
1743    // TODO: If the return value is .INVALID_PAGE_PROTECTION, call RtlFlushSecureMemoryCache and try again.
1744    return switch (ntdll.NtFreeVirtualMemory(hProcess, addr, size, free_type)) {
1745        .SUCCESS => return,
1746        .ACCESS_DENIED => NtFreeVirtualMemoryError.AccessDenied,
1747        .INVALID_PARAMETER => NtFreeVirtualMemoryError.InvalidParameter,
1748        else => NtFreeVirtualMemoryError.Unexpected,
1749    };
1750}
1751
1752pub const VirtualProtectError = error{
1753    InvalidAddress,
1754    Unexpected,
1755};
1756
1757pub fn VirtualProtect(lpAddress: ?LPVOID, dwSize: SIZE_T, flNewProtect: DWORD, lpflOldProtect: *DWORD) VirtualProtectError!void {
1758    // ntdll takes an extra level of indirection here
1759    var addr = lpAddress;
1760    var size = dwSize;
1761    switch (ntdll.NtProtectVirtualMemory(self_process_handle, &addr, &size, flNewProtect, lpflOldProtect)) {
1762        .SUCCESS => {},
1763        .INVALID_ADDRESS => return error.InvalidAddress,
1764        else => |st| return unexpectedStatus(st),
1765    }
1766}
1767
1768pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: DWORD) VirtualProtectError!DWORD {
1769    var old_prot: DWORD = undefined;
1770    var out_addr = addr;
1771    var out_size = size;
1772    switch (ntdll.NtProtectVirtualMemory(
1773        handle,
1774        &out_addr,
1775        &out_size,
1776        new_prot,
1777        &old_prot,
1778    )) {
1779        .SUCCESS => return old_prot,
1780        .INVALID_ADDRESS => return error.InvalidAddress,
1781        // TODO: map errors
1782        else => |rc| return unexpectedStatus(rc),
1783    }
1784}
1785
1786pub const SetConsoleTextAttributeError = error{Unexpected};
1787
1788pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
1789    if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
1790        switch (GetLastError()) {
1791            else => |err| return unexpectedError(err),
1792        }
1793    }
1794}
1795
1796pub fn SetConsoleCtrlHandler(handler_routine: ?HANDLER_ROUTINE, add: bool) !void {
1797    const success = kernel32.SetConsoleCtrlHandler(
1798        handler_routine,
1799        if (add) TRUE else FALSE,
1800    );
1801
1802    if (success == FALSE) {
1803        return switch (GetLastError()) {
1804            else => |err| unexpectedError(err),
1805        };
1806    }
1807}
1808
1809pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
1810    const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
1811    if (success == FALSE) {
1812        return switch (GetLastError()) {
1813            else => |err| unexpectedError(err),
1814        };
1815    }
1816}
1817
1818pub const CreateProcessError = error{
1819    FileNotFound,
1820    AccessDenied,
1821    InvalidName,
1822    NameTooLong,
1823    InvalidExe,
1824    SystemResources,
1825    FileBusy,
1826    Unexpected,
1827};
1828
1829pub const CreateProcessFlags = packed struct(u32) {
1830    debug_process: bool = false,
1831    debug_only_this_process: bool = false,
1832    create_suspended: bool = false,
1833    detached_process: bool = false,
1834    create_new_console: bool = false,
1835    normal_priority_class: bool = false,
1836    idle_priority_class: bool = false,
1837    high_priority_class: bool = false,
1838    realtime_priority_class: bool = false,
1839    create_new_process_group: bool = false,
1840    create_unicode_environment: bool = false,
1841    create_separate_wow_vdm: bool = false,
1842    create_shared_wow_vdm: bool = false,
1843    create_forcedos: bool = false,
1844    below_normal_priority_class: bool = false,
1845    above_normal_priority_class: bool = false,
1846    inherit_parent_affinity: bool = false,
1847    inherit_caller_priority: bool = false,
1848    create_protected_process: bool = false,
1849    extended_startupinfo_present: bool = false,
1850    process_mode_background_begin: bool = false,
1851    process_mode_background_end: bool = false,
1852    create_secure_process: bool = false,
1853    _reserved: bool = false,
1854    create_breakaway_from_job: bool = false,
1855    create_preserve_code_authz_level: bool = false,
1856    create_default_error_mode: bool = false,
1857    create_no_window: bool = false,
1858    profile_user: bool = false,
1859    profile_kernel: bool = false,
1860    profile_server: bool = false,
1861    create_ignore_system_default: bool = false,
1862};
1863
1864pub fn CreateProcessW(
1865    lpApplicationName: ?LPCWSTR,
1866    lpCommandLine: ?LPWSTR,
1867    lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
1868    lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
1869    bInheritHandles: BOOL,
1870    dwCreationFlags: CreateProcessFlags,
1871    lpEnvironment: ?*anyopaque,
1872    lpCurrentDirectory: ?LPCWSTR,
1873    lpStartupInfo: *STARTUPINFOW,
1874    lpProcessInformation: *PROCESS_INFORMATION,
1875) CreateProcessError!void {
1876    if (kernel32.CreateProcessW(
1877        lpApplicationName,
1878        lpCommandLine,
1879        lpProcessAttributes,
1880        lpThreadAttributes,
1881        bInheritHandles,
1882        dwCreationFlags,
1883        lpEnvironment,
1884        lpCurrentDirectory,
1885        lpStartupInfo,
1886        lpProcessInformation,
1887    ) == 0) {
1888        switch (GetLastError()) {
1889            .FILE_NOT_FOUND => return error.FileNotFound,
1890            .PATH_NOT_FOUND => return error.FileNotFound,
1891            .DIRECTORY => return error.FileNotFound,
1892            .ACCESS_DENIED => return error.AccessDenied,
1893            .INVALID_PARAMETER => unreachable,
1894            .INVALID_NAME => return error.InvalidName,
1895            .FILENAME_EXCED_RANGE => return error.NameTooLong,
1896            .SHARING_VIOLATION => return error.FileBusy,
1897            // These are all the system errors that are mapped to ENOEXEC by
1898            // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
1899            // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)
1900            // or urt/misc/errno.cpp (newer SDK) in the Windows SDK.
1901            .BAD_FORMAT,
1902            .INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp
1903            .INVALID_STACKSEG,
1904            .INVALID_MODULETYPE,
1905            .INVALID_EXE_SIGNATURE,
1906            .EXE_MARKED_INVALID,
1907            .BAD_EXE_FORMAT,
1908            .ITERATED_DATA_EXCEEDS_64k,
1909            .INVALID_MINALLOCSIZE,
1910            .DYNLINK_FROM_INVALID_RING,
1911            .IOPL_NOT_ENABLED,
1912            .INVALID_SEGDPL,
1913            .AUTODATASEG_EXCEEDS_64k,
1914            .RING2SEG_MUST_BE_MOVABLE,
1915            .RELOC_CHAIN_XEEDS_SEGLIM,
1916            .INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp
1917            // This one is not mapped to ENOEXEC but it is possible, for example
1918            // when calling CreateProcessW on a plain text file with a .exe extension
1919            .EXE_MACHINE_TYPE_MISMATCH,
1920            => return error.InvalidExe,
1921            .COMMITMENT_LIMIT => return error.SystemResources,
1922            else => |err| return unexpectedError(err),
1923        }
1924    }
1925}
1926
1927pub const LoadLibraryError = error{
1928    FileNotFound,
1929    Unexpected,
1930};
1931
1932pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE {
1933    return kernel32.LoadLibraryW(lpLibFileName) orelse {
1934        switch (GetLastError()) {
1935            .FILE_NOT_FOUND => return error.FileNotFound,
1936            .PATH_NOT_FOUND => return error.FileNotFound,
1937            .MOD_NOT_FOUND => return error.FileNotFound,
1938            else => |err| return unexpectedError(err),
1939        }
1940    };
1941}
1942
1943pub const LoadLibraryFlags = enum(DWORD) {
1944    none = 0,
1945    dont_resolve_dll_references = 0x00000001,
1946    load_ignore_code_authz_level = 0x00000010,
1947    load_library_as_datafile = 0x00000002,
1948    load_library_as_datafile_exclusive = 0x00000040,
1949    load_library_as_image_resource = 0x00000020,
1950    load_library_search_application_dir = 0x00000200,
1951    load_library_search_default_dirs = 0x00001000,
1952    load_library_search_dll_load_dir = 0x00000100,
1953    load_library_search_system32 = 0x00000800,
1954    load_library_search_user_dirs = 0x00000400,
1955    load_with_altered_search_path = 0x00000008,
1956    load_library_require_signed_target = 0x00000080,
1957    load_library_safe_current_dirs = 0x00002000,
1958};
1959
1960pub fn LoadLibraryExW(lpLibFileName: [*:0]const u16, dwFlags: LoadLibraryFlags) LoadLibraryError!HMODULE {
1961    return kernel32.LoadLibraryExW(lpLibFileName, null, @intFromEnum(dwFlags)) orelse {
1962        switch (GetLastError()) {
1963            .FILE_NOT_FOUND => return error.FileNotFound,
1964            .PATH_NOT_FOUND => return error.FileNotFound,
1965            .MOD_NOT_FOUND => return error.FileNotFound,
1966            else => |err| return unexpectedError(err),
1967        }
1968    };
1969}
1970
1971pub fn FreeLibrary(hModule: HMODULE) void {
1972    assert(kernel32.FreeLibrary(hModule) != 0);
1973}
1974
1975pub fn QueryPerformanceFrequency() u64 {
1976    // "On systems that run Windows XP or later, the function will always succeed"
1977    // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
1978    var result: LARGE_INTEGER = undefined;
1979    assert(ntdll.RtlQueryPerformanceFrequency(&result) != 0);
1980    // The kernel treats this integer as unsigned.
1981    return @as(u64, @bitCast(result));
1982}
1983
1984pub fn QueryPerformanceCounter() u64 {
1985    // "On systems that run Windows XP or later, the function will always succeed"
1986    // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
1987    var result: LARGE_INTEGER = undefined;
1988    assert(ntdll.RtlQueryPerformanceCounter(&result) != 0);
1989    // The kernel treats this integer as unsigned.
1990    return @as(u64, @bitCast(result));
1991}
1992
1993pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*anyopaque, Context: ?*anyopaque) void {
1994    assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0);
1995}
1996
1997pub const SetFileTimeError = error{Unexpected};
1998
1999pub fn SetFileTime(
2000    hFile: HANDLE,
2001    lpCreationTime: ?*const FILETIME,
2002    lpLastAccessTime: ?*const FILETIME,
2003    lpLastWriteTime: ?*const FILETIME,
2004) SetFileTimeError!void {
2005    const rc = kernel32.SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
2006    if (rc == 0) {
2007        switch (GetLastError()) {
2008            else => |err| return unexpectedError(err),
2009        }
2010    }
2011}
2012
2013pub const LockFileError = error{
2014    SystemResources,
2015    WouldBlock,
2016} || UnexpectedError;
2017
2018pub fn LockFile(
2019    FileHandle: HANDLE,
2020    Event: ?HANDLE,
2021    ApcRoutine: ?*IO_APC_ROUTINE,
2022    ApcContext: ?*anyopaque,
2023    IoStatusBlock: *IO_STATUS_BLOCK,
2024    ByteOffset: *const LARGE_INTEGER,
2025    Length: *const LARGE_INTEGER,
2026    Key: ?*ULONG,
2027    FailImmediately: BOOLEAN,
2028    ExclusiveLock: BOOLEAN,
2029) !void {
2030    const rc = ntdll.NtLockFile(
2031        FileHandle,
2032        Event,
2033        ApcRoutine,
2034        ApcContext,
2035        IoStatusBlock,
2036        ByteOffset,
2037        Length,
2038        Key,
2039        FailImmediately,
2040        ExclusiveLock,
2041    );
2042    switch (rc) {
2043        .SUCCESS => return,
2044        .INSUFFICIENT_RESOURCES => return error.SystemResources,
2045        .LOCK_NOT_GRANTED => return error.WouldBlock,
2046        .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
2047        else => return unexpectedStatus(rc),
2048    }
2049}
2050
2051pub const UnlockFileError = error{
2052    RangeNotLocked,
2053} || UnexpectedError;
2054
2055pub fn UnlockFile(
2056    FileHandle: HANDLE,
2057    IoStatusBlock: *IO_STATUS_BLOCK,
2058    ByteOffset: *const LARGE_INTEGER,
2059    Length: *const LARGE_INTEGER,
2060    Key: ?*ULONG,
2061) !void {
2062    const rc = ntdll.NtUnlockFile(FileHandle, IoStatusBlock, ByteOffset, Length, Key);
2063    switch (rc) {
2064        .SUCCESS => return,
2065        .RANGE_NOT_LOCKED => return error.RangeNotLocked,
2066        .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
2067        else => return unexpectedStatus(rc),
2068    }
2069}
2070
2071/// This is a workaround for the C backend until zig has the ability to put
2072/// C code in inline assembly.
2073extern fn zig_thumb_windows_teb() callconv(.c) *anyopaque;
2074extern fn zig_aarch64_windows_teb() callconv(.c) *anyopaque;
2075extern fn zig_x86_windows_teb() callconv(.c) *anyopaque;
2076extern fn zig_x86_64_windows_teb() callconv(.c) *anyopaque;
2077
2078pub fn teb() *TEB {
2079    return switch (native_arch) {
2080        .thumb => if (builtin.zig_backend == .stage2_c)
2081            @ptrCast(@alignCast(zig_thumb_windows_teb()))
2082        else
2083            asm (
2084                \\ mrc p15, 0, %[ptr], c13, c0, 2
2085                : [ptr] "=r" (-> *TEB),
2086            ),
2087        .aarch64 => if (builtin.zig_backend == .stage2_c)
2088            @ptrCast(@alignCast(zig_aarch64_windows_teb()))
2089        else
2090            asm (
2091                \\ mov %[ptr], x18
2092                : [ptr] "=r" (-> *TEB),
2093            ),
2094        .x86 => if (builtin.zig_backend == .stage2_c)
2095            @ptrCast(@alignCast(zig_x86_windows_teb()))
2096        else
2097            asm (
2098                \\ movl %%fs:0x18, %[ptr]
2099                : [ptr] "=r" (-> *TEB),
2100            ),
2101        .x86_64 => if (builtin.zig_backend == .stage2_c)
2102            @ptrCast(@alignCast(zig_x86_64_windows_teb()))
2103        else
2104            asm (
2105                \\ movq %%gs:0x30, %[ptr]
2106                : [ptr] "=r" (-> *TEB),
2107            ),
2108        else => @compileError("unsupported arch"),
2109    };
2110}
2111
2112pub fn peb() *PEB {
2113    return teb().ProcessEnvironmentBlock;
2114}
2115
2116/// A file time is a 64-bit value that represents the number of 100-nanosecond
2117/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
2118/// Universal Time (UTC).
2119/// This function returns the number of nanoseconds since the canonical epoch,
2120/// which is the POSIX one (Jan 01, 1970 AD).
2121pub fn fromSysTime(hns: i64) Io.Timestamp {
2122    const adjusted_epoch: i128 = hns + std.time.epoch.windows * (std.time.ns_per_s / 100);
2123    return .fromNanoseconds(@intCast(adjusted_epoch * 100));
2124}
2125
2126pub fn toSysTime(ns: Io.Timestamp) i64 {
2127    const hns = @divFloor(ns.nanoseconds, 100);
2128    return @as(i64, @intCast(hns)) - std.time.epoch.windows * (std.time.ns_per_s / 100);
2129}
2130
2131pub fn fileTimeToNanoSeconds(ft: FILETIME) Io.Timestamp {
2132    const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2133    return fromSysTime(hns);
2134}
2135
2136/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
2137pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME {
2138    const adjusted: u64 = @bitCast(toSysTime(ns));
2139    return .{
2140        .dwHighDateTime = @as(u32, @truncate(adjusted >> 32)),
2141        .dwLowDateTime = @as(u32, @truncate(adjusted)),
2142    };
2143}
2144
2145/// Compares two WTF16 strings using the equivalent functionality of
2146/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
2147/// This function can be called on any target.
2148pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
2149    if (@inComptime() or builtin.os.tag != .windows) {
2150        // This function compares the strings code unit by code unit (aka u16-to-u16),
2151        // so any length difference implies inequality. In other words, there's no possible
2152        // conversion that changes the number of WTF-16 code units needed for the uppercase/lowercase
2153        // version in the conversion table since only codepoints <= max(u16) are eligible
2154        // for conversion at all.
2155        if (a.len != b.len) return false;
2156
2157        for (a, b) |a_c, b_c| {
2158            // The slices are always WTF-16 LE, so need to convert the elements to native
2159            // endianness for the uppercasing
2160            const a_c_native = std.mem.littleToNative(u16, a_c);
2161            const b_c_native = std.mem.littleToNative(u16, b_c);
2162            if (a_c != b_c and nls.upcaseW(a_c_native) != nls.upcaseW(b_c_native)) {
2163                return false;
2164            }
2165        }
2166        return true;
2167    }
2168    // Use RtlEqualUnicodeString on Windows when not in comptime to avoid including a
2169    // redundant copy of the uppercase data.
2170    const a_bytes = @as(u16, @intCast(a.len * 2));
2171    const a_string = UNICODE_STRING{
2172        .Length = a_bytes,
2173        .MaximumLength = a_bytes,
2174        .Buffer = @constCast(a.ptr),
2175    };
2176    const b_bytes = @as(u16, @intCast(b.len * 2));
2177    const b_string = UNICODE_STRING{
2178        .Length = b_bytes,
2179        .MaximumLength = b_bytes,
2180        .Buffer = @constCast(b.ptr),
2181    };
2182    return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
2183}
2184
2185/// Compares two WTF-8 strings using the equivalent functionality of
2186/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
2187/// This function can be called on any target.
2188/// Assumes `a` and `b` are valid WTF-8.
2189pub fn eqlIgnoreCaseWtf8(a: []const u8, b: []const u8) bool {
2190    // A length equality check is not possible here because there are
2191    // some codepoints that have a different length uppercase UTF-8 representations
2192    // than their lowercase counterparts, e.g. U+0250 (2 bytes) <-> U+2C6F (3 bytes).
2193    // There are 7 such codepoints in the uppercase data used by Windows.
2194
2195    var a_wtf8_it = std.unicode.Wtf8View.initUnchecked(a).iterator();
2196    var b_wtf8_it = std.unicode.Wtf8View.initUnchecked(b).iterator();
2197
2198    // Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a
2199    // redundant copy of the uppercase data.
2200    const upcaseImpl = switch (builtin.os.tag) {
2201        .windows => if (@inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar,
2202        else => nls.upcaseW,
2203    };
2204
2205    while (true) {
2206        const a_cp = a_wtf8_it.nextCodepoint() orelse break;
2207        const b_cp = b_wtf8_it.nextCodepoint() orelse return false;
2208
2209        if (a_cp <= std.math.maxInt(u16) and b_cp <= std.math.maxInt(u16)) {
2210            if (a_cp != b_cp and upcaseImpl(@intCast(a_cp)) != upcaseImpl(@intCast(b_cp))) {
2211                return false;
2212            }
2213        } else if (a_cp != b_cp) {
2214            return false;
2215        }
2216    }
2217    // Make sure there are no leftover codepoints in b
2218    if (b_wtf8_it.nextCodepoint() != null) return false;
2219
2220    return true;
2221}
2222
2223fn testEqlIgnoreCase(comptime expect_eql: bool, comptime a: []const u8, comptime b: []const u8) !void {
2224    try std.testing.expectEqual(expect_eql, eqlIgnoreCaseWtf8(a, b));
2225    try std.testing.expectEqual(expect_eql, eqlIgnoreCaseWtf16(
2226        std.unicode.utf8ToUtf16LeStringLiteral(a),
2227        std.unicode.utf8ToUtf16LeStringLiteral(b),
2228    ));
2229
2230    try comptime std.testing.expect(expect_eql == eqlIgnoreCaseWtf8(a, b));
2231    try comptime std.testing.expect(expect_eql == eqlIgnoreCaseWtf16(
2232        std.unicode.utf8ToUtf16LeStringLiteral(a),
2233        std.unicode.utf8ToUtf16LeStringLiteral(b),
2234    ));
2235}
2236
2237test "eqlIgnoreCaseWtf16/Wtf8" {
2238    try testEqlIgnoreCase(true, "\x01 a B Λ ɐ", "\x01 A b λ Ɐ");
2239    // does not do case-insensitive comparison for codepoints >= U+10000
2240    try testEqlIgnoreCase(false, "𐓏", "𐓷");
2241}
2242
2243pub const PathSpace = struct {
2244    data: [PATH_MAX_WIDE:0]u16,
2245    len: usize,
2246
2247    pub fn span(self: *const PathSpace) [:0]const u16 {
2248        return self.data[0..self.len :0];
2249    }
2250};
2251
2252/// The error type for `removeDotDirsSanitized`
2253pub const RemoveDotDirsError = error{TooManyParentDirs};
2254
2255/// Removes '.' and '..' path components from a "sanitized relative path".
2256/// A "sanitized path" is one where:
2257///    1) all forward slashes have been replaced with back slashes
2258///    2) all repeating back slashes have been collapsed
2259///    3) the path is a relative one (does not start with a back slash)
2260pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize {
2261    std.debug.assert(path.len == 0 or path[0] != '\\');
2262
2263    var write_idx: usize = 0;
2264    var read_idx: usize = 0;
2265    while (read_idx < path.len) {
2266        if (path[read_idx] == '.') {
2267            if (read_idx + 1 == path.len)
2268                return write_idx;
2269
2270            const after_dot = path[read_idx + 1];
2271            if (after_dot == '\\') {
2272                read_idx += 2;
2273                continue;
2274            }
2275            if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) {
2276                if (write_idx == 0) return error.TooManyParentDirs;
2277                std.debug.assert(write_idx >= 2);
2278                write_idx -= 1;
2279                while (true) {
2280                    write_idx -= 1;
2281                    if (write_idx == 0) break;
2282                    if (path[write_idx] == '\\') {
2283                        write_idx += 1;
2284                        break;
2285                    }
2286                }
2287                if (read_idx + 2 == path.len)
2288                    return write_idx;
2289                read_idx += 3;
2290                continue;
2291            }
2292        }
2293
2294        // skip to the next path separator
2295        while (true) : (read_idx += 1) {
2296            if (read_idx == path.len)
2297                return write_idx;
2298            path[write_idx] = path[read_idx];
2299            write_idx += 1;
2300            if (path[read_idx] == '\\')
2301                break;
2302        }
2303        read_idx += 1;
2304    }
2305    return write_idx;
2306}
2307
2308/// Normalizes a Windows path with the following steps:
2309///     1) convert all forward slashes to back slashes
2310///     2) collapse duplicate back slashes
2311///     3) remove '.' and '..' directory parts
2312/// Returns the length of the new path.
2313pub fn normalizePath(comptime T: type, path: []T) RemoveDotDirsError!usize {
2314    mem.replaceScalar(T, path, '/', '\\');
2315    const new_len = mem.collapseRepeatsLen(T, path, '\\');
2316
2317    const prefix_len: usize = init: {
2318        if (new_len >= 1 and path[0] == '\\') break :init 1;
2319        if (new_len >= 2 and path[1] == ':')
2320            break :init if (new_len >= 3 and path[2] == '\\') @as(usize, 3) else @as(usize, 2);
2321        break :init 0;
2322    };
2323
2324    return prefix_len + try removeDotDirsSanitized(T, path[prefix_len..new_len]);
2325}
2326
2327pub const Wtf8ToPrefixedFileWError = Wtf16ToPrefixedFileWError;
2328
2329/// Same as `sliceToPrefixedFileW` but accepts a pointer
2330/// to a null-terminated WTF-8 encoded path.
2331/// https://wtf-8.codeberg.page/
2332pub fn cStrToPrefixedFileW(dir: ?HANDLE, s: [*:0]const u8) Wtf8ToPrefixedFileWError!PathSpace {
2333    return sliceToPrefixedFileW(dir, mem.sliceTo(s, 0));
2334}
2335
2336/// Same as `wToPrefixedFileW` but accepts a WTF-8 encoded path.
2337/// https://wtf-8.codeberg.page/
2338pub fn sliceToPrefixedFileW(dir: ?HANDLE, path: []const u8) Wtf8ToPrefixedFileWError!PathSpace {
2339    var temp_path: PathSpace = undefined;
2340    temp_path.len = std.unicode.wtf8ToWtf16Le(&temp_path.data, path) catch |err| switch (err) {
2341        error.InvalidWtf8 => return error.BadPathName,
2342    };
2343    temp_path.data[temp_path.len] = 0;
2344    return wToPrefixedFileW(dir, temp_path.span());
2345}
2346
2347pub const Wtf16ToPrefixedFileWError = error{
2348    AccessDenied,
2349    BadPathName,
2350    FileNotFound,
2351    NameTooLong,
2352    Unexpected,
2353};
2354
2355/// Converts the `path` to WTF16, null-terminated. If the path contains any
2356/// namespace prefix, or is anything but a relative path (rooted, drive relative,
2357/// etc) the result will have the NT-style prefix `\??\`.
2358///
2359/// Similar to RtlDosPathNameToNtPathName_U with a few differences:
2360/// - Does not allocate on the heap.
2361/// - Relative paths are kept as relative unless they contain too many ..
2362///   components, in which case they are resolved against the `dir` if it
2363///   is non-null, or the CWD if it is null.
2364/// - Special case device names like COM1, NUL, etc are not handled specially (TODO)
2365/// - . and space are not stripped from the end of relative paths (potential TODO)
2366pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWError!PathSpace {
2367    const nt_prefix = [_]u16{ '\\', '?', '?', '\\' };
2368    if (hasCommonNtPrefix(u16, path)) {
2369        // TODO: Figure out a way to design an API that can avoid the copy for NT,
2370        //       since it is always returned fully unmodified.
2371        var path_space: PathSpace = undefined;
2372        path_space.data[0..nt_prefix.len].* = nt_prefix;
2373        const len_after_prefix = path.len - nt_prefix.len;
2374        @memcpy(path_space.data[nt_prefix.len..][0..len_after_prefix], path[nt_prefix.len..]);
2375        path_space.len = path.len;
2376        path_space.data[path_space.len] = 0;
2377        return path_space;
2378    } else {
2379        const path_type = getWin32PathType(u16, path);
2380        var path_space: PathSpace = undefined;
2381        if (path_type == .local_device) {
2382            switch (getLocalDevicePathType(u16, path)) {
2383                .verbatim => {
2384                    path_space.data[0..nt_prefix.len].* = nt_prefix;
2385                    const len_after_prefix = path.len - nt_prefix.len;
2386                    @memcpy(path_space.data[nt_prefix.len..][0..len_after_prefix], path[nt_prefix.len..]);
2387                    path_space.len = path.len;
2388                    path_space.data[path_space.len] = 0;
2389                    return path_space;
2390                },
2391                .local_device, .fake_verbatim => {
2392                    const path_byte_len = ntdll.RtlGetFullPathName_U(
2393                        path.ptr,
2394                        path_space.data.len * 2,
2395                        &path_space.data,
2396                        null,
2397                    );
2398                    if (path_byte_len == 0) {
2399                        // TODO: This may not be the right error
2400                        return error.BadPathName;
2401                    } else if (path_byte_len / 2 > path_space.data.len) {
2402                        return error.NameTooLong;
2403                    }
2404                    path_space.len = path_byte_len / 2;
2405                    // Both prefixes will be normalized but retained, so all
2406                    // we need to do now is replace them with the NT prefix
2407                    path_space.data[0..nt_prefix.len].* = nt_prefix;
2408                    return path_space;
2409                },
2410            }
2411        }
2412        relative: {
2413            if (path_type == .relative) {
2414                // TODO: Handle special case device names like COM1, AUX, NUL, CONIN$, CONOUT$, etc.
2415                //       See https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
2416
2417                // TODO: Potentially strip all trailing . and space characters from the
2418                //       end of the path. This is something that both RtlDosPathNameToNtPathName_U
2419                //       and RtlGetFullPathName_U do. Technically, trailing . and spaces
2420                //       are allowed, but such paths may not interact well with Windows (i.e.
2421                //       files with these paths can't be deleted from explorer.exe, etc).
2422                //       This could be something that normalizePath may want to do.
2423
2424                @memcpy(path_space.data[0..path.len], path);
2425                // Try to normalize, but if we get too many parent directories,
2426                // then we need to start over and use RtlGetFullPathName_U instead.
2427                path_space.len = normalizePath(u16, path_space.data[0..path.len]) catch |err| switch (err) {
2428                    error.TooManyParentDirs => break :relative,
2429                };
2430                path_space.data[path_space.len] = 0;
2431                return path_space;
2432            }
2433        }
2434        // We now know we are going to return an absolute NT path, so
2435        // we can unconditionally prefix it with the NT prefix.
2436        path_space.data[0..nt_prefix.len].* = nt_prefix;
2437        if (path_type == .root_local_device) {
2438            // `\\.` and `\\?` always get converted to `\??\` exactly, so
2439            // we can just stop here
2440            path_space.len = nt_prefix.len;
2441            path_space.data[path_space.len] = 0;
2442            return path_space;
2443        }
2444        const path_buf_offset = switch (path_type) {
2445            // UNC paths will always start with `\\`. However, we want to
2446            // end up with something like `\??\UNC\server\share`, so to get
2447            // RtlGetFullPathName to write into the spot we want the `server`
2448            // part to end up, we need to provide an offset such that
2449            // the `\\` part gets written where the `C\` of `UNC\` will be
2450            // in the final NT path.
2451            .unc_absolute => nt_prefix.len + 2,
2452            else => nt_prefix.len,
2453        };
2454        const buf_len: u32 = @intCast(path_space.data.len - path_buf_offset);
2455        const path_to_get: [:0]const u16 = path_to_get: {
2456            // If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
2457            // RtlGetFullPathName_U will resolve relative paths against the CWD for us.
2458            if (path_type != .relative or dir == null) {
2459                break :path_to_get path;
2460            }
2461            // We can also skip GetFinalPathNameByHandle if the handle matches
2462            // the handle returned by fs.cwd()
2463            if (dir.? == std.fs.cwd().fd) {
2464                break :path_to_get path;
2465            }
2466            // At this point, we know we have a relative path that had too many
2467            // `..` components to be resolved by normalizePath, so we need to
2468            // convert it into an absolute path and let RtlGetFullPathName_U
2469            // canonicalize it. We do this by getting the path of the `dir`
2470            // and appending the relative path to it.
2471            var dir_path_buf: [PATH_MAX_WIDE:0]u16 = undefined;
2472            const dir_path = GetFinalPathNameByHandle(dir.?, .{}, &dir_path_buf) catch |err| switch (err) {
2473                // This mapping is not correct; it is actually expected
2474                // that calling GetFinalPathNameByHandle might return
2475                // error.UnrecognizedVolume, and in fact has been observed
2476                // in the wild. The problem is that wToPrefixedFileW was
2477                // never intended to make *any* OS syscall APIs. It's only
2478                // supposed to convert a string to one that is eligible to
2479                // be used in the ntdll syscalls.
2480                //
2481                // To solve this, this function needs to no longer call
2482                // GetFinalPathNameByHandle under any conditions, or the
2483                // calling function needs to get reworked to not need to
2484                // call this function.
2485                //
2486                // This may involve making breaking API changes.
2487                error.UnrecognizedVolume => return error.Unexpected,
2488                else => |e| return e,
2489            };
2490            if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) {
2491                return error.NameTooLong;
2492            }
2493            // We don't have to worry about potentially doubling up path separators
2494            // here since RtlGetFullPathName_U will handle canonicalizing it.
2495            dir_path_buf[dir_path.len] = '\\';
2496            @memcpy(dir_path_buf[dir_path.len + 1 ..][0..path.len], path);
2497            const full_len = dir_path.len + 1 + path.len;
2498            dir_path_buf[full_len] = 0;
2499            break :path_to_get dir_path_buf[0..full_len :0];
2500        };
2501        const path_byte_len = ntdll.RtlGetFullPathName_U(
2502            path_to_get.ptr,
2503            buf_len * 2,
2504            path_space.data[path_buf_offset..].ptr,
2505            null,
2506        );
2507        if (path_byte_len == 0) {
2508            // TODO: This may not be the right error
2509            return error.BadPathName;
2510        } else if (path_byte_len / 2 > buf_len) {
2511            return error.NameTooLong;
2512        }
2513        path_space.len = path_buf_offset + (path_byte_len / 2);
2514        if (path_type == .unc_absolute) {
2515            // Now add in the UNC, the `C` should overwrite the first `\` of the
2516            // FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>`
2517            std.debug.assert(path_space.data[path_buf_offset] == '\\');
2518            std.debug.assert(path_space.data[path_buf_offset + 1] == '\\');
2519            const unc = [_]u16{ 'U', 'N', 'C' };
2520            path_space.data[nt_prefix.len..][0..unc.len].* = unc;
2521        }
2522        return path_space;
2523    }
2524}
2525
2526/// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type.
2527pub const Win32PathType = enum {
2528    /// `\\server\share\foo`
2529    unc_absolute,
2530    /// `C:\foo`
2531    drive_absolute,
2532    /// `C:foo`
2533    drive_relative,
2534    /// `\foo`
2535    rooted,
2536    /// `foo`
2537    relative,
2538    /// `\\.\foo`, `\\?\foo`
2539    local_device,
2540    /// `\\.`, `\\?`
2541    root_local_device,
2542};
2543
2544/// Get the path type of a Win32 namespace path.
2545/// Similar to `RtlDetermineDosPathNameType_U`.
2546/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
2547pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
2548    if (path.len < 1) return .relative;
2549
2550    const windows_path = std.fs.path.PathType.windows;
2551    if (windows_path.isSep(T, path[0])) {
2552        // \x
2553        if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted;
2554        // \\. or \\?
2555        if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) {
2556            // exactly \\. or \\? with nothing trailing
2557            if (path.len == 3) return .root_local_device;
2558            // \\.\x or \\?\x
2559            if (windows_path.isSep(T, path[3])) return .local_device;
2560        }
2561        // \\x
2562        return .unc_absolute;
2563    } else {
2564        // Some choice has to be made about how non-ASCII code points as drive-letters are handled, since
2565        // path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification
2566        // for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code
2567        // units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so
2568        // checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16.
2569        //
2570        // `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers
2571        // `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get
2572        // classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded
2573        // in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path).
2574        //
2575        // The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both
2576        // WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI,
2577        // drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you
2578        // to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will
2579        // allow you to set any WTF-16 code unit as a drive letter.
2580        //
2581        // Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g.
2582        // `cd /D €:\` will work, filesystem functions still work, etc.
2583        //
2584        // The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't
2585        // just check path[0], path[1], path[2].
2586        const colon_i: usize = switch (T) {
2587            u8 => i: {
2588                const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative;
2589                // Conveniently, 4-byte sequences in WTF-8 have the same starting code point
2590                // as 2-code-unit sequences in WTF-16.
2591                if (code_point_len > 3) return .relative;
2592                break :i code_point_len;
2593            },
2594            u16 => 1,
2595            else => @compileError("unsupported type: " ++ @typeName(T)),
2596        };
2597        // x
2598        if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative;
2599        // x:\
2600        if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute;
2601        // x:
2602        return .drive_relative;
2603    }
2604}
2605
2606test getWin32PathType {
2607    try std.testing.expectEqual(.relative, getWin32PathType(u8, ""));
2608    try std.testing.expectEqual(.relative, getWin32PathType(u8, "x"));
2609    try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\"));
2610
2611    try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//."));
2612    try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?"));
2613    try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?"));
2614
2615    try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x"));
2616    try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x"));
2617    try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x"));
2618    // local device paths require a path separator after the root, otherwise it is considered a UNC path
2619    try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x"));
2620    try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x"));
2621
2622    try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//"));
2623    try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x"));
2624    try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x"));
2625
2626    try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x"));
2627    try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/"));
2628
2629    try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:"));
2630    try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc"));
2631    try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c"));
2632
2633    try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\"));
2634    try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc"));
2635    try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c"));
2636
2637    // Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter
2638    try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\"));
2639    try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\")));
2640    try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:"));
2641    try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:")));
2642    // But code points that are encoded as two WTF-16 code units are not
2643    try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\"));
2644    try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\")));
2645}
2646
2647/// Returns true if the path starts with `\??\`, which is indicative of an NT path
2648/// but is not enough to fully distinguish between NT paths and Win32 paths, as
2649/// `\??\` is not actually a distinct prefix but rather the path to a special virtual
2650/// folder in the Object Manager.
2651///
2652/// For example, `\Device\HarddiskVolume2` and `\DosDevices\C:` are also NT paths but
2653/// cannot be distinguished as such by their prefix.
2654///
2655/// So, inferring whether a path is an NT path or a Win32 path is usually a mistake;
2656/// that information should instead be known ahead-of-time.
2657///
2658/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
2659pub fn hasCommonNtPrefix(comptime T: type, path: []const T) bool {
2660    // Must be exactly \??\, forward slashes are not allowed
2661    const expected_wtf8_prefix = "\\??\\";
2662    const expected_prefix = switch (T) {
2663        u8 => expected_wtf8_prefix,
2664        u16 => std.unicode.wtf8ToWtf16LeStringLiteral(expected_wtf8_prefix),
2665        else => @compileError("unsupported type: " ++ @typeName(T)),
2666    };
2667    return mem.startsWith(T, path, expected_prefix);
2668}
2669
2670const LocalDevicePathType = enum {
2671    /// `\\.\` (path separators can be `\` or `/`)
2672    local_device,
2673    /// `\\?\`
2674    /// When converted to an NT path, everything past the prefix is left
2675    /// untouched and `\\?\` is replaced by `\??\`.
2676    verbatim,
2677    /// `\\?\` without all path separators being `\`.
2678    /// This seems to be recognized as a prefix, but the 'verbatim' aspect
2679    /// is not respected (i.e. if `//?/C:/foo` is converted to an NT path,
2680    /// it will become `\??\C:\foo` [it will be canonicalized and the //?/ won't
2681    /// be treated as part of the final path])
2682    fake_verbatim,
2683};
2684
2685/// Only relevant for Win32 -> NT path conversion.
2686/// Asserts `path` is of type `Win32PathType.local_device`.
2687fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType {
2688    if (std.debug.runtime_safety) {
2689        std.debug.assert(getWin32PathType(T, path) == .local_device);
2690    }
2691
2692    const backslash = mem.nativeToLittle(T, '\\');
2693    const all_backslash = path[0] == backslash and
2694        path[1] == backslash and
2695        path[3] == backslash;
2696    return switch (path[2]) {
2697        mem.nativeToLittle(T, '?') => if (all_backslash) .verbatim else .fake_verbatim,
2698        mem.nativeToLittle(T, '.') => .local_device,
2699        else => unreachable,
2700    };
2701}
2702
2703/// Similar to `RtlNtPathNameToDosPathName` but does not do any heap allocation.
2704/// The possible transformations are:
2705///   \??\C:\Some\Path -> C:\Some\Path
2706///   \??\UNC\server\share\foo -> \\server\share\foo
2707/// If the path does not have the NT namespace prefix, then `error.NotNtPath` is returned.
2708///
2709/// Functionality is based on the ReactOS test cases found here:
2710/// https://github.com/reactos/reactos/blob/master/modules/rostests/apitests/ntdll/RtlNtPathNameToDosPathName.c
2711///
2712/// `path` should be encoded as WTF-16LE.
2713///
2714/// Supports in-place modification (`path` and `out` may refer to the same slice).
2715pub fn ntToWin32Namespace(path: []const u16, out: []u16) error{ NameTooLong, NotNtPath }![]u16 {
2716    if (path.len > PATH_MAX_WIDE) return error.NameTooLong;
2717    if (!hasCommonNtPrefix(u16, path)) return error.NotNtPath;
2718
2719    var dest_index: usize = 0;
2720    var after_prefix = path[4..]; // after the `\??\`
2721    // The prefix \??\UNC\ means this is a UNC path, in which case the
2722    // `\??\UNC\` should be replaced by `\\` (two backslashes)
2723    const is_unc = after_prefix.len >= 4 and
2724        eqlIgnoreCaseWtf16(after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and
2725        std.fs.path.PathType.windows.isSep(u16, after_prefix[3]);
2726    const win32_len = path.len - @as(usize, if (is_unc) 6 else 4);
2727    if (out.len < win32_len) return error.NameTooLong;
2728    if (is_unc) {
2729        out[0] = comptime std.mem.nativeToLittle(u16, '\\');
2730        dest_index += 1;
2731        // We want to include the last `\` of `\??\UNC\`
2732        after_prefix = path[7..];
2733    }
2734    @memmove(out[dest_index..][0..after_prefix.len], after_prefix);
2735    return out[0..win32_len];
2736}
2737
2738test ntToWin32Namespace {
2739    const L = std.unicode.utf8ToUtf16LeStringLiteral;
2740
2741    var mutable_unc_path_buf = L("\\??\\UNC\\path1\\path2").*;
2742    try std.testing.expectEqualSlices(u16, L("\\\\path1\\path2"), try ntToWin32Namespace(&mutable_unc_path_buf, &mutable_unc_path_buf));
2743
2744    var mutable_path_buf = L("\\??\\C:\\test\\").*;
2745    try std.testing.expectEqualSlices(u16, L("C:\\test\\"), try ntToWin32Namespace(&mutable_path_buf, &mutable_path_buf));
2746
2747    var too_small_buf: [6]u16 = undefined;
2748    try std.testing.expectError(error.NameTooLong, ntToWin32Namespace(L("\\??\\C:\\test"), &too_small_buf));
2749}
2750
2751inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID {
2752    return (s << 10) | p;
2753}
2754
2755/// Call this when you made a windows DLL call or something that does SetLastError
2756/// and you get an unexpected error.
2757pub fn unexpectedError(err: Win32Error) UnexpectedError {
2758    if (std.posix.unexpected_error_tracing) {
2759        // 614 is the length of the longest windows error description
2760        var buf_wstr: [614:0]WCHAR = undefined;
2761        const len = kernel32.FormatMessageW(
2762            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
2763            null,
2764            err,
2765            MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT),
2766            &buf_wstr,
2767            buf_wstr.len,
2768            null,
2769        );
2770        std.debug.print("error.Unexpected: GetLastError({d}): {f}\n", .{
2771            err, std.unicode.fmtUtf16Le(buf_wstr[0..len]),
2772        });
2773        std.debug.dumpCurrentStackTrace(.{ .first_address = @returnAddress() });
2774    }
2775    return error.Unexpected;
2776}
2777
2778pub fn unexpectedWSAError(err: ws2_32.WinsockError) UnexpectedError {
2779    return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err))));
2780}
2781
2782/// Call this when you made a windows NtDll call
2783/// and you get an unexpected status.
2784pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError {
2785    if (std.posix.unexpected_error_tracing) {
2786        std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@intFromEnum(status)});
2787        std.debug.dumpCurrentStackTrace(.{ .first_address = @returnAddress() });
2788    }
2789    return error.Unexpected;
2790}
2791
2792pub fn statusBug(status: NTSTATUS) UnexpectedError {
2793    switch (builtin.mode) {
2794        .Debug => std.debug.panic("programmer bug caused syscall status: {t}", .{status}),
2795        else => return error.Unexpected,
2796    }
2797}
2798
2799pub fn errorBug(err: Win32Error) UnexpectedError {
2800    switch (builtin.mode) {
2801        .Debug => std.debug.panic("programmer bug caused syscall status: {t}", .{err}),
2802        else => return error.Unexpected,
2803    }
2804}
2805
2806pub const Win32Error = @import("windows/win32error.zig").Win32Error;
2807pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS;
2808pub const LANG = @import("windows/lang.zig");
2809pub const SUBLANG = @import("windows/sublang.zig");
2810
2811/// The standard input device. Initially, this is the console input buffer, CONIN$.
2812pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1;
2813
2814/// The standard output device. Initially, this is the active console screen buffer, CONOUT$.
2815pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1;
2816
2817/// The standard error device. Initially, this is the active console screen buffer, CONOUT$.
2818pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1;
2819
2820pub const BOOL = c_int;
2821pub const BOOLEAN = BYTE;
2822pub const BYTE = u8;
2823pub const CHAR = u8;
2824pub const UCHAR = u8;
2825pub const FLOAT = f32;
2826pub const HANDLE = *anyopaque;
2827pub const HCRYPTPROV = ULONG_PTR;
2828pub const ATOM = u16;
2829pub const HBRUSH = *opaque {};
2830pub const HCURSOR = *opaque {};
2831pub const HICON = *opaque {};
2832pub const HINSTANCE = *opaque {};
2833pub const HMENU = *opaque {};
2834pub const HMODULE = *opaque {};
2835pub const HWND = *opaque {};
2836pub const HDC = *opaque {};
2837pub const HGLRC = *opaque {};
2838pub const FARPROC = *opaque {};
2839pub const PROC = *opaque {};
2840pub const INT = c_int;
2841pub const LPCSTR = [*:0]const CHAR;
2842pub const LPCVOID = *const anyopaque;
2843pub const LPSTR = [*:0]CHAR;
2844pub const LPVOID = *anyopaque;
2845pub const LPWSTR = [*:0]WCHAR;
2846pub const LPCWSTR = [*:0]const WCHAR;
2847pub const PVOID = *anyopaque;
2848pub const PWSTR = [*:0]WCHAR;
2849pub const PCWSTR = [*:0]const WCHAR;
2850/// Allocated by SysAllocString, freed by SysFreeString
2851pub const BSTR = [*:0]WCHAR;
2852pub const SIZE_T = usize;
2853pub const UINT = c_uint;
2854pub const ULONG_PTR = usize;
2855pub const LONG_PTR = isize;
2856pub const DWORD_PTR = ULONG_PTR;
2857pub const WCHAR = u16;
2858pub const WORD = u16;
2859pub const DWORD = u32;
2860pub const DWORD64 = u64;
2861pub const LARGE_INTEGER = i64;
2862pub const ULARGE_INTEGER = u64;
2863pub const USHORT = u16;
2864pub const SHORT = i16;
2865pub const ULONG = u32;
2866pub const LONG = i32;
2867pub const ULONG64 = u64;
2868pub const ULONGLONG = u64;
2869pub const LONGLONG = i64;
2870pub const HLOCAL = HANDLE;
2871pub const LANGID = c_ushort;
2872
2873pub const WPARAM = usize;
2874pub const LPARAM = LONG_PTR;
2875pub const LRESULT = LONG_PTR;
2876
2877pub const va_list = *opaque {};
2878
2879pub const TCHAR = @compileError("Deprecated: choose between `CHAR` or `WCHAR` directly instead.");
2880pub const LPTSTR = @compileError("Deprecated: choose between `LPSTR` or `LPWSTR` directly instead.");
2881pub const LPCTSTR = @compileError("Deprecated: choose between `LPCSTR` or `LPCWSTR` directly instead.");
2882pub const PTSTR = @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead.");
2883pub const PCTSTR = @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead.");
2884
2885pub const TRUE = 1;
2886pub const FALSE = 0;
2887
2888pub const DEVICE_TYPE = ULONG;
2889pub const FILE_DEVICE_BEEP: DEVICE_TYPE = 0x0001;
2890pub const FILE_DEVICE_CD_ROM: DEVICE_TYPE = 0x0002;
2891pub const FILE_DEVICE_CD_ROM_FILE_SYSTEM: DEVICE_TYPE = 0x0003;
2892pub const FILE_DEVICE_CONTROLLER: DEVICE_TYPE = 0x0004;
2893pub const FILE_DEVICE_DATALINK: DEVICE_TYPE = 0x0005;
2894pub const FILE_DEVICE_DFS: DEVICE_TYPE = 0x0006;
2895pub const FILE_DEVICE_DISK: DEVICE_TYPE = 0x0007;
2896pub const FILE_DEVICE_DISK_FILE_SYSTEM: DEVICE_TYPE = 0x0008;
2897pub const FILE_DEVICE_FILE_SYSTEM: DEVICE_TYPE = 0x0009;
2898pub const FILE_DEVICE_INPORT_PORT: DEVICE_TYPE = 0x000a;
2899pub const FILE_DEVICE_KEYBOARD: DEVICE_TYPE = 0x000b;
2900pub const FILE_DEVICE_MAILSLOT: DEVICE_TYPE = 0x000c;
2901pub const FILE_DEVICE_MIDI_IN: DEVICE_TYPE = 0x000d;
2902pub const FILE_DEVICE_MIDI_OUT: DEVICE_TYPE = 0x000e;
2903pub const FILE_DEVICE_MOUSE: DEVICE_TYPE = 0x000f;
2904pub const FILE_DEVICE_MULTI_UNC_PROVIDER: DEVICE_TYPE = 0x0010;
2905pub const FILE_DEVICE_NAMED_PIPE: DEVICE_TYPE = 0x0011;
2906pub const FILE_DEVICE_NETWORK: DEVICE_TYPE = 0x0012;
2907pub const FILE_DEVICE_NETWORK_BROWSER: DEVICE_TYPE = 0x0013;
2908pub const FILE_DEVICE_NETWORK_FILE_SYSTEM: DEVICE_TYPE = 0x0014;
2909pub const FILE_DEVICE_NULL: DEVICE_TYPE = 0x0015;
2910pub const FILE_DEVICE_PARALLEL_PORT: DEVICE_TYPE = 0x0016;
2911pub const FILE_DEVICE_PHYSICAL_NETCARD: DEVICE_TYPE = 0x0017;
2912pub const FILE_DEVICE_PRINTER: DEVICE_TYPE = 0x0018;
2913pub const FILE_DEVICE_SCANNER: DEVICE_TYPE = 0x0019;
2914pub const FILE_DEVICE_SERIAL_MOUSE_PORT: DEVICE_TYPE = 0x001a;
2915pub const FILE_DEVICE_SERIAL_PORT: DEVICE_TYPE = 0x001b;
2916pub const FILE_DEVICE_SCREEN: DEVICE_TYPE = 0x001c;
2917pub const FILE_DEVICE_SOUND: DEVICE_TYPE = 0x001d;
2918pub const FILE_DEVICE_STREAMS: DEVICE_TYPE = 0x001e;
2919pub const FILE_DEVICE_TAPE: DEVICE_TYPE = 0x001f;
2920pub const FILE_DEVICE_TAPE_FILE_SYSTEM: DEVICE_TYPE = 0x0020;
2921pub const FILE_DEVICE_TRANSPORT: DEVICE_TYPE = 0x0021;
2922pub const FILE_DEVICE_UNKNOWN: DEVICE_TYPE = 0x0022;
2923pub const FILE_DEVICE_VIDEO: DEVICE_TYPE = 0x0023;
2924pub const FILE_DEVICE_VIRTUAL_DISK: DEVICE_TYPE = 0x0024;
2925pub const FILE_DEVICE_WAVE_IN: DEVICE_TYPE = 0x0025;
2926pub const FILE_DEVICE_WAVE_OUT: DEVICE_TYPE = 0x0026;
2927pub const FILE_DEVICE_8042_PORT: DEVICE_TYPE = 0x0027;
2928pub const FILE_DEVICE_NETWORK_REDIRECTOR: DEVICE_TYPE = 0x0028;
2929pub const FILE_DEVICE_BATTERY: DEVICE_TYPE = 0x0029;
2930pub const FILE_DEVICE_BUS_EXTENDER: DEVICE_TYPE = 0x002a;
2931pub const FILE_DEVICE_MODEM: DEVICE_TYPE = 0x002b;
2932pub const FILE_DEVICE_VDM: DEVICE_TYPE = 0x002c;
2933pub const FILE_DEVICE_MASS_STORAGE: DEVICE_TYPE = 0x002d;
2934pub const FILE_DEVICE_SMB: DEVICE_TYPE = 0x002e;
2935pub const FILE_DEVICE_KS: DEVICE_TYPE = 0x002f;
2936pub const FILE_DEVICE_CHANGER: DEVICE_TYPE = 0x0030;
2937pub const FILE_DEVICE_SMARTCARD: DEVICE_TYPE = 0x0031;
2938pub const FILE_DEVICE_ACPI: DEVICE_TYPE = 0x0032;
2939pub const FILE_DEVICE_DVD: DEVICE_TYPE = 0x0033;
2940pub const FILE_DEVICE_FULLSCREEN_VIDEO: DEVICE_TYPE = 0x0034;
2941pub const FILE_DEVICE_DFS_FILE_SYSTEM: DEVICE_TYPE = 0x0035;
2942pub const FILE_DEVICE_DFS_VOLUME: DEVICE_TYPE = 0x0036;
2943pub const FILE_DEVICE_SERENUM: DEVICE_TYPE = 0x0037;
2944pub const FILE_DEVICE_TERMSRV: DEVICE_TYPE = 0x0038;
2945pub const FILE_DEVICE_KSEC: DEVICE_TYPE = 0x0039;
2946pub const FILE_DEVICE_FIPS: DEVICE_TYPE = 0x003a;
2947pub const FILE_DEVICE_INFINIBAND: DEVICE_TYPE = 0x003b;
2948// TODO: missing values?
2949pub const FILE_DEVICE_VMBUS: DEVICE_TYPE = 0x003e;
2950pub const FILE_DEVICE_CRYPT_PROVIDER: DEVICE_TYPE = 0x003f;
2951pub const FILE_DEVICE_WPD: DEVICE_TYPE = 0x0040;
2952pub const FILE_DEVICE_BLUETOOTH: DEVICE_TYPE = 0x0041;
2953pub const FILE_DEVICE_MT_COMPOSITE: DEVICE_TYPE = 0x0042;
2954pub const FILE_DEVICE_MT_TRANSPORT: DEVICE_TYPE = 0x0043;
2955pub const FILE_DEVICE_BIOMETRIC: DEVICE_TYPE = 0x0044;
2956pub const FILE_DEVICE_PMI: DEVICE_TYPE = 0x0045;
2957pub const FILE_DEVICE_EHSTOR: DEVICE_TYPE = 0x0046;
2958pub const FILE_DEVICE_DEVAPI: DEVICE_TYPE = 0x0047;
2959pub const FILE_DEVICE_GPIO: DEVICE_TYPE = 0x0048;
2960pub const FILE_DEVICE_USBEX: DEVICE_TYPE = 0x0049;
2961pub const FILE_DEVICE_CONSOLE: DEVICE_TYPE = 0x0050;
2962pub const FILE_DEVICE_NFP: DEVICE_TYPE = 0x0051;
2963pub const FILE_DEVICE_SYSENV: DEVICE_TYPE = 0x0052;
2964pub const FILE_DEVICE_VIRTUAL_BLOCK: DEVICE_TYPE = 0x0053;
2965pub const FILE_DEVICE_POINT_OF_SERVICE: DEVICE_TYPE = 0x0054;
2966pub const FILE_DEVICE_STORAGE_REPLICATION: DEVICE_TYPE = 0x0055;
2967pub const FILE_DEVICE_TRUST_ENV: DEVICE_TYPE = 0x0056;
2968pub const FILE_DEVICE_UCM: DEVICE_TYPE = 0x0057;
2969pub const FILE_DEVICE_UCMTCPCI: DEVICE_TYPE = 0x0058;
2970pub const FILE_DEVICE_PERSISTENT_MEMORY: DEVICE_TYPE = 0x0059;
2971pub const FILE_DEVICE_NVDIMM: DEVICE_TYPE = 0x005a;
2972pub const FILE_DEVICE_HOLOGRAPHIC: DEVICE_TYPE = 0x005b;
2973pub const FILE_DEVICE_SDFXHCI: DEVICE_TYPE = 0x005c;
2974
2975/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-i-o-control-codes
2976pub const TransferType = enum(u2) {
2977    METHOD_BUFFERED = 0,
2978    METHOD_IN_DIRECT = 1,
2979    METHOD_OUT_DIRECT = 2,
2980    METHOD_NEITHER = 3,
2981};
2982
2983pub const FILE_ANY_ACCESS = 0;
2984pub const FILE_READ_ACCESS = 1;
2985pub const FILE_WRITE_ACCESS = 2;
2986
2987/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes
2988pub fn CTL_CODE(deviceType: u16, function: u12, method: TransferType, access: u2) DWORD {
2989    return (@as(DWORD, deviceType) << 16) |
2990        (@as(DWORD, access) << 14) |
2991        (@as(DWORD, function) << 2) |
2992        @intFromEnum(method);
2993}
2994
2995pub const INVALID_HANDLE_VALUE = @as(HANDLE, @ptrFromInt(maxInt(usize)));
2996
2997pub const INVALID_FILE_ATTRIBUTES = @as(DWORD, maxInt(DWORD));
2998
2999pub const FILE_ALL_INFORMATION = extern struct {
3000    BasicInformation: FILE_BASIC_INFORMATION,
3001    StandardInformation: FILE_STANDARD_INFORMATION,
3002    InternalInformation: FILE_INTERNAL_INFORMATION,
3003    EaInformation: FILE_EA_INFORMATION,
3004    AccessInformation: FILE_ACCESS_INFORMATION,
3005    PositionInformation: FILE_POSITION_INFORMATION,
3006    ModeInformation: FILE_MODE_INFORMATION,
3007    AlignmentInformation: FILE_ALIGNMENT_INFORMATION,
3008    NameInformation: FILE_NAME_INFORMATION,
3009};
3010
3011pub const FILE_BASIC_INFORMATION = extern struct {
3012    CreationTime: LARGE_INTEGER,
3013    LastAccessTime: LARGE_INTEGER,
3014    LastWriteTime: LARGE_INTEGER,
3015    ChangeTime: LARGE_INTEGER,
3016    FileAttributes: ULONG,
3017};
3018
3019pub const FILE_STANDARD_INFORMATION = extern struct {
3020    AllocationSize: LARGE_INTEGER,
3021    EndOfFile: LARGE_INTEGER,
3022    NumberOfLinks: ULONG,
3023    DeletePending: BOOLEAN,
3024    Directory: BOOLEAN,
3025};
3026
3027pub const FILE_INTERNAL_INFORMATION = extern struct {
3028    IndexNumber: LARGE_INTEGER,
3029};
3030
3031pub const FILE_EA_INFORMATION = extern struct {
3032    EaSize: ULONG,
3033};
3034
3035pub const FILE_ACCESS_INFORMATION = extern struct {
3036    AccessFlags: ACCESS_MASK,
3037};
3038
3039pub const FILE_POSITION_INFORMATION = extern struct {
3040    CurrentByteOffset: LARGE_INTEGER,
3041};
3042
3043pub const FILE_END_OF_FILE_INFORMATION = extern struct {
3044    EndOfFile: LARGE_INTEGER,
3045};
3046
3047pub const FILE_MODE_INFORMATION = extern struct {
3048    Mode: ULONG,
3049};
3050
3051pub const FILE_ALIGNMENT_INFORMATION = extern struct {
3052    AlignmentRequirement: ULONG,
3053};
3054
3055pub const FILE_NAME_INFORMATION = extern struct {
3056    FileNameLength: ULONG,
3057    FileName: [1]WCHAR,
3058};
3059
3060pub const FILE_DISPOSITION_INFORMATION_EX = extern struct {
3061    /// combination of FILE_DISPOSITION_* flags
3062    Flags: ULONG,
3063};
3064
3065pub const FILE_DISPOSITION_DO_NOT_DELETE: ULONG = 0x00000000;
3066pub const FILE_DISPOSITION_DELETE: ULONG = 0x00000001;
3067pub const FILE_DISPOSITION_POSIX_SEMANTICS: ULONG = 0x00000002;
3068pub const FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK: ULONG = 0x00000004;
3069pub const FILE_DISPOSITION_ON_CLOSE: ULONG = 0x00000008;
3070pub const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: ULONG = 0x00000010;
3071
3072// FILE_RENAME_INFORMATION.Flags
3073pub const FILE_RENAME_REPLACE_IF_EXISTS = 0x00000001;
3074pub const FILE_RENAME_POSIX_SEMANTICS = 0x00000002;
3075pub const FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE = 0x00000004;
3076pub const FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE = 0x00000008;
3077pub const FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE = 0x00000010;
3078pub const FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE = 0x00000020;
3079pub const FILE_RENAME_PRESERVE_AVAILABLE_SPACE = 0x00000030;
3080pub const FILE_RENAME_IGNORE_READONLY_ATTRIBUTE = 0x00000040;
3081pub const FILE_RENAME_FORCE_RESIZE_TARGET_SR = 0x00000080;
3082pub const FILE_RENAME_FORCE_RESIZE_SOURCE_SR = 0x00000100;
3083pub const FILE_RENAME_FORCE_RESIZE_SR = 0x00000180;
3084
3085pub const FILE_RENAME_INFORMATION = extern struct {
3086    Flags: BOOLEAN,
3087    RootDirectory: ?HANDLE,
3088    FileNameLength: ULONG,
3089    FileName: [1]WCHAR,
3090};
3091
3092// FileRenameInformationEx (since .win10_rs1)
3093pub const FILE_RENAME_INFORMATION_EX = extern struct {
3094    Flags: ULONG,
3095    RootDirectory: ?HANDLE,
3096    FileNameLength: ULONG,
3097    FileName: [1]WCHAR,
3098};
3099
3100pub const IO_STATUS_BLOCK = extern struct {
3101    // "DUMMYUNIONNAME" expands to "u"
3102    u: extern union {
3103        Status: NTSTATUS,
3104        Pointer: ?*anyopaque,
3105    },
3106    Information: ULONG_PTR,
3107};
3108
3109pub const FILE_INFORMATION_CLASS = enum(c_int) {
3110    FileDirectoryInformation = 1,
3111    FileFullDirectoryInformation,
3112    FileBothDirectoryInformation,
3113    FileBasicInformation,
3114    FileStandardInformation,
3115    FileInternalInformation,
3116    FileEaInformation,
3117    FileAccessInformation,
3118    FileNameInformation,
3119    FileRenameInformation,
3120    FileLinkInformation,
3121    FileNamesInformation,
3122    FileDispositionInformation,
3123    FilePositionInformation,
3124    FileFullEaInformation,
3125    FileModeInformation,
3126    FileAlignmentInformation,
3127    FileAllInformation,
3128    FileAllocationInformation,
3129    FileEndOfFileInformation,
3130    FileAlternateNameInformation,
3131    FileStreamInformation,
3132    FilePipeInformation,
3133    FilePipeLocalInformation,
3134    FilePipeRemoteInformation,
3135    FileMailslotQueryInformation,
3136    FileMailslotSetInformation,
3137    FileCompressionInformation,
3138    FileObjectIdInformation,
3139    FileCompletionInformation,
3140    FileMoveClusterInformation,
3141    FileQuotaInformation,
3142    FileReparsePointInformation,
3143    FileNetworkOpenInformation,
3144    FileAttributeTagInformation,
3145    FileTrackingInformation,
3146    FileIdBothDirectoryInformation,
3147    FileIdFullDirectoryInformation,
3148    FileValidDataLengthInformation,
3149    FileShortNameInformation,
3150    FileIoCompletionNotificationInformation,
3151    FileIoStatusBlockRangeInformation,
3152    FileIoPriorityHintInformation,
3153    FileSfioReserveInformation,
3154    FileSfioVolumeInformation,
3155    FileHardLinkInformation,
3156    FileProcessIdsUsingFileInformation,
3157    FileNormalizedNameInformation,
3158    FileNetworkPhysicalNameInformation,
3159    FileIdGlobalTxDirectoryInformation,
3160    FileIsRemoteDeviceInformation,
3161    FileUnusedInformation,
3162    FileNumaNodeInformation,
3163    FileStandardLinkInformation,
3164    FileRemoteProtocolInformation,
3165    FileRenameInformationBypassAccessCheck,
3166    FileLinkInformationBypassAccessCheck,
3167    FileVolumeNameInformation,
3168    FileIdInformation,
3169    FileIdExtdDirectoryInformation,
3170    FileReplaceCompletionInformation,
3171    FileHardLinkFullIdInformation,
3172    FileIdExtdBothDirectoryInformation,
3173    FileDispositionInformationEx,
3174    FileRenameInformationEx,
3175    FileRenameInformationExBypassAccessCheck,
3176    FileDesiredStorageClassInformation,
3177    FileStatInformation,
3178    FileMemoryPartitionInformation,
3179    FileStatLxInformation,
3180    FileCaseSensitiveInformation,
3181    FileLinkInformationEx,
3182    FileLinkInformationExBypassAccessCheck,
3183    FileStorageReserveIdInformation,
3184    FileCaseSensitiveInformationForceAccessCheck,
3185    FileMaximumInformation,
3186};
3187
3188pub const FILE_ATTRIBUTE_TAG_INFO = extern struct {
3189    FileAttributes: DWORD,
3190    ReparseTag: DWORD,
3191};
3192
3193/// "If this bit is set, the file or directory represents another named entity in the system."
3194/// https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-point-tags
3195pub const reparse_tag_name_surrogate_bit = 0x20000000;
3196
3197pub const FILE_DISPOSITION_INFORMATION = extern struct {
3198    DeleteFile: BOOLEAN,
3199};
3200
3201pub const FILE_FS_DEVICE_INFORMATION = extern struct {
3202    DeviceType: DEVICE_TYPE,
3203    Characteristics: ULONG,
3204};
3205
3206pub const FILE_FS_VOLUME_INFORMATION = extern struct {
3207    VolumeCreationTime: LARGE_INTEGER,
3208    VolumeSerialNumber: ULONG,
3209    VolumeLabelLength: ULONG,
3210    SupportsObjects: BOOLEAN,
3211    // Flexible array member
3212    VolumeLabel: [1]WCHAR,
3213};
3214
3215pub const FS_INFORMATION_CLASS = enum(c_int) {
3216    FileFsVolumeInformation = 1,
3217    FileFsLabelInformation,
3218    FileFsSizeInformation,
3219    FileFsDeviceInformation,
3220    FileFsAttributeInformation,
3221    FileFsControlInformation,
3222    FileFsFullSizeInformation,
3223    FileFsObjectIdInformation,
3224    FileFsDriverPathInformation,
3225    FileFsVolumeFlagsInformation,
3226    FileFsSectorSizeInformation,
3227    FileFsDataCopyInformation,
3228    FileFsMetadataSizeInformation,
3229    FileFsFullSizeInformationEx,
3230    FileFsMaximumInformation,
3231};
3232
3233pub const OVERLAPPED = extern struct {
3234    Internal: ULONG_PTR,
3235    InternalHigh: ULONG_PTR,
3236    DUMMYUNIONNAME: extern union {
3237        DUMMYSTRUCTNAME: extern struct {
3238            Offset: DWORD,
3239            OffsetHigh: DWORD,
3240        },
3241        Pointer: ?PVOID,
3242    },
3243    hEvent: ?HANDLE,
3244};
3245
3246pub const OVERLAPPED_ENTRY = extern struct {
3247    lpCompletionKey: ULONG_PTR,
3248    lpOverlapped: *OVERLAPPED,
3249    Internal: ULONG_PTR,
3250    dwNumberOfBytesTransferred: DWORD,
3251};
3252
3253pub const MAX_PATH = 260;
3254
3255pub const FILE_INFO_BY_HANDLE_CLASS = enum(u32) {
3256    FileBasicInfo = 0,
3257    FileStandardInfo = 1,
3258    FileNameInfo = 2,
3259    FileRenameInfo = 3,
3260    FileDispositionInfo = 4,
3261    FileAllocationInfo = 5,
3262    FileEndOfFileInfo = 6,
3263    FileStreamInfo = 7,
3264    FileCompressionInfo = 8,
3265    FileAttributeTagInfo = 9,
3266    FileIdBothDirectoryInfo = 10,
3267    FileIdBothDirectoryRestartInfo = 11,
3268    FileIoPriorityHintInfo = 12,
3269    FileRemoteProtocolInfo = 13,
3270    FileFullDirectoryInfo = 14,
3271    FileFullDirectoryRestartInfo = 15,
3272    FileStorageInfo = 16,
3273    FileAlignmentInfo = 17,
3274    FileIdInfo = 18,
3275    FileIdExtdDirectoryInfo = 19,
3276    FileIdExtdDirectoryRestartInfo = 20,
3277};
3278
3279pub const BY_HANDLE_FILE_INFORMATION = extern struct {
3280    dwFileAttributes: DWORD,
3281    ftCreationTime: FILETIME,
3282    ftLastAccessTime: FILETIME,
3283    ftLastWriteTime: FILETIME,
3284    dwVolumeSerialNumber: DWORD,
3285    nFileSizeHigh: DWORD,
3286    nFileSizeLow: DWORD,
3287    nNumberOfLinks: DWORD,
3288    nFileIndexHigh: DWORD,
3289    nFileIndexLow: DWORD,
3290};
3291
3292pub const FILE_NAME_INFO = extern struct {
3293    FileNameLength: DWORD,
3294    FileName: [1]WCHAR,
3295};
3296
3297/// Return the normalized drive name. This is the default.
3298pub const FILE_NAME_NORMALIZED = 0x0;
3299
3300/// Return the opened file name (not normalized).
3301pub const FILE_NAME_OPENED = 0x8;
3302
3303/// Return the path with the drive letter. This is the default.
3304pub const VOLUME_NAME_DOS = 0x0;
3305
3306/// Return the path with a volume GUID path instead of the drive name.
3307pub const VOLUME_NAME_GUID = 0x1;
3308
3309/// Return the path with no drive information.
3310pub const VOLUME_NAME_NONE = 0x4;
3311
3312/// Return the path with the volume device path.
3313pub const VOLUME_NAME_NT = 0x2;
3314
3315pub const SECURITY_ATTRIBUTES = extern struct {
3316    nLength: DWORD,
3317    lpSecurityDescriptor: ?*anyopaque,
3318    bInheritHandle: BOOL,
3319};
3320
3321pub const PIPE_ACCESS_INBOUND = 0x00000001;
3322pub const PIPE_ACCESS_OUTBOUND = 0x00000002;
3323pub const PIPE_ACCESS_DUPLEX = 0x00000003;
3324
3325pub const PIPE_TYPE_BYTE = 0x00000000;
3326pub const PIPE_TYPE_MESSAGE = 0x00000004;
3327
3328pub const PIPE_READMODE_BYTE = 0x00000000;
3329pub const PIPE_READMODE_MESSAGE = 0x00000002;
3330
3331pub const PIPE_WAIT = 0x00000000;
3332pub const PIPE_NOWAIT = 0x00000001;
3333
3334pub const GENERIC_READ = 0x80000000;
3335pub const GENERIC_WRITE = 0x40000000;
3336pub const GENERIC_EXECUTE = 0x20000000;
3337pub const GENERIC_ALL = 0x10000000;
3338
3339pub const FILE_SHARE_DELETE = 0x00000004;
3340pub const FILE_SHARE_READ = 0x00000001;
3341pub const FILE_SHARE_WRITE = 0x00000002;
3342
3343pub const DELETE = 0x00010000;
3344pub const READ_CONTROL = 0x00020000;
3345pub const WRITE_DAC = 0x00040000;
3346pub const WRITE_OWNER = 0x00080000;
3347pub const SYNCHRONIZE = 0x00100000;
3348pub const STANDARD_RIGHTS_READ = READ_CONTROL;
3349pub const STANDARD_RIGHTS_WRITE = READ_CONTROL;
3350pub const STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
3351pub const STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
3352pub const MAXIMUM_ALLOWED = 0x02000000;
3353
3354// disposition for NtCreateFile
3355pub const FILE_SUPERSEDE = 0;
3356pub const FILE_OPEN = 1;
3357pub const FILE_CREATE = 2;
3358pub const FILE_OPEN_IF = 3;
3359pub const FILE_OVERWRITE = 4;
3360pub const FILE_OVERWRITE_IF = 5;
3361pub const FILE_MAXIMUM_DISPOSITION = 5;
3362
3363// flags for NtCreateFile and NtOpenFile
3364pub const FILE_READ_DATA = 0x00000001;
3365pub const FILE_LIST_DIRECTORY = 0x00000001;
3366pub const FILE_WRITE_DATA = 0x00000002;
3367pub const FILE_ADD_FILE = 0x00000002;
3368pub const FILE_APPEND_DATA = 0x00000004;
3369pub const FILE_ADD_SUBDIRECTORY = 0x00000004;
3370pub const FILE_CREATE_PIPE_INSTANCE = 0x00000004;
3371pub const FILE_READ_EA = 0x00000008;
3372pub const FILE_WRITE_EA = 0x00000010;
3373pub const FILE_EXECUTE = 0x00000020;
3374pub const FILE_TRAVERSE = 0x00000020;
3375pub const FILE_DELETE_CHILD = 0x00000040;
3376pub const FILE_READ_ATTRIBUTES = 0x00000080;
3377pub const FILE_WRITE_ATTRIBUTES = 0x00000100;
3378
3379pub const FILE_DIRECTORY_FILE = 0x00000001;
3380pub const FILE_WRITE_THROUGH = 0x00000002;
3381pub const FILE_SEQUENTIAL_ONLY = 0x00000004;
3382pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
3383pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
3384pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
3385pub const FILE_NON_DIRECTORY_FILE = 0x00000040;
3386pub const FILE_CREATE_TREE_CONNECTION = 0x00000080;
3387pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
3388pub const FILE_NO_EA_KNOWLEDGE = 0x00000200;
3389pub const FILE_OPEN_FOR_RECOVERY = 0x00000400;
3390pub const FILE_RANDOM_ACCESS = 0x00000800;
3391pub const FILE_DELETE_ON_CLOSE = 0x00001000;
3392pub const FILE_OPEN_BY_FILE_ID = 0x00002000;
3393pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
3394pub const FILE_NO_COMPRESSION = 0x00008000;
3395pub const FILE_RESERVE_OPFILTER = 0x00100000;
3396pub const FILE_OPEN_REPARSE_POINT = 0x00200000;
3397pub const FILE_OPEN_OFFLINE_FILE = 0x00400000;
3398pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
3399
3400pub const CREATE_ALWAYS = 2;
3401pub const CREATE_NEW = 1;
3402pub const OPEN_ALWAYS = 4;
3403pub const OPEN_EXISTING = 3;
3404pub const TRUNCATE_EXISTING = 5;
3405
3406pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
3407pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
3408pub const FILE_ATTRIBUTE_DEVICE = 0x40;
3409pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
3410pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
3411pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
3412pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
3413pub const FILE_ATTRIBUTE_NORMAL = 0x80;
3414pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
3415pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
3416pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
3417pub const FILE_ATTRIBUTE_READONLY = 0x1;
3418pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
3419pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
3420pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
3421pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
3422pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
3423pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
3424pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
3425
3426pub const FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff;
3427pub const FILE_GENERIC_READ = STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE;
3428pub const FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE;
3429pub const FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE;
3430
3431// Flags for NtCreateNamedPipeFile
3432// NamedPipeType
3433pub const FILE_PIPE_BYTE_STREAM_TYPE = 0x0;
3434pub const FILE_PIPE_MESSAGE_TYPE = 0x1;
3435pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS = 0x0;
3436pub const FILE_PIPE_REJECT_REMOTE_CLIENTS = 0x2;
3437pub const FILE_PIPE_TYPE_VALID_MASK = 0x3;
3438// CompletionMode
3439pub const FILE_PIPE_QUEUE_OPERATION = 0x0;
3440pub const FILE_PIPE_COMPLETE_OPERATION = 0x1;
3441// ReadMode
3442pub const FILE_PIPE_BYTE_STREAM_MODE = 0x0;
3443pub const FILE_PIPE_MESSAGE_MODE = 0x1;
3444
3445// flags for CreateEvent
3446pub const CREATE_EVENT_INITIAL_SET = 0x00000002;
3447pub const CREATE_EVENT_MANUAL_RESET = 0x00000001;
3448
3449pub const EVENT_ALL_ACCESS = 0x1F0003;
3450pub const EVENT_MODIFY_STATE = 0x0002;
3451
3452// MEMORY_BASIC_INFORMATION.Type flags for VirtualQuery
3453pub const MEM_IMAGE = 0x1000000;
3454pub const MEM_MAPPED = 0x40000;
3455pub const MEM_PRIVATE = 0x20000;
3456
3457pub const PROCESS_INFORMATION = extern struct {
3458    hProcess: HANDLE,
3459    hThread: HANDLE,
3460    dwProcessId: DWORD,
3461    dwThreadId: DWORD,
3462};
3463
3464pub const STARTUPINFOW = extern struct {
3465    cb: DWORD,
3466    lpReserved: ?LPWSTR,
3467    lpDesktop: ?LPWSTR,
3468    lpTitle: ?LPWSTR,
3469    dwX: DWORD,
3470    dwY: DWORD,
3471    dwXSize: DWORD,
3472    dwYSize: DWORD,
3473    dwXCountChars: DWORD,
3474    dwYCountChars: DWORD,
3475    dwFillAttribute: DWORD,
3476    dwFlags: DWORD,
3477    wShowWindow: WORD,
3478    cbReserved2: WORD,
3479    lpReserved2: ?*BYTE,
3480    hStdInput: ?HANDLE,
3481    hStdOutput: ?HANDLE,
3482    hStdError: ?HANDLE,
3483};
3484
3485pub const STARTF_FORCEONFEEDBACK = 0x00000040;
3486pub const STARTF_FORCEOFFFEEDBACK = 0x00000080;
3487pub const STARTF_PREVENTPINNING = 0x00002000;
3488pub const STARTF_RUNFULLSCREEN = 0x00000020;
3489pub const STARTF_TITLEISAPPID = 0x00001000;
3490pub const STARTF_TITLEISLINKNAME = 0x00000800;
3491pub const STARTF_UNTRUSTEDSOURCE = 0x00008000;
3492pub const STARTF_USECOUNTCHARS = 0x00000008;
3493pub const STARTF_USEFILLATTRIBUTE = 0x00000010;
3494pub const STARTF_USEHOTKEY = 0x00000200;
3495pub const STARTF_USEPOSITION = 0x00000004;
3496pub const STARTF_USESHOWWINDOW = 0x00000001;
3497pub const STARTF_USESIZE = 0x00000002;
3498pub const STARTF_USESTDHANDLES = 0x00000100;
3499
3500pub const INFINITE = 4294967295;
3501
3502pub const MAXIMUM_WAIT_OBJECTS = 64;
3503
3504pub const WAIT_ABANDONED = 0x00000080;
3505pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0;
3506pub const WAIT_OBJECT_0 = 0x00000000;
3507pub const WAIT_TIMEOUT = 0x00000102;
3508pub const WAIT_FAILED = 0xFFFFFFFF;
3509
3510pub const HANDLE_FLAG_INHERIT = 0x00000001;
3511pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
3512
3513pub const MOVEFILE_COPY_ALLOWED = 2;
3514pub const MOVEFILE_CREATE_HARDLINK = 16;
3515pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4;
3516pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32;
3517pub const MOVEFILE_REPLACE_EXISTING = 1;
3518pub const MOVEFILE_WRITE_THROUGH = 8;
3519
3520pub const FILE_BEGIN = 0;
3521pub const FILE_CURRENT = 1;
3522pub const FILE_END = 2;
3523
3524pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
3525pub const HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
3526pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
3527pub const HEAP_NO_SERIALIZE = 0x00000001;
3528
3529// AllocationType values
3530pub const MEM_COMMIT = 0x1000;
3531pub const MEM_RESERVE = 0x2000;
3532pub const MEM_FREE = 0x10000;
3533pub const MEM_RESET = 0x80000;
3534pub const MEM_RESET_UNDO = 0x1000000;
3535pub const MEM_LARGE_PAGES = 0x20000000;
3536pub const MEM_PHYSICAL = 0x400000;
3537pub const MEM_TOP_DOWN = 0x100000;
3538pub const MEM_WRITE_WATCH = 0x200000;
3539pub const MEM_RESERVE_PLACEHOLDER = 0x00040000;
3540pub const MEM_PRESERVE_PLACEHOLDER = 0x00000400;
3541
3542// Protect values
3543pub const PAGE_EXECUTE = 0x10;
3544pub const PAGE_EXECUTE_READ = 0x20;
3545pub const PAGE_EXECUTE_READWRITE = 0x40;
3546pub const PAGE_EXECUTE_WRITECOPY = 0x80;
3547pub const PAGE_NOACCESS = 0x01;
3548pub const PAGE_READONLY = 0x02;
3549pub const PAGE_READWRITE = 0x04;
3550pub const PAGE_WRITECOPY = 0x08;
3551pub const PAGE_TARGETS_INVALID = 0x40000000;
3552pub const PAGE_TARGETS_NO_UPDATE = 0x40000000; // Same as PAGE_TARGETS_INVALID
3553pub const PAGE_GUARD = 0x100;
3554pub const PAGE_NOCACHE = 0x200;
3555pub const PAGE_WRITECOMBINE = 0x400;
3556
3557// FreeType values
3558pub const MEM_COALESCE_PLACEHOLDERS = 0x1;
3559pub const MEM_RESERVE_PLACEHOLDERS = 0x2;
3560pub const MEM_DECOMMIT = 0x4000;
3561pub const MEM_RELEASE = 0x8000;
3562
3563pub const PTHREAD_START_ROUTINE = *const fn (LPVOID) callconv(.winapi) DWORD;
3564pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
3565
3566pub const WIN32_FIND_DATAW = extern struct {
3567    dwFileAttributes: DWORD,
3568    ftCreationTime: FILETIME,
3569    ftLastAccessTime: FILETIME,
3570    ftLastWriteTime: FILETIME,
3571    nFileSizeHigh: DWORD,
3572    nFileSizeLow: DWORD,
3573    dwReserved0: DWORD,
3574    dwReserved1: DWORD,
3575    cFileName: [260]u16,
3576    cAlternateFileName: [14]u16,
3577};
3578
3579pub const FILETIME = extern struct {
3580    dwLowDateTime: DWORD,
3581    dwHighDateTime: DWORD,
3582};
3583
3584pub const SYSTEM_INFO = extern struct {
3585    anon1: extern union {
3586        dwOemId: DWORD,
3587        anon2: extern struct {
3588            wProcessorArchitecture: WORD,
3589            wReserved: WORD,
3590        },
3591    },
3592    dwPageSize: DWORD,
3593    lpMinimumApplicationAddress: LPVOID,
3594    lpMaximumApplicationAddress: LPVOID,
3595    dwActiveProcessorMask: DWORD_PTR,
3596    dwNumberOfProcessors: DWORD,
3597    dwProcessorType: DWORD,
3598    dwAllocationGranularity: DWORD,
3599    wProcessorLevel: WORD,
3600    wProcessorRevision: WORD,
3601};
3602
3603pub const HRESULT = c_long;
3604
3605pub const KNOWNFOLDERID = GUID;
3606pub const GUID = extern struct {
3607    Data1: u32,
3608    Data2: u16,
3609    Data3: u16,
3610    Data4: [8]u8,
3611
3612    const hex_offsets = switch (builtin.target.cpu.arch.endian()) {
3613        .big => [16]u6{
3614            0,  2,  4,  6,
3615            9,  11, 14, 16,
3616            19, 21, 24, 26,
3617            28, 30, 32, 34,
3618        },
3619        .little => [16]u6{
3620            6,  4,  2,  0,
3621            11, 9,  16, 14,
3622            19, 21, 24, 26,
3623            28, 30, 32, 34,
3624        },
3625    };
3626
3627    pub fn parse(s: []const u8) GUID {
3628        assert(s[0] == '{');
3629        assert(s[37] == '}');
3630        return parseNoBraces(s[1 .. s.len - 1]) catch @panic("invalid GUID string");
3631    }
3632
3633    pub fn parseNoBraces(s: []const u8) !GUID {
3634        assert(s.len == 36);
3635        assert(s[8] == '-');
3636        assert(s[13] == '-');
3637        assert(s[18] == '-');
3638        assert(s[23] == '-');
3639        var bytes: [16]u8 = undefined;
3640        for (hex_offsets, 0..) |hex_offset, i| {
3641            bytes[i] = (try std.fmt.charToDigit(s[hex_offset], 16)) << 4 |
3642                try std.fmt.charToDigit(s[hex_offset + 1], 16);
3643        }
3644        return @as(GUID, @bitCast(bytes));
3645    }
3646};
3647
3648test GUID {
3649    try std.testing.expectEqual(
3650        GUID{
3651            .Data1 = 0x01234567,
3652            .Data2 = 0x89ab,
3653            .Data3 = 0xef10,
3654            .Data4 = "\x32\x54\x76\x98\xba\xdc\xfe\x91".*,
3655        },
3656        GUID.parse("{01234567-89AB-EF10-3254-7698badcfe91}"),
3657    );
3658}
3659
3660pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
3661
3662pub const KF_FLAG_DEFAULT = 0;
3663pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
3664pub const KF_FLAG_CREATE = 32768;
3665pub const KF_FLAG_DONT_VERIFY = 16384;
3666pub const KF_FLAG_DONT_UNEXPAND = 8192;
3667pub const KF_FLAG_NO_ALIAS = 4096;
3668pub const KF_FLAG_INIT = 2048;
3669pub const KF_FLAG_DEFAULT_PATH = 1024;
3670pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
3671pub const KF_FLAG_SIMPLE_IDLIST = 256;
3672pub const KF_FLAG_ALIAS_ONLY = -2147483648;
3673
3674pub const S_OK = 0;
3675pub const S_FALSE = 0x00000001;
3676pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));
3677pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));
3678pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));
3679pub const E_ABORT = @as(c_long, @bitCast(@as(c_ulong, 0x80004004)));
3680pub const E_FAIL = @as(c_long, @bitCast(@as(c_ulong, 0x80004005)));
3681pub const E_UNEXPECTED = @as(c_long, @bitCast(@as(c_ulong, 0x8000FFFF)));
3682pub const E_ACCESSDENIED = @as(c_long, @bitCast(@as(c_ulong, 0x80070005)));
3683pub const E_HANDLE = @as(c_long, @bitCast(@as(c_ulong, 0x80070006)));
3684pub const E_OUTOFMEMORY = @as(c_long, @bitCast(@as(c_ulong, 0x8007000E)));
3685pub const E_INVALIDARG = @as(c_long, @bitCast(@as(c_ulong, 0x80070057)));
3686
3687pub fn HRESULT_CODE(hr: HRESULT) Win32Error {
3688    return @enumFromInt(hr & 0xFFFF);
3689}
3690
3691pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
3692pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
3693pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
3694pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
3695pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
3696pub const FILE_FLAG_OVERLAPPED = 0x40000000;
3697pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
3698pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
3699pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
3700pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
3701pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
3702
3703pub const RECT = extern struct {
3704    left: LONG,
3705    top: LONG,
3706    right: LONG,
3707    bottom: LONG,
3708};
3709
3710pub const SMALL_RECT = extern struct {
3711    Left: SHORT,
3712    Top: SHORT,
3713    Right: SHORT,
3714    Bottom: SHORT,
3715};
3716
3717pub const POINT = extern struct {
3718    x: LONG,
3719    y: LONG,
3720};
3721
3722pub const COORD = extern struct {
3723    X: SHORT,
3724    Y: SHORT,
3725};
3726
3727pub const CREATE_UNICODE_ENVIRONMENT = 1024;
3728
3729pub const TLS_OUT_OF_INDEXES = 4294967295;
3730pub const IMAGE_TLS_DIRECTORY = extern struct {
3731    StartAddressOfRawData: usize,
3732    EndAddressOfRawData: usize,
3733    AddressOfIndex: usize,
3734    AddressOfCallBacks: usize,
3735    SizeOfZeroFill: u32,
3736    Characteristics: u32,
3737};
3738pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
3739pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
3740
3741pub const PIMAGE_TLS_CALLBACK = ?*const fn (PVOID, DWORD, PVOID) callconv(.winapi) void;
3742
3743pub const PROV_RSA_FULL = 1;
3744
3745pub const REGSAM = ACCESS_MASK;
3746pub const ACCESS_MASK = DWORD;
3747pub const LSTATUS = LONG;
3748
3749pub const SECTION_INHERIT = enum(c_int) {
3750    ViewShare = 0,
3751    ViewUnmap = 1,
3752};
3753
3754pub const SECTION_QUERY = 0x0001;
3755pub const SECTION_MAP_WRITE = 0x0002;
3756pub const SECTION_MAP_READ = 0x0004;
3757pub const SECTION_MAP_EXECUTE = 0x0008;
3758pub const SECTION_EXTEND_SIZE = 0x0010;
3759pub const SECTION_ALL_ACCESS =
3760    STANDARD_RIGHTS_REQUIRED |
3761    SECTION_QUERY |
3762    SECTION_MAP_WRITE |
3763    SECTION_MAP_READ |
3764    SECTION_MAP_EXECUTE |
3765    SECTION_EXTEND_SIZE;
3766
3767pub const SEC_64K_PAGES = 0x80000;
3768pub const SEC_FILE = 0x800000;
3769pub const SEC_IMAGE = 0x1000000;
3770pub const SEC_PROTECTED_IMAGE = 0x2000000;
3771pub const SEC_RESERVE = 0x4000000;
3772pub const SEC_COMMIT = 0x8000000;
3773pub const SEC_IMAGE_NO_EXECUTE = SEC_IMAGE | SEC_NOCACHE;
3774pub const SEC_NOCACHE = 0x10000000;
3775pub const SEC_WRITECOMBINE = 0x40000000;
3776pub const SEC_LARGE_PAGES = 0x80000000;
3777
3778pub const HKEY = *opaque {};
3779
3780pub const HKEY_CLASSES_ROOT: HKEY = @ptrFromInt(0x80000000);
3781pub const HKEY_CURRENT_USER: HKEY = @ptrFromInt(0x80000001);
3782pub const HKEY_LOCAL_MACHINE: HKEY = @ptrFromInt(0x80000002);
3783pub const HKEY_USERS: HKEY = @ptrFromInt(0x80000003);
3784pub const HKEY_PERFORMANCE_DATA: HKEY = @ptrFromInt(0x80000004);
3785pub const HKEY_PERFORMANCE_TEXT: HKEY = @ptrFromInt(0x80000050);
3786pub const HKEY_PERFORMANCE_NLSTEXT: HKEY = @ptrFromInt(0x80000060);
3787pub const HKEY_CURRENT_CONFIG: HKEY = @ptrFromInt(0x80000005);
3788pub const HKEY_DYN_DATA: HKEY = @ptrFromInt(0x80000006);
3789pub const HKEY_CURRENT_USER_LOCAL_SETTINGS: HKEY = @ptrFromInt(0x80000007);
3790
3791/// Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
3792/// KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK access rights.
3793pub const KEY_ALL_ACCESS = 0xF003F;
3794/// Reserved for system use.
3795pub const KEY_CREATE_LINK = 0x0020;
3796/// Required to create a subkey of a registry key.
3797pub const KEY_CREATE_SUB_KEY = 0x0004;
3798/// Required to enumerate the subkeys of a registry key.
3799pub const KEY_ENUMERATE_SUB_KEYS = 0x0008;
3800/// Equivalent to KEY_READ.
3801pub const KEY_EXECUTE = 0x20019;
3802/// Required to request change notifications for a registry key or for subkeys of a registry key.
3803pub const KEY_NOTIFY = 0x0010;
3804/// Required to query the values of a registry key.
3805pub const KEY_QUERY_VALUE = 0x0001;
3806/// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
3807pub const KEY_READ = 0x20019;
3808/// Required to create, delete, or set a registry value.
3809pub const KEY_SET_VALUE = 0x0002;
3810/// Indicates that an application on 64-bit Windows should operate on the 32-bit registry view.
3811/// This flag is ignored by 32-bit Windows.
3812pub const KEY_WOW64_32KEY = 0x0200;
3813/// Indicates that an application on 64-bit Windows should operate on the 64-bit registry view.
3814/// This flag is ignored by 32-bit Windows.
3815pub const KEY_WOW64_64KEY = 0x0100;
3816/// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
3817pub const KEY_WRITE = 0x20006;
3818
3819/// Open symbolic link.
3820pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
3821
3822pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
3823    QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
3824    Flags: ULONG,
3825    Name: ?PWSTR,
3826    EntryContext: ?*anyopaque,
3827    DefaultType: ULONG,
3828    DefaultData: ?*anyopaque,
3829    DefaultLength: ULONG,
3830};
3831
3832pub const RTL_QUERY_REGISTRY_ROUTINE = ?*const fn (
3833    PWSTR,
3834    ULONG,
3835    ?*anyopaque,
3836    ULONG,
3837    ?*anyopaque,
3838    ?*anyopaque,
3839) callconv(.winapi) NTSTATUS;
3840
3841/// Path is a full path
3842pub const RTL_REGISTRY_ABSOLUTE = 0;
3843/// \Registry\Machine\System\CurrentControlSet\Services
3844pub const RTL_REGISTRY_SERVICES = 1;
3845/// \Registry\Machine\System\CurrentControlSet\Control
3846pub const RTL_REGISTRY_CONTROL = 2;
3847/// \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
3848pub const RTL_REGISTRY_WINDOWS_NT = 3;
3849/// \Registry\Machine\Hardware\DeviceMap
3850pub const RTL_REGISTRY_DEVICEMAP = 4;
3851/// \Registry\User\CurrentUser
3852pub const RTL_REGISTRY_USER = 5;
3853pub const RTL_REGISTRY_MAXIMUM = 6;
3854
3855/// Low order bits are registry handle
3856pub const RTL_REGISTRY_HANDLE = 0x40000000;
3857/// Indicates the key node is optional
3858pub const RTL_REGISTRY_OPTIONAL = 0x80000000;
3859
3860/// Name is a subkey and remainder of table or until next subkey are value
3861/// names for that subkey to look at.
3862pub const RTL_QUERY_REGISTRY_SUBKEY = 0x00000001;
3863
3864/// Reset current key to original key for this and all following table entries.
3865pub const RTL_QUERY_REGISTRY_TOPKEY = 0x00000002;
3866
3867/// Fail if no match found for this table entry.
3868pub const RTL_QUERY_REGISTRY_REQUIRED = 0x00000004;
3869
3870/// Used to mark a table entry that has no value name, just wants a call out, not
3871/// an enumeration of all values.
3872pub const RTL_QUERY_REGISTRY_NOVALUE = 0x00000008;
3873
3874/// Used to suppress the expansion of REG_MULTI_SZ into multiple callouts or
3875/// to prevent the expansion of environment variable values in REG_EXPAND_SZ.
3876pub const RTL_QUERY_REGISTRY_NOEXPAND = 0x00000010;
3877
3878/// QueryRoutine field ignored.  EntryContext field points to location to store value.
3879/// For null terminated strings, EntryContext points to UNICODE_STRING structure that
3880/// that describes maximum size of buffer. If .Buffer field is NULL then a buffer is
3881/// allocated.
3882pub const RTL_QUERY_REGISTRY_DIRECT = 0x00000020;
3883
3884/// Used to delete value keys after they are queried.
3885pub const RTL_QUERY_REGISTRY_DELETE = 0x00000040;
3886
3887/// Use this flag with the RTL_QUERY_REGISTRY_DIRECT flag to verify that the REG_XXX type
3888/// of the stored registry value matches the type expected by the caller.
3889/// If the types do not match, the call fails.
3890pub const RTL_QUERY_REGISTRY_TYPECHECK = 0x00000100;
3891
3892pub const REG = struct {
3893    /// No value type
3894    pub const NONE: ULONG = 0;
3895    /// Unicode nul terminated string
3896    pub const SZ: ULONG = 1;
3897    /// Unicode nul terminated string (with environment variable references)
3898    pub const EXPAND_SZ: ULONG = 2;
3899    /// Free form binary
3900    pub const BINARY: ULONG = 3;
3901    /// 32-bit number
3902    pub const DWORD: ULONG = 4;
3903    /// 32-bit number (same as REG_DWORD)
3904    pub const DWORD_LITTLE_ENDIAN: ULONG = 4;
3905    /// 32-bit number
3906    pub const DWORD_BIG_ENDIAN: ULONG = 5;
3907    /// Symbolic Link (unicode)
3908    pub const LINK: ULONG = 6;
3909    /// Multiple Unicode strings
3910    pub const MULTI_SZ: ULONG = 7;
3911    /// Resource list in the resource map
3912    pub const RESOURCE_LIST: ULONG = 8;
3913    /// Resource list in the hardware description
3914    pub const FULL_RESOURCE_DESCRIPTOR: ULONG = 9;
3915    pub const RESOURCE_REQUIREMENTS_LIST: ULONG = 10;
3916    /// 64-bit number
3917    pub const QWORD: ULONG = 11;
3918    /// 64-bit number (same as REG_QWORD)
3919    pub const QWORD_LITTLE_ENDIAN: ULONG = 11;
3920};
3921
3922pub const FILE_NOTIFY_INFORMATION = extern struct {
3923    NextEntryOffset: DWORD,
3924    Action: DWORD,
3925    FileNameLength: DWORD,
3926    // Flexible array member
3927    // FileName: [1]WCHAR,
3928};
3929
3930pub const FILE_ACTION_ADDED = 0x00000001;
3931pub const FILE_ACTION_REMOVED = 0x00000002;
3932pub const FILE_ACTION_MODIFIED = 0x00000003;
3933pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
3934pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
3935
3936pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?*const fn (DWORD, DWORD, *OVERLAPPED) callconv(.winapi) void;
3937
3938pub const FileNotifyChangeFilter = packed struct(DWORD) {
3939    file_name: bool = false,
3940    dir_name: bool = false,
3941    attributes: bool = false,
3942    size: bool = false,
3943    last_write: bool = false,
3944    last_access: bool = false,
3945    creation: bool = false,
3946    ea: bool = false,
3947    security: bool = false,
3948    stream_name: bool = false,
3949    stream_size: bool = false,
3950    stream_write: bool = false,
3951    _pad: u20 = 0,
3952};
3953
3954pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
3955    dwSize: COORD,
3956    dwCursorPosition: COORD,
3957    wAttributes: WORD,
3958    srWindow: SMALL_RECT,
3959    dwMaximumWindowSize: COORD,
3960};
3961
3962pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
3963pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
3964
3965pub const FOREGROUND_BLUE = 1;
3966pub const FOREGROUND_GREEN = 2;
3967pub const FOREGROUND_RED = 4;
3968pub const FOREGROUND_INTENSITY = 8;
3969
3970pub const LIST_ENTRY = extern struct {
3971    Flink: *LIST_ENTRY,
3972    Blink: *LIST_ENTRY,
3973};
3974
3975pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
3976    Type: WORD,
3977    CreatorBackTraceIndex: WORD,
3978    CriticalSection: *RTL_CRITICAL_SECTION,
3979    ProcessLocksList: LIST_ENTRY,
3980    EntryCount: DWORD,
3981    ContentionCount: DWORD,
3982    Flags: DWORD,
3983    CreatorBackTraceIndexHigh: WORD,
3984    SpareWORD: WORD,
3985};
3986
3987pub const RTL_CRITICAL_SECTION = extern struct {
3988    DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
3989    LockCount: LONG,
3990    RecursionCount: LONG,
3991    OwningThread: HANDLE,
3992    LockSemaphore: HANDLE,
3993    SpinCount: ULONG_PTR,
3994};
3995
3996pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
3997pub const INIT_ONCE = RTL_RUN_ONCE;
3998pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT;
3999pub const INIT_ONCE_FN = *const fn (InitOnce: *INIT_ONCE, Parameter: ?*anyopaque, Context: ?*anyopaque) callconv(.winapi) BOOL;
4000
4001pub const RTL_RUN_ONCE = extern struct {
4002    Ptr: ?*anyopaque,
4003};
4004
4005pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
4006
4007pub const COINIT = struct {
4008    pub const APARTMENTTHREADED = 2;
4009    pub const MULTITHREADED = 0;
4010    pub const DISABLE_OLE1DDE = 4;
4011    pub const SPEED_OVER_MEMORY = 8;
4012};
4013
4014pub const MEMORY_BASIC_INFORMATION = extern struct {
4015    BaseAddress: PVOID,
4016    AllocationBase: PVOID,
4017    AllocationProtect: DWORD,
4018    PartitionId: WORD,
4019    RegionSize: SIZE_T,
4020    State: DWORD,
4021    Protect: DWORD,
4022    Type: DWORD,
4023};
4024
4025pub const PMEMORY_BASIC_INFORMATION = *MEMORY_BASIC_INFORMATION;
4026
4027/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
4028/// > prefix may be expanded to a longer string by the system at run time, and
4029/// > this expansion applies to the total length.
4030/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
4031pub const PATH_MAX_WIDE = 32767;
4032
4033/// > [Each file name component can be] up to the value returned in the
4034/// > lpMaximumComponentLength parameter of the GetVolumeInformation function
4035/// > (this value is commonly 255 characters)
4036/// from https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
4037///
4038/// > The value that is stored in the variable that *lpMaximumComponentLength points to is
4039/// > used to indicate that a specified file system supports long names. For example, for
4040/// > a FAT file system that supports long names, the function stores the value 255, rather
4041/// > than the previous 8.3 indicator. Long names can also be supported on systems that use
4042/// > the NTFS file system.
4043/// from https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
4044///
4045/// The assumption being made here is that while lpMaximumComponentLength may vary, it will never
4046/// be larger than 255.
4047///
4048/// TODO: More verification of this assumption.
4049pub const NAME_MAX = 255;
4050
4051pub const FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
4052pub const FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
4053pub const FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
4054pub const FORMAT_MESSAGE_FROM_STRING = 0x00000400;
4055pub const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
4056pub const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
4057pub const FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF;
4058
4059pub const EXCEPTION_DATATYPE_MISALIGNMENT = 0x80000002;
4060pub const EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
4061pub const EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d;
4062pub const EXCEPTION_STACK_OVERFLOW = 0xc00000fd;
4063pub const EXCEPTION_CONTINUE_SEARCH = 0;
4064
4065pub const EXCEPTION_RECORD = extern struct {
4066    ExceptionCode: u32,
4067    ExceptionFlags: u32,
4068    ExceptionRecord: *EXCEPTION_RECORD,
4069    ExceptionAddress: *anyopaque,
4070    NumberParameters: u32,
4071    ExceptionInformation: [15]usize,
4072};
4073
4074pub const FLOATING_SAVE_AREA = switch (native_arch) {
4075    .x86 => extern struct {
4076        ControlWord: DWORD,
4077        StatusWord: DWORD,
4078        TagWord: DWORD,
4079        ErrorOffset: DWORD,
4080        ErrorSelector: DWORD,
4081        DataOffset: DWORD,
4082        DataSelector: DWORD,
4083        RegisterArea: [80]BYTE,
4084        Cr0NpxState: DWORD,
4085    },
4086    else => @compileError("FLOATING_SAVE_AREA only defined on x86"),
4087};
4088
4089pub const M128A = switch (native_arch) {
4090    .x86_64 => extern struct {
4091        Low: ULONGLONG,
4092        High: LONGLONG,
4093    },
4094    else => @compileError("M128A only defined on x86_64"),
4095};
4096
4097pub const XMM_SAVE_AREA32 = switch (native_arch) {
4098    .x86_64 => extern struct {
4099        ControlWord: WORD,
4100        StatusWord: WORD,
4101        TagWord: BYTE,
4102        Reserved1: BYTE,
4103        ErrorOpcode: WORD,
4104        ErrorOffset: DWORD,
4105        ErrorSelector: WORD,
4106        Reserved2: WORD,
4107        DataOffset: DWORD,
4108        DataSelector: WORD,
4109        Reserved3: WORD,
4110        MxCsr: DWORD,
4111        MxCsr_Mask: DWORD,
4112        FloatRegisters: [8]M128A,
4113        XmmRegisters: [16]M128A,
4114        Reserved4: [96]BYTE,
4115    },
4116    else => @compileError("XMM_SAVE_AREA32 only defined on x86_64"),
4117};
4118
4119pub const NEON128 = switch (native_arch) {
4120    .thumb => extern struct {
4121        Low: ULONGLONG,
4122        High: LONGLONG,
4123    },
4124    .aarch64 => extern union {
4125        DUMMYSTRUCTNAME: extern struct {
4126            Low: ULONGLONG,
4127            High: LONGLONG,
4128        },
4129        D: [2]f64,
4130        S: [4]f32,
4131        H: [8]WORD,
4132        B: [16]BYTE,
4133    },
4134    else => @compileError("NEON128 only defined on aarch64"),
4135};
4136
4137pub const CONTEXT = switch (native_arch) {
4138    .x86 => extern struct {
4139        ContextFlags: DWORD,
4140        Dr0: DWORD,
4141        Dr1: DWORD,
4142        Dr2: DWORD,
4143        Dr3: DWORD,
4144        Dr6: DWORD,
4145        Dr7: DWORD,
4146        FloatSave: FLOATING_SAVE_AREA,
4147        SegGs: DWORD,
4148        SegFs: DWORD,
4149        SegEs: DWORD,
4150        SegDs: DWORD,
4151        Edi: DWORD,
4152        Esi: DWORD,
4153        Ebx: DWORD,
4154        Edx: DWORD,
4155        Ecx: DWORD,
4156        Eax: DWORD,
4157        Ebp: DWORD,
4158        Eip: DWORD,
4159        SegCs: DWORD,
4160        EFlags: DWORD,
4161        Esp: DWORD,
4162        SegSs: DWORD,
4163        ExtendedRegisters: [512]BYTE,
4164
4165        pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
4166            return .{ .bp = ctx.Ebp, .ip = ctx.Eip, .sp = ctx.Esp };
4167        }
4168    },
4169    .x86_64 => extern struct {
4170        P1Home: DWORD64 align(16),
4171        P2Home: DWORD64,
4172        P3Home: DWORD64,
4173        P4Home: DWORD64,
4174        P5Home: DWORD64,
4175        P6Home: DWORD64,
4176        ContextFlags: DWORD,
4177        MxCsr: DWORD,
4178        SegCs: WORD,
4179        SegDs: WORD,
4180        SegEs: WORD,
4181        SegFs: WORD,
4182        SegGs: WORD,
4183        SegSs: WORD,
4184        EFlags: DWORD,
4185        Dr0: DWORD64,
4186        Dr1: DWORD64,
4187        Dr2: DWORD64,
4188        Dr3: DWORD64,
4189        Dr6: DWORD64,
4190        Dr7: DWORD64,
4191        Rax: DWORD64,
4192        Rcx: DWORD64,
4193        Rdx: DWORD64,
4194        Rbx: DWORD64,
4195        Rsp: DWORD64,
4196        Rbp: DWORD64,
4197        Rsi: DWORD64,
4198        Rdi: DWORD64,
4199        R8: DWORD64,
4200        R9: DWORD64,
4201        R10: DWORD64,
4202        R11: DWORD64,
4203        R12: DWORD64,
4204        R13: DWORD64,
4205        R14: DWORD64,
4206        R15: DWORD64,
4207        Rip: DWORD64,
4208        DUMMYUNIONNAME: extern union {
4209            FltSave: XMM_SAVE_AREA32,
4210            FloatSave: XMM_SAVE_AREA32,
4211            DUMMYSTRUCTNAME: extern struct {
4212                Header: [2]M128A,
4213                Legacy: [8]M128A,
4214                Xmm0: M128A,
4215                Xmm1: M128A,
4216                Xmm2: M128A,
4217                Xmm3: M128A,
4218                Xmm4: M128A,
4219                Xmm5: M128A,
4220                Xmm6: M128A,
4221                Xmm7: M128A,
4222                Xmm8: M128A,
4223                Xmm9: M128A,
4224                Xmm10: M128A,
4225                Xmm11: M128A,
4226                Xmm12: M128A,
4227                Xmm13: M128A,
4228                Xmm14: M128A,
4229                Xmm15: M128A,
4230            },
4231        },
4232        VectorRegister: [26]M128A,
4233        VectorControl: DWORD64,
4234        DebugControl: DWORD64,
4235        LastBranchToRip: DWORD64,
4236        LastBranchFromRip: DWORD64,
4237        LastExceptionToRip: DWORD64,
4238        LastExceptionFromRip: DWORD64,
4239
4240        pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
4241            return .{ .bp = ctx.Rbp, .ip = ctx.Rip, .sp = ctx.Rsp };
4242        }
4243
4244        pub fn setIp(ctx: *CONTEXT, ip: usize) void {
4245            ctx.Rip = ip;
4246        }
4247
4248        pub fn setSp(ctx: *CONTEXT, sp: usize) void {
4249            ctx.Rsp = sp;
4250        }
4251    },
4252    .thumb => extern struct {
4253        ContextFlags: ULONG,
4254        R0: ULONG,
4255        R1: ULONG,
4256        R2: ULONG,
4257        R3: ULONG,
4258        R4: ULONG,
4259        R5: ULONG,
4260        R6: ULONG,
4261        R7: ULONG,
4262        R8: ULONG,
4263        R9: ULONG,
4264        R10: ULONG,
4265        R11: ULONG,
4266        R12: ULONG,
4267        Sp: ULONG,
4268        Lr: ULONG,
4269        Pc: ULONG,
4270        Cpsr: ULONG,
4271        Fpcsr: ULONG,
4272        Padding: ULONG,
4273        DUMMYUNIONNAME: extern union {
4274            Q: [16]NEON128,
4275            D: [32]ULONGLONG,
4276            S: [32]ULONG,
4277        },
4278        Bvr: [8]ULONG,
4279        Bcr: [8]ULONG,
4280        Wvr: [1]ULONG,
4281        Wcr: [1]ULONG,
4282        Padding2: [2]ULONG,
4283
4284        pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
4285            return .{
4286                .bp = ctx.DUMMYUNIONNAME.S[11],
4287                .ip = ctx.Pc,
4288                .sp = ctx.Sp,
4289            };
4290        }
4291
4292        pub fn setIp(ctx: *CONTEXT, ip: usize) void {
4293            ctx.Pc = ip;
4294        }
4295
4296        pub fn setSp(ctx: *CONTEXT, sp: usize) void {
4297            ctx.Sp = sp;
4298        }
4299    },
4300    .aarch64 => extern struct {
4301        ContextFlags: ULONG align(16),
4302        Cpsr: ULONG,
4303        DUMMYUNIONNAME: extern union {
4304            DUMMYSTRUCTNAME: extern struct {
4305                X0: DWORD64,
4306                X1: DWORD64,
4307                X2: DWORD64,
4308                X3: DWORD64,
4309                X4: DWORD64,
4310                X5: DWORD64,
4311                X6: DWORD64,
4312                X7: DWORD64,
4313                X8: DWORD64,
4314                X9: DWORD64,
4315                X10: DWORD64,
4316                X11: DWORD64,
4317                X12: DWORD64,
4318                X13: DWORD64,
4319                X14: DWORD64,
4320                X15: DWORD64,
4321                X16: DWORD64,
4322                X17: DWORD64,
4323                X18: DWORD64,
4324                X19: DWORD64,
4325                X20: DWORD64,
4326                X21: DWORD64,
4327                X22: DWORD64,
4328                X23: DWORD64,
4329                X24: DWORD64,
4330                X25: DWORD64,
4331                X26: DWORD64,
4332                X27: DWORD64,
4333                X28: DWORD64,
4334                Fp: DWORD64,
4335                Lr: DWORD64,
4336            },
4337            X: [31]DWORD64,
4338        },
4339        Sp: DWORD64,
4340        Pc: DWORD64,
4341        V: [32]NEON128,
4342        Fpcr: DWORD,
4343        Fpsr: DWORD,
4344        Bcr: [8]DWORD,
4345        Bvr: [8]DWORD64,
4346        Wcr: [2]DWORD,
4347        Wvr: [2]DWORD64,
4348
4349        pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
4350            return .{
4351                .bp = ctx.DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp,
4352                .ip = ctx.Pc,
4353                .sp = ctx.Sp,
4354            };
4355        }
4356
4357        pub fn setIp(ctx: *CONTEXT, ip: usize) void {
4358            ctx.Pc = ip;
4359        }
4360
4361        pub fn setSp(ctx: *CONTEXT, sp: usize) void {
4362            ctx.Sp = sp;
4363        }
4364    },
4365    else => @compileError("CONTEXT is not defined for this architecture"),
4366};
4367
4368pub const RUNTIME_FUNCTION = switch (native_arch) {
4369    .x86_64 => extern struct {
4370        BeginAddress: DWORD,
4371        EndAddress: DWORD,
4372        UnwindData: DWORD,
4373    },
4374    .thumb => extern struct {
4375        BeginAddress: DWORD,
4376        DUMMYUNIONNAME: extern union {
4377            UnwindData: DWORD,
4378            DUMMYSTRUCTNAME: packed struct {
4379                Flag: u2,
4380                FunctionLength: u11,
4381                Ret: u2,
4382                H: u1,
4383                Reg: u3,
4384                R: u1,
4385                L: u1,
4386                C: u1,
4387                StackAdjust: u10,
4388            },
4389        },
4390    },
4391    .aarch64 => extern struct {
4392        BeginAddress: DWORD,
4393        DUMMYUNIONNAME: extern union {
4394            UnwindData: DWORD,
4395            DUMMYSTRUCTNAME: packed struct {
4396                Flag: u2,
4397                FunctionLength: u11,
4398                RegF: u3,
4399                RegI: u4,
4400                H: u1,
4401                CR: u2,
4402                FrameSize: u9,
4403            },
4404        },
4405    },
4406    else => @compileError("RUNTIME_FUNCTION is not defined for this architecture"),
4407};
4408
4409pub const KNONVOLATILE_CONTEXT_POINTERS = switch (native_arch) {
4410    .x86_64 => extern struct {
4411        FloatingContext: [16]?*M128A,
4412        IntegerContext: [16]?*ULONG64,
4413    },
4414    .thumb => extern struct {
4415        R4: ?*DWORD,
4416        R5: ?*DWORD,
4417        R6: ?*DWORD,
4418        R7: ?*DWORD,
4419        R8: ?*DWORD,
4420        R9: ?*DWORD,
4421        R10: ?*DWORD,
4422        R11: ?*DWORD,
4423        Lr: ?*DWORD,
4424        D8: ?*ULONGLONG,
4425        D9: ?*ULONGLONG,
4426        D10: ?*ULONGLONG,
4427        D11: ?*ULONGLONG,
4428        D12: ?*ULONGLONG,
4429        D13: ?*ULONGLONG,
4430        D14: ?*ULONGLONG,
4431        D15: ?*ULONGLONG,
4432    },
4433    .aarch64 => extern struct {
4434        X19: ?*DWORD64,
4435        X20: ?*DWORD64,
4436        X21: ?*DWORD64,
4437        X22: ?*DWORD64,
4438        X23: ?*DWORD64,
4439        X24: ?*DWORD64,
4440        X25: ?*DWORD64,
4441        X26: ?*DWORD64,
4442        X27: ?*DWORD64,
4443        X28: ?*DWORD64,
4444        Fp: ?*DWORD64,
4445        Lr: ?*DWORD64,
4446        D8: ?*DWORD64,
4447        D9: ?*DWORD64,
4448        D10: ?*DWORD64,
4449        D11: ?*DWORD64,
4450        D12: ?*DWORD64,
4451        D13: ?*DWORD64,
4452        D14: ?*DWORD64,
4453        D15: ?*DWORD64,
4454    },
4455    else => @compileError("KNONVOLATILE_CONTEXT_POINTERS is not defined for this architecture"),
4456};
4457
4458pub const EXCEPTION_POINTERS = extern struct {
4459    ExceptionRecord: *EXCEPTION_RECORD,
4460    ContextRecord: *CONTEXT,
4461};
4462
4463pub const VECTORED_EXCEPTION_HANDLER = *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(.winapi) c_long;
4464
4465pub const EXCEPTION_DISPOSITION = i32;
4466pub const EXCEPTION_ROUTINE = *const fn (
4467    ExceptionRecord: ?*EXCEPTION_RECORD,
4468    EstablisherFrame: PVOID,
4469    ContextRecord: *(Self.CONTEXT),
4470    DispatcherContext: PVOID,
4471) callconv(.winapi) EXCEPTION_DISPOSITION;
4472
4473pub const UNWIND_HISTORY_TABLE_SIZE = 12;
4474pub const UNWIND_HISTORY_TABLE_ENTRY = extern struct {
4475    ImageBase: ULONG64,
4476    FunctionEntry: *Self.RUNTIME_FUNCTION,
4477};
4478
4479pub const UNWIND_HISTORY_TABLE = extern struct {
4480    Count: ULONG,
4481    LocalHint: BYTE,
4482    GlobalHint: BYTE,
4483    Search: BYTE,
4484    Once: BYTE,
4485    LowAddress: ULONG64,
4486    HighAddress: ULONG64,
4487    Entry: [UNWIND_HISTORY_TABLE_SIZE]UNWIND_HISTORY_TABLE_ENTRY,
4488};
4489
4490pub const UNW_FLAG_NHANDLER = 0x0;
4491pub const UNW_FLAG_EHANDLER = 0x1;
4492pub const UNW_FLAG_UHANDLER = 0x2;
4493pub const UNW_FLAG_CHAININFO = 0x4;
4494
4495pub const OBJECT_ATTRIBUTES = extern struct {
4496    Length: ULONG,
4497    RootDirectory: ?HANDLE,
4498    ObjectName: *UNICODE_STRING,
4499    Attributes: ULONG,
4500    SecurityDescriptor: ?*anyopaque,
4501    SecurityQualityOfService: ?*anyopaque,
4502};
4503
4504pub const OBJ_INHERIT = 0x00000002;
4505pub const OBJ_PERMANENT = 0x00000010;
4506pub const OBJ_EXCLUSIVE = 0x00000020;
4507pub const OBJ_CASE_INSENSITIVE = 0x00000040;
4508pub const OBJ_OPENIF = 0x00000080;
4509pub const OBJ_OPENLINK = 0x00000100;
4510pub const OBJ_KERNEL_HANDLE = 0x00000200;
4511pub const OBJ_VALID_ATTRIBUTES = 0x000003F2;
4512
4513pub const UNICODE_STRING = extern struct {
4514    Length: c_ushort,
4515    MaximumLength: c_ushort,
4516    Buffer: ?[*]WCHAR,
4517};
4518
4519pub const ACTIVATION_CONTEXT_DATA = opaque {};
4520pub const ASSEMBLY_STORAGE_MAP = opaque {};
4521pub const FLS_CALLBACK_INFO = opaque {};
4522pub const RTL_BITMAP = opaque {};
4523pub const KAFFINITY = usize;
4524pub const KPRIORITY = i32;
4525
4526pub const CLIENT_ID = extern struct {
4527    UniqueProcess: HANDLE,
4528    UniqueThread: HANDLE,
4529};
4530
4531pub const THREAD_BASIC_INFORMATION = extern struct {
4532    ExitStatus: NTSTATUS,
4533    TebBaseAddress: PVOID,
4534    ClientId: CLIENT_ID,
4535    AffinityMask: KAFFINITY,
4536    Priority: KPRIORITY,
4537    BasePriority: KPRIORITY,
4538};
4539
4540pub const TEB = extern struct {
4541    NtTib: NT_TIB,
4542    EnvironmentPointer: PVOID,
4543    ClientId: CLIENT_ID,
4544    ActiveRpcHandle: PVOID,
4545    ThreadLocalStoragePointer: PVOID,
4546    ProcessEnvironmentBlock: *PEB,
4547    LastErrorValue: ULONG,
4548    Reserved2: [399 * @sizeOf(PVOID) - @sizeOf(ULONG)]u8,
4549    Reserved3: [1952]u8,
4550    TlsSlots: [64]PVOID,
4551    Reserved4: [8]u8,
4552    Reserved5: [26]PVOID,
4553    ReservedForOle: PVOID,
4554    Reserved6: [4]PVOID,
4555    TlsExpansionSlots: PVOID,
4556};
4557
4558comptime {
4559    // XXX: Without this check we cannot use `std.Io.Writer` on 16-bit platforms. `std.fmt.bufPrint` will hit the unreachable in `PEB.GdiHandleBuffer` without this guard.
4560    if (builtin.os.tag == .windows) {
4561        // Offsets taken from WinDbg info and Geoff Chappell[1] (RIP)
4562        // [1]: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm
4563        assert(@offsetOf(TEB, "NtTib") == 0x00);
4564        if (@sizeOf(usize) == 4) {
4565            assert(@offsetOf(TEB, "EnvironmentPointer") == 0x1C);
4566            assert(@offsetOf(TEB, "ClientId") == 0x20);
4567            assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x28);
4568            assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x2C);
4569            assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
4570            assert(@offsetOf(TEB, "LastErrorValue") == 0x34);
4571            assert(@offsetOf(TEB, "TlsSlots") == 0xe10);
4572        } else if (@sizeOf(usize) == 8) {
4573            assert(@offsetOf(TEB, "EnvironmentPointer") == 0x38);
4574            assert(@offsetOf(TEB, "ClientId") == 0x40);
4575            assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x50);
4576            assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x58);
4577            assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
4578            assert(@offsetOf(TEB, "LastErrorValue") == 0x68);
4579            assert(@offsetOf(TEB, "TlsSlots") == 0x1480);
4580        }
4581    }
4582}
4583
4584pub const EXCEPTION_REGISTRATION_RECORD = extern struct {
4585    Next: ?*EXCEPTION_REGISTRATION_RECORD,
4586    Handler: ?*EXCEPTION_DISPOSITION,
4587};
4588
4589pub const NT_TIB = extern struct {
4590    ExceptionList: ?*EXCEPTION_REGISTRATION_RECORD,
4591    StackBase: PVOID,
4592    StackLimit: PVOID,
4593    SubSystemTib: PVOID,
4594    DUMMYUNIONNAME: extern union { FiberData: PVOID, Version: DWORD },
4595    ArbitraryUserPointer: PVOID,
4596    Self: ?*@This(),
4597};
4598
4599/// Process Environment Block
4600/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
4601///  - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269
4602///  - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm
4603pub const PEB = extern struct {
4604    // Versions: All
4605    InheritedAddressSpace: BOOLEAN,
4606
4607    // Versions: 3.51+
4608    ReadImageFileExecOptions: BOOLEAN,
4609    BeingDebugged: BOOLEAN,
4610
4611    // Versions: 5.2+ (previously was padding)
4612    BitField: UCHAR,
4613
4614    // Versions: all
4615    Mutant: HANDLE,
4616    ImageBaseAddress: HMODULE,
4617    Ldr: *PEB_LDR_DATA,
4618    ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
4619    SubSystemData: PVOID,
4620    ProcessHeap: HANDLE,
4621
4622    // Versions: 5.1+
4623    FastPebLock: *RTL_CRITICAL_SECTION,
4624
4625    // Versions: 5.2+
4626    AtlThunkSListPtr: PVOID,
4627    IFEOKey: PVOID,
4628
4629    // Versions: 6.0+
4630
4631    /// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/crossprocessflags.htm
4632    CrossProcessFlags: ULONG,
4633
4634    // Versions: 6.0+
4635    union1: extern union {
4636        KernelCallbackTable: PVOID,
4637        UserSharedInfoPtr: PVOID,
4638    },
4639
4640    // Versions: 5.1+
4641    SystemReserved: ULONG,
4642
4643    // Versions: 5.1, (not 5.2, not 6.0), 6.1+
4644    AtlThunkSListPtr32: ULONG,
4645
4646    // Versions: 6.1+
4647    ApiSetMap: PVOID,
4648
4649    // Versions: all
4650    TlsExpansionCounter: ULONG,
4651    // note: there is padding here on 64 bit
4652    TlsBitmap: *RTL_BITMAP,
4653    TlsBitmapBits: [2]ULONG,
4654    ReadOnlySharedMemoryBase: PVOID,
4655
4656    // Versions: 1703+
4657    SharedData: PVOID,
4658
4659    // Versions: all
4660    ReadOnlyStaticServerData: *PVOID,
4661    AnsiCodePageData: PVOID,
4662    OemCodePageData: PVOID,
4663    UnicodeCaseTableData: PVOID,
4664
4665    // Versions: 3.51+
4666    NumberOfProcessors: ULONG,
4667    NtGlobalFlag: ULONG,
4668
4669    // Versions: all
4670    CriticalSectionTimeout: LARGE_INTEGER,
4671
4672    // End of Original PEB size
4673
4674    // Fields appended in 3.51:
4675    HeapSegmentReserve: ULONG_PTR,
4676    HeapSegmentCommit: ULONG_PTR,
4677    HeapDeCommitTotalFreeThreshold: ULONG_PTR,
4678    HeapDeCommitFreeBlockThreshold: ULONG_PTR,
4679    NumberOfHeaps: ULONG,
4680    MaximumNumberOfHeaps: ULONG,
4681    ProcessHeaps: *PVOID,
4682
4683    // Fields appended in 4.0:
4684    GdiSharedHandleTable: PVOID,
4685    ProcessStarterHelper: PVOID,
4686    GdiDCAttributeList: ULONG,
4687    // note: there is padding here on 64 bit
4688    LoaderLock: *RTL_CRITICAL_SECTION,
4689    OSMajorVersion: ULONG,
4690    OSMinorVersion: ULONG,
4691    OSBuildNumber: USHORT,
4692    OSCSDVersion: USHORT,
4693    OSPlatformId: ULONG,
4694    ImageSubSystem: ULONG,
4695    ImageSubSystemMajorVersion: ULONG,
4696    ImageSubSystemMinorVersion: ULONG,
4697    // note: there is padding here on 64 bit
4698    ActiveProcessAffinityMask: KAFFINITY,
4699    GdiHandleBuffer: [
4700        switch (@sizeOf(usize)) {
4701            4 => 0x22,
4702            8 => 0x3C,
4703            else => unreachable,
4704        }
4705    ]ULONG,
4706
4707    // Fields appended in 5.0 (Windows 2000):
4708    PostProcessInitRoutine: PVOID,
4709    TlsExpansionBitmap: *RTL_BITMAP,
4710    TlsExpansionBitmapBits: [32]ULONG,
4711    SessionId: ULONG,
4712    // note: there is padding here on 64 bit
4713    // Versions: 5.1+
4714    AppCompatFlags: ULARGE_INTEGER,
4715    AppCompatFlagsUser: ULARGE_INTEGER,
4716    ShimData: PVOID,
4717    // Versions: 5.0+
4718    AppCompatInfo: PVOID,
4719    CSDVersion: UNICODE_STRING,
4720
4721    // Fields appended in 5.1 (Windows XP):
4722    ActivationContextData: *const ACTIVATION_CONTEXT_DATA,
4723    ProcessAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
4724    SystemDefaultActivationData: *const ACTIVATION_CONTEXT_DATA,
4725    SystemAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
4726    MinimumStackCommit: ULONG_PTR,
4727
4728    // Fields appended in 5.2 (Windows Server 2003):
4729    FlsCallback: *FLS_CALLBACK_INFO,
4730    FlsListHead: LIST_ENTRY,
4731    FlsBitmap: *RTL_BITMAP,
4732    FlsBitmapBits: [4]ULONG,
4733    FlsHighIndex: ULONG,
4734
4735    // Fields appended in 6.0 (Windows Vista):
4736    WerRegistrationData: PVOID,
4737    WerShipAssertPtr: PVOID,
4738
4739    // Fields appended in 6.1 (Windows 7):
4740    pUnused: PVOID, // previously pContextData
4741    pImageHeaderHash: PVOID,
4742
4743    /// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/tracingflags.htm
4744    TracingFlags: ULONG,
4745
4746    // Fields appended in 6.2 (Windows 8):
4747    CsrServerReadOnlySharedMemoryBase: ULONGLONG,
4748
4749    // Fields appended in 1511:
4750    TppWorkerpListLock: ULONG,
4751    TppWorkerpList: LIST_ENTRY,
4752    WaitOnAddressHashTable: [0x80]PVOID,
4753
4754    // Fields appended in 1709:
4755    TelemetryCoverageHeader: PVOID,
4756    CloudFileFlags: ULONG,
4757};
4758
4759/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
4760/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module.
4761///
4762/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
4763///  - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
4764pub const PEB_LDR_DATA = extern struct {
4765    // Versions: 3.51 and higher
4766    /// The size in bytes of the structure
4767    Length: ULONG,
4768
4769    /// TRUE if the structure is prepared.
4770    Initialized: BOOLEAN,
4771
4772    SsHandle: PVOID,
4773    InLoadOrderModuleList: LIST_ENTRY,
4774    InMemoryOrderModuleList: LIST_ENTRY,
4775    InInitializationOrderModuleList: LIST_ENTRY,
4776
4777    // Versions: 5.1 and higher
4778
4779    /// No known use of this field is known in Windows 8 and higher.
4780    EntryInProgress: PVOID,
4781
4782    // Versions: 6.0 from Windows Vista SP1, and higher
4783    ShutdownInProgress: BOOLEAN,
4784
4785    /// Though ShutdownThreadId is declared as a HANDLE,
4786    /// it is indeed the thread ID as suggested by its name.
4787    /// It is picked up from the UniqueThread member of the CLIENT_ID in the
4788    /// TEB of the thread that asks to terminate the process.
4789    ShutdownThreadId: HANDLE,
4790};
4791
4792/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
4793///  - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
4794///  - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
4795pub const LDR_DATA_TABLE_ENTRY = extern struct {
4796    InLoadOrderLinks: LIST_ENTRY,
4797    InMemoryOrderLinks: LIST_ENTRY,
4798    InInitializationOrderLinks: LIST_ENTRY,
4799    DllBase: PVOID,
4800    EntryPoint: PVOID,
4801    SizeOfImage: ULONG,
4802    FullDllName: UNICODE_STRING,
4803    BaseDllName: UNICODE_STRING,
4804    Reserved5: [3]PVOID,
4805    DUMMYUNIONNAME: extern union {
4806        CheckSum: ULONG,
4807        Reserved6: PVOID,
4808    },
4809    TimeDateStamp: ULONG,
4810};
4811
4812pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
4813    AllocationSize: ULONG,
4814    Size: ULONG,
4815    Flags: ULONG,
4816    DebugFlags: ULONG,
4817    ConsoleHandle: HANDLE,
4818    ConsoleFlags: ULONG,
4819    hStdInput: HANDLE,
4820    hStdOutput: HANDLE,
4821    hStdError: HANDLE,
4822    CurrentDirectory: CURDIR,
4823    DllPath: UNICODE_STRING,
4824    ImagePathName: UNICODE_STRING,
4825    CommandLine: UNICODE_STRING,
4826    /// Points to a NUL-terminated sequence of NUL-terminated
4827    /// WTF-16 LE encoded `name=value` sequences.
4828    /// Example using string literal syntax:
4829    /// `"NAME=value\x00foo=bar\x00\x00"`
4830    Environment: [*:0]WCHAR,
4831    dwX: ULONG,
4832    dwY: ULONG,
4833    dwXSize: ULONG,
4834    dwYSize: ULONG,
4835    dwXCountChars: ULONG,
4836    dwYCountChars: ULONG,
4837    dwFillAttribute: ULONG,
4838    dwFlags: ULONG,
4839    dwShowWindow: ULONG,
4840    WindowTitle: UNICODE_STRING,
4841    Desktop: UNICODE_STRING,
4842    ShellInfo: UNICODE_STRING,
4843    RuntimeInfo: UNICODE_STRING,
4844    DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
4845};
4846
4847pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
4848    Flags: c_ushort,
4849    Length: c_ushort,
4850    TimeStamp: ULONG,
4851    DosPath: UNICODE_STRING,
4852};
4853
4854pub const PPS_POST_PROCESS_INIT_ROUTINE = ?*const fn () callconv(.winapi) void;
4855
4856pub const FILE_DIRECTORY_INFORMATION = extern struct {
4857    NextEntryOffset: ULONG,
4858    FileIndex: ULONG,
4859    CreationTime: LARGE_INTEGER,
4860    LastAccessTime: LARGE_INTEGER,
4861    LastWriteTime: LARGE_INTEGER,
4862    ChangeTime: LARGE_INTEGER,
4863    EndOfFile: LARGE_INTEGER,
4864    AllocationSize: LARGE_INTEGER,
4865    FileAttributes: ULONG,
4866    FileNameLength: ULONG,
4867    FileName: [1]WCHAR,
4868};
4869
4870pub const FILE_BOTH_DIR_INFORMATION = extern struct {
4871    NextEntryOffset: ULONG,
4872    FileIndex: ULONG,
4873    CreationTime: LARGE_INTEGER,
4874    LastAccessTime: LARGE_INTEGER,
4875    LastWriteTime: LARGE_INTEGER,
4876    ChangeTime: LARGE_INTEGER,
4877    EndOfFile: LARGE_INTEGER,
4878    AllocationSize: LARGE_INTEGER,
4879    FileAttributes: ULONG,
4880    FileNameLength: ULONG,
4881    EaSize: ULONG,
4882    ShortNameLength: CHAR,
4883    ShortName: [12]WCHAR,
4884    FileName: [1]WCHAR,
4885};
4886pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
4887
4888/// Helper for iterating a byte buffer of FILE_*_INFORMATION structures (from
4889/// things like NtQueryDirectoryFile calls).
4890pub fn FileInformationIterator(comptime FileInformationType: type) type {
4891    return struct {
4892        byte_offset: usize = 0,
4893        buf: []u8 align(@alignOf(FileInformationType)),
4894
4895        pub fn next(self: *@This()) ?*FileInformationType {
4896            if (self.byte_offset >= self.buf.len) return null;
4897            const cur: *FileInformationType = @ptrCast(@alignCast(&self.buf[self.byte_offset]));
4898            if (cur.NextEntryOffset == 0) {
4899                self.byte_offset = self.buf.len;
4900            } else {
4901                self.byte_offset += cur.NextEntryOffset;
4902            }
4903            return cur;
4904        }
4905    };
4906}
4907
4908pub const IO_APC_ROUTINE = *const fn (PVOID, *IO_STATUS_BLOCK, ULONG) callconv(.winapi) void;
4909
4910pub const CURDIR = extern struct {
4911    DosPath: UNICODE_STRING,
4912    Handle: HANDLE,
4913};
4914
4915pub const DUPLICATE_SAME_ACCESS = 2;
4916
4917pub const MODULEINFO = extern struct {
4918    lpBaseOfDll: LPVOID,
4919    SizeOfImage: DWORD,
4920    EntryPoint: LPVOID,
4921};
4922
4923pub const PSAPI_WS_WATCH_INFORMATION = extern struct {
4924    FaultingPc: LPVOID,
4925    FaultingVa: LPVOID,
4926};
4927
4928pub const VM_COUNTERS = extern struct {
4929    PeakVirtualSize: SIZE_T,
4930    VirtualSize: SIZE_T,
4931    PageFaultCount: ULONG,
4932    PeakWorkingSetSize: SIZE_T,
4933    WorkingSetSize: SIZE_T,
4934    QuotaPeakPagedPoolUsage: SIZE_T,
4935    QuotaPagedPoolUsage: SIZE_T,
4936    QuotaPeakNonPagedPoolUsage: SIZE_T,
4937    QuotaNonPagedPoolUsage: SIZE_T,
4938    PagefileUsage: SIZE_T,
4939    PeakPagefileUsage: SIZE_T,
4940};
4941
4942pub const PROCESS_MEMORY_COUNTERS = extern struct {
4943    cb: DWORD,
4944    PageFaultCount: DWORD,
4945    PeakWorkingSetSize: SIZE_T,
4946    WorkingSetSize: SIZE_T,
4947    QuotaPeakPagedPoolUsage: SIZE_T,
4948    QuotaPagedPoolUsage: SIZE_T,
4949    QuotaPeakNonPagedPoolUsage: SIZE_T,
4950    QuotaNonPagedPoolUsage: SIZE_T,
4951    PagefileUsage: SIZE_T,
4952    PeakPagefileUsage: SIZE_T,
4953};
4954
4955pub const PROCESS_MEMORY_COUNTERS_EX = extern struct {
4956    cb: DWORD,
4957    PageFaultCount: DWORD,
4958    PeakWorkingSetSize: SIZE_T,
4959    WorkingSetSize: SIZE_T,
4960    QuotaPeakPagedPoolUsage: SIZE_T,
4961    QuotaPagedPoolUsage: SIZE_T,
4962    QuotaPeakNonPagedPoolUsage: SIZE_T,
4963    QuotaNonPagedPoolUsage: SIZE_T,
4964    PagefileUsage: SIZE_T,
4965    PeakPagefileUsage: SIZE_T,
4966    PrivateUsage: SIZE_T,
4967};
4968
4969pub const GetProcessMemoryInfoError = error{
4970    AccessDenied,
4971    InvalidHandle,
4972    Unexpected,
4973};
4974
4975pub fn GetProcessMemoryInfo(hProcess: HANDLE) GetProcessMemoryInfoError!VM_COUNTERS {
4976    var vmc: VM_COUNTERS = undefined;
4977    const rc = ntdll.NtQueryInformationProcess(hProcess, .ProcessVmCounters, &vmc, @sizeOf(VM_COUNTERS), null);
4978    switch (rc) {
4979        .SUCCESS => return vmc,
4980        .ACCESS_DENIED => return error.AccessDenied,
4981        .INVALID_HANDLE => return error.InvalidHandle,
4982        .INVALID_PARAMETER => unreachable,
4983        else => return unexpectedStatus(rc),
4984    }
4985}
4986
4987pub const PERFORMANCE_INFORMATION = extern struct {
4988    cb: DWORD,
4989    CommitTotal: SIZE_T,
4990    CommitLimit: SIZE_T,
4991    CommitPeak: SIZE_T,
4992    PhysicalTotal: SIZE_T,
4993    PhysicalAvailable: SIZE_T,
4994    SystemCache: SIZE_T,
4995    KernelTotal: SIZE_T,
4996    KernelPaged: SIZE_T,
4997    KernelNonpaged: SIZE_T,
4998    PageSize: SIZE_T,
4999    HandleCount: DWORD,
5000    ProcessCount: DWORD,
5001    ThreadCount: DWORD,
5002};
5003
5004pub const ENUM_PAGE_FILE_INFORMATION = extern struct {
5005    cb: DWORD,
5006    Reserved: DWORD,
5007    TotalSize: SIZE_T,
5008    TotalInUse: SIZE_T,
5009    PeakUsage: SIZE_T,
5010};
5011
5012pub const PENUM_PAGE_FILE_CALLBACKW = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.winapi) BOOL;
5013pub const PENUM_PAGE_FILE_CALLBACKA = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.winapi) BOOL;
5014
5015pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct {
5016    BasicInfo: PSAPI_WS_WATCH_INFORMATION,
5017    FaultingThreadId: ULONG_PTR,
5018    Flags: ULONG_PTR,
5019};
5020
5021pub const OSVERSIONINFOW = extern struct {
5022    dwOSVersionInfoSize: ULONG,
5023    dwMajorVersion: ULONG,
5024    dwMinorVersion: ULONG,
5025    dwBuildNumber: ULONG,
5026    dwPlatformId: ULONG,
5027    szCSDVersion: [128]WCHAR,
5028};
5029pub const RTL_OSVERSIONINFOW = OSVERSIONINFOW;
5030
5031pub const REPARSE_DATA_BUFFER = extern struct {
5032    ReparseTag: ULONG,
5033    ReparseDataLength: USHORT,
5034    Reserved: USHORT,
5035    DataBuffer: [1]UCHAR,
5036};
5037pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct {
5038    SubstituteNameOffset: USHORT,
5039    SubstituteNameLength: USHORT,
5040    PrintNameOffset: USHORT,
5041    PrintNameLength: USHORT,
5042    Flags: ULONG,
5043    PathBuffer: [1]WCHAR,
5044};
5045pub const MOUNT_POINT_REPARSE_BUFFER = extern struct {
5046    SubstituteNameOffset: USHORT,
5047    SubstituteNameLength: USHORT,
5048    PrintNameOffset: USHORT,
5049    PrintNameLength: USHORT,
5050    PathBuffer: [1]WCHAR,
5051};
5052pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024;
5053pub const FSCTL_SET_REPARSE_POINT: DWORD = 0x900a4;
5054pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8;
5055pub const IO_REPARSE_TAG_SYMLINK: ULONG = 0xa000000c;
5056pub const IO_REPARSE_TAG_MOUNT_POINT: ULONG = 0xa0000003;
5057pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;
5058
5059pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
5060pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;
5061
5062pub const MOUNTMGRCONTROLTYPE = 0x0000006D;
5063
5064pub const MOUNTMGR_MOUNT_POINT = extern struct {
5065    SymbolicLinkNameOffset: ULONG,
5066    SymbolicLinkNameLength: USHORT,
5067    Reserved1: USHORT,
5068    UniqueIdOffset: ULONG,
5069    UniqueIdLength: USHORT,
5070    Reserved2: USHORT,
5071    DeviceNameOffset: ULONG,
5072    DeviceNameLength: USHORT,
5073    Reserved3: USHORT,
5074};
5075pub const MOUNTMGR_MOUNT_POINTS = extern struct {
5076    Size: ULONG,
5077    NumberOfMountPoints: ULONG,
5078    MountPoints: [1]MOUNTMGR_MOUNT_POINT,
5079};
5080pub const IOCTL_MOUNTMGR_QUERY_POINTS = CTL_CODE(MOUNTMGRCONTROLTYPE, 2, .METHOD_BUFFERED, FILE_ANY_ACCESS);
5081
5082pub const MOUNTMGR_TARGET_NAME = extern struct {
5083    DeviceNameLength: USHORT,
5084    DeviceName: [1]WCHAR,
5085};
5086pub const MOUNTMGR_VOLUME_PATHS = extern struct {
5087    MultiSzLength: ULONG,
5088    MultiSz: [1]WCHAR,
5089};
5090pub const IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH = CTL_CODE(MOUNTMGRCONTROLTYPE, 12, .METHOD_BUFFERED, FILE_ANY_ACCESS);
5091
5092pub const OBJECT_INFORMATION_CLASS = enum(c_int) {
5093    ObjectBasicInformation = 0,
5094    ObjectNameInformation = 1,
5095    ObjectTypeInformation = 2,
5096    ObjectTypesInformation = 3,
5097    ObjectHandleFlagInformation = 4,
5098    ObjectSessionInformation = 5,
5099    MaxObjectInfoClass,
5100};
5101
5102pub const OBJECT_NAME_INFORMATION = extern struct {
5103    Name: UNICODE_STRING,
5104};
5105
5106pub const SRWLOCK_INIT = SRWLOCK{};
5107pub const SRWLOCK = extern struct {
5108    Ptr: ?PVOID = null,
5109};
5110
5111pub const CONDITION_VARIABLE_INIT = CONDITION_VARIABLE{};
5112pub const CONDITION_VARIABLE = extern struct {
5113    Ptr: ?PVOID = null,
5114};
5115
5116pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1;
5117pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2;
5118
5119pub const CTRL_C_EVENT: DWORD = 0;
5120pub const CTRL_BREAK_EVENT: DWORD = 1;
5121pub const CTRL_CLOSE_EVENT: DWORD = 2;
5122pub const CTRL_LOGOFF_EVENT: DWORD = 5;
5123pub const CTRL_SHUTDOWN_EVENT: DWORD = 6;
5124
5125pub const HANDLER_ROUTINE = *const fn (dwCtrlType: DWORD) callconv(.winapi) BOOL;
5126
5127/// Processor feature enumeration.
5128pub const PF = enum(DWORD) {
5129    /// On a Pentium, a floating-point precision error can occur in rare circumstances.
5130    FLOATING_POINT_PRECISION_ERRATA = 0,
5131
5132    /// Floating-point operations are emulated using software emulator.
5133    /// This function returns a nonzero value if floating-point operations are emulated; otherwise, it returns zero.
5134    FLOATING_POINT_EMULATED = 1,
5135
5136    /// The atomic compare and exchange operation (cmpxchg) is available.
5137    COMPARE_EXCHANGE_DOUBLE = 2,
5138
5139    /// The MMX instruction set is available.
5140    MMX_INSTRUCTIONS_AVAILABLE = 3,
5141
5142    PPC_MOVEMEM_64BIT_OK = 4,
5143    ALPHA_BYTE_INSTRUCTIONS = 5,
5144
5145    /// The SSE instruction set is available.
5146    XMMI_INSTRUCTIONS_AVAILABLE = 6,
5147
5148    /// The 3D-Now instruction is available.
5149    @"3DNOW_INSTRUCTIONS_AVAILABLE" = 7,
5150
5151    /// The RDTSC instruction is available.
5152    RDTSC_INSTRUCTION_AVAILABLE = 8,
5153
5154    /// The processor is PAE-enabled.
5155    PAE_ENABLED = 9,
5156
5157    /// The SSE2 instruction set is available.
5158    XMMI64_INSTRUCTIONS_AVAILABLE = 10,
5159
5160    SSE_DAZ_MODE_AVAILABLE = 11,
5161
5162    /// Data execution prevention is enabled.
5163    NX_ENABLED = 12,
5164
5165    /// The SSE3 instruction set is available.
5166    SSE3_INSTRUCTIONS_AVAILABLE = 13,
5167
5168    /// The atomic compare and exchange 128-bit operation (cmpxchg16b) is available.
5169    COMPARE_EXCHANGE128 = 14,
5170
5171    /// The atomic compare 64 and exchange 128-bit operation (cmp8xchg16) is available.
5172    COMPARE64_EXCHANGE128 = 15,
5173
5174    /// The processor channels are enabled.
5175    CHANNELS_ENABLED = 16,
5176
5177    /// The processor implements the XSAVI and XRSTOR instructions.
5178    XSAVE_ENABLED = 17,
5179
5180    /// The VFP/Neon: 32 x 64bit register bank is present.
5181    /// This flag has the same meaning as PF_ARM_VFP_EXTENDED_REGISTERS.
5182    ARM_VFP_32_REGISTERS_AVAILABLE = 18,
5183
5184    /// This ARM processor implements the ARM v8 NEON instruction set.
5185    ARM_NEON_INSTRUCTIONS_AVAILABLE = 19,
5186
5187    /// Second Level Address Translation is supported by the hardware.
5188    SECOND_LEVEL_ADDRESS_TRANSLATION = 20,
5189
5190    /// Virtualization is enabled in the firmware and made available by the operating system.
5191    VIRT_FIRMWARE_ENABLED = 21,
5192
5193    /// RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE instructions are available.
5194    RDWRFSGBASE_AVAILABLE = 22,
5195
5196    /// _fastfail() is available.
5197    FASTFAIL_AVAILABLE = 23,
5198
5199    /// The divide instruction_available.
5200    ARM_DIVIDE_INSTRUCTION_AVAILABLE = 24,
5201
5202    /// The 64-bit load/store atomic instructions are available.
5203    ARM_64BIT_LOADSTORE_ATOMIC = 25,
5204
5205    /// The external cache is available.
5206    ARM_EXTERNAL_CACHE_AVAILABLE = 26,
5207
5208    /// The floating-point multiply-accumulate instruction is available.
5209    ARM_FMAC_INSTRUCTIONS_AVAILABLE = 27,
5210
5211    RDRAND_INSTRUCTION_AVAILABLE = 28,
5212
5213    /// This ARM processor implements the ARM v8 instructions set.
5214    ARM_V8_INSTRUCTIONS_AVAILABLE = 29,
5215
5216    /// This ARM processor implements the ARM v8 extra cryptographic instructions (i.e., AES, SHA1 and SHA2).
5217    ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30,
5218
5219    /// This ARM processor implements the ARM v8 extra CRC32 instructions.
5220    ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31,
5221
5222    RDTSCP_INSTRUCTION_AVAILABLE = 32,
5223    RDPID_INSTRUCTION_AVAILABLE = 33,
5224
5225    /// This ARM processor implements the ARM v8.1 atomic instructions (e.g., CAS, SWP).
5226    ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34,
5227
5228    MONITORX_INSTRUCTION_AVAILABLE = 35,
5229
5230    /// The SSSE3 instruction set is available.
5231    SSSE3_INSTRUCTIONS_AVAILABLE = 36,
5232
5233    /// The SSE4_1 instruction set is available.
5234    SSE4_1_INSTRUCTIONS_AVAILABLE = 37,
5235
5236    /// The SSE4_2 instruction set is available.
5237    SSE4_2_INSTRUCTIONS_AVAILABLE = 38,
5238
5239    /// The AVX instruction set is available.
5240    AVX_INSTRUCTIONS_AVAILABLE = 39,
5241
5242    /// The AVX2 instruction set is available.
5243    AVX2_INSTRUCTIONS_AVAILABLE = 40,
5244
5245    /// The AVX512F instruction set is available.
5246    AVX512F_INSTRUCTIONS_AVAILABLE = 41,
5247
5248    ERMS_AVAILABLE = 42,
5249
5250    /// This ARM processor implements the ARM v8.2 Dot Product (DP) instructions.
5251    ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43,
5252
5253    /// This ARM processor implements the ARM v8.3 JavaScript conversion (JSCVT) instructions.
5254    ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44,
5255
5256    /// This Arm processor implements the Arm v8.3 LRCPC instructions (for example, LDAPR). Note that certain Arm v8.2 CPUs may optionally support the LRCPC instructions.
5257    ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE,
5258};
5259
5260pub const MAX_WOW64_SHARED_ENTRIES = 16;
5261pub const PROCESSOR_FEATURE_MAX = 64;
5262pub const MAXIMUM_XSTATE_FEATURES = 64;
5263
5264pub const KSYSTEM_TIME = extern struct {
5265    LowPart: ULONG,
5266    High1Time: LONG,
5267    High2Time: LONG,
5268};
5269
5270pub const NT_PRODUCT_TYPE = enum(INT) {
5271    NtProductWinNt = 1,
5272    NtProductLanManNt,
5273    NtProductServer,
5274};
5275
5276pub const ALTERNATIVE_ARCHITECTURE_TYPE = enum(INT) {
5277    StandardDesign,
5278    NEC98x86,
5279    EndAlternatives,
5280};
5281
5282pub const XSTATE_FEATURE = extern struct {
5283    Offset: ULONG,
5284    Size: ULONG,
5285};
5286
5287pub const XSTATE_CONFIGURATION = extern struct {
5288    EnabledFeatures: ULONG64,
5289    Size: ULONG,
5290    OptimizedSave: ULONG,
5291    Features: [MAXIMUM_XSTATE_FEATURES]XSTATE_FEATURE,
5292};
5293
5294/// Shared Kernel User Data
5295pub const KUSER_SHARED_DATA = extern struct {
5296    TickCountLowDeprecated: ULONG,
5297    TickCountMultiplier: ULONG,
5298    InterruptTime: KSYSTEM_TIME,
5299    SystemTime: KSYSTEM_TIME,
5300    TimeZoneBias: KSYSTEM_TIME,
5301    ImageNumberLow: USHORT,
5302    ImageNumberHigh: USHORT,
5303    NtSystemRoot: [260]WCHAR,
5304    MaxStackTraceDepth: ULONG,
5305    CryptoExponent: ULONG,
5306    TimeZoneId: ULONG,
5307    LargePageMinimum: ULONG,
5308    AitSamplingValue: ULONG,
5309    AppCompatFlag: ULONG,
5310    RNGSeedVersion: ULONGLONG,
5311    GlobalValidationRunlevel: ULONG,
5312    TimeZoneBiasStamp: LONG,
5313    NtBuildNumber: ULONG,
5314    NtProductType: NT_PRODUCT_TYPE,
5315    ProductTypeIsValid: BOOLEAN,
5316    Reserved0: [1]BOOLEAN,
5317    NativeProcessorArchitecture: USHORT,
5318    NtMajorVersion: ULONG,
5319    NtMinorVersion: ULONG,
5320    ProcessorFeatures: [PROCESSOR_FEATURE_MAX]BOOLEAN,
5321    Reserved1: ULONG,
5322    Reserved3: ULONG,
5323    TimeSlip: ULONG,
5324    AlternativeArchitecture: ALTERNATIVE_ARCHITECTURE_TYPE,
5325    BootId: ULONG,
5326    SystemExpirationDate: LARGE_INTEGER,
5327    SuiteMaskY: ULONG,
5328    KdDebuggerEnabled: BOOLEAN,
5329    DummyUnion1: extern union {
5330        MitigationPolicies: UCHAR,
5331        Alt: packed struct {
5332            NXSupportPolicy: u2,
5333            SEHValidationPolicy: u2,
5334            CurDirDevicesSkippedForDlls: u2,
5335            Reserved: u2,
5336        },
5337    },
5338    CyclesPerYield: USHORT,
5339    ActiveConsoleId: ULONG,
5340    DismountCount: ULONG,
5341    ComPlusPackage: ULONG,
5342    LastSystemRITEventTickCount: ULONG,
5343    NumberOfPhysicalPages: ULONG,
5344    SafeBootMode: BOOLEAN,
5345    DummyUnion2: extern union {
5346        VirtualizationFlags: UCHAR,
5347        Alt: packed struct {
5348            ArchStartedInEl2: u1,
5349            QcSlIsSupported: u1,
5350            SpareBits: u6,
5351        },
5352    },
5353    Reserved12: [2]UCHAR,
5354    DummyUnion3: extern union {
5355        SharedDataFlags: ULONG,
5356        Alt: packed struct {
5357            DbgErrorPortPresent: u1,
5358            DbgElevationEnabled: u1,
5359            DbgVirtEnabled: u1,
5360            DbgInstallerDetectEnabled: u1,
5361            DbgLkgEnabled: u1,
5362            DbgDynProcessorEnabled: u1,
5363            DbgConsoleBrokerEnabled: u1,
5364            DbgSecureBootEnabled: u1,
5365            DbgMultiSessionSku: u1,
5366            DbgMultiUsersInSessionSku: u1,
5367            DbgStateSeparationEnabled: u1,
5368            SpareBits: u21,
5369        },
5370    },
5371    DataFlagsPad: [1]ULONG,
5372    TestRetInstruction: ULONGLONG,
5373    QpcFrequency: LONGLONG,
5374    SystemCall: ULONG,
5375    Reserved2: ULONG,
5376    SystemCallPad: [2]ULONGLONG,
5377    DummyUnion4: extern union {
5378        TickCount: KSYSTEM_TIME,
5379        TickCountQuad: ULONG64,
5380        Alt: extern struct {
5381            ReservedTickCountOverlay: [3]ULONG,
5382            TickCountPad: [1]ULONG,
5383        },
5384    },
5385    Cookie: ULONG,
5386    CookiePad: [1]ULONG,
5387    ConsoleSessionForegroundProcessId: LONGLONG,
5388    TimeUpdateLock: ULONGLONG,
5389    BaselineSystemTimeQpc: ULONGLONG,
5390    BaselineInterruptTimeQpc: ULONGLONG,
5391    QpcSystemTimeIncrement: ULONGLONG,
5392    QpcInterruptTimeIncrement: ULONGLONG,
5393    QpcSystemTimeIncrementShift: UCHAR,
5394    QpcInterruptTimeIncrementShift: UCHAR,
5395    UnparkedProcessorCount: USHORT,
5396    EnclaveFeatureMask: [4]ULONG,
5397    TelemetryCoverageRound: ULONG,
5398    UserModeGlobalLogger: [16]USHORT,
5399    ImageFileExecutionOptions: ULONG,
5400    LangGenerationCount: ULONG,
5401    Reserved4: ULONGLONG,
5402    InterruptTimeBias: ULONGLONG,
5403    QpcBias: ULONGLONG,
5404    ActiveProcessorCount: ULONG,
5405    ActiveGroupCount: UCHAR,
5406    Reserved9: UCHAR,
5407    DummyUnion5: extern union {
5408        QpcData: USHORT,
5409        Alt: extern struct {
5410            QpcBypassEnabled: UCHAR,
5411            QpcShift: UCHAR,
5412        },
5413    },
5414    TimeZoneBiasEffectiveStart: LARGE_INTEGER,
5415    TimeZoneBiasEffectiveEnd: LARGE_INTEGER,
5416    XState: XSTATE_CONFIGURATION,
5417    FeatureConfigurationChangeStamp: KSYSTEM_TIME,
5418    Spare: ULONG,
5419    UserPointerAuthMask: ULONG64,
5420};
5421
5422/// Read-only user-mode address for the shared data.
5423/// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
5424/// https://msrc-blog.microsoft.com/2022/04/05/randomizing-the-kuser_shared_data-structure-on-windows/
5425pub const SharedUserData: *const KUSER_SHARED_DATA = @as(*const KUSER_SHARED_DATA, @ptrFromInt(0x7FFE0000));
5426
5427pub fn IsProcessorFeaturePresent(feature: PF) bool {
5428    if (@intFromEnum(feature) >= PROCESSOR_FEATURE_MAX) return false;
5429    return SharedUserData.ProcessorFeatures[@intFromEnum(feature)] == 1;
5430}
5431
5432pub const TH32CS_SNAPHEAPLIST = 0x00000001;
5433pub const TH32CS_SNAPPROCESS = 0x00000002;
5434pub const TH32CS_SNAPTHREAD = 0x00000004;
5435pub const TH32CS_SNAPMODULE = 0x00000008;
5436pub const TH32CS_SNAPMODULE32 = 0x00000010;
5437pub const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE;
5438pub const TH32CS_INHERIT = 0x80000000;
5439
5440pub const MAX_MODULE_NAME32 = 255;
5441pub const MODULEENTRY32 = extern struct {
5442    dwSize: DWORD,
5443    th32ModuleID: DWORD,
5444    th32ProcessID: DWORD,
5445    GlblcntUsage: DWORD,
5446    ProccntUsage: DWORD,
5447    modBaseAddr: *BYTE,
5448    modBaseSize: DWORD,
5449    hModule: HMODULE,
5450    szModule: [MAX_MODULE_NAME32 + 1]CHAR,
5451    szExePath: [MAX_PATH]CHAR,
5452};
5453
5454pub const SYSTEM_INFORMATION_CLASS = enum(c_int) {
5455    SystemBasicInformation = 0,
5456    SystemPerformanceInformation = 2,
5457    SystemTimeOfDayInformation = 3,
5458    SystemProcessInformation = 5,
5459    SystemProcessorPerformanceInformation = 8,
5460    SystemInterruptInformation = 23,
5461    SystemExceptionInformation = 33,
5462    SystemRegistryQuotaInformation = 37,
5463    SystemLookasideInformation = 45,
5464    SystemCodeIntegrityInformation = 103,
5465    SystemPolicyInformation = 134,
5466};
5467
5468pub const SYSTEM_BASIC_INFORMATION = extern struct {
5469    Reserved: ULONG,
5470    TimerResolution: ULONG,
5471    PageSize: ULONG,
5472    NumberOfPhysicalPages: ULONG,
5473    LowestPhysicalPageNumber: ULONG,
5474    HighestPhysicalPageNumber: ULONG,
5475    AllocationGranularity: ULONG,
5476    MinimumUserModeAddress: ULONG_PTR,
5477    MaximumUserModeAddress: ULONG_PTR,
5478    ActiveProcessorsAffinityMask: KAFFINITY,
5479    NumberOfProcessors: UCHAR,
5480};
5481
5482pub const THREADINFOCLASS = enum(c_int) {
5483    ThreadBasicInformation,
5484    ThreadTimes,
5485    ThreadPriority,
5486    ThreadBasePriority,
5487    ThreadAffinityMask,
5488    ThreadImpersonationToken,
5489    ThreadDescriptorTableEntry,
5490    ThreadEnableAlignmentFaultFixup,
5491    ThreadEventPair_Reusable,
5492    ThreadQuerySetWin32StartAddress,
5493    ThreadZeroTlsCell,
5494    ThreadPerformanceCount,
5495    ThreadAmILastThread,
5496    ThreadIdealProcessor,
5497    ThreadPriorityBoost,
5498    ThreadSetTlsArrayAddress,
5499    ThreadIsIoPending,
5500    // Windows 2000+ from here
5501    ThreadHideFromDebugger,
5502    // Windows XP+ from here
5503    ThreadBreakOnTermination,
5504    ThreadSwitchLegacyState,
5505    ThreadIsTerminated,
5506    // Windows Vista+ from here
5507    ThreadLastSystemCall,
5508    ThreadIoPriority,
5509    ThreadCycleTime,
5510    ThreadPagePriority,
5511    ThreadActualBasePriority,
5512    ThreadTebInformation,
5513    ThreadCSwitchMon,
5514    // Windows 7+ from here
5515    ThreadCSwitchPmu,
5516    ThreadWow64Context,
5517    ThreadGroupInformation,
5518    ThreadUmsInformation,
5519    ThreadCounterProfiling,
5520    ThreadIdealProcessorEx,
5521    // Windows 8+ from here
5522    ThreadCpuAccountingInformation,
5523    // Windows 8.1+ from here
5524    ThreadSuspendCount,
5525    // Windows 10+ from here
5526    ThreadHeterogeneousCpuPolicy,
5527    ThreadContainerId,
5528    ThreadNameInformation,
5529    ThreadSelectedCpuSets,
5530    ThreadSystemThreadInformation,
5531    ThreadActualGroupAffinity,
5532};
5533
5534pub const PROCESSINFOCLASS = enum(c_int) {
5535    ProcessBasicInformation,
5536    ProcessQuotaLimits,
5537    ProcessIoCounters,
5538    ProcessVmCounters,
5539    ProcessTimes,
5540    ProcessBasePriority,
5541    ProcessRaisePriority,
5542    ProcessDebugPort,
5543    ProcessExceptionPort,
5544    ProcessAccessToken,
5545    ProcessLdtInformation,
5546    ProcessLdtSize,
5547    ProcessDefaultHardErrorMode,
5548    ProcessIoPortHandlers,
5549    ProcessPooledUsageAndLimits,
5550    ProcessWorkingSetWatch,
5551    ProcessUserModeIOPL,
5552    ProcessEnableAlignmentFaultFixup,
5553    ProcessPriorityClass,
5554    ProcessWx86Information,
5555    ProcessHandleCount,
5556    ProcessAffinityMask,
5557    ProcessPriorityBoost,
5558    ProcessDeviceMap,
5559    ProcessSessionInformation,
5560    ProcessForegroundInformation,
5561    ProcessWow64Information,
5562    ProcessImageFileName,
5563    ProcessLUIDDeviceMapsEnabled,
5564    ProcessBreakOnTermination,
5565    ProcessDebugObjectHandle,
5566    ProcessDebugFlags,
5567    ProcessHandleTracing,
5568    ProcessIoPriority,
5569    ProcessExecuteFlags,
5570    ProcessTlsInformation,
5571    ProcessCookie,
5572    ProcessImageInformation,
5573    ProcessCycleTime,
5574    ProcessPagePriority,
5575    ProcessInstrumentationCallback,
5576    ProcessThreadStackAllocation,
5577    ProcessWorkingSetWatchEx,
5578    ProcessImageFileNameWin32,
5579    ProcessImageFileMapping,
5580    ProcessAffinityUpdateMode,
5581    ProcessMemoryAllocationMode,
5582    ProcessGroupInformation,
5583    ProcessTokenVirtualizationEnabled,
5584    ProcessConsoleHostProcess,
5585    ProcessWindowInformation,
5586    MaxProcessInfoClass,
5587};
5588
5589pub const PROCESS_BASIC_INFORMATION = extern struct {
5590    ExitStatus: NTSTATUS,
5591    PebBaseAddress: *PEB,
5592    AffinityMask: ULONG_PTR,
5593    BasePriority: KPRIORITY,
5594    UniqueProcessId: ULONG_PTR,
5595    InheritedFromUniqueProcessId: ULONG_PTR,
5596};
5597
5598pub const ReadMemoryError = error{
5599    Unexpected,
5600};
5601
5602pub fn ReadProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []u8) ReadMemoryError![]u8 {
5603    var nread: usize = 0;
5604    switch (ntdll.NtReadVirtualMemory(
5605        handle,
5606        addr,
5607        buffer.ptr,
5608        buffer.len,
5609        &nread,
5610    )) {
5611        .SUCCESS => return buffer[0..nread],
5612        // TODO: map errors
5613        else => |rc| return unexpectedStatus(rc),
5614    }
5615}
5616
5617pub const WriteMemoryError = error{
5618    Unexpected,
5619};
5620
5621pub fn WriteProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []const u8) WriteMemoryError!usize {
5622    var nwritten: usize = 0;
5623    switch (ntdll.NtWriteVirtualMemory(
5624        handle,
5625        addr,
5626        buffer.ptr,
5627        buffer.len,
5628        &nwritten,
5629    )) {
5630        .SUCCESS => return nwritten,
5631        // TODO: map errors
5632        else => |rc| return unexpectedStatus(rc),
5633    }
5634}
5635
5636pub const ProcessBaseAddressError = GetProcessMemoryInfoError || ReadMemoryError;
5637
5638/// Returns the base address of the process loaded into memory.
5639pub fn ProcessBaseAddress(handle: HANDLE) ProcessBaseAddressError!HMODULE {
5640    var info: PROCESS_BASIC_INFORMATION = undefined;
5641    var nread: DWORD = 0;
5642    const rc = ntdll.NtQueryInformationProcess(
5643        handle,
5644        .ProcessBasicInformation,
5645        &info,
5646        @sizeOf(PROCESS_BASIC_INFORMATION),
5647        &nread,
5648    );
5649    switch (rc) {
5650        .SUCCESS => {},
5651        .ACCESS_DENIED => return error.AccessDenied,
5652        .INVALID_HANDLE => return error.InvalidHandle,
5653        .INVALID_PARAMETER => unreachable,
5654        else => return unexpectedStatus(rc),
5655    }
5656
5657    var peb_buf: [@sizeOf(PEB)]u8 align(@alignOf(PEB)) = undefined;
5658    const peb_out = try ReadProcessMemory(handle, info.PebBaseAddress, &peb_buf);
5659    const ppeb: *const PEB = @ptrCast(@alignCast(peb_out.ptr));
5660    return ppeb.ImageBaseAddress;
5661}
5662
5663pub fn wtf8ToWtf16Le(wtf16le: []u16, wtf8: []const u8) error{ BadPathName, NameTooLong }!usize {
5664    // Each u8 in UTF-8/WTF-8 correlates to at most one u16 in UTF-16LE/WTF-16LE.
5665    if (wtf16le.len < wtf8.len) {
5666        const utf16_len = std.unicode.calcUtf16LeLenImpl(wtf8, .can_encode_surrogate_half) catch
5667            return error.BadPathName;
5668        if (utf16_len > wtf16le.len)
5669            return error.NameTooLong;
5670    }
5671    return std.unicode.wtf8ToWtf16Le(wtf16le, wtf8) catch |err| switch (err) {
5672        error.InvalidWtf8 => return error.BadPathName,
5673    };
5674}