switch most windows calls to use W versions instead of A

See #534
This commit is contained in:
Andrew Kelley 2018-09-02 23:25:04 -04:00
parent d5968086fe
commit 92f7474359
8 changed files with 224 additions and 200 deletions

View File

@ -581,8 +581,6 @@ set(ZIG_STD_FILES
"os/windows/ntdll.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
"os/windows/shlwapi.zig"
"os/windows/user32.zig"
"os/windows/util.zig"
"os/zen.zig"
"pdb.zig"

View File

@ -1,5 +1,6 @@
const std = @import("../index.zig");
const cstr = std.cstr;
const unicode = std.unicode;
const io = std.io;
const os = std.os;
const posix = os.posix;
@ -12,6 +13,7 @@ const Buffer = std.Buffer;
const builtin = @import("builtin");
const Os = builtin.Os;
const LinkedList = std.LinkedList;
const windows_util = @import("windows/util.zig");
const is_windows = builtin.os == Os.windows;
@ -520,8 +522,8 @@ pub const ChildProcess = struct {
const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
defer self.allocator.free(cmd_line);
var siStartInfo = windows.STARTUPINFOA{
.cb = @sizeOf(windows.STARTUPINFOA),
var siStartInfo = windows.STARTUPINFOW{
.cb = @sizeOf(windows.STARTUPINFOW),
.hStdError = g_hChildStd_ERR_Wr,
.hStdOutput = g_hChildStd_OUT_Wr,
.hStdInput = g_hChildStd_IN_Rd,
@ -545,7 +547,9 @@ pub const ChildProcess = struct {
const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;
defer if (cwd_slice) |cwd| self.allocator.free(cwd);
const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null;
defer if (cwd_w) |cwd| self.allocator.free(cwd);
const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;
defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
@ -564,7 +568,13 @@ pub const ChildProcess = struct {
};
defer self.allocator.free(app_name);
windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
defer self.allocator.free(app_name_w);
const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
defer self.allocator.free(cmd_line_w);
windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
if (no_path_err != error.FileNotFound) return no_path_err;
const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
@ -575,7 +585,10 @@ pub const ChildProcess = struct {
const joined_path = try os.path.join(self.allocator, search_path, app_name);
defer self.allocator.free(joined_path);
if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| {
const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
defer self.allocator.free(joined_path_w);
if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
break;
} else |err| if (err == error.FileNotFound) {
continue;
@ -626,15 +639,36 @@ pub const ChildProcess = struct {
}
};
fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) {
fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
// TODO the docs for environment pointer say:
// > A pointer to the environment block for the new process. If this parameter
// > is NULL, the new process uses the environment of the calling process.
// > ...
// > An environment block can contain either Unicode or ANSI characters. If
// > the environment block pointed to by lpEnvironment contains Unicode
// > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
// > If this parameter is NULL and the environment block of the parent process
// > contains Unicode characters, you must also ensure that dwCreationFlags
// > includes CREATE_UNICODE_ENVIRONMENT.
// This seems to imply that we have to somehow know whether our process parent passed
// CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
// Since we do not know this information that would imply that we must not pass NULL
// for the parameter.
// However this would imply that programs compiled with -DUNICODE could not pass
// environment variables to programs that were not, which seems unlikely.
// More investigation is needed.
if (windows.CreateProcessW(
app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT,
@ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation,
) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
switch (err) {
windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
windows.ERROR.INVALID_PARAMETER => unreachable,
windows.ERROR.INVALID_NAME => error.InvalidName,
else => os.unexpectedErrorWindows(err),
};
windows.ERROR.INVALID_NAME => return error.InvalidName,
else => return os.unexpectedErrorWindows(err),
}
}
}

View File

@ -819,37 +819,40 @@ test "os.getCwd" {
pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {
/// TODO add a symLinkC variant
pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void {
if (is_windows) {
return symLinkWindows(allocator, existing_path, new_path);
return symLinkWindows(existing_path, new_path);
} else {
return symLinkPosix(allocator, existing_path, new_path);
return symLinkPosix(existing_path, new_path);
}
}
pub const WindowsSymLinkError = error{
OutOfMemory,
NameTooLong,
InvalidUtf8,
BadPathName,
/// See https://github.com/ziglang/zig/issues/1396
Unexpected,
};
pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
const existing_with_null = try cstr.addNullByte(allocator, existing_path);
defer allocator.free(existing_with_null);
const new_with_null = try cstr.addNullByte(allocator, new_path);
defer allocator.free(new_with_null);
if (windows.CreateSymbolicLinkA(existing_with_null.ptr, new_with_null.ptr, 0) == 0) {
pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void {
if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) {
const err = windows.GetLastError();
return switch (err) {
else => unexpectedErrorWindows(err),
};
switch (err) {
else => return unexpectedErrorWindows(err),
}
}
}
pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path);
const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
return symLinkW(&existing_path_w, &new_path_w);
}
pub const PosixSymLinkError = error{
OutOfMemory,
AccessDenied,
DiskQuota,
PathAlreadyExists,
@ -866,43 +869,40 @@ pub const PosixSymLinkError = error{
Unexpected,
};
pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
defer allocator.free(full_buf);
const existing_buf = full_buf;
mem.copy(u8, existing_buf, existing_path);
existing_buf[existing_path.len] = 0;
const new_buf = full_buf[existing_path.len + 1 ..];
mem.copy(u8, new_buf, new_path);
new_buf[new_path.len] = 0;
const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
if (err > 0) {
return switch (err) {
posix.EFAULT, posix.EINVAL => unreachable,
posix.EACCES, posix.EPERM => error.AccessDenied,
posix.EDQUOT => error.DiskQuota,
posix.EEXIST => error.PathAlreadyExists,
posix.EIO => error.FileSystem,
posix.ELOOP => error.SymLinkLoop,
posix.ENAMETOOLONG => error.NameTooLong,
posix.ENOENT => error.FileNotFound,
posix.ENOTDIR => error.NotDir,
posix.ENOMEM => error.SystemResources,
posix.ENOSPC => error.NoSpaceLeft,
posix.EROFS => error.ReadOnlyFileSystem,
else => unexpectedErrorPosix(err),
};
pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void {
const err = posix.getErrno(posix.symlink(existing_path, new_path));
switch (err) {
0 => return,
posix.EFAULT => unreachable,
posix.EINVAL => unreachable,
posix.EACCES => return error.AccessDenied,
posix.EPERM => return error.AccessDenied,
posix.EDQUOT => return error.DiskQuota,
posix.EEXIST => return error.PathAlreadyExists,
posix.EIO => return error.FileSystem,
posix.ELOOP => return error.SymLinkLoop,
posix.ENAMETOOLONG => return error.NameTooLong,
posix.ENOENT => return error.FileNotFound,
posix.ENOTDIR => return error.NotDir,
posix.ENOMEM => return error.SystemResources,
posix.ENOSPC => return error.NoSpaceLeft,
posix.EROFS => return error.ReadOnlyFileSystem,
else => return unexpectedErrorPosix(err),
}
}
pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
const existing_path_c = try toPosixPath(existing_path);
const new_path_c = try toPosixPath(new_path);
return symLinkPosixC(&existing_path_c, &new_path_c);
}
// here we replace the standard +/ with -_ so that it can be used in a file name
const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
/// TODO remove the allocator requirement from this API
pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
if (symLink(allocator, existing_path, new_path)) {
if (symLink(existing_path, new_path)) {
return;
} else |err| switch (err) {
error.PathAlreadyExists => {},
@ -920,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
try getRandomBytes(rand_buf[0..]);
b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
if (symLink(allocator, existing_path, tmp_path)) {
if (symLink(existing_path, tmp_path)) {
return rename(tmp_path, new_path);
} else |err| switch (err) {
error.PathAlreadyExists => continue,
@ -1252,54 +1252,70 @@ pub const DeleteDirError = error{
NotDir,
DirNotEmpty,
ReadOnlyFileSystem,
OutOfMemory,
InvalidUtf8,
BadPathName,
/// See https://github.com/ziglang/zig/issues/1396
Unexpected,
};
/// Returns ::error.DirNotEmpty if the directory is not empty.
/// To delete a directory recursively, see ::deleteTree
pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
const path_buf = try allocator.alloc(u8, dir_path.len + 1);
defer allocator.free(path_buf);
mem.copy(u8, path_buf, dir_path);
path_buf[dir_path.len] = 0;
pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
switch (builtin.os) {
Os.windows => {
if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
else => unexpectedErrorWindows(err),
};
}
const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path);
return deleteDirW(&dir_path_w);
},
Os.linux, Os.macosx, Os.ios => {
const err = posix.getErrno(posix.rmdir(path_buf.ptr));
if (err > 0) {
return switch (err) {
posix.EACCES, posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
posix.EFAULT, posix.EINVAL => unreachable,
posix.ELOOP => error.SymLinkLoop,
posix.ENAMETOOLONG => error.NameTooLong,
posix.ENOENT => error.FileNotFound,
posix.ENOMEM => error.SystemResources,
posix.ENOTDIR => error.NotDir,
posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
posix.EROFS => error.ReadOnlyFileSystem,
else => unexpectedErrorPosix(err),
};
const err = posix.getErrno(posix.rmdir(dir_path));
switch (err) {
0 => return,
posix.EACCES => return error.AccessDenied,
posix.EPERM => return error.AccessDenied,
posix.EBUSY => return error.FileBusy,
posix.EFAULT => unreachable,
posix.EINVAL => unreachable,
posix.ELOOP => return error.SymLinkLoop,
posix.ENAMETOOLONG => return error.NameTooLong,
posix.ENOENT => return error.FileNotFound,
posix.ENOMEM => return error.SystemResources,
posix.ENOTDIR => return error.NotDir,
posix.EEXIST => return error.DirNotEmpty,
posix.ENOTEMPTY => return error.DirNotEmpty,
posix.EROFS => return error.ReadOnlyFileSystem,
else => return unexpectedErrorPosix(err),
}
},
else => @compileError("unimplemented"),
}
}
pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void {
if (windows.RemoveDirectoryW(dir_path_w) == 0) {
const err = windows.GetLastError();
switch (err) {
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
else => return unexpectedErrorWindows(err),
}
}
}
/// Returns ::error.DirNotEmpty if the directory is not empty.
/// To delete a directory recursively, see ::deleteTree
pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
switch (builtin.os) {
Os.windows => {
const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
return deleteDirW(&dir_path_w);
},
Os.linux, Os.macosx, Os.ios => {
const dir_path_c = try toPosixPath(dir_path);
return deleteDirC(&dir_path_c);
},
else => @compileError("unimplemented"),
}
}
/// Whether ::full_path describes a symlink, file, or directory, this function
/// removes it. If it cannot be removed because it is a non-empty directory,
/// this function recursively removes its entries and then tries again.
@ -1346,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
error.IsDir => {},
error.AccessDenied => got_access_denied = true,
error.InvalidUtf8,
error.SymLinkLoop,
error.NameTooLong,
error.SystemResources,
@ -1353,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
error.NotDir,
error.FileSystem,
error.FileBusy,
error.InvalidUtf8,
error.BadPathName,
error.Unexpected,
=> return err,
@ -1381,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
error.NoSpaceLeft,
error.PathAlreadyExists,
error.Unexpected,
error.InvalidUtf8,
error.BadPathName,
=> return err,
};
defer dir.close();
@ -1398,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
try deleteTree(allocator, full_entry_path);
}
}
return deleteDir(allocator, full_path);
return deleteDir(full_path);
}
}
@ -1422,8 +1440,9 @@ pub const Dir = struct {
},
Os.windows => struct {
handle: windows.HANDLE,
find_file_data: windows.WIN32_FIND_DATAA,
find_file_data: windows.WIN32_FIND_DATAW,
first: bool,
name_data: [256]u8,
},
else => @compileError("unimplemented"),
};
@ -1460,6 +1479,8 @@ pub const Dir = struct {
NoSpaceLeft,
PathAlreadyExists,
OutOfMemory,
InvalidUtf8,
BadPathName,
/// See https://github.com/ziglang/zig/issues/1396
Unexpected,
@ -1471,12 +1492,13 @@ pub const Dir = struct {
.allocator = allocator,
.handle = switch (builtin.os) {
Os.windows => blk: {
var find_file_data: windows.WIN32_FIND_DATAA = undefined;
const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
var find_file_data: windows.WIN32_FIND_DATAW = undefined;
const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
break :blk Handle{
.handle = handle,
.find_file_data = find_file_data, // TODO guaranteed copy elision
.first = true,
.name_data = undefined,
};
},
Os.macosx, Os.ios => Handle{
@ -1591,9 +1613,12 @@ pub const Dir = struct {
if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
return null;
}
const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
continue;
// Trust that Windows gives us valid UTF-16LE
const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
const name_utf8 = self.handle.name_data[0..name_utf8_len];
const kind = blk: {
const attrs = self.handle.find_file_data.dwFileAttributes;
if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
@ -1602,7 +1627,7 @@ pub const Dir = struct {
break :blk Entry.Kind.Unknown;
};
return Entry{
.name = name,
.name = name_utf8,
.kind = kind,
};
}
@ -2070,7 +2095,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
}
// TODO make this a build variable that you can set
const unexpected_error_tracing = true;
const unexpected_error_tracing = false;
const UnexpectedError = error{
/// The Operating System returned an undocumented error code.
Unexpected,

View File

@ -6,8 +6,6 @@ pub use @import("kernel32.zig");
pub use @import("ntdll.zig");
pub use @import("ole32.zig");
pub use @import("shell32.zig");
pub use @import("shlwapi.zig");
pub use @import("user32.zig");
test "import" {
_ = @import("util.zig");
@ -174,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {
dwThreadId: DWORD,
};
pub const STARTUPINFOA = extern struct {
pub const STARTUPINFOW = extern struct {
cb: DWORD,
lpReserved: ?LPSTR,
lpDesktop: ?LPSTR,
lpTitle: ?LPSTR,
lpReserved: ?LPWSTR,
lpDesktop: ?LPWSTR,
lpTitle: ?LPWSTR,
dwX: DWORD,
dwY: DWORD,
dwXSize: DWORD,
@ -238,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
pub const WIN32_FIND_DATAA = extern struct {
pub const WIN32_FIND_DATAW = extern struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
@ -247,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {
nFileSizeLow: DWORD,
dwReserved0: DWORD,
dwReserved1: DWORD,
cFileName: [260]CHAR,
cAlternateFileName: [14]CHAR,
cFileName: [260]u16,
cAlternateFileName: [14]u16,
};
pub const FILETIME = extern struct {
@ -377,3 +375,5 @@ pub const COORD = extern struct {
X: SHORT,
Y: SHORT,
};
pub const CREATE_UNICODE_ENVIRONMENT = 1024;

View File

@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
pub extern "kernel32" stdcallcc fn CreateFileA(
lpFileName: [*]const u8, // TODO null terminated pointer type
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: ?HANDLE,
) HANDLE;
pub extern "kernel32" stdcallcc fn CreateFileW(
lpFileName: [*]const u16, // TODO null terminated pointer type
dwDesiredAccess: DWORD,
@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(
nSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateProcessA(
lpApplicationName: ?LPCSTR,
lpCommandLine: LPSTR,
pub extern "kernel32" stdcallcc fn CreateProcessW(
lpApplicationName: ?LPWSTR,
lpCommandLine: LPWSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: DWORD,
lpEnvironment: ?*c_void,
lpCurrentDirectory: ?LPCSTR,
lpStartupInfo: *STARTUPINFOA,
lpCurrentDirectory: ?LPWSTR,
lpStartupInfo: *STARTUPINFOW,
lpProcessInformation: *PROCESS_INFORMATION,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
lpSymlinkFileName: LPCSTR,
lpTargetFileName: LPCSTR,
dwFlags: DWORD,
) BOOLEAN;
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL;
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
@ -74,7 +58,6 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out
pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
@ -88,10 +71,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
@ -105,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
in_dwBufferSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
hFile: HANDLE,
lpszFilePath: LPSTR,
cchFilePath: DWORD,
dwFlags: DWORD,
) DWORD;
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
hFile: HANDLE,
lpszFilePath: [*]u16,
@ -142,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
pub extern "kernel32" stdcallcc fn MoveFileExA(
lpExistingFileName: [*]const u8,
lpNewFileName: [*]const u8,
dwFlags: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn MoveFileExW(
lpExistingFileName: [*]const u16,
lpNewFileName: [*]const u16,
@ -179,7 +147,7 @@ pub extern "kernel32" stdcallcc fn ReadFile(
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(
pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
//TODO: call unicode versions instead of relying on ANSI code page
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;

View File

@ -1,4 +0,0 @@
use @import("index.zig");
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;

View File

@ -1,4 +0,0 @@
use @import("index.zig");
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;

View File

@ -1,6 +1,7 @@
const std = @import("../../index.zig");
const builtin = @import("builtin");
const os = std.os;
const unicode = std.unicode;
const windows = std.os.windows;
const assert = std.debug.assert;
const mem = std.mem;
@ -156,41 +157,51 @@ pub fn windowsOpen(
}
/// Caller must free result.
pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 {
pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
// count bytes needed
const bytes_needed = x: {
var bytes_needed: usize = 1; // 1 for the final null byte
const max_chars_needed = x: {
var max_chars_needed: usize = 1; // 1 for the final null byte
var it = env_map.iterator();
while (it.next()) |pair| {
// +1 for '='
// +1 for null byte
bytes_needed += pair.key.len + pair.value.len + 2;
max_chars_needed += pair.key.len + pair.value.len + 2;
}
break :x bytes_needed;
break :x max_chars_needed;
};
const result = try allocator.alloc(u8, bytes_needed);
const result = try allocator.alloc(u16, max_chars_needed);
errdefer allocator.free(result);
var it = env_map.iterator();
var i: usize = 0;
while (it.next()) |pair| {
mem.copy(u8, result[i..], pair.key);
i += pair.key.len;
i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
result[i] = '=';
i += 1;
mem.copy(u8, result[i..], pair.value);
i += pair.value.len;
i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
result[i] = 0;
i += 1;
}
result[i] = 0;
return result;
i += 1;
return allocator.shrink(u16, result, i);
}
pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
const padded_buff = try cstr.addNullByte(allocator, dll_path);
defer allocator.free(padded_buff);
return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE {
return windows.LoadLibraryW(dll_path_w) orelse {
const err = windows.GetLastError();
switch (err) {
windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,
else => return os.unexpectedErrorWindows(err),
}
};
}
pub fn windowsLoadDll(dll_path: []const u8) !windows.HMODULE {
const dll_path_w = try sliceToPrefixedFileW(dll_path);
return windowsLoadDllW(&dll_path_w);
}
pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
@ -200,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
test "InvalidDll" {
if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
const DllName = "asdf.dll";
const allocator = std.debug.global_allocator;
const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
assert(err == error.DllNotFound);
const handle = os.windowsLoadDll("asdf.dll") catch |err| {
assert(err == error.FileNotFound);
return;
};
@panic("Expected error from function");
}
pub fn windowsFindFirstFile(
allocator: *mem.Allocator,
dir_path: []const u8,
find_file_data: *windows.WIN32_FIND_DATAA,
find_file_data: *windows.WIN32_FIND_DATAW,
) !windows.HANDLE {
const wild_and_null = []u8{ '\\', '*', 0 };
const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
defer allocator.free(path_with_wild_and_null);
mem.copy(u8, path_with_wild_and_null, dir_path);
mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0});
const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
if (handle == windows.INVALID_HANDLE_VALUE) {
const err = windows.GetLastError();
@ -235,8 +238,8 @@ pub fn windowsFindFirstFile(
}
/// Returns `true` if there was another file, `false` otherwise.
pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
if (windows.FindNextFileA(handle, find_file_data) == 0) {
pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
if (windows.FindNextFileW(handle, find_file_data) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.NO_MORE_FILES => false,
@ -297,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
}
pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
return sliceToPrefixedSuffixedFileW(s, []u16{0});
}
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 + 1]u16 = undefined;
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 "\\?\"
@ -306,11 +313,12 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
// 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|
for (s) |byte| {
switch (byte) {
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
else => {},
};
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
else => {},
}
}
const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
const prefix = []u16{ '\\', '\\', '?', '\\' };
mem.copy(u16, result[0..], prefix);
@ -318,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
};
const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
assert(end_index <= result.len);
if (end_index == result.len) return error.NameTooLong;
result[end_index] = 0;
if (end_index + suffix.len > result.len) return error.NameTooLong;
mem.copy(u16, result[end_index..], suffix);
return result;
}