diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2018-09-02 23:25:04 -0400 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2018-09-02 23:25:04 -0400 |
| commit | 92f747435930bc4d54114e414b372c7eafe7cc02 (patch) | |
| tree | f525a6d2c52e1bf4336ea359dd4f055a5c2bcfe4 | |
| parent | d5968086fe357aa5cf678327295677dba5102fc8 (diff) | |
| download | zig-92f747435930bc4d54114e414b372c7eafe7cc02.tar.gz zig-92f747435930bc4d54114e414b372c7eafe7cc02.zip | |
switch most windows calls to use W versions instead of A
See #534
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | std/os/child_process.zig | 58 | ||||
| -rw-r--r-- | std/os/index.zig | 199 | ||||
| -rw-r--r-- | std/os/windows/index.zig | 18 | ||||
| -rw-r--r-- | std/os/windows/kernel32.zig | 53 | ||||
| -rw-r--r-- | std/os/windows/shlwapi.zig | 4 | ||||
| -rw-r--r-- | std/os/windows/user32.zig | 4 | ||||
| -rw-r--r-- | std/os/windows/util.zig | 84 |
8 files changed, 223 insertions, 199 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5664f1db19..d02de451f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/std/os/child_process.zig b/std/os/child_process.zig index decd8d04fa..a8f307f3b6 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -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), + } } } diff --git a/std/os/index.zig b/std/os/index.zig index 9b49a05067..3ab73a79ac 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -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,49 +1252,65 @@ 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); +pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { + switch (builtin.os) { + Os.windows => { + 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(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"), + } +} - mem.copy(u8, path_buf, dir_path); - path_buf[dir_path.len] = 0; +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 => { - 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.sliceToPrefixedFileW(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 dir_path_c = try toPosixPath(dir_path); + return deleteDirC(&dir_path_c); }, else => @compileError("unimplemented"), } @@ -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, diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 9286b7d090..fc64db7c37 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -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; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index ffa4422760..1a7b28eac2 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -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; diff --git a/std/os/windows/shlwapi.zig b/std/os/windows/shlwapi.zig deleted file mode 100644 index 6bccefaf98..0000000000 --- a/std/os/windows/shlwapi.zig +++ /dev/null @@ -1,4 +0,0 @@ -use @import("index.zig"); - -pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; - diff --git a/std/os/windows/user32.zig b/std/os/windows/user32.zig deleted file mode 100644 index 37f9f6f3b8..0000000000 --- a/std/os/windows/user32.zig +++ /dev/null @@ -1,4 +0,0 @@ -use @import("index.zig"); - -pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; - diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 168f6c3861..a489cca163 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -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; } |
