| ... | @@ -28,6 +28,9 @@ pub const gdi32 = @import("windows/gdi32.zig"); | ... | @@ -28,6 +28,9 @@ pub const gdi32 = @import("windows/gdi32.zig"); |
| 28 | | 28 | |
| 29 | pub usingnamespace @import("windows/bits.zig"); | 29 | pub usingnamespace @import("windows/bits.zig"); |
| 30 | | 30 | |
| | 31 | //version detection |
| | 32 | usingnamespace std.zig.system.windows; |
| | 33 | |
| 31 | pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); | 34 | pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); |
| 32 | | 35 | |
| 33 | pub const OpenError = error{ | 36 | pub const OpenError = error{ |
| ... | @@ -953,12 +956,19 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { | ... | @@ -953,12 +956,19 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { |
| 953 | return @bitCast(u64, result); | 956 | return @bitCast(u64, result); |
| 954 | } | 957 | } |
| 955 | | 958 | |
| 956 | pub const GetFinalPathNameByHandleError = error{ | 959 | pub const GetFinalPathNameByHandleError = error { |
| 957 | BadPathName, | 960 | BadPathName, |
| 958 | FileNotFound, | 961 | FileNotFound, |
| 959 | NameTooLong, | 962 | NameTooLong, |
| 960 | Unexpected, | 963 | Unexpected, |
| 961 | }; | 964 | } |
| | 965 | || if((comptime builtin.os.tag != .windows) or (targetVersionIsAtLeast(WindowsVersion.win10_rs4) == true)) |
| | 966 | error {} |
| | 967 | else |
| | 968 | error { |
| | 969 | AccessDenied, |
| | 970 | SystemResources, |
| | 971 | }; |
| 962 | | 972 | |
| 963 | /// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`. | 973 | /// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`. |
| 964 | /// Defaults to DOS volume names. | 974 | /// Defaults to DOS volume names. |
| ... | @@ -981,8 +991,52 @@ pub fn GetFinalPathNameByHandle( | ... | @@ -981,8 +991,52 @@ pub fn GetFinalPathNameByHandle( |
| 981 | fmt: GetFinalPathNameByHandleFormat, | 991 | fmt: GetFinalPathNameByHandleFormat, |
| 982 | out_buffer: []u16, | 992 | out_buffer: []u16, |
| 983 | ) GetFinalPathNameByHandleError![]u16 { | 993 | ) GetFinalPathNameByHandleError![]u16 { |
| 984 | // Get normalized path; doesn't include volume name though. | 994 | |
| 985 | var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; | 995 | var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; |
| | 996 | |
| | 997 | if ((comptime (targetVersionIsAtLeast(WindowsVersion.win10_rs4) != true)) //need explicit comptime, because error returns affect return type |
| | 998 | and !runtimeVersionIsAtLeast(WindowsVersion.win10_rs4)) { |
| | 999 | // TODO: directly replace/emulate QueryInformationFile of .FileNormalizedNameInformation |
| | 1000 | // with ntdll instead of calling into kernel32 |
| | 1001 | // (probably using some less-powerful query and looping over path segments) |
| | 1002 | const flags: DWORD = FILE_NAME_NORMALIZED | switch(fmt.volume_name) { |
| | 1003 | .Dos => @as(DWORD, VOLUME_NAME_DOS), |
| | 1004 | .Nt => @as(DWORD, VOLUME_NAME_NT), |
| | 1005 | }; |
| | 1006 | const wide_path_buffer = std.mem.bytesAsSlice(u16, path_buffer[0..]); |
| | 1007 | const rc = kernel32.GetFinalPathNameByHandleW(hFile, wide_path_buffer.ptr, @intCast(u32, wide_path_buffer.len), flags); |
| | 1008 | if (rc == 0) { |
| | 1009 | switch (kernel32.GetLastError()) { |
| | 1010 | .FILE_NOT_FOUND => return error.FileNotFound, |
| | 1011 | .PATH_NOT_FOUND => return error.FileNotFound, |
| | 1012 | .NOT_ENOUGH_MEMORY => return error.SystemResources, |
| | 1013 | .FILENAME_EXCED_RANGE => return error.NameTooLong, |
| | 1014 | .ACCESS_DENIED => return error.AccessDenied, //can happen in SMB sub-queries for parent path segments |
| | 1015 | .INVALID_PARAMETER => unreachable, |
| | 1016 | else => |err| return unexpectedError(err), |
| | 1017 | } |
| | 1018 | } |
| | 1019 | |
| | 1020 | //in case of failure, rc == length of string INCLUDING null terminator, |
| | 1021 | if (rc > wide_path_buffer.len) return error.NameTooLong; |
| | 1022 | //in case of success, rc == length of string EXCLUDING null terminator |
| | 1023 | const result_slice = switch(fmt.volume_name) { |
| | 1024 | .Dos => blk: { |
| | 1025 | const expected_prefix = [_]u16{'\\', '\\', '?', '\\'}; |
| | 1026 | if (!std.mem.eql(u16, expected_prefix[0..], wide_path_buffer[0..expected_prefix.len])) { |
| | 1027 | return error.BadPathName; |
| | 1028 | } |
| | 1029 | break :blk wide_path_buffer[expected_prefix.len..rc:0]; |
| | 1030 | }, |
| | 1031 | //no prefix here |
| | 1032 | .Nt => wide_path_buffer[0..rc:0], |
| | 1033 | }; |
| | 1034 | if(result_slice.len > out_buffer.len) return error.NameTooLong; |
| | 1035 | std.mem.copy(u16, out_buffer[0..], result_slice); |
| | 1036 | return out_buffer[0..result_slice.len]; |
| | 1037 | } |
| | 1038 | |
| | 1039 | // Get normalized path; doesn't include volume name though. |
| 986 | try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]); | 1040 | try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]); |
| 987 | | 1041 | |
| 988 | // Get NT volume name. | 1042 | // Get NT volume name. |