2019-05-24 16:36:09 -07:00
|
|
|
// This file contains thin wrappers around Windows-specific APIs, with these
|
|
|
|
// specific goals in mind:
|
|
|
|
// * Convert "errno"-style error codes into Zig errors.
|
|
|
|
// * When null-terminated or UTF16LE byte buffers are required, provide APIs which accept
|
|
|
|
// slices as well as APIs which accept null-terminated UTF16LE byte buffers.
|
|
|
|
|
2019-05-26 10:17:34 -07:00
|
|
|
const builtin = @import("builtin");
|
2019-03-02 13:46:04 -08:00
|
|
|
const std = @import("../std.zig");
|
2019-05-26 21:48:56 -07:00
|
|
|
const mem = std.mem;
|
2018-07-16 21:01:36 -07:00
|
|
|
const assert = std.debug.assert;
|
2019-05-26 21:48:56 -07:00
|
|
|
const math = std.math;
|
2018-10-26 11:59:58 -07:00
|
|
|
const maxInt = std.math.maxInt;
|
2018-07-20 20:37:37 -07:00
|
|
|
|
2019-05-23 21:13:13 -07:00
|
|
|
pub const is_the_target = builtin.os == .windows;
|
|
|
|
pub const advapi32 = @import("windows/advapi32.zig");
|
|
|
|
pub const kernel32 = @import("windows/kernel32.zig");
|
|
|
|
pub const ntdll = @import("windows/ntdll.zig");
|
|
|
|
pub const ole32 = @import("windows/ole32.zig");
|
|
|
|
pub const shell32 = @import("windows/shell32.zig");
|
2018-06-11 22:55:08 -07:00
|
|
|
|
2019-05-29 15:55:42 -07:00
|
|
|
pub usingnamespace @import("windows/bits.zig");
|
2019-05-23 21:13:13 -07:00
|
|
|
|
2019-07-05 11:08:56 -07:00
|
|
|
/// `builtin` is missing `subsystem` when the subsystem is automatically detected,
|
|
|
|
/// so Zig standard library has the subsystem detection logic here. This should generally be
|
|
|
|
/// used rather than `builtin.subsystem`.
|
|
|
|
/// On non-windows targets, this is `null`.
|
|
|
|
pub const subsystem: ?builtin.SubSystem = blk: {
|
|
|
|
if (@hasDecl(builtin, "subsystem")) break :blk builtin.subsystem;
|
|
|
|
switch (builtin.os) {
|
|
|
|
.windows => {
|
|
|
|
if (builtin.is_test) {
|
|
|
|
break :blk builtin.SubSystem.Console;
|
|
|
|
}
|
|
|
|
const root = @import("root");
|
|
|
|
if (@hasDecl(root, "WinMain") or
|
|
|
|
@hasDecl(root, "wWinMain") or
|
|
|
|
@hasDecl(root, "WinMainCRTStartup") or
|
|
|
|
@hasDecl(root, "wWinMainCRTStartup"))
|
|
|
|
{
|
|
|
|
break :blk builtin.SubSystem.Windows;
|
|
|
|
} else {
|
|
|
|
break :blk builtin.SubSystem.Console;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
.uefi => break :blk builtin.SubSystem.EfiApplication,
|
|
|
|
else => break :blk null,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-05-24 15:27:18 -07:00
|
|
|
pub const CreateFileError = error{
|
2019-05-23 21:13:13 -07:00
|
|
|
SharingViolation,
|
|
|
|
PathAlreadyExists,
|
|
|
|
|
|
|
|
/// When any of the path components can not be found or the file component can not
|
|
|
|
/// be found. Some operating systems distinguish between path components not found and
|
|
|
|
/// file components not found, but they are collapsed into FileNotFound to gain
|
|
|
|
/// consistency across operating systems.
|
|
|
|
FileNotFound,
|
|
|
|
|
|
|
|
AccessDenied,
|
|
|
|
PipeBusy,
|
|
|
|
NameTooLong,
|
|
|
|
|
|
|
|
/// On Windows, file paths must be valid Unicode.
|
|
|
|
InvalidUtf8,
|
|
|
|
|
|
|
|
/// On Windows, file paths cannot contain these characters:
|
|
|
|
/// '/', '*', '?', '"', '<', '>', '|'
|
|
|
|
BadPathName,
|
|
|
|
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn CreateFile(
|
|
|
|
file_path: []const u8,
|
|
|
|
desired_access: DWORD,
|
|
|
|
share_mode: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
2019-05-23 21:13:13 -07:00
|
|
|
creation_disposition: DWORD,
|
|
|
|
flags_and_attrs: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
hTemplateFile: ?HANDLE,
|
2019-05-24 16:36:09 -07:00
|
|
|
) CreateFileError!HANDLE {
|
2019-05-23 21:13:13 -07:00
|
|
|
const file_path_w = try sliceToPrefixedFileW(file_path);
|
2019-05-27 12:34:46 -07:00
|
|
|
return CreateFileW(&file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
|
2019-05-23 21:13:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CreateFileW(
|
|
|
|
file_path_w: [*]const u16,
|
|
|
|
desired_access: DWORD,
|
|
|
|
share_mode: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
2019-05-23 21:13:13 -07:00
|
|
|
creation_disposition: DWORD,
|
|
|
|
flags_and_attrs: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
hTemplateFile: ?HANDLE,
|
2019-05-24 15:27:18 -07:00
|
|
|
) CreateFileError!HANDLE {
|
2019-05-27 12:34:46 -07:00
|
|
|
const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
|
2019-05-23 21:13:13 -07:00
|
|
|
|
|
|
|
if (result == INVALID_HANDLE_VALUE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.SHARING_VIOLATION => return error.SharingViolation,
|
|
|
|
ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,
|
|
|
|
ERROR.FILE_EXISTS => return error.PathAlreadyExists,
|
|
|
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.ACCESS_DENIED => return error.AccessDenied,
|
|
|
|
ERROR.PIPE_BUSY => return error.PipeBusy,
|
2019-05-24 15:27:18 -07:00
|
|
|
ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
2019-05-25 10:07:44 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
2019-05-23 21:13:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const CreatePipeError = error{Unexpected};
|
|
|
|
|
2019-05-26 23:00:39 -07:00
|
|
|
pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
|
2019-05-23 21:13:13 -07:00
|
|
|
if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const SetHandleInformationError = error{Unexpected};
|
|
|
|
|
2019-05-26 23:00:39 -07:00
|
|
|
pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void {
|
|
|
|
if (kernel32.SetHandleInformation(h, mask, flags) == 0) {
|
2019-05-23 21:13:13 -07:00
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-24 15:27:18 -07:00
|
|
|
pub const RtlGenRandomError = error{Unexpected};
|
|
|
|
|
|
|
|
/// Call RtlGenRandom() instead of CryptGetRandom() on Windows
|
|
|
|
/// https://github.com/rust-lang-nursery/rand/issues/111
|
|
|
|
/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
|
|
|
|
pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void {
|
|
|
|
if (advapi32.RtlGenRandom(output.ptr, output.len) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const WaitForSingleObjectError = error{
|
|
|
|
WaitAbandoned,
|
|
|
|
WaitTimeOut,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void {
|
|
|
|
switch (kernel32.WaitForSingleObject(handle, milliseconds)) {
|
|
|
|
WAIT_ABANDONED => return error.WaitAbandoned,
|
|
|
|
WAIT_OBJECT_0 => return,
|
|
|
|
WAIT_TIMEOUT => return error.WaitTimeOut,
|
|
|
|
WAIT_FAILED => switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
},
|
|
|
|
else => return error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const FindFirstFileError = error{
|
|
|
|
FileNotFound,
|
2019-05-26 22:35:58 -07:00
|
|
|
InvalidUtf8,
|
|
|
|
BadPathName,
|
|
|
|
NameTooLong,
|
2019-05-24 15:27:18 -07:00
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn FindFirstFile(dir_path: []const u8, find_file_data: *WIN32_FIND_DATAW) FindFirstFileError!HANDLE {
|
2019-06-09 16:24:24 -07:00
|
|
|
const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, [_]u16{ '\\', '*', 0 });
|
2019-05-24 15:27:18 -07:00
|
|
|
const handle = kernel32.FindFirstFileW(&dir_path_w, find_file_data);
|
|
|
|
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const FindNextFileError = error{Unexpected};
|
|
|
|
|
|
|
|
/// Returns `true` if there was another file, `false` otherwise.
|
|
|
|
pub fn FindNextFile(handle: HANDLE, find_file_data: *WIN32_FIND_DATAW) FindNextFileError!bool {
|
|
|
|
if (kernel32.FindNextFileW(handle, find_file_data) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.NO_MORE_FILES => return false,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const CreateIoCompletionPortError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn CreateIoCompletionPort(
|
|
|
|
file_handle: HANDLE,
|
|
|
|
existing_completion_port: ?HANDLE,
|
|
|
|
completion_key: usize,
|
|
|
|
concurrent_thread_count: DWORD,
|
|
|
|
) CreateIoCompletionPortError!HANDLE {
|
|
|
|
const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.INVALID_PARAMETER => unreachable,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const PostQueuedCompletionStatusError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn PostQueuedCompletionStatus(
|
|
|
|
completion_port: HANDLE,
|
|
|
|
bytes_transferred_count: DWORD,
|
|
|
|
completion_key: usize,
|
|
|
|
lpOverlapped: ?*OVERLAPPED,
|
|
|
|
) PostQueuedCompletionStatusError!void {
|
|
|
|
if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2019-05-26 22:35:58 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetQueuedCompletionStatusResult = enum {
|
|
|
|
Normal,
|
|
|
|
Aborted,
|
|
|
|
Cancelled,
|
|
|
|
EOF,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn GetQueuedCompletionStatus(
|
|
|
|
completion_port: HANDLE,
|
|
|
|
bytes_transferred_count: *DWORD,
|
|
|
|
lpCompletionKey: *usize,
|
|
|
|
lpOverlapped: *?*OVERLAPPED,
|
|
|
|
dwMilliseconds: DWORD,
|
|
|
|
) GetQueuedCompletionStatusResult {
|
|
|
|
if (kernel32.GetQueuedCompletionStatus(
|
|
|
|
completion_port,
|
|
|
|
bytes_transferred_count,
|
|
|
|
lpCompletionKey,
|
|
|
|
lpOverlapped,
|
|
|
|
dwMilliseconds,
|
|
|
|
) == FALSE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
|
|
|
|
ERROR.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled,
|
|
|
|
ERROR.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
|
|
|
|
else => |err| {
|
|
|
|
if (std.debug.runtime_safety) {
|
|
|
|
std.debug.panic("unexpected error: {}\n", err);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return GetQueuedCompletionStatusResult.Normal;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CloseHandle(hObject: HANDLE) void {
|
|
|
|
assert(kernel32.CloseHandle(hObject) != 0);
|
|
|
|
}
|
|
|
|
|
2019-05-24 19:52:07 -07:00
|
|
|
pub fn FindClose(hFindFile: HANDLE) void {
|
|
|
|
assert(kernel32.FindClose(hFindFile) != 0);
|
|
|
|
}
|
|
|
|
|
2019-05-24 15:27:18 -07:00
|
|
|
pub const ReadFileError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn ReadFile(in_hFile: HANDLE, buffer: []u8) ReadFileError!usize {
|
|
|
|
var index: usize = 0;
|
|
|
|
while (index < buffer.len) {
|
|
|
|
const want_read_count = @intCast(DWORD, math.min(DWORD(maxInt(DWORD)), buffer.len - index));
|
|
|
|
var amt_read: DWORD = undefined;
|
2019-05-26 21:48:56 -07:00
|
|
|
if (kernel32.ReadFile(in_hFile, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
|
2019-05-24 15:27:18 -07:00
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.OPERATION_ABORTED => continue,
|
|
|
|
ERROR.BROKEN_PIPE => return index,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (amt_read == 0) return index;
|
|
|
|
index += amt_read;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const WriteFileError = error{
|
|
|
|
SystemResources,
|
|
|
|
OperationAborted,
|
|
|
|
BrokenPipe,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// This function is for blocking file descriptors only. For non-blocking, see
|
|
|
|
/// `WriteFileAsync`.
|
2019-05-26 21:48:56 -07:00
|
|
|
pub fn WriteFile(handle: HANDLE, bytes: []const u8) WriteFileError!void {
|
2019-05-24 15:27:18 -07:00
|
|
|
var bytes_written: DWORD = undefined;
|
|
|
|
// TODO replace this @intCast with a loop that writes all the bytes
|
|
|
|
if (kernel32.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.INVALID_USER_BUFFER => return error.SystemResources,
|
|
|
|
ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
|
|
|
|
ERROR.OPERATION_ABORTED => return error.OperationAborted,
|
|
|
|
ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources,
|
|
|
|
ERROR.IO_PENDING => unreachable, // this function is for blocking files only
|
|
|
|
ERROR.BROKEN_PIPE => return error.BrokenPipe,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetCurrentDirectoryError = error{
|
|
|
|
NameTooLong,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2019-05-26 21:48:56 -07:00
|
|
|
/// The result is a slice of `buffer`, indexed from 0.
|
2019-05-24 15:27:18 -07:00
|
|
|
pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
|
|
|
|
var utf16le_buf: [PATH_MAX_WIDE]u16 = undefined;
|
|
|
|
const result = kernel32.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf);
|
|
|
|
if (result == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(result <= utf16le_buf.len);
|
|
|
|
const utf16le_slice = utf16le_buf[0..result];
|
|
|
|
// Trust that Windows gives us valid UTF-16LE.
|
|
|
|
var end_index: usize = 0;
|
2019-05-26 21:48:56 -07:00
|
|
|
var it = std.unicode.Utf16LeIterator.init(utf16le_slice);
|
2019-05-24 15:27:18 -07:00
|
|
|
while (it.nextCodepoint() catch unreachable) |codepoint| {
|
2019-05-26 21:48:56 -07:00
|
|
|
const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
|
|
|
|
if (end_index + seq_len >= buffer.len)
|
2019-05-24 15:27:18 -07:00
|
|
|
return error.NameTooLong;
|
2019-05-26 21:48:56 -07:00
|
|
|
end_index += std.unicode.utf8Encode(codepoint, buffer[end_index..]) catch unreachable;
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
2019-05-26 21:48:56 -07:00
|
|
|
return buffer[0..end_index];
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub const CreateSymbolicLinkError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn CreateSymbolicLink(
|
|
|
|
sym_link_path: []const u8,
|
|
|
|
target_path: []const u8,
|
|
|
|
flags: DWORD,
|
|
|
|
) CreateSymbolicLinkError!void {
|
|
|
|
const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path);
|
|
|
|
const target_path_w = try sliceToPrefixedFileW(target_path);
|
|
|
|
return CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CreateSymbolicLinkW(
|
|
|
|
sym_link_path: [*]const u16,
|
|
|
|
target_path: [*]const u16,
|
|
|
|
flags: DWORD,
|
|
|
|
) CreateSymbolicLinkError!void {
|
|
|
|
if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2019-05-26 23:00:39 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const DeleteFileError = error{
|
|
|
|
FileNotFound,
|
|
|
|
AccessDenied,
|
|
|
|
NameTooLong,
|
2019-06-26 11:32:19 -07:00
|
|
|
FileBusy,
|
2019-05-24 15:27:18 -07:00
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn DeleteFile(filename: []const u8) DeleteFileError!void {
|
|
|
|
const filename_w = try sliceToPrefixedFileW(filename);
|
|
|
|
return DeleteFileW(&filename_w);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn DeleteFileW(filename: [*]const u16) DeleteFileError!void {
|
2019-05-26 22:35:58 -07:00
|
|
|
if (kernel32.DeleteFileW(filename) == 0) {
|
2019-05-24 15:27:18 -07:00
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
2019-06-30 12:31:45 -07:00
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
2019-05-24 15:27:18 -07:00
|
|
|
ERROR.ACCESS_DENIED => return error.AccessDenied,
|
|
|
|
ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
|
|
|
ERROR.INVALID_PARAMETER => return error.NameTooLong,
|
2019-06-26 11:32:19 -07:00
|
|
|
ERROR.SHARING_VIOLATION => return error.FileBusy,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const MoveFileError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
|
|
|
|
const old_path_w = try sliceToPrefixedFileW(old_path);
|
|
|
|
const new_path_w = try sliceToPrefixedFileW(new_path);
|
|
|
|
return MoveFileExW(&old_path_w, &new_path_w, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn MoveFileExW(old_path: [*]const u16, new_path: [*]const u16, flags: DWORD) MoveFileError!void {
|
|
|
|
if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const CreateDirectoryError = error{
|
|
|
|
PathAlreadyExists,
|
|
|
|
FileNotFound,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn CreateDirectory(pathname: []const u8, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
|
|
|
|
const pathname_w = try sliceToPrefixedFileW(pathname);
|
|
|
|
return CreateDirectoryW(&pathname_w, attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CreateDirectoryW(pathname: [*]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
|
|
|
|
if (kernel32.CreateDirectoryW(pathname, attrs) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const RemoveDirectoryError = error{
|
|
|
|
FileNotFound,
|
|
|
|
DirNotEmpty,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void {
|
|
|
|
const dir_path_w = try sliceToPrefixedFileW(dir_path);
|
|
|
|
return RemoveDirectoryW(&dir_path_w);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn RemoveDirectoryW(dir_path_w: [*]const u16) RemoveDirectoryError!void {
|
|
|
|
if (kernel32.RemoveDirectoryW(dir_path_w) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetStdHandleError = error{
|
|
|
|
NoStandardHandleAttached,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2019-05-24 16:36:09 -07:00
|
|
|
pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {
|
2019-05-24 15:27:18 -07:00
|
|
|
const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const SetFilePointerError = error{Unexpected};
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
|
|
|
|
pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
|
|
|
|
// "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
|
|
|
|
// is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
|
|
|
|
const ipos = @bitCast(LARGE_INTEGER, offset);
|
|
|
|
if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.INVALID_PARAMETER => unreachable,
|
|
|
|
ERROR.INVALID_HANDLE => unreachable,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
|
|
|
|
pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
|
|
|
|
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.INVALID_PARAMETER => unreachable,
|
|
|
|
ERROR.INVALID_HANDLE => unreachable,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
|
|
|
|
pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
|
|
|
|
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.INVALID_PARAMETER => unreachable,
|
|
|
|
ERROR.INVALID_HANDLE => unreachable,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with parameters to get the current offset.
|
|
|
|
pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
|
|
|
|
var result: LARGE_INTEGER = undefined;
|
|
|
|
if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.INVALID_PARAMETER => unreachable,
|
|
|
|
ERROR.INVALID_HANDLE => unreachable,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Based on the docs for FILE_BEGIN, it seems that the returned signed integer
|
|
|
|
// should be interpreted as an unsigned integer.
|
|
|
|
return @bitCast(u64, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetFinalPathNameByHandleError = error{
|
|
|
|
FileNotFound,
|
|
|
|
SystemResources,
|
|
|
|
NameTooLong,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn GetFinalPathNameByHandleW(
|
|
|
|
hFile: HANDLE,
|
|
|
|
buf_ptr: [*]u16,
|
|
|
|
buf_len: DWORD,
|
|
|
|
flags: DWORD,
|
|
|
|
) GetFinalPathNameByHandleError!DWORD {
|
2019-05-26 22:35:58 -07:00
|
|
|
const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags);
|
2019-05-24 15:27:18 -07:00
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
|
|
|
|
ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
|
|
|
ERROR.INVALID_PARAMETER => unreachable,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetFileSizeError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 {
|
|
|
|
var file_size: LARGE_INTEGER = undefined;
|
|
|
|
if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return @bitCast(u64, file_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetFileAttributesError = error{
|
|
|
|
FileNotFound,
|
|
|
|
PermissionDenied,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD {
|
|
|
|
const filename_w = try sliceToPrefixedFileW(filename);
|
|
|
|
return GetFileAttributesW(&filename_w);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn GetFileAttributesW(lpFileName: [*]const u16) GetFileAttributesError!DWORD {
|
2019-05-26 22:35:58 -07:00
|
|
|
const rc = kernel32.GetFileAttributesW(lpFileName);
|
2019-05-24 15:27:18 -07:00
|
|
|
if (rc == INVALID_FILE_ATTRIBUTES) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.ACCESS_DENIED => return error.PermissionDenied,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-05-24 19:52:07 -07:00
|
|
|
const GetModuleFileNameError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![]u16 {
|
|
|
|
const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
|
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf_ptr[0..rc];
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const TerminateProcessError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void {
|
|
|
|
if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-25 10:07:44 -07:00
|
|
|
pub const VirtualAllocError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn VirtualAlloc(addr: ?LPVOID, size: usize, alloc_type: DWORD, flProtect: DWORD) VirtualAllocError!LPVOID {
|
|
|
|
return kernel32.VirtualAlloc(addr, size, alloc_type, flProtect) orelse {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn VirtualFree(lpAddress: ?LPVOID, dwSize: usize, dwFreeType: DWORD) void {
|
|
|
|
assert(kernel32.VirtualFree(lpAddress, dwSize, dwFreeType) != 0);
|
|
|
|
}
|
|
|
|
|
2019-05-26 10:17:34 -07:00
|
|
|
pub const SetConsoleTextAttributeError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
|
|
|
|
if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetEnvironmentStringsError = error{OutOfMemory};
|
|
|
|
|
|
|
|
pub fn GetEnvironmentStringsW() GetEnvironmentStringsError![*]u16 {
|
|
|
|
return kernel32.GetEnvironmentStringsW() orelse return error.OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn FreeEnvironmentStringsW(penv: [*]u16) void {
|
|
|
|
assert(kernel32.FreeEnvironmentStringsW(penv) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetEnvironmentVariableError = error{
|
|
|
|
EnvironmentVariableNotFound,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) GetEnvironmentVariableError!DWORD {
|
|
|
|
const rc = kernel32.GetEnvironmentVariableW(lpName, lpBuffer, nSize);
|
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.ENVVAR_NOT_FOUND => return error.EnvironmentVariableNotFound,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-05-26 10:37:34 -07:00
|
|
|
pub const CreateProcessError = error{
|
|
|
|
FileNotFound,
|
2019-06-18 00:40:37 -07:00
|
|
|
AccessDenied,
|
2019-05-26 10:37:34 -07:00
|
|
|
InvalidName,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn CreateProcessW(
|
|
|
|
lpApplicationName: ?LPWSTR,
|
|
|
|
lpCommandLine: LPWSTR,
|
|
|
|
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
|
|
|
|
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
|
|
|
|
bInheritHandles: BOOL,
|
|
|
|
dwCreationFlags: DWORD,
|
|
|
|
lpEnvironment: ?*c_void,
|
|
|
|
lpCurrentDirectory: ?LPWSTR,
|
|
|
|
lpStartupInfo: *STARTUPINFOW,
|
|
|
|
lpProcessInformation: *PROCESS_INFORMATION,
|
|
|
|
) CreateProcessError!void {
|
|
|
|
if (kernel32.CreateProcessW(
|
|
|
|
lpApplicationName,
|
|
|
|
lpCommandLine,
|
|
|
|
lpProcessAttributes,
|
|
|
|
lpThreadAttributes,
|
2019-05-26 23:00:39 -07:00
|
|
|
bInheritHandles,
|
2019-05-26 10:37:34 -07:00
|
|
|
dwCreationFlags,
|
|
|
|
lpEnvironment,
|
|
|
|
lpCurrentDirectory,
|
|
|
|
lpStartupInfo,
|
|
|
|
lpProcessInformation,
|
|
|
|
) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
2019-06-18 00:40:37 -07:00
|
|
|
ERROR.ACCESS_DENIED => return error.AccessDenied,
|
2019-05-26 10:37:34 -07:00
|
|
|
ERROR.INVALID_PARAMETER => unreachable,
|
|
|
|
ERROR.INVALID_NAME => return error.InvalidName,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 20:35:26 -07:00
|
|
|
pub const LoadLibraryError = error{
|
|
|
|
FileNotFound,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn LoadLibraryW(lpLibFileName: [*]const u16) LoadLibraryError!HMODULE {
|
|
|
|
return kernel32.LoadLibraryW(lpLibFileName) orelse {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
ERROR.MOD_NOT_FOUND => return error.FileNotFound,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn FreeLibrary(hModule: HMODULE) void {
|
|
|
|
assert(kernel32.FreeLibrary(hModule) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn QueryPerformanceFrequency() u64 {
|
|
|
|
// "On systems that run Windows XP or later, the function will always succeed"
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
|
|
|
|
var result: LARGE_INTEGER = undefined;
|
|
|
|
assert(kernel32.QueryPerformanceFrequency(&result) != 0);
|
|
|
|
// The kernel treats this integer as unsigned.
|
|
|
|
return @bitCast(u64, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn QueryPerformanceCounter() u64 {
|
|
|
|
// "On systems that run Windows XP or later, the function will always succeed"
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
|
|
|
|
var result: LARGE_INTEGER = undefined;
|
|
|
|
assert(kernel32.QueryPerformanceCounter(&result) != 0);
|
|
|
|
// The kernel treats this integer as unsigned.
|
|
|
|
return @bitCast(u64, result);
|
|
|
|
}
|
|
|
|
|
2019-05-26 21:48:56 -07:00
|
|
|
pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) void {
|
|
|
|
assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0);
|
|
|
|
}
|
|
|
|
|
2019-05-26 22:35:58 -07:00
|
|
|
pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) void {
|
|
|
|
assert(kernel32.HeapFree(hHeap, dwFlags, lpMem) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn HeapDestroy(hHeap: HANDLE) void {
|
|
|
|
assert(kernel32.HeapDestroy(hHeap) != 0);
|
|
|
|
}
|
|
|
|
|
2019-05-23 21:13:13 -07:00
|
|
|
pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
|
|
|
|
return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
|
2019-06-09 16:24:24 -07:00
|
|
|
return sliceToPrefixedSuffixedFileW(s, [_]u16{0});
|
2019-05-23 21:13:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
|
|
|
|
// TODO well defined copy elision
|
|
|
|
var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined;
|
|
|
|
|
|
|
|
// > File I/O functions in the Windows API convert "/" to "\" as part of
|
|
|
|
// > converting the name to an NT-style name, except when using the "\\?\"
|
|
|
|
// > prefix as detailed in the following sections.
|
|
|
|
// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
|
|
|
|
// Because we want the larger maximum path length for absolute paths, we
|
|
|
|
// disallow forward slashes in zig std lib file functions on Windows.
|
|
|
|
for (s) |byte| {
|
|
|
|
switch (byte) {
|
|
|
|
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
|
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
}
|
2019-05-26 10:17:34 -07:00
|
|
|
const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: {
|
2019-06-09 16:24:24 -07:00
|
|
|
const prefix = [_]u16{ '\\', '\\', '?', '\\' };
|
2019-05-23 21:13:13 -07:00
|
|
|
mem.copy(u16, result[0..], prefix);
|
|
|
|
break :blk prefix.len;
|
|
|
|
};
|
|
|
|
const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
|
|
|
|
assert(end_index <= result.len);
|
|
|
|
if (end_index + suffix.len > result.len) return error.NameTooLong;
|
|
|
|
mem.copy(u16, result[end_index..], suffix);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-06-05 13:30:01 -07:00
|
|
|
inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID {
|
2019-06-05 12:42:36 -07:00
|
|
|
return (s << 10) | p;
|
|
|
|
}
|
|
|
|
|
2019-05-23 21:13:13 -07:00
|
|
|
/// Call this when you made a windows DLL call or something that does SetLastError
|
|
|
|
/// and you get an unexpected error.
|
|
|
|
pub fn unexpectedError(err: DWORD) std.os.UnexpectedError {
|
|
|
|
if (std.os.unexpected_error_tracing) {
|
2019-06-05 15:18:56 -07:00
|
|
|
// 614 is the length of the longest windows error desciption
|
|
|
|
var buf_u16: [614]u16 = undefined;
|
|
|
|
var buf_u8: [614]u8 = undefined;
|
2019-06-09 16:24:24 -07:00
|
|
|
var len = kernel32.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, err, MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT), buf_u16[0..].ptr, buf_u16.len / @sizeOf(TCHAR), null);
|
2019-06-05 15:18:56 -07:00
|
|
|
_ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable;
|
|
|
|
std.debug.warn("error.Unexpected: GetLastError({}): {}\n", err, buf_u8[0..len]);
|
2019-05-23 21:13:13 -07:00
|
|
|
std.debug.dumpCurrentStackTrace(null);
|
|
|
|
}
|
|
|
|
return error.Unexpected;
|
|
|
|
}
|