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