| ... | @@ -956,6 +956,51 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { | ... | @@ -956,6 +956,51 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { |
| 956 | return @bitCast(u64, result); | 956 | return @bitCast(u64, result); |
| 957 | } | 957 | } |
| 958 | | 958 | |
| | 959 | pub fn QueryObjectName( |
| | 960 | handle: HANDLE, |
| | 961 | out_buffer: []u16, |
| | 962 | ) ![]u16 { |
| | 963 | var full_buffer: [@sizeOf(OBJECT_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(OBJECT_NAME_INFORMATION)) = undefined; |
| | 964 | var info = @ptrCast(*OBJECT_NAME_INFORMATION, &full_buffer); |
| | 965 | //buffer size is specified in bytes |
| | 966 | const full_buffer_length = @intCast(ULONG, @sizeOf(OBJECT_NAME_INFORMATION) + std.math.min(PATH_MAX_WIDE, (out_buffer.len + 1) * 2)); |
| | 967 | //last argument would return the length required for full_buffer, not exposed here |
| | 968 | const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, full_buffer[0..], full_buffer_length, null); |
| | 969 | return switch (rc) { |
| | 970 | .SUCCESS => if (@ptrCast(?[*]WCHAR, info.Name.Buffer)) |buffer| blk: { |
| | 971 | //resulting string length is specified in bytes |
| | 972 | const path_length_unterminated = @divExact(info.Name.Length, 2); |
| | 973 | if (out_buffer.len < path_length_unterminated) { |
| | 974 | return error.NameTooLong; |
| | 975 | } |
| | 976 | std.mem.copy(WCHAR, out_buffer[0..path_length_unterminated], buffer[0..path_length_unterminated :0]); |
| | 977 | break :blk out_buffer[0..path_length_unterminated]; |
| | 978 | } else error.Unexpected, |
| | 979 | .ACCESS_DENIED => error.AccessDenied, |
| | 980 | .INVALID_HANDLE => error.InvalidHandle, |
| | 981 | .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong, |
| | 982 | //name_buffer.len >= @sizeOf(OBJECT_NAME_INFORMATION) holds statically |
| | 983 | .INFO_LENGTH_MISMATCH => unreachable, |
| | 984 | else => |e| unexpectedStatus(e), |
| | 985 | }; |
| | 986 | } |
| | 987 | test "QueryObjectName" { |
| | 988 | if (comptime builtin.os.tag != .windows) |
| | 989 | return; |
| | 990 | |
| | 991 | //any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths. |
| | 992 | const file = try std.fs.openSelfExe(.{}); |
| | 993 | defer file.close(); |
| | 994 | //make this large enough for the test runner exe path |
| | 995 | var out_buffer align(16) = std.mem.zeroes([1 << 10]u16); |
| | 996 | |
| | 997 | var result_path = try QueryObjectName(file.handle, out_buffer[0..]); |
| | 998 | //insufficient size |
| | 999 | std.testing.expectError(error.NameTooLong, QueryObjectName(file.handle, out_buffer[0 .. result_path.len - 1])); |
| | 1000 | //exactly-sufficient size |
| | 1001 | _ = try QueryObjectName(file.handle, out_buffer[0..result_path.len]); |
| | 1002 | } |
| | 1003 | |
| 959 | pub const GetFinalPathNameByHandleError = error { | 1004 | pub const GetFinalPathNameByHandleError = error { |
| 960 | BadPathName, | 1005 | BadPathName, |
| 961 | FileNotFound, | 1006 | FileNotFound, |