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 UTF16LE byte buffers are required, provide APIs which accept
5//!   slices as well as APIs which accept null-terminated UTF16LE byte buffers.
6
7const builtin = @import("builtin");
8const std = @import("../std.zig");
9const mem = std.mem;
10const assert = std.debug.assert;
11const math = std.math;
12const maxInt = std.math.maxInt;
13const native_arch = builtin.cpu.arch;
14
15test {
16    if (builtin.os.tag == .windows) {
17        _ = @import("windows/test.zig");
18    }
19}
20
21pub const advapi32 = @import("windows/advapi32.zig");
22pub const kernel32 = @import("windows/kernel32.zig");
23pub const ntdll = @import("windows/ntdll.zig");
24pub const ole32 = @import("windows/ole32.zig");
25pub const psapi = @import("windows/psapi.zig");
26pub const shell32 = @import("windows/shell32.zig");
27pub const user32 = @import("windows/user32.zig");
28pub const ws2_32 = @import("windows/ws2_32.zig");
29pub const gdi32 = @import("windows/gdi32.zig");
30pub const winmm = @import("windows/winmm.zig");
31
32pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
33
34pub const OpenError = error{
35    IsDir,
36    NotDir,
37    FileNotFound,
38    NoDevice,
39    AccessDenied,
40    PipeBusy,
41    PathAlreadyExists,
42    Unexpected,
43    NameTooLong,
44    WouldBlock,
45};
46
47pub const OpenFileOptions = struct {
48    access_mask: ACCESS_MASK,
49    dir: ?HANDLE = null,
50    sa: ?*SECURITY_ATTRIBUTES = null,
51    share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
52    creation: ULONG,
53    io_mode: std.io.ModeOverride,
54    /// If true, tries to open path as a directory.
55    /// Defaults to false.
56    open_dir: bool = false,
57    /// If false, tries to open path as a reparse point without dereferencing it.
58    /// Defaults to true.
59    follow_symlinks: bool = true,
60};
61
62pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
63    if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) {
64        return error.IsDir;
65    }
66    if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) {
67        return error.IsDir;
68    }
69
70    var result: HANDLE = undefined;
71
72    const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) {
73        error.Overflow => return error.NameTooLong,
74    };
75    var nt_name = UNICODE_STRING{
76        .Length = path_len_bytes,
77        .MaximumLength = path_len_bytes,
78        .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
79    };
80    var attr = OBJECT_ATTRIBUTES{
81        .Length = @sizeOf(OBJECT_ATTRIBUTES),
82        .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,
83        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
84        .ObjectName = &nt_name,
85        .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null,
86        .SecurityQualityOfService = null,
87    };
88    var io: IO_STATUS_BLOCK = undefined;
89    const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
90    const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE else FILE_NON_DIRECTORY_FILE;
91    // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
92    const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;
93
94    const rc = ntdll.NtCreateFile(
95        &result,
96        options.access_mask,
97        &attr,
98        &io,
99        null,
100        FILE_ATTRIBUTE_NORMAL,
101        options.share_access,
102        options.creation,
103        flags,
104        null,
105        0,
106    );
107    switch (rc) {
108        .SUCCESS => {
109            if (std.io.is_async and options.io_mode == .evented) {
110                _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined;
111            }
112            return result;
113        },
114        .OBJECT_NAME_INVALID => unreachable,
115        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
116        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
117        .NO_MEDIA_IN_DEVICE => return error.NoDevice,
118        .INVALID_PARAMETER => unreachable,
119        .SHARING_VIOLATION => return error.AccessDenied,
120        .ACCESS_DENIED => return error.AccessDenied,
121        .PIPE_BUSY => return error.PipeBusy,
122        .OBJECT_PATH_SYNTAX_BAD => unreachable,
123        .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
124        .FILE_IS_A_DIRECTORY => return error.IsDir,
125        .NOT_A_DIRECTORY => return error.NotDir,
126        else => return unexpectedStatus(rc),
127    }
128}
129
130pub const CreatePipeError = error{Unexpected};
131
132pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
133    if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) {
134        switch (kernel32.GetLastError()) {
135            else => |err| return unexpectedError(err),
136        }
137    }
138}
139
140pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE {
141    const nameW = try sliceToPrefixedFileW(name);
142    return CreateEventExW(attributes, nameW.span().ptr, flags, desired_access);
143}
144
145pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, flags: DWORD, desired_access: DWORD) !HANDLE {
146    const handle = kernel32.CreateEventExW(attributes, nameW, flags, desired_access);
147    if (handle) |h| {
148        return h;
149    } else {
150        switch (kernel32.GetLastError()) {
151            else => |err| return unexpectedError(err),
152        }
153    }
154}
155
156pub const DeviceIoControlError = error{ AccessDenied, Unexpected };
157
158/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
159/// It implements similar behavior to `DeviceIoControl` and is meant to serve
160/// as a direct substitute for that call.
161/// TODO work out if we need to expose other arguments to the underlying syscalls.
162pub fn DeviceIoControl(
163    h: HANDLE,
164    ioControlCode: ULONG,
165    in: ?[]const u8,
166    out: ?[]u8,
167) DeviceIoControlError!void {
168    // Logic from: https://doxygen.reactos.org/d3/d74/deviceio_8c.html
169    const is_fsctl = (ioControlCode >> 16) == FILE_DEVICE_FILE_SYSTEM;
170
171    var io: IO_STATUS_BLOCK = undefined;
172    const in_ptr = if (in) |i| i.ptr else null;
173    const in_len = if (in) |i| @intCast(ULONG, i.len) else 0;
174    const out_ptr = if (out) |o| o.ptr else null;
175    const out_len = if (out) |o| @intCast(ULONG, o.len) else 0;
176
177    const rc = blk: {
178        if (is_fsctl) {
179            break :blk ntdll.NtFsControlFile(
180                h,
181                null,
182                null,
183                null,
184                &io,
185                ioControlCode,
186                in_ptr,
187                in_len,
188                out_ptr,
189                out_len,
190            );
191        } else {
192            break :blk ntdll.NtDeviceIoControlFile(
193                h,
194                null,
195                null,
196                null,
197                &io,
198                ioControlCode,
199                in_ptr,
200                in_len,
201                out_ptr,
202                out_len,
203            );
204        }
205    };
206    switch (rc) {
207        .SUCCESS => {},
208        .PRIVILEGE_NOT_HELD => return error.AccessDenied,
209        .ACCESS_DENIED => return error.AccessDenied,
210        .INVALID_PARAMETER => unreachable,
211        else => return unexpectedStatus(rc),
212    }
213}
214
215pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
216    var bytes: DWORD = undefined;
217    if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @boolToInt(wait)) == 0) {
218        switch (kernel32.GetLastError()) {
219            .IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable,
220            else => |err| return unexpectedError(err),
221        }
222    }
223    return bytes;
224}
225
226pub const SetHandleInformationError = error{Unexpected};
227
228pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void {
229    if (kernel32.SetHandleInformation(h, mask, flags) == 0) {
230        switch (kernel32.GetLastError()) {
231            else => |err| return unexpectedError(err),
232        }
233    }
234}
235
236pub const RtlGenRandomError = error{Unexpected};
237
238/// Call RtlGenRandom() instead of CryptGetRandom() on Windows
239/// https://github.com/rust-lang-nursery/rand/issues/111
240/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
241pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void {
242    var total_read: usize = 0;
243    var buff: []u8 = output[0..];
244    const max_read_size: ULONG = maxInt(ULONG);
245
246    while (total_read < output.len) {
247        const to_read: ULONG = math.min(buff.len, max_read_size);
248
249        if (advapi32.RtlGenRandom(buff.ptr, to_read) == 0) {
250            return unexpectedError(kernel32.GetLastError());
251        }
252
253        total_read += to_read;
254        buff = buff[to_read..];
255    }
256}
257
258pub const WaitForSingleObjectError = error{
259    WaitAbandoned,
260    WaitTimeOut,
261    Unexpected,
262};
263
264pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void {
265    return WaitForSingleObjectEx(handle, milliseconds, false);
266}
267
268pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void {
269    switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @boolToInt(alertable))) {
270        WAIT_ABANDONED => return error.WaitAbandoned,
271        WAIT_OBJECT_0 => return,
272        WAIT_TIMEOUT => return error.WaitTimeOut,
273        WAIT_FAILED => switch (kernel32.GetLastError()) {
274            else => |err| return unexpectedError(err),
275        },
276        else => return error.Unexpected,
277    }
278}
279
280pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 {
281    assert(handles.len < MAXIMUM_WAIT_OBJECTS);
282    const nCount: DWORD = @intCast(DWORD, handles.len);
283    switch (kernel32.WaitForMultipleObjectsEx(
284        nCount,
285        handles.ptr,
286        @boolToInt(waitAll),
287        milliseconds,
288        @boolToInt(alertable),
289    )) {
290        WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| {
291            const handle_index = n - WAIT_OBJECT_0;
292            assert(handle_index < nCount);
293            return handle_index;
294        },
295        WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| {
296            const handle_index = n - WAIT_ABANDONED_0;
297            assert(handle_index < nCount);
298            return error.WaitAbandoned;
299        },
300        WAIT_TIMEOUT => return error.WaitTimeOut,
301        WAIT_FAILED => switch (kernel32.GetLastError()) {
302            else => |err| return unexpectedError(err),
303        },
304        else => return error.Unexpected,
305    }
306}
307
308pub const CreateIoCompletionPortError = error{Unexpected};
309
310pub fn CreateIoCompletionPort(
311    file_handle: HANDLE,
312    existing_completion_port: ?HANDLE,
313    completion_key: usize,
314    concurrent_thread_count: DWORD,
315) CreateIoCompletionPortError!HANDLE {
316    const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
317        switch (kernel32.GetLastError()) {
318            .INVALID_PARAMETER => unreachable,
319            else => |err| return unexpectedError(err),
320        }
321    };
322    return handle;
323}
324
325pub const PostQueuedCompletionStatusError = error{Unexpected};
326
327pub fn PostQueuedCompletionStatus(
328    completion_port: HANDLE,
329    bytes_transferred_count: DWORD,
330    completion_key: usize,
331    lpOverlapped: ?*OVERLAPPED,
332) PostQueuedCompletionStatusError!void {
333    if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
334        switch (kernel32.GetLastError()) {
335            else => |err| return unexpectedError(err),
336        }
337    }
338}
339
340pub const GetQueuedCompletionStatusResult = enum {
341    Normal,
342    Aborted,
343    Cancelled,
344    EOF,
345};
346
347pub fn GetQueuedCompletionStatus(
348    completion_port: HANDLE,
349    bytes_transferred_count: *DWORD,
350    lpCompletionKey: *usize,
351    lpOverlapped: *?*OVERLAPPED,
352    dwMilliseconds: DWORD,
353) GetQueuedCompletionStatusResult {
354    if (kernel32.GetQueuedCompletionStatus(
355        completion_port,
356        bytes_transferred_count,
357        lpCompletionKey,
358        lpOverlapped,
359        dwMilliseconds,
360    ) == FALSE) {
361        switch (kernel32.GetLastError()) {
362            .ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
363            .OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled,
364            .HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
365            else => |err| {
366                if (std.debug.runtime_safety) {
367                    @setEvalBranchQuota(2500);
368                    std.debug.panic("unexpected error: {}\n", .{err});
369                }
370            },
371        }
372    }
373    return GetQueuedCompletionStatusResult.Normal;
374}
375
376pub const GetQueuedCompletionStatusError = error{
377    Aborted,
378    Cancelled,
379    EOF,
380    Timeout,
381} || std.os.UnexpectedError;
382
383pub fn GetQueuedCompletionStatusEx(
384    completion_port: HANDLE,
385    completion_port_entries: []OVERLAPPED_ENTRY,
386    timeout_ms: ?DWORD,
387    alertable: bool,
388) GetQueuedCompletionStatusError!u32 {
389    var num_entries_removed: u32 = 0;
390
391    const success = kernel32.GetQueuedCompletionStatusEx(
392        completion_port,
393        completion_port_entries.ptr,
394        @intCast(ULONG, completion_port_entries.len),
395        &num_entries_removed,
396        timeout_ms orelse INFINITE,
397        @boolToInt(alertable),
398    );
399
400    if (success == FALSE) {
401        return switch (kernel32.GetLastError()) {
402            .ABANDONED_WAIT_0 => error.Aborted,
403            .OPERATION_ABORTED => error.Cancelled,
404            .HANDLE_EOF => error.EOF,
405            .IMEOUT => error.Timeout,
406            else => |err| unexpectedError(err),
407        };
408    }
409
410    return num_entries_removed;
411}
412
413pub fn CloseHandle(hObject: HANDLE) void {
414    assert(ntdll.NtClose(hObject) == .SUCCESS);
415}
416
417pub fn FindClose(hFindFile: HANDLE) void {
418    assert(kernel32.FindClose(hFindFile) != 0);
419}
420
421pub const ReadFileError = error{
422    OperationAborted,
423    BrokenPipe,
424    Unexpected,
425};
426
427/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
428/// multiple non-atomic reads.
429pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, io_mode: std.io.ModeOverride) ReadFileError!usize {
430    if (io_mode != .blocking) {
431        const loop = std.event.Loop.instance.?;
432        // TODO make getting the file position non-blocking
433        const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(in_hFile);
434        var resume_node = std.event.Loop.ResumeNode.Basic{
435            .base = .{
436                .id = .Basic,
437                .handle = @frame(),
438                .overlapped = OVERLAPPED{
439                    .Internal = 0,
440                    .InternalHigh = 0,
441                    .DUMMYUNIONNAME = .{
442                        .DUMMYSTRUCTNAME = .{
443                            .Offset = @truncate(u32, off),
444                            .OffsetHigh = @truncate(u32, off >> 32),
445                        },
446                    },
447                    .hEvent = null,
448                },
449            },
450        };
451        loop.beginOneEvent();
452        suspend {
453            // TODO handle buffer bigger than DWORD can hold
454            _ = kernel32.ReadFile(in_hFile, buffer.ptr, @intCast(DWORD, buffer.len), null, &resume_node.base.overlapped);
455        }
456        var bytes_transferred: DWORD = undefined;
457        if (kernel32.GetOverlappedResult(in_hFile, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) {
458            switch (kernel32.GetLastError()) {
459                .IO_PENDING => unreachable,
460                .OPERATION_ABORTED => return error.OperationAborted,
461                .BROKEN_PIPE => return error.BrokenPipe,
462                .HANDLE_EOF => return @as(usize, bytes_transferred),
463                else => |err| return unexpectedError(err),
464            }
465        }
466        if (offset == null) {
467            // TODO make setting the file position non-blocking
468            const new_off = off + bytes_transferred;
469            try SetFilePointerEx_CURRENT(in_hFile, @bitCast(i64, new_off));
470        }
471        return @as(usize, bytes_transferred);
472    } else {
473        while (true) {
474            const want_read_count = @intCast(DWORD, math.min(@as(DWORD, maxInt(DWORD)), buffer.len));
475            var amt_read: DWORD = undefined;
476            var overlapped_data: OVERLAPPED = undefined;
477            const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
478                overlapped_data = .{
479                    .Internal = 0,
480                    .InternalHigh = 0,
481                    .DUMMYUNIONNAME = .{
482                        .DUMMYSTRUCTNAME = .{
483                            .Offset = @truncate(u32, off),
484                            .OffsetHigh = @truncate(u32, off >> 32),
485                        },
486                    },
487                    .hEvent = null,
488                };
489                break :blk &overlapped_data;
490            } else null;
491            if (kernel32.ReadFile(in_hFile, buffer.ptr, want_read_count, &amt_read, overlapped) == 0) {
492                switch (kernel32.GetLastError()) {
493                    .OPERATION_ABORTED => continue,
494                    .BROKEN_PIPE => return 0,
495                    .HANDLE_EOF => return 0,
496                    else => |err| return unexpectedError(err),
497                }
498            }
499            return amt_read;
500        }
501    }
502}
503
504pub const WriteFileError = error{
505    SystemResources,
506    OperationAborted,
507    BrokenPipe,
508    NotOpenForWriting,
509    Unexpected,
510};
511
512pub fn WriteFile(
513    handle: HANDLE,
514    bytes: []const u8,
515    offset: ?u64,
516    io_mode: std.io.ModeOverride,
517) WriteFileError!usize {
518    if (std.event.Loop.instance != null and io_mode != .blocking) {
519        const loop = std.event.Loop.instance.?;
520        // TODO make getting the file position non-blocking
521        const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(handle);
522        var resume_node = std.event.Loop.ResumeNode.Basic{
523            .base = .{
524                .id = .Basic,
525                .handle = @frame(),
526                .overlapped = OVERLAPPED{
527                    .Internal = 0,
528                    .InternalHigh = 0,
529                    .DUMMYUNIONNAME = .{
530                        .DUMMYSTRUCTNAME = .{
531                            .Offset = @truncate(u32, off),
532                            .OffsetHigh = @truncate(u32, off >> 32),
533                        },
534                    },
535                    .hEvent = null,
536                },
537            },
538        };
539        loop.beginOneEvent();
540        suspend {
541            const adjusted_len = math.cast(DWORD, bytes.len) catch maxInt(DWORD);
542            _ = kernel32.WriteFile(handle, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped);
543        }
544        var bytes_transferred: DWORD = undefined;
545        if (kernel32.GetOverlappedResult(handle, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) {
546            switch (kernel32.GetLastError()) {
547                .IO_PENDING => unreachable,
548                .INVALID_USER_BUFFER => return error.SystemResources,
549                .NOT_ENOUGH_MEMORY => return error.SystemResources,
550                .OPERATION_ABORTED => return error.OperationAborted,
551                .NOT_ENOUGH_QUOTA => return error.SystemResources,
552                .BROKEN_PIPE => return error.BrokenPipe,
553                else => |err| return unexpectedError(err),
554            }
555        }
556        if (offset == null) {
557            // TODO make setting the file position non-blocking
558            const new_off = off + bytes_transferred;
559            try SetFilePointerEx_CURRENT(handle, @bitCast(i64, new_off));
560        }
561        return bytes_transferred;
562    } else {
563        var bytes_written: DWORD = undefined;
564        var overlapped_data: OVERLAPPED = undefined;
565        const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
566            overlapped_data = .{
567                .Internal = 0,
568                .InternalHigh = 0,
569                .DUMMYUNIONNAME = .{
570                    .DUMMYSTRUCTNAME = .{
571                        .Offset = @truncate(u32, off),
572                        .OffsetHigh = @truncate(u32, off >> 32),
573                    },
574                },
575                .hEvent = null,
576            };
577            break :blk &overlapped_data;
578        } else null;
579        const adjusted_len = math.cast(u32, bytes.len) catch maxInt(u32);
580        if (kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, overlapped) == 0) {
581            switch (kernel32.GetLastError()) {
582                .INVALID_USER_BUFFER => return error.SystemResources,
583                .NOT_ENOUGH_MEMORY => return error.SystemResources,
584                .OPERATION_ABORTED => return error.OperationAborted,
585                .NOT_ENOUGH_QUOTA => return error.SystemResources,
586                .IO_PENDING => unreachable,
587                .BROKEN_PIPE => return error.BrokenPipe,
588                .INVALID_HANDLE => return error.NotOpenForWriting,
589                else => |err| return unexpectedError(err),
590            }
591        }
592        return bytes_written;
593    }
594}
595
596pub const SetCurrentDirectoryError = error{
597    NameTooLong,
598    InvalidUtf8,
599    FileNotFound,
600    NotDir,
601    AccessDenied,
602    NoDevice,
603    BadPathName,
604    Unexpected,
605};
606
607pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void {
608    const path_len_bytes = math.cast(u16, path_name.len * 2) catch |err| switch (err) {
609        error.Overflow => return error.NameTooLong,
610    };
611
612    var nt_name = UNICODE_STRING{
613        .Length = path_len_bytes,
614        .MaximumLength = path_len_bytes,
615        .Buffer = @intToPtr([*]u16, @ptrToInt(path_name.ptr)),
616    };
617
618    const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);
619    switch (rc) {
620        .SUCCESS => {},
621        .OBJECT_NAME_INVALID => return error.BadPathName,
622        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
623        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
624        .NO_MEDIA_IN_DEVICE => return error.NoDevice,
625        .INVALID_PARAMETER => unreachable,
626        .ACCESS_DENIED => return error.AccessDenied,
627        .OBJECT_PATH_SYNTAX_BAD => unreachable,
628        .NOT_A_DIRECTORY => return error.NotDir,
629        else => return unexpectedStatus(rc),
630    }
631}
632
633pub const GetCurrentDirectoryError = error{
634    NameTooLong,
635    Unexpected,
636};
637
638/// The result is a slice of `buffer`, indexed from 0.
639pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
640    var utf16le_buf: [PATH_MAX_WIDE]u16 = undefined;
641    const result = kernel32.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf);
642    if (result == 0) {
643        switch (kernel32.GetLastError()) {
644            else => |err| return unexpectedError(err),
645        }
646    }
647    assert(result <= utf16le_buf.len);
648    const utf16le_slice = utf16le_buf[0..result];
649    // Trust that Windows gives us valid UTF-16LE.
650    var end_index: usize = 0;
651    var it = std.unicode.Utf16LeIterator.init(utf16le_slice);
652    while (it.nextCodepoint() catch unreachable) |codepoint| {
653        const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
654        if (end_index + seq_len >= buffer.len)
655            return error.NameTooLong;
656        end_index += std.unicode.utf8Encode(codepoint, buffer[end_index..]) catch unreachable;
657    }
658    return buffer[0..end_index];
659}
660
661pub const CreateSymbolicLinkError = error{
662    AccessDenied,
663    PathAlreadyExists,
664    FileNotFound,
665    NameTooLong,
666    NoDevice,
667    Unexpected,
668};
669
670/// Needs either:
671/// - `SeCreateSymbolicLinkPrivilege` privilege
672/// or
673/// - Developer mode on Windows 10
674/// otherwise fails with `error.AccessDenied`. In which case `sym_link_path` may still
675/// be created on the file system but will lack reparse processing data applied to it.
676pub fn CreateSymbolicLink(
677    dir: ?HANDLE,
678    sym_link_path: []const u16,
679    target_path: []const u16,
680    is_directory: bool,
681) CreateSymbolicLinkError!void {
682    const SYMLINK_DATA = extern struct {
683        ReparseTag: ULONG,
684        ReparseDataLength: USHORT,
685        Reserved: USHORT,
686        SubstituteNameOffset: USHORT,
687        SubstituteNameLength: USHORT,
688        PrintNameOffset: USHORT,
689        PrintNameLength: USHORT,
690        Flags: ULONG,
691    };
692
693    const symlink_handle = OpenFile(sym_link_path, .{
694        .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
695        .dir = dir,
696        .creation = FILE_CREATE,
697        .io_mode = .blocking,
698        .open_dir = is_directory,
699    }) catch |err| switch (err) {
700        error.IsDir => return error.PathAlreadyExists,
701        error.NotDir => unreachable,
702        error.WouldBlock => unreachable,
703        error.PipeBusy => unreachable,
704        else => |e| return e,
705    };
706    defer CloseHandle(symlink_handle);
707
708    // prepare reparse data buffer
709    var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
710    const buf_len = @sizeOf(SYMLINK_DATA) + target_path.len * 4;
711    const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2;
712    const symlink_data = SYMLINK_DATA{
713        .ReparseTag = IO_REPARSE_TAG_SYMLINK,
714        .ReparseDataLength = @intCast(u16, buf_len - header_len),
715        .Reserved = 0,
716        .SubstituteNameOffset = @intCast(u16, target_path.len * 2),
717        .SubstituteNameLength = @intCast(u16, target_path.len * 2),
718        .PrintNameOffset = 0,
719        .PrintNameLength = @intCast(u16, target_path.len * 2),
720        .Flags = if (dir) |_| SYMLINK_FLAG_RELATIVE else 0,
721    };
722
723    std.mem.copy(u8, buffer[0..], std.mem.asBytes(&symlink_data));
724    @memcpy(buffer[@sizeOf(SYMLINK_DATA)..], @ptrCast([*]const u8, target_path), target_path.len * 2);
725    const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2;
726    @memcpy(buffer[paths_start..].ptr, @ptrCast([*]const u8, target_path), target_path.len * 2);
727    _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
728}
729
730pub const ReadLinkError = error{
731    FileNotFound,
732    AccessDenied,
733    Unexpected,
734    NameTooLong,
735    UnsupportedReparsePointType,
736};
737
738pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
739    // Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper.
740    // With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that
741    // failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible
742    // to open the symlink there and then.
743    const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) {
744        error.Overflow => return error.NameTooLong,
745    };
746    var nt_name = UNICODE_STRING{
747        .Length = path_len_bytes,
748        .MaximumLength = path_len_bytes,
749        .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
750    };
751    var attr = OBJECT_ATTRIBUTES{
752        .Length = @sizeOf(OBJECT_ATTRIBUTES),
753        .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else dir,
754        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
755        .ObjectName = &nt_name,
756        .SecurityDescriptor = null,
757        .SecurityQualityOfService = null,
758    };
759    var result_handle: HANDLE = undefined;
760    var io: IO_STATUS_BLOCK = undefined;
761
762    const rc = ntdll.NtCreateFile(
763        &result_handle,
764        FILE_READ_ATTRIBUTES,
765        &attr,
766        &io,
767        null,
768        FILE_ATTRIBUTE_NORMAL,
769        FILE_SHARE_READ,
770        FILE_OPEN,
771        FILE_OPEN_REPARSE_POINT,
772        null,
773        0,
774    );
775    switch (rc) {
776        .SUCCESS => {},
777        .OBJECT_NAME_INVALID => unreachable,
778        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
779        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
780        .NO_MEDIA_IN_DEVICE => return error.FileNotFound,
781        .INVALID_PARAMETER => unreachable,
782        .SHARING_VIOLATION => return error.AccessDenied,
783        .ACCESS_DENIED => return error.AccessDenied,
784        .PIPE_BUSY => return error.AccessDenied,
785        .OBJECT_PATH_SYNTAX_BAD => unreachable,
786        .OBJECT_NAME_COLLISION => unreachable,
787        .FILE_IS_A_DIRECTORY => unreachable,
788        else => return unexpectedStatus(rc),
789    }
790    defer CloseHandle(result_handle);
791
792    var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
793    _ = DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) {
794        error.AccessDenied => unreachable,
795        else => |e| return e,
796    };
797
798    const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0]));
799    switch (reparse_struct.ReparseTag) {
800        IO_REPARSE_TAG_SYMLINK => {
801            const buf = @ptrCast(*const SYMBOLIC_LINK_REPARSE_BUFFER, @alignCast(@alignOf(SYMBOLIC_LINK_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
802            const offset = buf.SubstituteNameOffset >> 1;
803            const len = buf.SubstituteNameLength >> 1;
804            const path_buf = @as([*]const u16, &buf.PathBuffer);
805            const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
806            return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);
807        },
808        IO_REPARSE_TAG_MOUNT_POINT => {
809            const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
810            const offset = buf.SubstituteNameOffset >> 1;
811            const len = buf.SubstituteNameLength >> 1;
812            const path_buf = @as([*]const u16, &buf.PathBuffer);
813            return parseReadlinkPath(path_buf[offset .. offset + len], false, out_buffer);
814        },
815        else => |value| {
816            std.debug.print("unsupported symlink type: {}", .{value});
817            return error.UnsupportedReparsePointType;
818        },
819    }
820}
821
822fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u8 {
823    const prefix = [_]u16{ '\\', '?', '?', '\\' };
824    var start_index: usize = 0;
825    if (!is_relative and std.mem.startsWith(u16, path, &prefix)) {
826        start_index = prefix.len;
827    }
828    const out_len = std.unicode.utf16leToUtf8(out_buffer, path[start_index..]) catch unreachable;
829    return out_buffer[0..out_len];
830}
831
832pub const DeleteFileError = error{
833    FileNotFound,
834    AccessDenied,
835    NameTooLong,
836    /// Also known as sharing violation.
837    FileBusy,
838    Unexpected,
839    NotDir,
840    IsDir,
841};
842
843pub const DeleteFileOptions = struct {
844    dir: ?HANDLE,
845    remove_dir: bool = false,
846};
847
848pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
849    const create_options_flags: ULONG = if (options.remove_dir)
850        FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
851    else
852        FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
853
854    const path_len_bytes = @intCast(u16, sub_path_w.len * 2);
855    var nt_name = UNICODE_STRING{
856        .Length = path_len_bytes,
857        .MaximumLength = path_len_bytes,
858        // The Windows API makes this mutable, but it will not mutate here.
859        .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
860    };
861
862    if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
863        // Windows does not recognize this, but it does work with empty string.
864        nt_name.Length = 0;
865    }
866    if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
867        // Can't remove the parent directory with an open handle.
868        return error.FileBusy;
869    }
870
871    var attr = OBJECT_ATTRIBUTES{
872        .Length = @sizeOf(OBJECT_ATTRIBUTES),
873        .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,
874        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
875        .ObjectName = &nt_name,
876        .SecurityDescriptor = null,
877        .SecurityQualityOfService = null,
878    };
879    var io: IO_STATUS_BLOCK = undefined;
880    var tmp_handle: HANDLE = undefined;
881    var rc = ntdll.NtCreateFile(
882        &tmp_handle,
883        SYNCHRONIZE | DELETE,
884        &attr,
885        &io,
886        null,
887        0,
888        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
889        FILE_OPEN,
890        create_options_flags,
891        null,
892        0,
893    );
894    switch (rc) {
895        .SUCCESS => return CloseHandle(tmp_handle),
896        .OBJECT_NAME_INVALID => unreachable,
897        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
898        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
899        .INVALID_PARAMETER => unreachable,
900        .FILE_IS_A_DIRECTORY => return error.IsDir,
901        .NOT_A_DIRECTORY => return error.NotDir,
902        .SHARING_VIOLATION => return error.FileBusy,
903        else => return unexpectedStatus(rc),
904    }
905}
906
907pub const MoveFileError = error{ FileNotFound, AccessDenied, Unexpected };
908
909pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
910    const old_path_w = try sliceToPrefixedFileW(old_path);
911    const new_path_w = try sliceToPrefixedFileW(new_path);
912    return MoveFileExW(old_path_w.span().ptr, new_path_w.span().ptr, flags);
913}
914
915pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
916    if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
917        switch (kernel32.GetLastError()) {
918            .FILE_NOT_FOUND => return error.FileNotFound,
919            .ACCESS_DENIED => return error.AccessDenied,
920            else => |err| return unexpectedError(err),
921        }
922    }
923}
924
925pub const GetStdHandleError = error{
926    NoStandardHandleAttached,
927    Unexpected,
928};
929
930pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {
931    const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
932    if (handle == INVALID_HANDLE_VALUE) {
933        switch (kernel32.GetLastError()) {
934            else => |err| return unexpectedError(err),
935        }
936    }
937    return handle;
938}
939
940pub const SetFilePointerError = error{Unexpected};
941
942/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
943pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
944    // "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
945    // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
946    // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
947    const ipos = @bitCast(LARGE_INTEGER, offset);
948    if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
949        switch (kernel32.GetLastError()) {
950            .INVALID_PARAMETER => unreachable,
951            .INVALID_HANDLE => unreachable,
952            else => |err| return unexpectedError(err),
953        }
954    }
955}
956
957/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
958pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
959    if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
960        switch (kernel32.GetLastError()) {
961            .INVALID_PARAMETER => unreachable,
962            .INVALID_HANDLE => unreachable,
963            else => |err| return unexpectedError(err),
964        }
965    }
966}
967
968/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
969pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
970    if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
971        switch (kernel32.GetLastError()) {
972            .INVALID_PARAMETER => unreachable,
973            .INVALID_HANDLE => unreachable,
974            else => |err| return unexpectedError(err),
975        }
976    }
977}
978
979/// The SetFilePointerEx function with parameters to get the current offset.
980pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
981    var result: LARGE_INTEGER = undefined;
982    if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
983        switch (kernel32.GetLastError()) {
984            .INVALID_PARAMETER => unreachable,
985            .INVALID_HANDLE => unreachable,
986            else => |err| return unexpectedError(err),
987        }
988    }
989    // Based on the docs for FILE_BEGIN, it seems that the returned signed integer
990    // should be interpreted as an unsigned integer.
991    return @bitCast(u64, result);
992}
993
994pub fn QueryObjectName(
995    handle: HANDLE,
996    out_buffer: []u16,
997) ![]u16 {
998    const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
999
1000    const info = @ptrCast(*OBJECT_NAME_INFORMATION, out_buffer_aligned);
1001    //buffer size is specified in bytes
1002    const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) catch |e| switch (e) {
1003        error.Overflow => std.math.maxInt(ULONG),
1004    };
1005    //last argument would return the length required for full_buffer, not exposed here
1006    const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null);
1007    switch (rc) {
1008        .SUCCESS => {
1009            // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
1010            // if the object was "unnamed", not sure if this can happen for file handles
1011            if (info.Name.MaximumLength == 0) return error.Unexpected;
1012            // resulting string length is specified in bytes
1013            const path_length_unterminated = @divExact(info.Name.Length, 2);
1014            return info.Name.Buffer[0..path_length_unterminated];
1015        },
1016        .ACCESS_DENIED => return error.AccessDenied,
1017        .INVALID_HANDLE => return error.InvalidHandle,
1018        // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
1019        // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
1020        .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong,
1021        else => |e| return unexpectedStatus(e),
1022    }
1023}
1024test "QueryObjectName" {
1025    if (builtin.os.tag != .windows)
1026        return;
1027
1028    //any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
1029    var tmp = std.testing.tmpDir(.{});
1030    defer tmp.cleanup();
1031    const handle = tmp.dir.fd;
1032    var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
1033
1034    var result_path = try QueryObjectName(handle, &out_buffer);
1035    const required_len_in_u16 = result_path.len + @divExact(@ptrToInt(result_path.ptr) - @ptrToInt(&out_buffer), 2) + 1;
1036    //insufficient size
1037    try std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
1038    //exactly-sufficient size
1039    _ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
1040}
1041
1042pub const GetFinalPathNameByHandleError = error{
1043    AccessDenied,
1044    BadPathName,
1045    FileNotFound,
1046    NameTooLong,
1047    Unexpected,
1048};
1049
1050/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
1051/// Defaults to DOS volume names.
1052pub const GetFinalPathNameByHandleFormat = struct {
1053    volume_name: enum {
1054        /// Format as DOS volume name
1055        Dos,
1056        /// Format as NT volume name
1057        Nt,
1058    } = .Dos,
1059};
1060
1061/// Returns canonical (normalized) path of handle.
1062/// Use `GetFinalPathNameByHandleFormat` to specify whether the path is meant to include
1063/// NT or DOS volume name (e.g., `\Device\HarddiskVolume0\foo.txt` versus `C:\foo.txt`).
1064/// If DOS volume name format is selected, note that this function does *not* prepend
1065/// `\\?\` prefix to the resultant path.
1066pub fn GetFinalPathNameByHandle(
1067    hFile: HANDLE,
1068    fmt: GetFinalPathNameByHandleFormat,
1069    out_buffer: []u16,
1070) GetFinalPathNameByHandleError![]u16 {
1071    const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
1072        // we assume InvalidHandle is close enough to FileNotFound in semantics
1073        // to not further complicate the error set
1074        error.InvalidHandle => return error.FileNotFound,
1075        else => |e| return e,
1076    };
1077
1078    switch (fmt.volume_name) {
1079        .Nt => {
1080            // the returned path is already in .Nt format
1081            return final_path;
1082        },
1083        .Dos => {
1084            // parse the string to separate volume path from file path
1085            const expected_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
1086
1087            // TODO find out if a path can start with something besides `\Device\<volume name>`,
1088            // and if we need to handle it differently
1089            // (i.e. how to determine the start and end of the volume name in that case)
1090            if (!mem.eql(u16, expected_prefix, final_path[0..expected_prefix.len])) return error.Unexpected;
1091
1092            const file_path_begin_index = mem.indexOfPos(u16, final_path, expected_prefix.len, &[_]u16{'\\'}) orelse unreachable;
1093            const volume_name_u16 = final_path[0..file_path_begin_index];
1094            const file_name_u16 = final_path[file_path_begin_index..];
1095
1096            // Get DOS volume name. DOS volume names are actually symbolic link objects to the
1097            // actual NT volume. For example:
1098            // (NT) \Device\HarddiskVolume4 => (DOS) \DosDevices\C: == (DOS) C:
1099            const MIN_SIZE = @sizeOf(MOUNTMGR_MOUNT_POINT) + MAX_PATH;
1100            // We initialize the input buffer to all zeros for convenience since
1101            // `DeviceIoControl` with `IOCTL_MOUNTMGR_QUERY_POINTS` expects this.
1102            var input_buf: [MIN_SIZE]u8 align(@alignOf(MOUNTMGR_MOUNT_POINT)) = [_]u8{0} ** MIN_SIZE;
1103            var output_buf: [MIN_SIZE * 4]u8 align(@alignOf(MOUNTMGR_MOUNT_POINTS)) = undefined;
1104
1105            // This surprising path is a filesystem path to the mount manager on Windows.
1106            // Source: https://stackoverflow.com/questions/3012828/using-ioctl-mountmgr-query-points
1107            const mgmt_path = "\\MountPointManager";
1108            const mgmt_path_u16 = sliceToPrefixedFileW(mgmt_path) catch unreachable;
1109            const mgmt_handle = OpenFile(mgmt_path_u16.span(), .{
1110                .access_mask = SYNCHRONIZE,
1111                .share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
1112                .creation = FILE_OPEN,
1113                .io_mode = .blocking,
1114            }) catch |err| switch (err) {
1115                error.IsDir => unreachable,
1116                error.NotDir => unreachable,
1117                error.NoDevice => unreachable,
1118                error.AccessDenied => unreachable,
1119                error.PipeBusy => unreachable,
1120                error.PathAlreadyExists => unreachable,
1121                error.WouldBlock => unreachable,
1122                else => |e| return e,
1123            };
1124            defer CloseHandle(mgmt_handle);
1125
1126            var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);
1127            input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
1128            input_struct.DeviceNameLength = @intCast(USHORT, volume_name_u16.len * 2);
1129            @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, volume_name_u16.ptr), volume_name_u16.len * 2);
1130
1131            DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {
1132                error.AccessDenied => unreachable,
1133                else => |e| return e,
1134            };
1135            const mount_points_struct = @ptrCast(*const MOUNTMGR_MOUNT_POINTS, &output_buf[0]);
1136
1137            const mount_points = @ptrCast(
1138                [*]const MOUNTMGR_MOUNT_POINT,
1139                &mount_points_struct.MountPoints[0],
1140            )[0..mount_points_struct.NumberOfMountPoints];
1141
1142            for (mount_points) |mount_point| {
1143                const symlink = @ptrCast(
1144                    [*]const u16,
1145                    @alignCast(@alignOf(u16), &output_buf[mount_point.SymbolicLinkNameOffset]),
1146                )[0 .. mount_point.SymbolicLinkNameLength / 2];
1147
1148                // Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
1149                // with traditional DOS drive letters, so pick the first one available.
1150                var prefix_buf = std.unicode.utf8ToUtf16LeStringLiteral("\\DosDevices\\");
1151                const prefix = prefix_buf[0..prefix_buf.len];
1152
1153                if (mem.startsWith(u16, symlink, prefix)) {
1154                    const drive_letter = symlink[prefix.len..];
1155
1156                    if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
1157
1158                    mem.copy(u16, out_buffer, drive_letter);
1159                    mem.copy(u16, out_buffer[drive_letter.len..], file_name_u16);
1160                    const total_len = drive_letter.len + file_name_u16.len;
1161
1162                    // Validate that DOS does not contain any spurious nul bytes.
1163                    if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
1164                        return error.BadPathName;
1165                    }
1166
1167                    return out_buffer[0..total_len];
1168                }
1169            }
1170
1171            // If we've ended up here, then something went wrong/is corrupted in the OS,
1172            // so error out!
1173            return error.FileNotFound;
1174        },
1175    }
1176}
1177
1178test "GetFinalPathNameByHandle" {
1179    if (builtin.os.tag != .windows)
1180        return;
1181
1182    //any file will do
1183    var tmp = std.testing.tmpDir(.{});
1184    defer tmp.cleanup();
1185    const handle = tmp.dir.fd;
1186    var buffer: [PATH_MAX_WIDE]u16 = undefined;
1187
1188    //check with sufficient size
1189    const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
1190    _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
1191
1192    const required_len_in_u16 = nt_path.len + @divExact(@ptrToInt(nt_path.ptr) - @ptrToInt(&buffer), 2) + 1;
1193    //check with insufficient size
1194    try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
1195    try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
1196
1197    //check with exactly-sufficient size
1198    _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
1199    _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
1200}
1201
1202pub const QueryInformationFileError = error{Unexpected};
1203
1204pub fn QueryInformationFile(
1205    handle: HANDLE,
1206    info_class: FILE_INFORMATION_CLASS,
1207    out_buffer: []u8,
1208) QueryInformationFileError!void {
1209    var io: IO_STATUS_BLOCK = undefined;
1210    const len_bytes = std.math.cast(u32, out_buffer.len) catch unreachable;
1211    const rc = ntdll.NtQueryInformationFile(handle, &io, out_buffer.ptr, len_bytes, info_class);
1212    switch (rc) {
1213        .SUCCESS => {},
1214        .INVALID_PARAMETER => unreachable,
1215        else => return unexpectedStatus(rc),
1216    }
1217}
1218
1219pub const GetFileSizeError = error{Unexpected};
1220
1221pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 {
1222    var file_size: LARGE_INTEGER = undefined;
1223    if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) {
1224        switch (kernel32.GetLastError()) {
1225            else => |err| return unexpectedError(err),
1226        }
1227    }
1228    return @bitCast(u64, file_size);
1229}
1230
1231pub const GetFileAttributesError = error{
1232    FileNotFound,
1233    PermissionDenied,
1234    Unexpected,
1235};
1236
1237pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD {
1238    const filename_w = try sliceToPrefixedFileW(filename);
1239    return GetFileAttributesW(filename_w.span().ptr);
1240}
1241
1242pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWORD {
1243    const rc = kernel32.GetFileAttributesW(lpFileName);
1244    if (rc == INVALID_FILE_ATTRIBUTES) {
1245        switch (kernel32.GetLastError()) {
1246            .FILE_NOT_FOUND => return error.FileNotFound,
1247            .PATH_NOT_FOUND => return error.FileNotFound,
1248            .ACCESS_DENIED => return error.PermissionDenied,
1249            else => |err| return unexpectedError(err),
1250        }
1251    }
1252    return rc;
1253}
1254
1255pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
1256    var wsadata: ws2_32.WSADATA = undefined;
1257    return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {
1258        0 => wsadata,
1259        else => |err_int| switch (@intToEnum(ws2_32.WinsockError, @intCast(u16, err_int))) {
1260            .WSASYSNOTREADY => return error.SystemNotAvailable,
1261            .WSAVERNOTSUPPORTED => return error.VersionNotSupported,
1262            .WSAEINPROGRESS => return error.BlockingOperationInProgress,
1263            .WSAEPROCLIM => return error.ProcessFdQuotaExceeded,
1264            else => |err| return unexpectedWSAError(err),
1265        },
1266    };
1267}
1268
1269pub fn WSACleanup() !void {
1270    return switch (ws2_32.WSACleanup()) {
1271        0 => {},
1272        ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
1273            .WSANOTINITIALISED => return error.NotInitialized,
1274            .WSAENETDOWN => return error.NetworkNotAvailable,
1275            .WSAEINPROGRESS => return error.BlockingOperationInProgress,
1276            else => |err| return unexpectedWSAError(err),
1277        },
1278        else => unreachable,
1279    };
1280}
1281
1282var wsa_startup_mutex: std.Thread.Mutex = .{};
1283
1284/// Microsoft requires WSAStartup to be called to initialize, or else
1285/// WSASocketW will return WSANOTINITIALISED.
1286/// Since this is a standard library, we do not have the luxury of
1287/// putting initialization code anywhere, because we would not want
1288/// to pay the cost of calling WSAStartup if there ended up being no
1289/// networking. Also, if Zig code is used as a library, Zig is not in
1290/// charge of the start code, and we couldn't put in any initialization
1291/// code even if we wanted to.
1292/// The documentation for WSAStartup mentions that there must be a
1293/// matching WSACleanup call. It is not possible for the Zig Standard
1294/// Library to honor this for the same reason - there is nowhere to put
1295/// deinitialization code.
1296/// So, API users of the zig std lib have two options:
1297///  * (recommended) The simple, cross-platform way: just call `WSASocketW`
1298///    and don't worry about it. Zig will call WSAStartup() in a thread-safe
1299///    manner and never deinitialize networking. This is ideal for an
1300///    application which has the capability to do networking.
1301///  * The getting-your-hands-dirty way: call `WSAStartup()` before doing
1302///    networking, so that the error handling code for WSANOTINITIALISED never
1303///    gets run, which then allows the application or library to call `WSACleanup()`.
1304///    This could make sense for a library, which has init and deinit
1305///    functions for the whole library's lifetime.
1306pub fn WSASocketW(
1307    af: i32,
1308    socket_type: i32,
1309    protocol: i32,
1310    protocolInfo: ?*ws2_32.WSAPROTOCOL_INFOW,
1311    g: ws2_32.GROUP,
1312    dwFlags: DWORD,
1313) !ws2_32.SOCKET {
1314    var first = true;
1315    while (true) {
1316        const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags);
1317        if (rc == ws2_32.INVALID_SOCKET) {
1318            switch (ws2_32.WSAGetLastError()) {
1319                .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
1320                .WSAEMFILE => return error.ProcessFdQuotaExceeded,
1321                .WSAENOBUFS => return error.SystemResources,
1322                .WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
1323                .WSANOTINITIALISED => {
1324                    if (!first) return error.Unexpected;
1325                    first = false;
1326
1327                    wsa_startup_mutex.lock();
1328                    defer wsa_startup_mutex.unlock();
1329
1330                    // Here we could use a flag to prevent multiple threads to prevent
1331                    // multiple calls to WSAStartup, but it doesn't matter. We're globally
1332                    // leaking the resource intentionally, and the mutex already prevents
1333                    // data races within the WSAStartup function.
1334                    _ = WSAStartup(2, 2) catch |err| switch (err) {
1335                        error.SystemNotAvailable => return error.SystemResources,
1336                        error.VersionNotSupported => return error.Unexpected,
1337                        error.BlockingOperationInProgress => return error.Unexpected,
1338                        error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
1339                        error.Unexpected => return error.Unexpected,
1340                    };
1341                    continue;
1342                },
1343                else => |err| return unexpectedWSAError(err),
1344            }
1345        }
1346        return rc;
1347    }
1348}
1349
1350pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 {
1351    return ws2_32.bind(s, name, @intCast(i32, namelen));
1352}
1353
1354pub fn listen(s: ws2_32.SOCKET, backlog: u31) i32 {
1355    return ws2_32.listen(s, backlog);
1356}
1357
1358pub fn closesocket(s: ws2_32.SOCKET) !void {
1359    switch (ws2_32.closesocket(s)) {
1360        0 => {},
1361        ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
1362            else => |err| return unexpectedWSAError(err),
1363        },
1364        else => unreachable,
1365    }
1366}
1367
1368pub fn accept(s: ws2_32.SOCKET, name: ?*ws2_32.sockaddr, namelen: ?*ws2_32.socklen_t) ws2_32.SOCKET {
1369    assert((name == null) == (namelen == null));
1370    return ws2_32.accept(s, name, @ptrCast(?*i32, namelen));
1371}
1372
1373pub fn getsockname(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
1374    return ws2_32.getsockname(s, name, @ptrCast(*i32, namelen));
1375}
1376
1377pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
1378    return ws2_32.getpeername(s, name, @ptrCast(*i32, namelen));
1379}
1380
1381pub fn sendmsg(
1382    s: ws2_32.SOCKET,
1383    msg: *const ws2_32.WSAMSG,
1384    flags: u32,
1385) i32 {
1386    var bytes_send: DWORD = undefined;
1387    if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
1388        return ws2_32.SOCKET_ERROR;
1389    } else {
1390        return @as(i32, @intCast(u31, bytes_send));
1391    }
1392}
1393
1394pub 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 {
1395    var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) };
1396    var bytes_send: DWORD = undefined;
1397    if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) {
1398        return ws2_32.SOCKET_ERROR;
1399    } else {
1400        return @as(i32, @intCast(u31, bytes_send));
1401    }
1402}
1403
1404pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
1405    var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = buf };
1406    var bytes_received: DWORD = undefined;
1407    var flags_inout = flags;
1408    if (ws2_32.WSARecvFrom(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_received, &flags_inout, from, @ptrCast(?*i32, from_len), null, null) == ws2_32.SOCKET_ERROR) {
1409        return ws2_32.SOCKET_ERROR;
1410    } else {
1411        return @as(i32, @intCast(u31, bytes_received));
1412    }
1413}
1414
1415pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
1416    return ws2_32.WSAPoll(fds, n, timeout);
1417}
1418
1419pub fn WSAIoctl(
1420    s: ws2_32.SOCKET,
1421    dwIoControlCode: DWORD,
1422    inBuffer: ?[]const u8,
1423    outBuffer: []u8,
1424    overlapped: ?*OVERLAPPED,
1425    completionRoutine: ?ws2_32.LPWSAOVERLAPPED_COMPLETION_ROUTINE,
1426) !DWORD {
1427    var bytes: DWORD = undefined;
1428    switch (ws2_32.WSAIoctl(
1429        s,
1430        dwIoControlCode,
1431        if (inBuffer) |i| i.ptr else null,
1432        if (inBuffer) |i| @intCast(DWORD, i.len) else 0,
1433        outBuffer.ptr,
1434        @intCast(DWORD, outBuffer.len),
1435        &bytes,
1436        overlapped,
1437        completionRoutine,
1438    )) {
1439        0 => {},
1440        ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
1441            else => |err| return unexpectedWSAError(err),
1442        },
1443        else => unreachable,
1444    }
1445    return bytes;
1446}
1447
1448const GetModuleFileNameError = error{Unexpected};
1449
1450pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 {
1451    const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
1452    if (rc == 0) {
1453        switch (kernel32.GetLastError()) {
1454            else => |err| return unexpectedError(err),
1455        }
1456    }
1457    return buf_ptr[0..rc :0];
1458}
1459
1460pub const TerminateProcessError = error{Unexpected};
1461
1462pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void {
1463    if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) {
1464        switch (kernel32.GetLastError()) {
1465            else => |err| return unexpectedError(err),
1466        }
1467    }
1468}
1469
1470pub const VirtualAllocError = error{Unexpected};
1471
1472pub fn VirtualAlloc(addr: ?LPVOID, size: usize, alloc_type: DWORD, flProtect: DWORD) VirtualAllocError!LPVOID {
1473    return kernel32.VirtualAlloc(addr, size, alloc_type, flProtect) orelse {
1474        switch (kernel32.GetLastError()) {
1475            else => |err| return unexpectedError(err),
1476        }
1477    };
1478}
1479
1480pub fn VirtualFree(lpAddress: ?LPVOID, dwSize: usize, dwFreeType: DWORD) void {
1481    assert(kernel32.VirtualFree(lpAddress, dwSize, dwFreeType) != 0);
1482}
1483
1484pub const SetConsoleTextAttributeError = error{Unexpected};
1485
1486pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
1487    if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
1488        switch (kernel32.GetLastError()) {
1489            else => |err| return unexpectedError(err),
1490        }
1491    }
1492}
1493
1494pub fn SetConsoleCtrlHandler(handler_routine: ?HANDLER_ROUTINE, add: bool) !void {
1495    const success = kernel32.SetConsoleCtrlHandler(
1496        handler_routine,
1497        if (add) TRUE else FALSE,
1498    );
1499
1500    if (success == FALSE) {
1501        return switch (kernel32.GetLastError()) {
1502            else => |err| unexpectedError(err),
1503        };
1504    }
1505}
1506
1507pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
1508    const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
1509    if (success == FALSE) {
1510        return switch (kernel32.GetLastError()) {
1511            else => |err| unexpectedError(err),
1512        };
1513    }
1514}
1515
1516pub const GetEnvironmentStringsError = error{OutOfMemory};
1517
1518pub fn GetEnvironmentStringsW() GetEnvironmentStringsError![*:0]u16 {
1519    return kernel32.GetEnvironmentStringsW() orelse return error.OutOfMemory;
1520}
1521
1522pub fn FreeEnvironmentStringsW(penv: [*:0]u16) void {
1523    assert(kernel32.FreeEnvironmentStringsW(penv) != 0);
1524}
1525
1526pub const GetEnvironmentVariableError = error{
1527    EnvironmentVariableNotFound,
1528    Unexpected,
1529};
1530
1531pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: [*]u16, nSize: DWORD) GetEnvironmentVariableError!DWORD {
1532    const rc = kernel32.GetEnvironmentVariableW(lpName, lpBuffer, nSize);
1533    if (rc == 0) {
1534        switch (kernel32.GetLastError()) {
1535            .ENVVAR_NOT_FOUND => return error.EnvironmentVariableNotFound,
1536            else => |err| return unexpectedError(err),
1537        }
1538    }
1539    return rc;
1540}
1541
1542pub const CreateProcessError = error{
1543    FileNotFound,
1544    AccessDenied,
1545    InvalidName,
1546    Unexpected,
1547};
1548
1549pub fn CreateProcessW(
1550    lpApplicationName: ?LPWSTR,
1551    lpCommandLine: LPWSTR,
1552    lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
1553    lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
1554    bInheritHandles: BOOL,
1555    dwCreationFlags: DWORD,
1556    lpEnvironment: ?*anyopaque,
1557    lpCurrentDirectory: ?LPWSTR,
1558    lpStartupInfo: *STARTUPINFOW,
1559    lpProcessInformation: *PROCESS_INFORMATION,
1560) CreateProcessError!void {
1561    if (kernel32.CreateProcessW(
1562        lpApplicationName,
1563        lpCommandLine,
1564        lpProcessAttributes,
1565        lpThreadAttributes,
1566        bInheritHandles,
1567        dwCreationFlags,
1568        lpEnvironment,
1569        lpCurrentDirectory,
1570        lpStartupInfo,
1571        lpProcessInformation,
1572    ) == 0) {
1573        switch (kernel32.GetLastError()) {
1574            .FILE_NOT_FOUND => return error.FileNotFound,
1575            .PATH_NOT_FOUND => return error.FileNotFound,
1576            .ACCESS_DENIED => return error.AccessDenied,
1577            .INVALID_PARAMETER => unreachable,
1578            .INVALID_NAME => return error.InvalidName,
1579            else => |err| return unexpectedError(err),
1580        }
1581    }
1582}
1583
1584pub const LoadLibraryError = error{
1585    FileNotFound,
1586    Unexpected,
1587};
1588
1589pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE {
1590    return kernel32.LoadLibraryW(lpLibFileName) orelse {
1591        switch (kernel32.GetLastError()) {
1592            .FILE_NOT_FOUND => return error.FileNotFound,
1593            .PATH_NOT_FOUND => return error.FileNotFound,
1594            .MOD_NOT_FOUND => return error.FileNotFound,
1595            else => |err| return unexpectedError(err),
1596        }
1597    };
1598}
1599
1600pub fn FreeLibrary(hModule: HMODULE) void {
1601    assert(kernel32.FreeLibrary(hModule) != 0);
1602}
1603
1604pub fn QueryPerformanceFrequency() u64 {
1605    // "On systems that run Windows XP or later, the function will always succeed"
1606    // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
1607    var result: LARGE_INTEGER = undefined;
1608    assert(kernel32.QueryPerformanceFrequency(&result) != 0);
1609    // The kernel treats this integer as unsigned.
1610    return @bitCast(u64, result);
1611}
1612
1613pub fn QueryPerformanceCounter() u64 {
1614    // "On systems that run Windows XP or later, the function will always succeed"
1615    // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
1616    var result: LARGE_INTEGER = undefined;
1617    assert(kernel32.QueryPerformanceCounter(&result) != 0);
1618    // The kernel treats this integer as unsigned.
1619    return @bitCast(u64, result);
1620}
1621
1622pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*anyopaque, Context: ?*anyopaque) void {
1623    assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0);
1624}
1625
1626pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *anyopaque) void {
1627    assert(kernel32.HeapFree(hHeap, dwFlags, lpMem) != 0);
1628}
1629
1630pub fn HeapDestroy(hHeap: HANDLE) void {
1631    assert(kernel32.HeapDestroy(hHeap) != 0);
1632}
1633
1634pub fn LocalFree(hMem: HLOCAL) void {
1635    assert(kernel32.LocalFree(hMem) == null);
1636}
1637
1638pub const GetFileInformationByHandleError = error{Unexpected};
1639
1640pub fn GetFileInformationByHandle(
1641    hFile: HANDLE,
1642) GetFileInformationByHandleError!BY_HANDLE_FILE_INFORMATION {
1643    var info: BY_HANDLE_FILE_INFORMATION = undefined;
1644    const rc = ntdll.GetFileInformationByHandle(hFile, &info);
1645    if (rc == 0) {
1646        switch (kernel32.GetLastError()) {
1647            else => |err| return unexpectedError(err),
1648        }
1649    }
1650    return info;
1651}
1652
1653pub const SetFileTimeError = error{Unexpected};
1654
1655pub fn SetFileTime(
1656    hFile: HANDLE,
1657    lpCreationTime: ?*const FILETIME,
1658    lpLastAccessTime: ?*const FILETIME,
1659    lpLastWriteTime: ?*const FILETIME,
1660) SetFileTimeError!void {
1661    const rc = kernel32.SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
1662    if (rc == 0) {
1663        switch (kernel32.GetLastError()) {
1664            else => |err| return unexpectedError(err),
1665        }
1666    }
1667}
1668
1669pub const LockFileError = error{
1670    SystemResources,
1671    WouldBlock,
1672} || std.os.UnexpectedError;
1673
1674pub fn LockFile(
1675    FileHandle: HANDLE,
1676    Event: ?HANDLE,
1677    ApcRoutine: ?*IO_APC_ROUTINE,
1678    ApcContext: ?*anyopaque,
1679    IoStatusBlock: *IO_STATUS_BLOCK,
1680    ByteOffset: *const LARGE_INTEGER,
1681    Length: *const LARGE_INTEGER,
1682    Key: ?*ULONG,
1683    FailImmediately: BOOLEAN,
1684    ExclusiveLock: BOOLEAN,
1685) !void {
1686    const rc = ntdll.NtLockFile(
1687        FileHandle,
1688        Event,
1689        ApcRoutine,
1690        ApcContext,
1691        IoStatusBlock,
1692        ByteOffset,
1693        Length,
1694        Key,
1695        FailImmediately,
1696        ExclusiveLock,
1697    );
1698    switch (rc) {
1699        .SUCCESS => return,
1700        .INSUFFICIENT_RESOURCES => return error.SystemResources,
1701        .LOCK_NOT_GRANTED => return error.WouldBlock,
1702        .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
1703        else => return unexpectedStatus(rc),
1704    }
1705}
1706
1707pub const UnlockFileError = error{
1708    RangeNotLocked,
1709} || std.os.UnexpectedError;
1710
1711pub fn UnlockFile(
1712    FileHandle: HANDLE,
1713    IoStatusBlock: *IO_STATUS_BLOCK,
1714    ByteOffset: *const LARGE_INTEGER,
1715    Length: *const LARGE_INTEGER,
1716    Key: ?*ULONG,
1717) !void {
1718    const rc = ntdll.NtUnlockFile(FileHandle, IoStatusBlock, ByteOffset, Length, Key);
1719    switch (rc) {
1720        .SUCCESS => return,
1721        .RANGE_NOT_LOCKED => return error.RangeNotLocked,
1722        .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
1723        else => return unexpectedStatus(rc),
1724    }
1725}
1726
1727pub fn teb() *TEB {
1728    return switch (native_arch) {
1729        .i386 => asm volatile (
1730            \\ movl %%fs:0x18, %[ptr]
1731            : [ptr] "=r" (-> *TEB),
1732        ),
1733        .x86_64 => asm volatile (
1734            \\ movq %%gs:0x30, %[ptr]
1735            : [ptr] "=r" (-> *TEB),
1736        ),
1737        .aarch64 => asm volatile (
1738            \\ mov %[ptr], x18
1739            : [ptr] "=r" (-> *TEB),
1740        ),
1741        else => @compileError("unsupported arch"),
1742    };
1743}
1744
1745pub fn peb() *PEB {
1746    return teb().ProcessEnvironmentBlock;
1747}
1748
1749/// A file time is a 64-bit value that represents the number of 100-nanosecond
1750/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
1751/// Universal Time (UTC).
1752/// This function returns the number of nanoseconds since the canonical epoch,
1753/// which is the POSIX one (Jan 01, 1970 AD).
1754pub fn fromSysTime(hns: i64) i128 {
1755    const adjusted_epoch: i128 = hns + std.time.epoch.windows * (std.time.ns_per_s / 100);
1756    return adjusted_epoch * 100;
1757}
1758
1759pub fn toSysTime(ns: i128) i64 {
1760    const hns = @divFloor(ns, 100);
1761    return @intCast(i64, hns) - std.time.epoch.windows * (std.time.ns_per_s / 100);
1762}
1763
1764pub fn fileTimeToNanoSeconds(ft: FILETIME) i128 {
1765    const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
1766    return fromSysTime(hns);
1767}
1768
1769/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
1770pub fn nanoSecondsToFileTime(ns: i128) FILETIME {
1771    const adjusted = @bitCast(u64, toSysTime(ns));
1772    return FILETIME{
1773        .dwHighDateTime = @truncate(u32, adjusted >> 32),
1774        .dwLowDateTime = @truncate(u32, adjusted),
1775    };
1776}
1777
1778pub const PathSpace = struct {
1779    data: [PATH_MAX_WIDE:0]u16,
1780    len: usize,
1781
1782    pub fn span(self: PathSpace) [:0]const u16 {
1783        return self.data[0..self.len :0];
1784    }
1785};
1786
1787/// The error type for `removeDotDirsSanitized`
1788pub const RemoveDotDirsError = error{TooManyParentDirs};
1789
1790/// Removes '.' and '..' path components from a "sanitized relative path".
1791/// A "sanitized path" is one where:
1792///    1) all forward slashes have been replaced with back slashes
1793///    2) all repeating back slashes have been collapsed
1794///    3) the path is a relative one (does not start with a back slash)
1795pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize {
1796    std.debug.assert(path.len == 0 or path[0] != '\\');
1797
1798    var write_idx: usize = 0;
1799    var read_idx: usize = 0;
1800    while (read_idx < path.len) {
1801        if (path[read_idx] == '.') {
1802            if (read_idx + 1 == path.len)
1803                return write_idx;
1804
1805            const after_dot = path[read_idx + 1];
1806            if (after_dot == '\\') {
1807                read_idx += 2;
1808                continue;
1809            }
1810            if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) {
1811                if (write_idx == 0) return error.TooManyParentDirs;
1812                std.debug.assert(write_idx >= 2);
1813                write_idx -= 1;
1814                while (true) {
1815                    write_idx -= 1;
1816                    if (write_idx == 0) break;
1817                    if (path[write_idx] == '\\') {
1818                        write_idx += 1;
1819                        break;
1820                    }
1821                }
1822                if (read_idx + 2 == path.len)
1823                    return write_idx;
1824                read_idx += 3;
1825                continue;
1826            }
1827        }
1828
1829        // skip to the next path separator
1830        while (true) : (read_idx += 1) {
1831            if (read_idx == path.len)
1832                return write_idx;
1833            path[write_idx] = path[read_idx];
1834            write_idx += 1;
1835            if (path[read_idx] == '\\')
1836                break;
1837        }
1838        read_idx += 1;
1839    }
1840    return write_idx;
1841}
1842
1843/// Normalizes a Windows path with the following steps:
1844///     1) convert all forward slashes to back slashes
1845///     2) collapse duplicate back slashes
1846///     3) remove '.' and '..' directory parts
1847/// Returns the length of the new path.
1848pub fn normalizePath(comptime T: type, path: []T) RemoveDotDirsError!usize {
1849    mem.replaceScalar(T, path, '/', '\\');
1850    const new_len = mem.collapseRepeatsLen(T, path, '\\');
1851
1852    const prefix_len: usize = init: {
1853        if (new_len >= 1 and path[0] == '\\') break :init 1;
1854        if (new_len >= 2 and path[1] == ':')
1855            break :init if (new_len >= 3 and path[2] == '\\') @as(usize, 3) else @as(usize, 2);
1856        break :init 0;
1857    };
1858
1859    return prefix_len + try removeDotDirsSanitized(T, path[prefix_len..new_len]);
1860}
1861
1862/// Same as `sliceToPrefixedFileW` but accepts a pointer
1863/// to a null-terminated path.
1864pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
1865    return sliceToPrefixedFileW(mem.sliceTo(s, 0));
1866}
1867
1868/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
1869/// it will get NT-style prefix `\??\` prepended automatically.
1870pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
1871    // TODO https://github.com/ziglang/zig/issues/2765
1872    var path_space: PathSpace = undefined;
1873    const prefix = "\\??\\";
1874    const prefix_index: usize = if (mem.startsWith(u8, s, prefix)) prefix.len else 0;
1875    for (s[prefix_index..]) |byte| {
1876        switch (byte) {
1877            '*', '?', '"', '<', '>', '|' => return error.BadPathName,
1878            else => {},
1879        }
1880    }
1881    const prefix_u16 = [_]u16{ '\\', '?', '?', '\\' };
1882    const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: {
1883        mem.copy(u16, path_space.data[0..], prefix_u16[0..]);
1884        break :blk prefix_u16.len;
1885    };
1886    path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s);
1887    if (path_space.len > path_space.data.len) return error.NameTooLong;
1888    path_space.len = start_index + (normalizePath(u16, path_space.data[start_index..path_space.len]) catch |err| switch (err) {
1889        error.TooManyParentDirs => {
1890            if (!std.fs.path.isAbsolute(s)) {
1891                var temp_path: PathSpace = undefined;
1892                temp_path.len = try std.unicode.utf8ToUtf16Le(&temp_path.data, s);
1893                std.debug.assert(temp_path.len == path_space.len);
1894                temp_path.data[path_space.len] = 0;
1895                path_space.len = prefix_u16.len + try getFullPathNameW(&temp_path.data, path_space.data[prefix_u16.len..]);
1896                mem.copy(u16, &path_space.data, &prefix_u16);
1897                std.debug.assert(path_space.data[path_space.len] == 0);
1898                return path_space;
1899            }
1900            return error.BadPathName;
1901        },
1902    });
1903    path_space.data[path_space.len] = 0;
1904    return path_space;
1905}
1906
1907fn getFullPathNameW(path: [*:0]const u16, out: []u16) !usize {
1908    const result = kernel32.GetFullPathNameW(path, @intCast(u32, out.len), std.meta.assumeSentinel(out.ptr, 0), null);
1909    if (result == 0) {
1910        switch (kernel32.GetLastError()) {
1911            else => |err| return unexpectedError(err),
1912        }
1913    }
1914    return result;
1915}
1916
1917/// Assumes an absolute path.
1918pub fn wToPrefixedFileW(s: []const u16) !PathSpace {
1919    // TODO https://github.com/ziglang/zig/issues/2765
1920    var path_space: PathSpace = undefined;
1921
1922    const start_index = if (mem.startsWith(u16, s, &[_]u16{ '\\', '?' })) 0 else blk: {
1923        const prefix = [_]u16{ '\\', '?', '?', '\\' };
1924        mem.copy(u16, path_space.data[0..], &prefix);
1925        break :blk prefix.len;
1926    };
1927    path_space.len = start_index + s.len;
1928    if (path_space.len > path_space.data.len) return error.NameTooLong;
1929    mem.copy(u16, path_space.data[start_index..], s);
1930    // > File I/O functions in the Windows API convert "/" to "\" as part of
1931    // > converting the name to an NT-style name, except when using the "\\?\"
1932    // > prefix as detailed in the following sections.
1933    // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
1934    // Because we want the larger maximum path length for absolute paths, we
1935    // convert forward slashes to backward slashes here.
1936    for (path_space.data[0..path_space.len]) |*elem| {
1937        if (elem.* == '/') {
1938            elem.* = '\\';
1939        }
1940    }
1941    path_space.data[path_space.len] = 0;
1942    return path_space;
1943}
1944
1945inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID {
1946    return (s << 10) | p;
1947}
1948
1949/// Loads a Winsock extension function in runtime specified by a GUID.
1950pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid: GUID) !T {
1951    var function: T = undefined;
1952    var num_bytes: DWORD = undefined;
1953
1954    const rc = ws2_32.WSAIoctl(
1955        sock,
1956        ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER,
1957        @ptrCast(*const anyopaque, &guid),
1958        @sizeOf(GUID),
1959        &function,
1960        @sizeOf(T),
1961        &num_bytes,
1962        null,
1963        null,
1964    );
1965
1966    if (rc == ws2_32.SOCKET_ERROR) {
1967        return switch (ws2_32.WSAGetLastError()) {
1968            .WSAEOPNOTSUPP => error.OperationNotSupported,
1969            .WSAENOTSOCK => error.FileDescriptorNotASocket,
1970            else => |err| unexpectedWSAError(err),
1971        };
1972    }
1973
1974    if (num_bytes != @sizeOf(T)) {
1975        return error.ShortRead;
1976    }
1977
1978    return function;
1979}
1980
1981/// Call this when you made a windows DLL call or something that does SetLastError
1982/// and you get an unexpected error.
1983pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
1984    if (std.os.unexpected_error_tracing) {
1985        // 614 is the length of the longest windows error desciption
1986        var buf_wstr: [614]WCHAR = undefined;
1987        var buf_utf8: [614]u8 = undefined;
1988        const len = kernel32.FormatMessageW(
1989            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
1990            null,
1991            err,
1992            MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT),
1993            &buf_wstr,
1994            buf_wstr.len,
1995            null,
1996        );
1997        _ = std.unicode.utf16leToUtf8(&buf_utf8, buf_wstr[0..len]) catch unreachable;
1998        std.debug.print("error.Unexpected: GetLastError({}): {s}\n", .{ @enumToInt(err), buf_utf8[0..len] });
1999        std.debug.dumpCurrentStackTrace(null);
2000    }
2001    return error.Unexpected;
2002}
2003
2004pub fn unexpectedWSAError(err: ws2_32.WinsockError) std.os.UnexpectedError {
2005    return unexpectedError(@intToEnum(Win32Error, @enumToInt(err)));
2006}
2007
2008/// Call this when you made a windows NtDll call
2009/// and you get an unexpected status.
2010pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
2011    if (std.os.unexpected_error_tracing) {
2012        std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@enumToInt(status)});
2013        std.debug.dumpCurrentStackTrace(null);
2014    }
2015    return error.Unexpected;
2016}
2017
2018pub fn SetThreadDescription(hThread: HANDLE, lpThreadDescription: LPCWSTR) !void {
2019    if (kernel32.SetThreadDescription(hThread, lpThreadDescription) == 0) {
2020        switch (kernel32.GetLastError()) {
2021            else => |err| return unexpectedError(err),
2022        }
2023    }
2024}
2025pub fn GetThreadDescription(hThread: HANDLE, ppszThreadDescription: *LPWSTR) !void {
2026    if (kernel32.GetThreadDescription(hThread, ppszThreadDescription) == 0) {
2027        switch (kernel32.GetLastError()) {
2028            else => |err| return unexpectedError(err),
2029        }
2030    }
2031}
2032
2033pub const Win32Error = @import("windows/win32error.zig").Win32Error;
2034pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS;
2035pub const LANG = @import("windows/lang.zig");
2036pub const SUBLANG = @import("windows/sublang.zig");
2037
2038/// The standard input device. Initially, this is the console input buffer, CONIN$.
2039pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1;
2040
2041/// The standard output device. Initially, this is the active console screen buffer, CONOUT$.
2042pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1;
2043
2044/// The standard error device. Initially, this is the active console screen buffer, CONOUT$.
2045pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1;
2046
2047pub const WINAPI: std.builtin.CallingConvention = if (native_arch == .i386)
2048    .Stdcall
2049else
2050    .C;
2051
2052pub const BOOL = c_int;
2053pub const BOOLEAN = BYTE;
2054pub const BYTE = u8;
2055pub const CHAR = u8;
2056pub const UCHAR = u8;
2057pub const FLOAT = f32;
2058pub const HANDLE = *anyopaque;
2059pub const HCRYPTPROV = ULONG_PTR;
2060pub const ATOM = u16;
2061pub const HBRUSH = *opaque {};
2062pub const HCURSOR = *opaque {};
2063pub const HICON = *opaque {};
2064pub const HINSTANCE = *opaque {};
2065pub const HMENU = *opaque {};
2066pub const HMODULE = *opaque {};
2067pub const HWND = *opaque {};
2068pub const HDC = *opaque {};
2069pub const HGLRC = *opaque {};
2070pub const FARPROC = *opaque {};
2071pub const INT = c_int;
2072pub const LPCSTR = [*:0]const CHAR;
2073pub const LPCVOID = *const anyopaque;
2074pub const LPSTR = [*:0]CHAR;
2075pub const LPVOID = *anyopaque;
2076pub const LPWSTR = [*:0]WCHAR;
2077pub const LPCWSTR = [*:0]const WCHAR;
2078pub const PVOID = *anyopaque;
2079pub const PWSTR = [*:0]WCHAR;
2080pub const SIZE_T = usize;
2081pub const UINT = c_uint;
2082pub const ULONG_PTR = usize;
2083pub const LONG_PTR = isize;
2084pub const DWORD_PTR = ULONG_PTR;
2085pub const WCHAR = u16;
2086pub const WORD = u16;
2087pub const DWORD = u32;
2088pub const DWORD64 = u64;
2089pub const LARGE_INTEGER = i64;
2090pub const ULARGE_INTEGER = u64;
2091pub const USHORT = u16;
2092pub const SHORT = i16;
2093pub const ULONG = u32;
2094pub const LONG = i32;
2095pub const ULONGLONG = u64;
2096pub const LONGLONG = i64;
2097pub const HLOCAL = HANDLE;
2098pub const LANGID = c_ushort;
2099
2100pub const WPARAM = usize;
2101pub const LPARAM = LONG_PTR;
2102pub const LRESULT = LONG_PTR;
2103
2104pub const va_list = *opaque {};
2105
2106pub const TRUE = 1;
2107pub const FALSE = 0;
2108
2109pub const DEVICE_TYPE = ULONG;
2110pub const FILE_DEVICE_BEEP: DEVICE_TYPE = 0x0001;
2111pub const FILE_DEVICE_CD_ROM: DEVICE_TYPE = 0x0002;
2112pub const FILE_DEVICE_CD_ROM_FILE_SYSTEM: DEVICE_TYPE = 0x0003;
2113pub const FILE_DEVICE_CONTROLLER: DEVICE_TYPE = 0x0004;
2114pub const FILE_DEVICE_DATALINK: DEVICE_TYPE = 0x0005;
2115pub const FILE_DEVICE_DFS: DEVICE_TYPE = 0x0006;
2116pub const FILE_DEVICE_DISK: DEVICE_TYPE = 0x0007;
2117pub const FILE_DEVICE_DISK_FILE_SYSTEM: DEVICE_TYPE = 0x0008;
2118pub const FILE_DEVICE_FILE_SYSTEM: DEVICE_TYPE = 0x0009;
2119pub const FILE_DEVICE_INPORT_PORT: DEVICE_TYPE = 0x000a;
2120pub const FILE_DEVICE_KEYBOARD: DEVICE_TYPE = 0x000b;
2121pub const FILE_DEVICE_MAILSLOT: DEVICE_TYPE = 0x000c;
2122pub const FILE_DEVICE_MIDI_IN: DEVICE_TYPE = 0x000d;
2123pub const FILE_DEVICE_MIDI_OUT: DEVICE_TYPE = 0x000e;
2124pub const FILE_DEVICE_MOUSE: DEVICE_TYPE = 0x000f;
2125pub const FILE_DEVICE_MULTI_UNC_PROVIDER: DEVICE_TYPE = 0x0010;
2126pub const FILE_DEVICE_NAMED_PIPE: DEVICE_TYPE = 0x0011;
2127pub const FILE_DEVICE_NETWORK: DEVICE_TYPE = 0x0012;
2128pub const FILE_DEVICE_NETWORK_BROWSER: DEVICE_TYPE = 0x0013;
2129pub const FILE_DEVICE_NETWORK_FILE_SYSTEM: DEVICE_TYPE = 0x0014;
2130pub const FILE_DEVICE_NULL: DEVICE_TYPE = 0x0015;
2131pub const FILE_DEVICE_PARALLEL_PORT: DEVICE_TYPE = 0x0016;
2132pub const FILE_DEVICE_PHYSICAL_NETCARD: DEVICE_TYPE = 0x0017;
2133pub const FILE_DEVICE_PRINTER: DEVICE_TYPE = 0x0018;
2134pub const FILE_DEVICE_SCANNER: DEVICE_TYPE = 0x0019;
2135pub const FILE_DEVICE_SERIAL_MOUSE_PORT: DEVICE_TYPE = 0x001a;
2136pub const FILE_DEVICE_SERIAL_PORT: DEVICE_TYPE = 0x001b;
2137pub const FILE_DEVICE_SCREEN: DEVICE_TYPE = 0x001c;
2138pub const FILE_DEVICE_SOUND: DEVICE_TYPE = 0x001d;
2139pub const FILE_DEVICE_STREAMS: DEVICE_TYPE = 0x001e;
2140pub const FILE_DEVICE_TAPE: DEVICE_TYPE = 0x001f;
2141pub const FILE_DEVICE_TAPE_FILE_SYSTEM: DEVICE_TYPE = 0x0020;
2142pub const FILE_DEVICE_TRANSPORT: DEVICE_TYPE = 0x0021;
2143pub const FILE_DEVICE_UNKNOWN: DEVICE_TYPE = 0x0022;
2144pub const FILE_DEVICE_VIDEO: DEVICE_TYPE = 0x0023;
2145pub const FILE_DEVICE_VIRTUAL_DISK: DEVICE_TYPE = 0x0024;
2146pub const FILE_DEVICE_WAVE_IN: DEVICE_TYPE = 0x0025;
2147pub const FILE_DEVICE_WAVE_OUT: DEVICE_TYPE = 0x0026;
2148pub const FILE_DEVICE_8042_PORT: DEVICE_TYPE = 0x0027;
2149pub const FILE_DEVICE_NETWORK_REDIRECTOR: DEVICE_TYPE = 0x0028;
2150pub const FILE_DEVICE_BATTERY: DEVICE_TYPE = 0x0029;
2151pub const FILE_DEVICE_BUS_EXTENDER: DEVICE_TYPE = 0x002a;
2152pub const FILE_DEVICE_MODEM: DEVICE_TYPE = 0x002b;
2153pub const FILE_DEVICE_VDM: DEVICE_TYPE = 0x002c;
2154pub const FILE_DEVICE_MASS_STORAGE: DEVICE_TYPE = 0x002d;
2155pub const FILE_DEVICE_SMB: DEVICE_TYPE = 0x002e;
2156pub const FILE_DEVICE_KS: DEVICE_TYPE = 0x002f;
2157pub const FILE_DEVICE_CHANGER: DEVICE_TYPE = 0x0030;
2158pub const FILE_DEVICE_SMARTCARD: DEVICE_TYPE = 0x0031;
2159pub const FILE_DEVICE_ACPI: DEVICE_TYPE = 0x0032;
2160pub const FILE_DEVICE_DVD: DEVICE_TYPE = 0x0033;
2161pub const FILE_DEVICE_FULLSCREEN_VIDEO: DEVICE_TYPE = 0x0034;
2162pub const FILE_DEVICE_DFS_FILE_SYSTEM: DEVICE_TYPE = 0x0035;
2163pub const FILE_DEVICE_DFS_VOLUME: DEVICE_TYPE = 0x0036;
2164pub const FILE_DEVICE_SERENUM: DEVICE_TYPE = 0x0037;
2165pub const FILE_DEVICE_TERMSRV: DEVICE_TYPE = 0x0038;
2166pub const FILE_DEVICE_KSEC: DEVICE_TYPE = 0x0039;
2167pub const FILE_DEVICE_FIPS: DEVICE_TYPE = 0x003a;
2168pub const FILE_DEVICE_INFINIBAND: DEVICE_TYPE = 0x003b;
2169// TODO: missing values?
2170pub const FILE_DEVICE_VMBUS: DEVICE_TYPE = 0x003e;
2171pub const FILE_DEVICE_CRYPT_PROVIDER: DEVICE_TYPE = 0x003f;
2172pub const FILE_DEVICE_WPD: DEVICE_TYPE = 0x0040;
2173pub const FILE_DEVICE_BLUETOOTH: DEVICE_TYPE = 0x0041;
2174pub const FILE_DEVICE_MT_COMPOSITE: DEVICE_TYPE = 0x0042;
2175pub const FILE_DEVICE_MT_TRANSPORT: DEVICE_TYPE = 0x0043;
2176pub const FILE_DEVICE_BIOMETRIC: DEVICE_TYPE = 0x0044;
2177pub const FILE_DEVICE_PMI: DEVICE_TYPE = 0x0045;
2178pub const FILE_DEVICE_EHSTOR: DEVICE_TYPE = 0x0046;
2179pub const FILE_DEVICE_DEVAPI: DEVICE_TYPE = 0x0047;
2180pub const FILE_DEVICE_GPIO: DEVICE_TYPE = 0x0048;
2181pub const FILE_DEVICE_USBEX: DEVICE_TYPE = 0x0049;
2182pub const FILE_DEVICE_CONSOLE: DEVICE_TYPE = 0x0050;
2183pub const FILE_DEVICE_NFP: DEVICE_TYPE = 0x0051;
2184pub const FILE_DEVICE_SYSENV: DEVICE_TYPE = 0x0052;
2185pub const FILE_DEVICE_VIRTUAL_BLOCK: DEVICE_TYPE = 0x0053;
2186pub const FILE_DEVICE_POINT_OF_SERVICE: DEVICE_TYPE = 0x0054;
2187pub const FILE_DEVICE_STORAGE_REPLICATION: DEVICE_TYPE = 0x0055;
2188pub const FILE_DEVICE_TRUST_ENV: DEVICE_TYPE = 0x0056;
2189pub const FILE_DEVICE_UCM: DEVICE_TYPE = 0x0057;
2190pub const FILE_DEVICE_UCMTCPCI: DEVICE_TYPE = 0x0058;
2191pub const FILE_DEVICE_PERSISTENT_MEMORY: DEVICE_TYPE = 0x0059;
2192pub const FILE_DEVICE_NVDIMM: DEVICE_TYPE = 0x005a;
2193pub const FILE_DEVICE_HOLOGRAPHIC: DEVICE_TYPE = 0x005b;
2194pub const FILE_DEVICE_SDFXHCI: DEVICE_TYPE = 0x005c;
2195
2196/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-i-o-control-codes
2197pub const TransferType = enum(u2) {
2198    METHOD_BUFFERED = 0,
2199    METHOD_IN_DIRECT = 1,
2200    METHOD_OUT_DIRECT = 2,
2201    METHOD_NEITHER = 3,
2202};
2203
2204pub const FILE_ANY_ACCESS = 0;
2205pub const FILE_READ_ACCESS = 1;
2206pub const FILE_WRITE_ACCESS = 2;
2207
2208/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes
2209pub fn CTL_CODE(deviceType: u16, function: u12, method: TransferType, access: u2) DWORD {
2210    return (@as(DWORD, deviceType) << 16) |
2211        (@as(DWORD, access) << 14) |
2212        (@as(DWORD, function) << 2) |
2213        @enumToInt(method);
2214}
2215
2216pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize));
2217
2218pub const INVALID_FILE_ATTRIBUTES = @as(DWORD, maxInt(DWORD));
2219
2220pub const FILE_ALL_INFORMATION = extern struct {
2221    BasicInformation: FILE_BASIC_INFORMATION,
2222    StandardInformation: FILE_STANDARD_INFORMATION,
2223    InternalInformation: FILE_INTERNAL_INFORMATION,
2224    EaInformation: FILE_EA_INFORMATION,
2225    AccessInformation: FILE_ACCESS_INFORMATION,
2226    PositionInformation: FILE_POSITION_INFORMATION,
2227    ModeInformation: FILE_MODE_INFORMATION,
2228    AlignmentInformation: FILE_ALIGNMENT_INFORMATION,
2229    NameInformation: FILE_NAME_INFORMATION,
2230};
2231
2232pub const FILE_BASIC_INFORMATION = extern struct {
2233    CreationTime: LARGE_INTEGER,
2234    LastAccessTime: LARGE_INTEGER,
2235    LastWriteTime: LARGE_INTEGER,
2236    ChangeTime: LARGE_INTEGER,
2237    FileAttributes: ULONG,
2238};
2239
2240pub const FILE_STANDARD_INFORMATION = extern struct {
2241    AllocationSize: LARGE_INTEGER,
2242    EndOfFile: LARGE_INTEGER,
2243    NumberOfLinks: ULONG,
2244    DeletePending: BOOLEAN,
2245    Directory: BOOLEAN,
2246};
2247
2248pub const FILE_INTERNAL_INFORMATION = extern struct {
2249    IndexNumber: LARGE_INTEGER,
2250};
2251
2252pub const FILE_EA_INFORMATION = extern struct {
2253    EaSize: ULONG,
2254};
2255
2256pub const FILE_ACCESS_INFORMATION = extern struct {
2257    AccessFlags: ACCESS_MASK,
2258};
2259
2260pub const FILE_POSITION_INFORMATION = extern struct {
2261    CurrentByteOffset: LARGE_INTEGER,
2262};
2263
2264pub const FILE_END_OF_FILE_INFORMATION = extern struct {
2265    EndOfFile: LARGE_INTEGER,
2266};
2267
2268pub const FILE_MODE_INFORMATION = extern struct {
2269    Mode: ULONG,
2270};
2271
2272pub const FILE_ALIGNMENT_INFORMATION = extern struct {
2273    AlignmentRequirement: ULONG,
2274};
2275
2276pub const FILE_NAME_INFORMATION = extern struct {
2277    FileNameLength: ULONG,
2278    FileName: [1]WCHAR,
2279};
2280
2281pub const FILE_RENAME_INFORMATION = extern struct {
2282    ReplaceIfExists: BOOLEAN,
2283    RootDirectory: ?HANDLE,
2284    FileNameLength: ULONG,
2285    FileName: [1]WCHAR,
2286};
2287
2288pub const IO_STATUS_BLOCK = extern struct {
2289    // "DUMMYUNIONNAME" expands to "u"
2290    u: extern union {
2291        Status: NTSTATUS,
2292        Pointer: ?*anyopaque,
2293    },
2294    Information: ULONG_PTR,
2295};
2296
2297pub const FILE_INFORMATION_CLASS = enum(c_int) {
2298    FileDirectoryInformation = 1,
2299    FileFullDirectoryInformation,
2300    FileBothDirectoryInformation,
2301    FileBasicInformation,
2302    FileStandardInformation,
2303    FileInternalInformation,
2304    FileEaInformation,
2305    FileAccessInformation,
2306    FileNameInformation,
2307    FileRenameInformation,
2308    FileLinkInformation,
2309    FileNamesInformation,
2310    FileDispositionInformation,
2311    FilePositionInformation,
2312    FileFullEaInformation,
2313    FileModeInformation,
2314    FileAlignmentInformation,
2315    FileAllInformation,
2316    FileAllocationInformation,
2317    FileEndOfFileInformation,
2318    FileAlternateNameInformation,
2319    FileStreamInformation,
2320    FilePipeInformation,
2321    FilePipeLocalInformation,
2322    FilePipeRemoteInformation,
2323    FileMailslotQueryInformation,
2324    FileMailslotSetInformation,
2325    FileCompressionInformation,
2326    FileObjectIdInformation,
2327    FileCompletionInformation,
2328    FileMoveClusterInformation,
2329    FileQuotaInformation,
2330    FileReparsePointInformation,
2331    FileNetworkOpenInformation,
2332    FileAttributeTagInformation,
2333    FileTrackingInformation,
2334    FileIdBothDirectoryInformation,
2335    FileIdFullDirectoryInformation,
2336    FileValidDataLengthInformation,
2337    FileShortNameInformation,
2338    FileIoCompletionNotificationInformation,
2339    FileIoStatusBlockRangeInformation,
2340    FileIoPriorityHintInformation,
2341    FileSfioReserveInformation,
2342    FileSfioVolumeInformation,
2343    FileHardLinkInformation,
2344    FileProcessIdsUsingFileInformation,
2345    FileNormalizedNameInformation,
2346    FileNetworkPhysicalNameInformation,
2347    FileIdGlobalTxDirectoryInformation,
2348    FileIsRemoteDeviceInformation,
2349    FileUnusedInformation,
2350    FileNumaNodeInformation,
2351    FileStandardLinkInformation,
2352    FileRemoteProtocolInformation,
2353    FileRenameInformationBypassAccessCheck,
2354    FileLinkInformationBypassAccessCheck,
2355    FileVolumeNameInformation,
2356    FileIdInformation,
2357    FileIdExtdDirectoryInformation,
2358    FileReplaceCompletionInformation,
2359    FileHardLinkFullIdInformation,
2360    FileIdExtdBothDirectoryInformation,
2361    FileDispositionInformationEx,
2362    FileRenameInformationEx,
2363    FileRenameInformationExBypassAccessCheck,
2364    FileDesiredStorageClassInformation,
2365    FileStatInformation,
2366    FileMemoryPartitionInformation,
2367    FileStatLxInformation,
2368    FileCaseSensitiveInformation,
2369    FileLinkInformationEx,
2370    FileLinkInformationExBypassAccessCheck,
2371    FileStorageReserveIdInformation,
2372    FileCaseSensitiveInformationForceAccessCheck,
2373    FileMaximumInformation,
2374};
2375
2376pub const OVERLAPPED = extern struct {
2377    Internal: ULONG_PTR,
2378    InternalHigh: ULONG_PTR,
2379    DUMMYUNIONNAME: extern union {
2380        DUMMYSTRUCTNAME: extern struct {
2381            Offset: DWORD,
2382            OffsetHigh: DWORD,
2383        },
2384        Pointer: ?PVOID,
2385    },
2386    hEvent: ?HANDLE,
2387};
2388
2389pub const OVERLAPPED_ENTRY = extern struct {
2390    lpCompletionKey: ULONG_PTR,
2391    lpOverlapped: *OVERLAPPED,
2392    Internal: ULONG_PTR,
2393    dwNumberOfBytesTransferred: DWORD,
2394};
2395
2396pub const MAX_PATH = 260;
2397
2398// TODO issue #305
2399pub const FILE_INFO_BY_HANDLE_CLASS = u32;
2400pub const FileBasicInfo = 0;
2401pub const FileStandardInfo = 1;
2402pub const FileNameInfo = 2;
2403pub const FileRenameInfo = 3;
2404pub const FileDispositionInfo = 4;
2405pub const FileAllocationInfo = 5;
2406pub const FileEndOfFileInfo = 6;
2407pub const FileStreamInfo = 7;
2408pub const FileCompressionInfo = 8;
2409pub const FileAttributeTagInfo = 9;
2410pub const FileIdBothDirectoryInfo = 10;
2411pub const FileIdBothDirectoryRestartInfo = 11;
2412pub const FileIoPriorityHintInfo = 12;
2413pub const FileRemoteProtocolInfo = 13;
2414pub const FileFullDirectoryInfo = 14;
2415pub const FileFullDirectoryRestartInfo = 15;
2416pub const FileStorageInfo = 16;
2417pub const FileAlignmentInfo = 17;
2418pub const FileIdInfo = 18;
2419pub const FileIdExtdDirectoryInfo = 19;
2420pub const FileIdExtdDirectoryRestartInfo = 20;
2421
2422pub const BY_HANDLE_FILE_INFORMATION = extern struct {
2423    dwFileAttributes: DWORD,
2424    ftCreationTime: FILETIME,
2425    ftLastAccessTime: FILETIME,
2426    ftLastWriteTime: FILETIME,
2427    dwVolumeSerialNumber: DWORD,
2428    nFileSizeHigh: DWORD,
2429    nFileSizeLow: DWORD,
2430    nNumberOfLinks: DWORD,
2431    nFileIndexHigh: DWORD,
2432    nFileIndexLow: DWORD,
2433};
2434
2435pub const FILE_NAME_INFO = extern struct {
2436    FileNameLength: DWORD,
2437    FileName: [1]WCHAR,
2438};
2439
2440/// Return the normalized drive name. This is the default.
2441pub const FILE_NAME_NORMALIZED = 0x0;
2442
2443/// Return the opened file name (not normalized).
2444pub const FILE_NAME_OPENED = 0x8;
2445
2446/// Return the path with the drive letter. This is the default.
2447pub const VOLUME_NAME_DOS = 0x0;
2448
2449/// Return the path with a volume GUID path instead of the drive name.
2450pub const VOLUME_NAME_GUID = 0x1;
2451
2452/// Return the path with no drive information.
2453pub const VOLUME_NAME_NONE = 0x4;
2454
2455/// Return the path with the volume device path.
2456pub const VOLUME_NAME_NT = 0x2;
2457
2458pub const SECURITY_ATTRIBUTES = extern struct {
2459    nLength: DWORD,
2460    lpSecurityDescriptor: ?*anyopaque,
2461    bInheritHandle: BOOL,
2462};
2463
2464pub const PIPE_ACCESS_INBOUND = 0x00000001;
2465pub const PIPE_ACCESS_OUTBOUND = 0x00000002;
2466pub const PIPE_ACCESS_DUPLEX = 0x00000003;
2467
2468pub const PIPE_TYPE_BYTE = 0x00000000;
2469pub const PIPE_TYPE_MESSAGE = 0x00000004;
2470
2471pub const PIPE_READMODE_BYTE = 0x00000000;
2472pub const PIPE_READMODE_MESSAGE = 0x00000002;
2473
2474pub const PIPE_WAIT = 0x00000000;
2475pub const PIPE_NOWAIT = 0x00000001;
2476
2477pub const GENERIC_READ = 0x80000000;
2478pub const GENERIC_WRITE = 0x40000000;
2479pub const GENERIC_EXECUTE = 0x20000000;
2480pub const GENERIC_ALL = 0x10000000;
2481
2482pub const FILE_SHARE_DELETE = 0x00000004;
2483pub const FILE_SHARE_READ = 0x00000001;
2484pub const FILE_SHARE_WRITE = 0x00000002;
2485
2486pub const DELETE = 0x00010000;
2487pub const READ_CONTROL = 0x00020000;
2488pub const WRITE_DAC = 0x00040000;
2489pub const WRITE_OWNER = 0x00080000;
2490pub const SYNCHRONIZE = 0x00100000;
2491pub const STANDARD_RIGHTS_READ = READ_CONTROL;
2492pub const STANDARD_RIGHTS_WRITE = READ_CONTROL;
2493pub const STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
2494pub const STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
2495
2496// disposition for NtCreateFile
2497pub const FILE_SUPERSEDE = 0;
2498pub const FILE_OPEN = 1;
2499pub const FILE_CREATE = 2;
2500pub const FILE_OPEN_IF = 3;
2501pub const FILE_OVERWRITE = 4;
2502pub const FILE_OVERWRITE_IF = 5;
2503pub const FILE_MAXIMUM_DISPOSITION = 5;
2504
2505// flags for NtCreateFile and NtOpenFile
2506pub const FILE_READ_DATA = 0x00000001;
2507pub const FILE_LIST_DIRECTORY = 0x00000001;
2508pub const FILE_WRITE_DATA = 0x00000002;
2509pub const FILE_ADD_FILE = 0x00000002;
2510pub const FILE_APPEND_DATA = 0x00000004;
2511pub const FILE_ADD_SUBDIRECTORY = 0x00000004;
2512pub const FILE_CREATE_PIPE_INSTANCE = 0x00000004;
2513pub const FILE_READ_EA = 0x00000008;
2514pub const FILE_WRITE_EA = 0x00000010;
2515pub const FILE_EXECUTE = 0x00000020;
2516pub const FILE_TRAVERSE = 0x00000020;
2517pub const FILE_DELETE_CHILD = 0x00000040;
2518pub const FILE_READ_ATTRIBUTES = 0x00000080;
2519pub const FILE_WRITE_ATTRIBUTES = 0x00000100;
2520
2521pub const FILE_DIRECTORY_FILE = 0x00000001;
2522pub const FILE_WRITE_THROUGH = 0x00000002;
2523pub const FILE_SEQUENTIAL_ONLY = 0x00000004;
2524pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
2525pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
2526pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
2527pub const FILE_NON_DIRECTORY_FILE = 0x00000040;
2528pub const FILE_CREATE_TREE_CONNECTION = 0x00000080;
2529pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
2530pub const FILE_NO_EA_KNOWLEDGE = 0x00000200;
2531pub const FILE_OPEN_FOR_RECOVERY = 0x00000400;
2532pub const FILE_RANDOM_ACCESS = 0x00000800;
2533pub const FILE_DELETE_ON_CLOSE = 0x00001000;
2534pub const FILE_OPEN_BY_FILE_ID = 0x00002000;
2535pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
2536pub const FILE_NO_COMPRESSION = 0x00008000;
2537pub const FILE_RESERVE_OPFILTER = 0x00100000;
2538pub const FILE_OPEN_REPARSE_POINT = 0x00200000;
2539pub const FILE_OPEN_OFFLINE_FILE = 0x00400000;
2540pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
2541
2542pub const CREATE_ALWAYS = 2;
2543pub const CREATE_NEW = 1;
2544pub const OPEN_ALWAYS = 4;
2545pub const OPEN_EXISTING = 3;
2546pub const TRUNCATE_EXISTING = 5;
2547
2548pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
2549pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
2550pub const FILE_ATTRIBUTE_DEVICE = 0x40;
2551pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
2552pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
2553pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
2554pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
2555pub const FILE_ATTRIBUTE_NORMAL = 0x80;
2556pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
2557pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
2558pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
2559pub const FILE_ATTRIBUTE_READONLY = 0x1;
2560pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
2561pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
2562pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
2563pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
2564pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
2565pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
2566pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
2567
2568// flags for CreateEvent
2569pub const CREATE_EVENT_INITIAL_SET = 0x00000002;
2570pub const CREATE_EVENT_MANUAL_RESET = 0x00000001;
2571
2572pub const EVENT_ALL_ACCESS = 0x1F0003;
2573pub const EVENT_MODIFY_STATE = 0x0002;
2574
2575pub const PROCESS_INFORMATION = extern struct {
2576    hProcess: HANDLE,
2577    hThread: HANDLE,
2578    dwProcessId: DWORD,
2579    dwThreadId: DWORD,
2580};
2581
2582pub const STARTUPINFOW = extern struct {
2583    cb: DWORD,
2584    lpReserved: ?LPWSTR,
2585    lpDesktop: ?LPWSTR,
2586    lpTitle: ?LPWSTR,
2587    dwX: DWORD,
2588    dwY: DWORD,
2589    dwXSize: DWORD,
2590    dwYSize: DWORD,
2591    dwXCountChars: DWORD,
2592    dwYCountChars: DWORD,
2593    dwFillAttribute: DWORD,
2594    dwFlags: DWORD,
2595    wShowWindow: WORD,
2596    cbReserved2: WORD,
2597    lpReserved2: ?*BYTE,
2598    hStdInput: ?HANDLE,
2599    hStdOutput: ?HANDLE,
2600    hStdError: ?HANDLE,
2601};
2602
2603pub const STARTF_FORCEONFEEDBACK = 0x00000040;
2604pub const STARTF_FORCEOFFFEEDBACK = 0x00000080;
2605pub const STARTF_PREVENTPINNING = 0x00002000;
2606pub const STARTF_RUNFULLSCREEN = 0x00000020;
2607pub const STARTF_TITLEISAPPID = 0x00001000;
2608pub const STARTF_TITLEISLINKNAME = 0x00000800;
2609pub const STARTF_UNTRUSTEDSOURCE = 0x00008000;
2610pub const STARTF_USECOUNTCHARS = 0x00000008;
2611pub const STARTF_USEFILLATTRIBUTE = 0x00000010;
2612pub const STARTF_USEHOTKEY = 0x00000200;
2613pub const STARTF_USEPOSITION = 0x00000004;
2614pub const STARTF_USESHOWWINDOW = 0x00000001;
2615pub const STARTF_USESIZE = 0x00000002;
2616pub const STARTF_USESTDHANDLES = 0x00000100;
2617
2618pub const INFINITE = 4294967295;
2619
2620pub const MAXIMUM_WAIT_OBJECTS = 64;
2621
2622pub const WAIT_ABANDONED = 0x00000080;
2623pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0;
2624pub const WAIT_OBJECT_0 = 0x00000000;
2625pub const WAIT_TIMEOUT = 0x00000102;
2626pub const WAIT_FAILED = 0xFFFFFFFF;
2627
2628pub const HANDLE_FLAG_INHERIT = 0x00000001;
2629pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
2630
2631pub const MOVEFILE_COPY_ALLOWED = 2;
2632pub const MOVEFILE_CREATE_HARDLINK = 16;
2633pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4;
2634pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32;
2635pub const MOVEFILE_REPLACE_EXISTING = 1;
2636pub const MOVEFILE_WRITE_THROUGH = 8;
2637
2638pub const FILE_BEGIN = 0;
2639pub const FILE_CURRENT = 1;
2640pub const FILE_END = 2;
2641
2642pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
2643pub const HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
2644pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
2645pub const HEAP_NO_SERIALIZE = 0x00000001;
2646
2647// AllocationType values
2648pub const MEM_COMMIT = 0x1000;
2649pub const MEM_RESERVE = 0x2000;
2650pub const MEM_RESET = 0x80000;
2651pub const MEM_RESET_UNDO = 0x1000000;
2652pub const MEM_LARGE_PAGES = 0x20000000;
2653pub const MEM_PHYSICAL = 0x400000;
2654pub const MEM_TOP_DOWN = 0x100000;
2655pub const MEM_WRITE_WATCH = 0x200000;
2656
2657// Protect values
2658pub const PAGE_EXECUTE = 0x10;
2659pub const PAGE_EXECUTE_READ = 0x20;
2660pub const PAGE_EXECUTE_READWRITE = 0x40;
2661pub const PAGE_EXECUTE_WRITECOPY = 0x80;
2662pub const PAGE_NOACCESS = 0x01;
2663pub const PAGE_READONLY = 0x02;
2664pub const PAGE_READWRITE = 0x04;
2665pub const PAGE_WRITECOPY = 0x08;
2666pub const PAGE_TARGETS_INVALID = 0x40000000;
2667pub const PAGE_TARGETS_NO_UPDATE = 0x40000000; // Same as PAGE_TARGETS_INVALID
2668pub const PAGE_GUARD = 0x100;
2669pub const PAGE_NOCACHE = 0x200;
2670pub const PAGE_WRITECOMBINE = 0x400;
2671
2672// FreeType values
2673pub const MEM_COALESCE_PLACEHOLDERS = 0x1;
2674pub const MEM_RESERVE_PLACEHOLDERS = 0x2;
2675pub const MEM_DECOMMIT = 0x4000;
2676pub const MEM_RELEASE = 0x8000;
2677
2678pub const PTHREAD_START_ROUTINE = fn (LPVOID) callconv(.C) DWORD;
2679pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
2680
2681pub const WIN32_FIND_DATAW = extern struct {
2682    dwFileAttributes: DWORD,
2683    ftCreationTime: FILETIME,
2684    ftLastAccessTime: FILETIME,
2685    ftLastWriteTime: FILETIME,
2686    nFileSizeHigh: DWORD,
2687    nFileSizeLow: DWORD,
2688    dwReserved0: DWORD,
2689    dwReserved1: DWORD,
2690    cFileName: [260]u16,
2691    cAlternateFileName: [14]u16,
2692};
2693
2694pub const FILETIME = extern struct {
2695    dwLowDateTime: DWORD,
2696    dwHighDateTime: DWORD,
2697};
2698
2699pub const SYSTEM_INFO = extern struct {
2700    anon1: extern union {
2701        dwOemId: DWORD,
2702        anon2: extern struct {
2703            wProcessorArchitecture: WORD,
2704            wReserved: WORD,
2705        },
2706    },
2707    dwPageSize: DWORD,
2708    lpMinimumApplicationAddress: LPVOID,
2709    lpMaximumApplicationAddress: LPVOID,
2710    dwActiveProcessorMask: DWORD_PTR,
2711    dwNumberOfProcessors: DWORD,
2712    dwProcessorType: DWORD,
2713    dwAllocationGranularity: DWORD,
2714    wProcessorLevel: WORD,
2715    wProcessorRevision: WORD,
2716};
2717
2718pub const HRESULT = c_long;
2719
2720pub const KNOWNFOLDERID = GUID;
2721pub const GUID = extern struct {
2722    Data1: c_ulong,
2723    Data2: c_ushort,
2724    Data3: c_ushort,
2725    Data4: [8]u8,
2726
2727    pub fn parse(str: []const u8) GUID {
2728        var guid: GUID = undefined;
2729        var index: usize = 0;
2730        assert(str[index] == '{');
2731        index += 1;
2732
2733        guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable;
2734        index += 8;
2735
2736        assert(str[index] == '-');
2737        index += 1;
2738
2739        guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable;
2740        index += 4;
2741
2742        assert(str[index] == '-');
2743        index += 1;
2744
2745        guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable;
2746        index += 4;
2747
2748        assert(str[index] == '-');
2749        index += 1;
2750
2751        guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
2752        index += 2;
2753        guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
2754        index += 2;
2755
2756        assert(str[index] == '-');
2757        index += 1;
2758
2759        var i: usize = 2;
2760        while (i < guid.Data4.len) : (i += 1) {
2761            guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
2762            index += 2;
2763        }
2764
2765        assert(str[index] == '}');
2766        index += 1;
2767        return guid;
2768    }
2769};
2770
2771pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
2772
2773pub const KF_FLAG_DEFAULT = 0;
2774pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
2775pub const KF_FLAG_CREATE = 32768;
2776pub const KF_FLAG_DONT_VERIFY = 16384;
2777pub const KF_FLAG_DONT_UNEXPAND = 8192;
2778pub const KF_FLAG_NO_ALIAS = 4096;
2779pub const KF_FLAG_INIT = 2048;
2780pub const KF_FLAG_DEFAULT_PATH = 1024;
2781pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
2782pub const KF_FLAG_SIMPLE_IDLIST = 256;
2783pub const KF_FLAG_ALIAS_ONLY = -2147483648;
2784
2785pub const S_OK = 0;
2786pub const E_NOTIMPL = @bitCast(c_long, @as(c_ulong, 0x80004001));
2787pub const E_NOINTERFACE = @bitCast(c_long, @as(c_ulong, 0x80004002));
2788pub const E_POINTER = @bitCast(c_long, @as(c_ulong, 0x80004003));
2789pub const E_ABORT = @bitCast(c_long, @as(c_ulong, 0x80004004));
2790pub const E_FAIL = @bitCast(c_long, @as(c_ulong, 0x80004005));
2791pub const E_UNEXPECTED = @bitCast(c_long, @as(c_ulong, 0x8000FFFF));
2792pub const E_ACCESSDENIED = @bitCast(c_long, @as(c_ulong, 0x80070005));
2793pub const E_HANDLE = @bitCast(c_long, @as(c_ulong, 0x80070006));
2794pub const E_OUTOFMEMORY = @bitCast(c_long, @as(c_ulong, 0x8007000E));
2795pub const E_INVALIDARG = @bitCast(c_long, @as(c_ulong, 0x80070057));
2796
2797pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
2798pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
2799pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
2800pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
2801pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
2802pub const FILE_FLAG_OVERLAPPED = 0x40000000;
2803pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
2804pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
2805pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
2806pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
2807pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
2808
2809pub const RECT = extern struct {
2810    left: LONG,
2811    top: LONG,
2812    right: LONG,
2813    bottom: LONG,
2814};
2815
2816pub const SMALL_RECT = extern struct {
2817    Left: SHORT,
2818    Top: SHORT,
2819    Right: SHORT,
2820    Bottom: SHORT,
2821};
2822
2823pub const POINT = extern struct {
2824    x: LONG,
2825    y: LONG,
2826};
2827
2828pub const COORD = extern struct {
2829    X: SHORT,
2830    Y: SHORT,
2831};
2832
2833pub const CREATE_UNICODE_ENVIRONMENT = 1024;
2834
2835pub const TLS_OUT_OF_INDEXES = 4294967295;
2836pub const IMAGE_TLS_DIRECTORY = extern struct {
2837    StartAddressOfRawData: usize,
2838    EndAddressOfRawData: usize,
2839    AddressOfIndex: usize,
2840    AddressOfCallBacks: usize,
2841    SizeOfZeroFill: u32,
2842    Characteristics: u32,
2843};
2844pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
2845pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
2846
2847pub const PIMAGE_TLS_CALLBACK = ?fn (PVOID, DWORD, PVOID) callconv(.C) void;
2848
2849pub const PROV_RSA_FULL = 1;
2850
2851pub const REGSAM = ACCESS_MASK;
2852pub const ACCESS_MASK = DWORD;
2853pub const HKEY = *HKEY__;
2854pub const HKEY__ = extern struct {
2855    unused: c_int,
2856};
2857pub const LSTATUS = LONG;
2858
2859pub const FILE_NOTIFY_INFORMATION = extern struct {
2860    NextEntryOffset: DWORD,
2861    Action: DWORD,
2862    FileNameLength: DWORD,
2863    // Flexible array member
2864    // FileName: [1]WCHAR,
2865};
2866
2867pub const FILE_ACTION_ADDED = 0x00000001;
2868pub const FILE_ACTION_REMOVED = 0x00000002;
2869pub const FILE_ACTION_MODIFIED = 0x00000003;
2870pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
2871pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
2872
2873pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?fn (DWORD, DWORD, *OVERLAPPED) callconv(.C) void;
2874
2875pub const FILE_NOTIFY_CHANGE_CREATION = 64;
2876pub const FILE_NOTIFY_CHANGE_SIZE = 8;
2877pub const FILE_NOTIFY_CHANGE_SECURITY = 256;
2878pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32;
2879pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
2880pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
2881pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
2882pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
2883
2884pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
2885    dwSize: COORD,
2886    dwCursorPosition: COORD,
2887    wAttributes: WORD,
2888    srWindow: SMALL_RECT,
2889    dwMaximumWindowSize: COORD,
2890};
2891
2892pub const FOREGROUND_BLUE = 1;
2893pub const FOREGROUND_GREEN = 2;
2894pub const FOREGROUND_RED = 4;
2895pub const FOREGROUND_INTENSITY = 8;
2896
2897pub const LIST_ENTRY = extern struct {
2898    Flink: *LIST_ENTRY,
2899    Blink: *LIST_ENTRY,
2900};
2901
2902pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
2903    Type: WORD,
2904    CreatorBackTraceIndex: WORD,
2905    CriticalSection: *RTL_CRITICAL_SECTION,
2906    ProcessLocksList: LIST_ENTRY,
2907    EntryCount: DWORD,
2908    ContentionCount: DWORD,
2909    Flags: DWORD,
2910    CreatorBackTraceIndexHigh: WORD,
2911    SpareWORD: WORD,
2912};
2913
2914pub const RTL_CRITICAL_SECTION = extern struct {
2915    DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
2916    LockCount: LONG,
2917    RecursionCount: LONG,
2918    OwningThread: HANDLE,
2919    LockSemaphore: HANDLE,
2920    SpinCount: ULONG_PTR,
2921};
2922
2923pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
2924pub const INIT_ONCE = RTL_RUN_ONCE;
2925pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT;
2926pub const INIT_ONCE_FN = fn (InitOnce: *INIT_ONCE, Parameter: ?*anyopaque, Context: ?*anyopaque) callconv(.C) BOOL;
2927
2928pub const RTL_RUN_ONCE = extern struct {
2929    Ptr: ?*anyopaque,
2930};
2931
2932pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
2933
2934pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
2935pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
2936pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
2937pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
2938pub const COINIT = enum(c_int) {
2939    COINIT_APARTMENTTHREADED = 2,
2940    COINIT_MULTITHREADED = 0,
2941    COINIT_DISABLE_OLE1DDE = 4,
2942    COINIT_SPEED_OVER_MEMORY = 8,
2943};
2944
2945/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
2946/// > prefix may be expanded to a longer string by the system at run time, and
2947/// > this expansion applies to the total length.
2948/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
2949pub const PATH_MAX_WIDE = 32767;
2950
2951pub const FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
2952pub const FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
2953pub const FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
2954pub const FORMAT_MESSAGE_FROM_STRING = 0x00000400;
2955pub const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
2956pub const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
2957pub const FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF;
2958
2959pub const EXCEPTION_DATATYPE_MISALIGNMENT = 0x80000002;
2960pub const EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
2961pub const EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d;
2962pub const EXCEPTION_STACK_OVERFLOW = 0xc00000fd;
2963pub const EXCEPTION_CONTINUE_SEARCH = 0;
2964
2965pub const EXCEPTION_RECORD = extern struct {
2966    ExceptionCode: u32,
2967    ExceptionFlags: u32,
2968    ExceptionRecord: *EXCEPTION_RECORD,
2969    ExceptionAddress: *anyopaque,
2970    NumberParameters: u32,
2971    ExceptionInformation: [15]usize,
2972};
2973
2974pub usingnamespace switch (native_arch) {
2975    .i386 => struct {
2976        pub const FLOATING_SAVE_AREA = extern struct {
2977            ControlWord: DWORD,
2978            StatusWord: DWORD,
2979            TagWord: DWORD,
2980            ErrorOffset: DWORD,
2981            ErrorSelector: DWORD,
2982            DataOffset: DWORD,
2983            DataSelector: DWORD,
2984            RegisterArea: [80]BYTE,
2985            Cr0NpxState: DWORD,
2986        };
2987
2988        pub const CONTEXT = extern struct {
2989            ContextFlags: DWORD,
2990            Dr0: DWORD,
2991            Dr1: DWORD,
2992            Dr2: DWORD,
2993            Dr3: DWORD,
2994            Dr6: DWORD,
2995            Dr7: DWORD,
2996            FloatSave: FLOATING_SAVE_AREA,
2997            SegGs: DWORD,
2998            SegFs: DWORD,
2999            SegEs: DWORD,
3000            SegDs: DWORD,
3001            Edi: DWORD,
3002            Esi: DWORD,
3003            Ebx: DWORD,
3004            Edx: DWORD,
3005            Ecx: DWORD,
3006            Eax: DWORD,
3007            Ebp: DWORD,
3008            Eip: DWORD,
3009            SegCs: DWORD,
3010            EFlags: DWORD,
3011            Esp: DWORD,
3012            SegSs: DWORD,
3013            ExtendedRegisters: [512]BYTE,
3014
3015            pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } {
3016                return .{ .bp = ctx.Ebp, .ip = ctx.Eip };
3017            }
3018        };
3019    },
3020    .x86_64 => struct {
3021        pub const M128A = extern struct {
3022            Low: ULONGLONG,
3023            High: LONGLONG,
3024        };
3025
3026        pub const XMM_SAVE_AREA32 = extern struct {
3027            ControlWord: WORD,
3028            StatusWord: WORD,
3029            TagWord: BYTE,
3030            Reserved1: BYTE,
3031            ErrorOpcode: WORD,
3032            ErrorOffset: DWORD,
3033            ErrorSelector: WORD,
3034            Reserved2: WORD,
3035            DataOffset: DWORD,
3036            DataSelector: WORD,
3037            Reserved3: WORD,
3038            MxCsr: DWORD,
3039            MxCsr_Mask: DWORD,
3040            FloatRegisters: [8]M128A,
3041            XmmRegisters: [16]M128A,
3042            Reserved4: [96]BYTE,
3043        };
3044
3045        pub const CONTEXT = extern struct {
3046            P1Home: DWORD64,
3047            P2Home: DWORD64,
3048            P3Home: DWORD64,
3049            P4Home: DWORD64,
3050            P5Home: DWORD64,
3051            P6Home: DWORD64,
3052            ContextFlags: DWORD,
3053            MxCsr: DWORD,
3054            SegCs: WORD,
3055            SegDs: WORD,
3056            SegEs: WORD,
3057            SegFs: WORD,
3058            SegGs: WORD,
3059            SegSs: WORD,
3060            EFlags: DWORD,
3061            Dr0: DWORD64,
3062            Dr1: DWORD64,
3063            Dr2: DWORD64,
3064            Dr3: DWORD64,
3065            Dr6: DWORD64,
3066            Dr7: DWORD64,
3067            Rax: DWORD64,
3068            Rcx: DWORD64,
3069            Rdx: DWORD64,
3070            Rbx: DWORD64,
3071            Rsp: DWORD64,
3072            Rbp: DWORD64,
3073            Rsi: DWORD64,
3074            Rdi: DWORD64,
3075            R8: DWORD64,
3076            R9: DWORD64,
3077            R10: DWORD64,
3078            R11: DWORD64,
3079            R12: DWORD64,
3080            R13: DWORD64,
3081            R14: DWORD64,
3082            R15: DWORD64,
3083            Rip: DWORD64,
3084            DUMMYUNIONNAME: extern union {
3085                FltSave: XMM_SAVE_AREA32,
3086                FloatSave: XMM_SAVE_AREA32,
3087                DUMMYSTRUCTNAME: extern struct {
3088                    Header: [2]M128A,
3089                    Legacy: [8]M128A,
3090                    Xmm0: M128A,
3091                    Xmm1: M128A,
3092                    Xmm2: M128A,
3093                    Xmm3: M128A,
3094                    Xmm4: M128A,
3095                    Xmm5: M128A,
3096                    Xmm6: M128A,
3097                    Xmm7: M128A,
3098                    Xmm8: M128A,
3099                    Xmm9: M128A,
3100                    Xmm10: M128A,
3101                    Xmm11: M128A,
3102                    Xmm12: M128A,
3103                    Xmm13: M128A,
3104                    Xmm14: M128A,
3105                    Xmm15: M128A,
3106                },
3107            },
3108            VectorRegister: [26]M128A,
3109            VectorControl: DWORD64,
3110            DebugControl: DWORD64,
3111            LastBranchToRip: DWORD64,
3112            LastBranchFromRip: DWORD64,
3113            LastExceptionToRip: DWORD64,
3114            LastExceptionFromRip: DWORD64,
3115
3116            pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } {
3117                return .{ .bp = ctx.Rbp, .ip = ctx.Rip };
3118            }
3119        };
3120    },
3121    .aarch64 => struct {
3122        pub const NEON128 = extern union {
3123            DUMMYSTRUCTNAME: extern struct {
3124                Low: ULONGLONG,
3125                High: LONGLONG,
3126            },
3127            D: [2]f64,
3128            S: [4]f32,
3129            H: [8]WORD,
3130            B: [16]BYTE,
3131        };
3132
3133        pub const CONTEXT = extern struct {
3134            ContextFlags: ULONG,
3135            Cpsr: ULONG,
3136            DUMMYUNIONNAME: extern union {
3137                DUMMYSTRUCTNAME: extern struct {
3138                    X0: DWORD64,
3139                    X1: DWORD64,
3140                    X2: DWORD64,
3141                    X3: DWORD64,
3142                    X4: DWORD64,
3143                    X5: DWORD64,
3144                    X6: DWORD64,
3145                    X7: DWORD64,
3146                    X8: DWORD64,
3147                    X9: DWORD64,
3148                    X10: DWORD64,
3149                    X11: DWORD64,
3150                    X12: DWORD64,
3151                    X13: DWORD64,
3152                    X14: DWORD64,
3153                    X15: DWORD64,
3154                    X16: DWORD64,
3155                    X17: DWORD64,
3156                    X18: DWORD64,
3157                    X19: DWORD64,
3158                    X20: DWORD64,
3159                    X21: DWORD64,
3160                    X22: DWORD64,
3161                    X23: DWORD64,
3162                    X24: DWORD64,
3163                    X25: DWORD64,
3164                    X26: DWORD64,
3165                    X27: DWORD64,
3166                    X28: DWORD64,
3167                    Fp: DWORD64,
3168                    Lr: DWORD64,
3169                },
3170                X: [31]DWORD64,
3171            },
3172            Sp: DWORD64,
3173            Pc: DWORD64,
3174            V: [32]NEON128,
3175            Fpcr: DWORD,
3176            Fpsr: DWORD,
3177            Bcr: [8]DWORD,
3178            Bvr: [8]DWORD64,
3179            Wcr: [2]DWORD,
3180            Wvr: [2]DWORD64,
3181
3182            pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } {
3183                return .{
3184                    .bp = ctx.DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp,
3185                    .ip = ctx.Pc,
3186                };
3187            }
3188        };
3189    },
3190    else => struct {},
3191};
3192
3193pub const EXCEPTION_POINTERS = extern struct {
3194    ExceptionRecord: *EXCEPTION_RECORD,
3195    ContextRecord: *std.os.windows.CONTEXT,
3196};
3197
3198pub const VECTORED_EXCEPTION_HANDLER = fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long;
3199
3200pub const OBJECT_ATTRIBUTES = extern struct {
3201    Length: ULONG,
3202    RootDirectory: ?HANDLE,
3203    ObjectName: *UNICODE_STRING,
3204    Attributes: ULONG,
3205    SecurityDescriptor: ?*anyopaque,
3206    SecurityQualityOfService: ?*anyopaque,
3207};
3208
3209pub const OBJ_INHERIT = 0x00000002;
3210pub const OBJ_PERMANENT = 0x00000010;
3211pub const OBJ_EXCLUSIVE = 0x00000020;
3212pub const OBJ_CASE_INSENSITIVE = 0x00000040;
3213pub const OBJ_OPENIF = 0x00000080;
3214pub const OBJ_OPENLINK = 0x00000100;
3215pub const OBJ_KERNEL_HANDLE = 0x00000200;
3216pub const OBJ_VALID_ATTRIBUTES = 0x000003F2;
3217
3218pub const UNICODE_STRING = extern struct {
3219    Length: c_ushort,
3220    MaximumLength: c_ushort,
3221    Buffer: [*]WCHAR,
3222};
3223
3224pub const ACTIVATION_CONTEXT_DATA = opaque {};
3225pub const ASSEMBLY_STORAGE_MAP = opaque {};
3226pub const FLS_CALLBACK_INFO = opaque {};
3227pub const RTL_BITMAP = opaque {};
3228pub const KAFFINITY = usize;
3229
3230pub const TEB = extern struct {
3231    Reserved1: [12]PVOID,
3232    ProcessEnvironmentBlock: *PEB,
3233    Reserved2: [399]PVOID,
3234    Reserved3: [1952]u8,
3235    TlsSlots: [64]PVOID,
3236    Reserved4: [8]u8,
3237    Reserved5: [26]PVOID,
3238    ReservedForOle: PVOID,
3239    Reserved6: [4]PVOID,
3240    TlsExpansionSlots: PVOID,
3241};
3242
3243/// Process Environment Block
3244/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
3245///  - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269
3246///  - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm
3247pub const PEB = extern struct {
3248    // Versions: All
3249    InheritedAddressSpace: BOOLEAN,
3250
3251    // Versions: 3.51+
3252    ReadImageFileExecOptions: BOOLEAN,
3253    BeingDebugged: BOOLEAN,
3254
3255    // Versions: 5.2+ (previously was padding)
3256    BitField: UCHAR,
3257
3258    // Versions: all
3259    Mutant: HANDLE,
3260    ImageBaseAddress: HMODULE,
3261    Ldr: *PEB_LDR_DATA,
3262    ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
3263    SubSystemData: PVOID,
3264    ProcessHeap: HANDLE,
3265
3266    // Versions: 5.1+
3267    FastPebLock: *RTL_CRITICAL_SECTION,
3268
3269    // Versions: 5.2+
3270    AtlThunkSListPtr: PVOID,
3271    IFEOKey: PVOID,
3272
3273    // Versions: 6.0+
3274
3275    /// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/crossprocessflags.htm
3276    CrossProcessFlags: ULONG,
3277
3278    // Versions: 6.0+
3279    union1: extern union {
3280        KernelCallbackTable: PVOID,
3281        UserSharedInfoPtr: PVOID,
3282    },
3283
3284    // Versions: 5.1+
3285    SystemReserved: ULONG,
3286
3287    // Versions: 5.1, (not 5.2, not 6.0), 6.1+
3288    AtlThunkSListPtr32: ULONG,
3289
3290    // Versions: 6.1+
3291    ApiSetMap: PVOID,
3292
3293    // Versions: all
3294    TlsExpansionCounter: ULONG,
3295    // note: there is padding here on 64 bit
3296    TlsBitmap: *RTL_BITMAP,
3297    TlsBitmapBits: [2]ULONG,
3298    ReadOnlySharedMemoryBase: PVOID,
3299
3300    // Versions: 1703+
3301    SharedData: PVOID,
3302
3303    // Versions: all
3304    ReadOnlyStaticServerData: *PVOID,
3305    AnsiCodePageData: PVOID,
3306    OemCodePageData: PVOID,
3307    UnicodeCaseTableData: PVOID,
3308
3309    // Versions: 3.51+
3310    NumberOfProcessors: ULONG,
3311    NtGlobalFlag: ULONG,
3312
3313    // Versions: all
3314    CriticalSectionTimeout: LARGE_INTEGER,
3315
3316    // End of Original PEB size
3317
3318    // Fields appended in 3.51:
3319    HeapSegmentReserve: ULONG_PTR,
3320    HeapSegmentCommit: ULONG_PTR,
3321    HeapDeCommitTotalFreeThreshold: ULONG_PTR,
3322    HeapDeCommitFreeBlockThreshold: ULONG_PTR,
3323    NumberOfHeaps: ULONG,
3324    MaximumNumberOfHeaps: ULONG,
3325    ProcessHeaps: *PVOID,
3326
3327    // Fields appended in 4.0:
3328    GdiSharedHandleTable: PVOID,
3329    ProcessStarterHelper: PVOID,
3330    GdiDCAttributeList: ULONG,
3331    // note: there is padding here on 64 bit
3332    LoaderLock: *RTL_CRITICAL_SECTION,
3333    OSMajorVersion: ULONG,
3334    OSMinorVersion: ULONG,
3335    OSBuildNumber: USHORT,
3336    OSCSDVersion: USHORT,
3337    OSPlatformId: ULONG,
3338    ImageSubSystem: ULONG,
3339    ImageSubSystemMajorVersion: ULONG,
3340    ImageSubSystemMinorVersion: ULONG,
3341    // note: there is padding here on 64 bit
3342    ActiveProcessAffinityMask: KAFFINITY,
3343    GdiHandleBuffer: [
3344        switch (@sizeOf(usize)) {
3345            4 => 0x22,
3346            8 => 0x3C,
3347            else => unreachable,
3348        }
3349    ]ULONG,
3350
3351    // Fields appended in 5.0 (Windows 2000):
3352    PostProcessInitRoutine: PVOID,
3353    TlsExpansionBitmap: *RTL_BITMAP,
3354    TlsExpansionBitmapBits: [32]ULONG,
3355    SessionId: ULONG,
3356    // note: there is padding here on 64 bit
3357    // Versions: 5.1+
3358    AppCompatFlags: ULARGE_INTEGER,
3359    AppCompatFlagsUser: ULARGE_INTEGER,
3360    ShimData: PVOID,
3361    // Versions: 5.0+
3362    AppCompatInfo: PVOID,
3363    CSDVersion: UNICODE_STRING,
3364
3365    // Fields appended in 5.1 (Windows XP):
3366    ActivationContextData: *const ACTIVATION_CONTEXT_DATA,
3367    ProcessAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
3368    SystemDefaultActivationData: *const ACTIVATION_CONTEXT_DATA,
3369    SystemAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
3370    MinimumStackCommit: ULONG_PTR,
3371
3372    // Fields appended in 5.2 (Windows Server 2003):
3373    FlsCallback: *FLS_CALLBACK_INFO,
3374    FlsListHead: LIST_ENTRY,
3375    FlsBitmap: *RTL_BITMAP,
3376    FlsBitmapBits: [4]ULONG,
3377    FlsHighIndex: ULONG,
3378
3379    // Fields appended in 6.0 (Windows Vista):
3380    WerRegistrationData: PVOID,
3381    WerShipAssertPtr: PVOID,
3382
3383    // Fields appended in 6.1 (Windows 7):
3384    pUnused: PVOID, // previously pContextData
3385    pImageHeaderHash: PVOID,
3386
3387    /// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/tracingflags.htm
3388    TracingFlags: ULONG,
3389
3390    // Fields appended in 6.2 (Windows 8):
3391    CsrServerReadOnlySharedMemoryBase: ULONGLONG,
3392
3393    // Fields appended in 1511:
3394    TppWorkerpListLock: ULONG,
3395    TppWorkerpList: LIST_ENTRY,
3396    WaitOnAddressHashTable: [0x80]PVOID,
3397
3398    // Fields appended in 1709:
3399    TelemetryCoverageHeader: PVOID,
3400    CloudFileFlags: ULONG,
3401};
3402
3403/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
3404/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module.
3405///
3406/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
3407///  - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
3408pub const PEB_LDR_DATA = extern struct {
3409    // Versions: 3.51 and higher
3410    /// The size in bytes of the structure
3411    Length: ULONG,
3412
3413    /// TRUE if the structure is prepared.
3414    Initialized: BOOLEAN,
3415
3416    SsHandle: PVOID,
3417    InLoadOrderModuleList: LIST_ENTRY,
3418    InMemoryOrderModuleList: LIST_ENTRY,
3419    InInitializationOrderModuleList: LIST_ENTRY,
3420
3421    // Versions: 5.1 and higher
3422
3423    /// No known use of this field is known in Windows 8 and higher.
3424    EntryInProgress: PVOID,
3425
3426    // Versions: 6.0 from Windows Vista SP1, and higher
3427    ShutdownInProgress: BOOLEAN,
3428
3429    /// Though ShutdownThreadId is declared as a HANDLE,
3430    /// it is indeed the thread ID as suggested by its name.
3431    /// It is picked up from the UniqueThread member of the CLIENT_ID in the
3432    /// TEB of the thread that asks to terminate the process.
3433    ShutdownThreadId: HANDLE,
3434};
3435
3436pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
3437    AllocationSize: ULONG,
3438    Size: ULONG,
3439    Flags: ULONG,
3440    DebugFlags: ULONG,
3441    ConsoleHandle: HANDLE,
3442    ConsoleFlags: ULONG,
3443    hStdInput: HANDLE,
3444    hStdOutput: HANDLE,
3445    hStdError: HANDLE,
3446    CurrentDirectory: CURDIR,
3447    DllPath: UNICODE_STRING,
3448    ImagePathName: UNICODE_STRING,
3449    CommandLine: UNICODE_STRING,
3450    Environment: [*:0]WCHAR,
3451    dwX: ULONG,
3452    dwY: ULONG,
3453    dwXSize: ULONG,
3454    dwYSize: ULONG,
3455    dwXCountChars: ULONG,
3456    dwYCountChars: ULONG,
3457    dwFillAttribute: ULONG,
3458    dwFlags: ULONG,
3459    dwShowWindow: ULONG,
3460    WindowTitle: UNICODE_STRING,
3461    Desktop: UNICODE_STRING,
3462    ShellInfo: UNICODE_STRING,
3463    RuntimeInfo: UNICODE_STRING,
3464    DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
3465};
3466
3467pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
3468    Flags: c_ushort,
3469    Length: c_ushort,
3470    TimeStamp: ULONG,
3471    DosPath: UNICODE_STRING,
3472};
3473
3474pub const PPS_POST_PROCESS_INIT_ROUTINE = ?fn () callconv(.C) void;
3475
3476pub const FILE_BOTH_DIR_INFORMATION = extern struct {
3477    NextEntryOffset: ULONG,
3478    FileIndex: ULONG,
3479    CreationTime: LARGE_INTEGER,
3480    LastAccessTime: LARGE_INTEGER,
3481    LastWriteTime: LARGE_INTEGER,
3482    ChangeTime: LARGE_INTEGER,
3483    EndOfFile: LARGE_INTEGER,
3484    AllocationSize: LARGE_INTEGER,
3485    FileAttributes: ULONG,
3486    FileNameLength: ULONG,
3487    EaSize: ULONG,
3488    ShortNameLength: CHAR,
3489    ShortName: [12]WCHAR,
3490    FileName: [1]WCHAR,
3491};
3492pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
3493
3494pub const IO_APC_ROUTINE = fn (PVOID, *IO_STATUS_BLOCK, ULONG) callconv(.C) void;
3495
3496pub const CURDIR = extern struct {
3497    DosPath: UNICODE_STRING,
3498    Handle: HANDLE,
3499};
3500
3501pub const DUPLICATE_SAME_ACCESS = 2;
3502
3503pub const MODULEINFO = extern struct {
3504    lpBaseOfDll: LPVOID,
3505    SizeOfImage: DWORD,
3506    EntryPoint: LPVOID,
3507};
3508
3509pub const PSAPI_WS_WATCH_INFORMATION = extern struct {
3510    FaultingPc: LPVOID,
3511    FaultingVa: LPVOID,
3512};
3513
3514pub const PROCESS_MEMORY_COUNTERS = extern struct {
3515    cb: DWORD,
3516    PageFaultCount: DWORD,
3517    PeakWorkingSetSize: SIZE_T,
3518    WorkingSetSize: SIZE_T,
3519    QuotaPeakPagedPoolUsage: SIZE_T,
3520    QuotaPagedPoolUsage: SIZE_T,
3521    QuotaPeakNonPagedPoolUsage: SIZE_T,
3522    QuotaNonPagedPoolUsage: SIZE_T,
3523    PagefileUsage: SIZE_T,
3524    PeakPagefileUsage: SIZE_T,
3525};
3526
3527pub const PROCESS_MEMORY_COUNTERS_EX = extern struct {
3528    cb: DWORD,
3529    PageFaultCount: DWORD,
3530    PeakWorkingSetSize: SIZE_T,
3531    WorkingSetSize: SIZE_T,
3532    QuotaPeakPagedPoolUsage: SIZE_T,
3533    QuotaPagedPoolUsage: SIZE_T,
3534    QuotaPeakNonPagedPoolUsage: SIZE_T,
3535    QuotaNonPagedPoolUsage: SIZE_T,
3536    PagefileUsage: SIZE_T,
3537    PeakPagefileUsage: SIZE_T,
3538    PrivateUsage: SIZE_T,
3539};
3540
3541pub const PERFORMANCE_INFORMATION = extern struct {
3542    cb: DWORD,
3543    CommitTotal: SIZE_T,
3544    CommitLimit: SIZE_T,
3545    CommitPeak: SIZE_T,
3546    PhysicalTotal: SIZE_T,
3547    PhysicalAvailable: SIZE_T,
3548    SystemCache: SIZE_T,
3549    KernelTotal: SIZE_T,
3550    KernelPaged: SIZE_T,
3551    KernelNonpaged: SIZE_T,
3552    PageSize: SIZE_T,
3553    HandleCount: DWORD,
3554    ProcessCount: DWORD,
3555    ThreadCount: DWORD,
3556};
3557
3558pub const ENUM_PAGE_FILE_INFORMATION = extern struct {
3559    cb: DWORD,
3560    Reserved: DWORD,
3561    TotalSize: SIZE_T,
3562    TotalInUse: SIZE_T,
3563    PeakUsage: SIZE_T,
3564};
3565
3566pub const PENUM_PAGE_FILE_CALLBACKW = ?fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.C) BOOL;
3567pub const PENUM_PAGE_FILE_CALLBACKA = ?fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.C) BOOL;
3568
3569pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct {
3570    BasicInfo: PSAPI_WS_WATCH_INFORMATION,
3571    FaultingThreadId: ULONG_PTR,
3572    Flags: ULONG_PTR,
3573};
3574
3575pub const OSVERSIONINFOW = extern struct {
3576    dwOSVersionInfoSize: ULONG,
3577    dwMajorVersion: ULONG,
3578    dwMinorVersion: ULONG,
3579    dwBuildNumber: ULONG,
3580    dwPlatformId: ULONG,
3581    szCSDVersion: [128]WCHAR,
3582};
3583pub const RTL_OSVERSIONINFOW = OSVERSIONINFOW;
3584
3585pub const REPARSE_DATA_BUFFER = extern struct {
3586    ReparseTag: ULONG,
3587    ReparseDataLength: USHORT,
3588    Reserved: USHORT,
3589    DataBuffer: [1]UCHAR,
3590};
3591pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct {
3592    SubstituteNameOffset: USHORT,
3593    SubstituteNameLength: USHORT,
3594    PrintNameOffset: USHORT,
3595    PrintNameLength: USHORT,
3596    Flags: ULONG,
3597    PathBuffer: [1]WCHAR,
3598};
3599pub const MOUNT_POINT_REPARSE_BUFFER = extern struct {
3600    SubstituteNameOffset: USHORT,
3601    SubstituteNameLength: USHORT,
3602    PrintNameOffset: USHORT,
3603    PrintNameLength: USHORT,
3604    PathBuffer: [1]WCHAR,
3605};
3606pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024;
3607pub const FSCTL_SET_REPARSE_POINT: DWORD = 0x900a4;
3608pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8;
3609pub const IO_REPARSE_TAG_SYMLINK: ULONG = 0xa000000c;
3610pub const IO_REPARSE_TAG_MOUNT_POINT: ULONG = 0xa0000003;
3611pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;
3612
3613pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
3614pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;
3615
3616pub const MOUNTMGR_MOUNT_POINT = extern struct {
3617    SymbolicLinkNameOffset: ULONG,
3618    SymbolicLinkNameLength: USHORT,
3619    Reserved1: USHORT,
3620    UniqueIdOffset: ULONG,
3621    UniqueIdLength: USHORT,
3622    Reserved2: USHORT,
3623    DeviceNameOffset: ULONG,
3624    DeviceNameLength: USHORT,
3625    Reserved3: USHORT,
3626};
3627pub const MOUNTMGR_MOUNT_POINTS = extern struct {
3628    Size: ULONG,
3629    NumberOfMountPoints: ULONG,
3630    MountPoints: [1]MOUNTMGR_MOUNT_POINT,
3631};
3632pub const IOCTL_MOUNTMGR_QUERY_POINTS: ULONG = 0x6d0008;
3633
3634pub const OBJECT_INFORMATION_CLASS = enum(c_int) {
3635    ObjectBasicInformation = 0,
3636    ObjectNameInformation = 1,
3637    ObjectTypeInformation = 2,
3638    ObjectTypesInformation = 3,
3639    ObjectHandleFlagInformation = 4,
3640    ObjectSessionInformation = 5,
3641    MaxObjectInfoClass,
3642};
3643
3644pub const OBJECT_NAME_INFORMATION = extern struct {
3645    Name: UNICODE_STRING,
3646};
3647
3648pub const SRWLOCK = usize;
3649pub const SRWLOCK_INIT: SRWLOCK = 0;
3650pub const CONDITION_VARIABLE = usize;
3651pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = 0;
3652
3653pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1;
3654pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2;
3655
3656pub const CTRL_C_EVENT: DWORD = 0;
3657pub const CTRL_BREAK_EVENT: DWORD = 1;
3658pub const CTRL_CLOSE_EVENT: DWORD = 2;
3659pub const CTRL_LOGOFF_EVENT: DWORD = 5;
3660pub const CTRL_SHUTDOWN_EVENT: DWORD = 6;
3661
3662pub const HANDLER_ROUTINE = fn (dwCtrlType: DWORD) callconv(.C) BOOL;
3663