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