From ae334e069c042b2d3feba13ddaf63325ff3a4078 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Mon, 13 May 2024 14:14:32 +0300 Subject: [PATCH] [Windows] Always use absolute UNC paths and long path aware APIs, add "long path aware" flag to the application manifest. --- drivers/windows/dir_access_windows.cpp | 204 +++++++++---------- drivers/windows/file_access_windows.cpp | 72 ++++--- godot.manifest | 5 + platform/windows/console_wrapper_windows.cpp | 4 +- platform/windows/display_server_windows.cpp | 27 ++- platform/windows/godot_res_wrap.rc | 5 + platform/windows/os_windows.cpp | 111 ++++++++-- 7 files changed, 261 insertions(+), 167 deletions(-) diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 63ba6a6c968..1d1f2ce415e 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -35,6 +35,7 @@ #include "core/config/project_settings.h" #include "core/os/memory.h" +#include "core/os/os.h" #include "core/string/print_string.h" #include @@ -69,9 +70,17 @@ struct DirAccessWindowsPrivate { }; String DirAccessWindows::fix_path(const String &p_path) const { - String r_path = DirAccess::fix_path(p_path); - if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) { - r_path = "\\\\?\\" + r_path.replace("/", "\\"); + String r_path = DirAccess::fix_path(p_path.trim_prefix(R"(\\?\)").replace("\\", "/")); + + if (r_path.is_relative_path()) { + r_path = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/").path_join(r_path); + } else if (r_path == ".") { + r_path = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/"); + } + r_path = r_path.simplify_path(); + r_path = r_path.replace("/", "\\"); + if (!r_path.is_network_share_path() && !r_path.begins_with(R"(\\?\)")) { + r_path = R"(\\?\)" + r_path; } return r_path; } @@ -140,28 +149,33 @@ String DirAccessWindows::get_drive(int p_drive) { Error DirAccessWindows::change_dir(String p_dir) { GLOBAL_LOCK_FUNCTION - p_dir = fix_path(p_dir); + String dir = fix_path(p_dir); - WCHAR real_current_dir_name[2048]; - GetCurrentDirectoryW(2048, real_current_dir_name); - String prev_dir = String::utf16((const char16_t *)real_current_dir_name); + Char16String real_current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + real_current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); + String prev_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()); SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data())); - bool worked = (SetCurrentDirectoryW((LPCWSTR)(p_dir.utf16().get_data())) != 0); + bool worked = (SetCurrentDirectoryW((LPCWSTR)(dir.utf16().get_data())) != 0); String base = _get_root_path(); if (!base.is_empty()) { - GetCurrentDirectoryW(2048, real_current_dir_name); - String new_dir = String::utf16((const char16_t *)real_current_dir_name).replace("\\", "/"); + str_len = GetCurrentDirectoryW(0, nullptr); + real_current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); + String new_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace("\\", "/"); if (!new_dir.begins_with(base)) { worked = false; } } if (worked) { - GetCurrentDirectoryW(2048, real_current_dir_name); - current_dir = String::utf16((const char16_t *)real_current_dir_name); - current_dir = current_dir.replace("\\", "/"); + str_len = GetCurrentDirectoryW(0, nullptr); + real_current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); + current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()); } SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data())); @@ -172,12 +186,6 @@ Error DirAccessWindows::change_dir(String p_dir) { Error DirAccessWindows::make_dir(String p_dir) { GLOBAL_LOCK_FUNCTION - p_dir = fix_path(p_dir); - if (p_dir.is_relative_path()) { - p_dir = current_dir.path_join(p_dir); - p_dir = fix_path(p_dir); - } - if (FileAccessWindows::is_path_invalid(p_dir)) { #ifdef DEBUG_ENABLED WARN_PRINT("The path :" + p_dir + " is a reserved Windows system pipe, so it can't be used for creating directories."); @@ -185,12 +193,12 @@ Error DirAccessWindows::make_dir(String p_dir) { return ERR_INVALID_PARAMETER; } - p_dir = p_dir.simplify_path().replace("/", "\\"); + String dir = fix_path(p_dir); bool success; int err; - success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr); + success = CreateDirectoryW((LPCWSTR)(dir.utf16().get_data()), nullptr); err = GetLastError(); if (success) { @@ -205,9 +213,10 @@ Error DirAccessWindows::make_dir(String p_dir) { } String DirAccessWindows::get_current_dir(bool p_include_drive) const { + String cdir = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/"); String base = _get_root_path(); if (!base.is_empty()) { - String bd = current_dir.replace("\\", "/").replace_first(base, ""); + String bd = cdir.replace_first(base, ""); if (bd.begins_with("/")) { return _get_root_string() + bd.substr(1, bd.length()); } else { @@ -216,30 +225,25 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) const { } if (p_include_drive) { - return current_dir; + return cdir; } else { if (_get_root_string().is_empty()) { - int pos = current_dir.find(":"); + int pos = cdir.find(":"); if (pos != -1) { - return current_dir.substr(pos + 1); + return cdir.substr(pos + 1); } } - return current_dir; + return cdir; } } bool DirAccessWindows::file_exists(String p_file) { GLOBAL_LOCK_FUNCTION - if (!p_file.is_absolute_path()) { - p_file = get_current_dir().path_join(p_file); - } - - p_file = fix_path(p_file); + String file = fix_path(p_file); DWORD fileAttr; - - fileAttr = GetFileAttributesW((LPCWSTR)(p_file.utf16().get_data())); + fileAttr = GetFileAttributesW((LPCWSTR)(file.utf16().get_data())); if (INVALID_FILE_ATTRIBUTES == fileAttr) { return false; } @@ -250,14 +254,10 @@ bool DirAccessWindows::file_exists(String p_file) { bool DirAccessWindows::dir_exists(String p_dir) { GLOBAL_LOCK_FUNCTION - if (p_dir.is_relative_path()) { - p_dir = get_current_dir().path_join(p_dir); - } - - p_dir = fix_path(p_dir); + String dir = fix_path(p_dir); DWORD fileAttr; - fileAttr = GetFileAttributesW((LPCWSTR)(p_dir.utf16().get_data())); + fileAttr = GetFileAttributesW((LPCWSTR)(dir.utf16().get_data())); if (INVALID_FILE_ATTRIBUTES == fileAttr) { return false; } @@ -265,66 +265,63 @@ bool DirAccessWindows::dir_exists(String p_dir) { } Error DirAccessWindows::rename(String p_path, String p_new_path) { - if (p_path.is_relative_path()) { - p_path = get_current_dir().path_join(p_path); - } - - p_path = fix_path(p_path); - - if (p_new_path.is_relative_path()) { - p_new_path = get_current_dir().path_join(p_new_path); - } - - p_new_path = fix_path(p_new_path); + String path = fix_path(p_path); + String new_path = fix_path(p_new_path); // If we're only changing file name case we need to do a little juggling - if (p_path.to_lower() == p_new_path.to_lower()) { - if (dir_exists(p_path)) { + if (path.to_lower() == new_path.to_lower()) { + if (dir_exists(path)) { // The path is a dir; just rename - return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED; + return MoveFileW((LPCWSTR)(path.utf16().get_data()), (LPCWSTR)(new_path.utf16().get_data())) != 0 ? OK : FAILED; } // The path is a file; juggle - WCHAR tmpfile[MAX_PATH]; - - if (!GetTempFileNameW((LPCWSTR)(fix_path(get_current_dir()).utf16().get_data()), nullptr, 0, tmpfile)) { - return FAILED; - } - - if (!::ReplaceFileW(tmpfile, (LPCWSTR)(p_path.utf16().get_data()), nullptr, 0, nullptr, nullptr)) { - DeleteFileW(tmpfile); - return FAILED; - } - - return ::_wrename(tmpfile, (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED; - - } else { - if (file_exists(p_new_path)) { - if (remove(p_new_path) != OK) { + // Note: do not use GetTempFileNameW, it's not long path aware! + Char16String tmpfile_utf16; + uint64_t id = OS::get_singleton()->get_ticks_usec(); + while (true) { + tmpfile_utf16 = (path + itos(id++) + ".tmp").utf16(); + HANDLE handle = CreateFileW((LPCWSTR)tmpfile_utf16.get_data(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + break; + } + if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_SHARING_VIOLATION) { return FAILED; } } - return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED; + if (!::ReplaceFileW((LPCWSTR)tmpfile_utf16.get_data(), (LPCWSTR)(path.utf16().get_data()), nullptr, 0, nullptr, nullptr)) { + DeleteFileW((LPCWSTR)tmpfile_utf16.get_data()); + return FAILED; + } + + return MoveFileW((LPCWSTR)tmpfile_utf16.get_data(), (LPCWSTR)(new_path.utf16().get_data())) != 0 ? OK : FAILED; + + } else { + if (file_exists(new_path)) { + if (remove(new_path) != OK) { + return FAILED; + } + } + + return MoveFileW((LPCWSTR)(path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) != 0 ? OK : FAILED; } } Error DirAccessWindows::remove(String p_path) { - if (p_path.is_relative_path()) { - p_path = get_current_dir().path_join(p_path); - } - - p_path = fix_path(p_path); + String path = fix_path(p_path); + const Char16String &path_utf16 = path.utf16(); DWORD fileAttr; - fileAttr = GetFileAttributesW((LPCWSTR)(p_path.utf16().get_data())); + fileAttr = GetFileAttributesW((LPCWSTR)(path_utf16.get_data())); if (INVALID_FILE_ATTRIBUTES == fileAttr) { return FAILED; } if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY)) { - return ::_wrmdir((LPCWSTR)(p_path.utf16().get_data())) == 0 ? OK : FAILED; + return RemoveDirectoryW((LPCWSTR)(path_utf16.get_data())) != 0 ? OK : FAILED; } else { - return ::_wunlink((LPCWSTR)(p_path.utf16().get_data())) == 0 ? OK : FAILED; + return DeleteFileW((LPCWSTR)(path_utf16.get_data())) != 0 ? OK : FAILED; } } @@ -339,16 +336,16 @@ uint64_t DirAccessWindows::get_space_left() { } String DirAccessWindows::get_filesystem_type() const { - String path = fix_path(const_cast(this)->get_current_dir()); - - int unit_end = path.find(":"); - ERR_FAIL_COND_V(unit_end == -1, String()); - String unit = path.substr(0, unit_end + 1) + "\\"; + String path = current_dir.trim_prefix(R"(\\?\)"); if (path.is_network_share_path()) { return "Network Share"; } + int unit_end = path.find(":"); + ERR_FAIL_COND_V(unit_end == -1, String()); + String unit = path.substr(0, unit_end + 1) + "\\"; + WCHAR szVolumeName[100]; WCHAR szFileSystemName[10]; DWORD dwSerialNumber = 0; @@ -370,11 +367,7 @@ String DirAccessWindows::get_filesystem_type() const { } bool DirAccessWindows::is_case_sensitive(const String &p_path) const { - String f = p_path; - if (!f.is_absolute_path()) { - f = get_current_dir().path_join(f); - } - f = fix_path(f); + String f = fix_path(p_path); HANDLE h_file = ::CreateFileW((LPCWSTR)(f.utf16().get_data()), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -397,12 +390,7 @@ bool DirAccessWindows::is_case_sensitive(const String &p_path) const { } bool DirAccessWindows::is_link(String p_file) { - String f = p_file; - - if (!f.is_absolute_path()) { - f = get_current_dir().path_join(f); - } - f = fix_path(f); + String f = fix_path(p_file); DWORD attr = GetFileAttributesW((LPCWSTR)(f.utf16().get_data())); if (attr == INVALID_FILE_ATTRIBUTES) { @@ -413,12 +401,7 @@ bool DirAccessWindows::is_link(String p_file) { } String DirAccessWindows::read_link(String p_file) { - String f = p_file; - - if (!f.is_absolute_path()) { - f = get_current_dir().path_join(f); - } - f = fix_path(f); + String f = fix_path(p_file); HANDLE hfile = CreateFileW((LPCWSTR)(f.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); if (hfile == INVALID_HANDLE_VALUE) { @@ -434,22 +417,18 @@ String DirAccessWindows::read_link(String p_file) { GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED); CloseHandle(hfile); - return String::utf16((const char16_t *)cs.ptr(), ret).trim_prefix(R"(\\?\)"); + return String::utf16((const char16_t *)cs.ptr(), ret).trim_prefix(R"(\\?\)").replace("\\", "/"); } Error DirAccessWindows::create_link(String p_source, String p_target) { - if (p_target.is_relative_path()) { - p_target = get_current_dir().path_join(p_target); - } + String source = fix_path(p_source); + String target = fix_path(p_target); - p_source = fix_path(p_source); - p_target = fix_path(p_target); - - DWORD file_attr = GetFileAttributesW((LPCWSTR)(p_source.utf16().get_data())); + DWORD file_attr = GetFileAttributesW((LPCWSTR)(source.utf16().get_data())); bool is_dir = (file_attr & FILE_ATTRIBUTE_DIRECTORY); DWORD flags = ((is_dir) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - if (CreateSymbolicLinkW((LPCWSTR)p_target.utf16().get_data(), (LPCWSTR)p_source.utf16().get_data(), flags) != 0) { + if (CreateSymbolicLinkW((LPCWSTR)target.utf16().get_data(), (LPCWSTR)source.utf16().get_data(), flags) != 0) { return OK; } else { return FAILED; @@ -459,7 +438,12 @@ Error DirAccessWindows::create_link(String p_source, String p_target) { DirAccessWindows::DirAccessWindows() { p = memnew(DirAccessWindowsPrivate); p->h = INVALID_HANDLE_VALUE; - current_dir = "."; + + Char16String real_current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + real_current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); + current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()); DWORD mask = GetLogicalDrives(); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 9885d9d7eed..f6f721639c3 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -73,8 +73,18 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) { String FileAccessWindows::fix_path(const String &p_path) const { String r_path = FileAccess::fix_path(p_path); - if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) { - r_path = "\\\\?\\" + r_path.replace("/", "\\"); + + if (r_path.is_relative_path()) { + Char16String current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw()); + r_path = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace("\\", "/").path_join(r_path); + } + r_path = r_path.simplify_path(); + r_path = r_path.replace("/", "\\"); + if (!r_path.is_network_share_path() && !r_path.begins_with(R"(\\?\)")) { + r_path = R"(\\?\)" + r_path; } return r_path; } @@ -108,9 +118,6 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { return ERR_INVALID_PARAMETER; } - /* Pretty much every implementation that uses fopen as primary - backend supports utf8 encoding. */ - struct _stat st; if (_wstat((LPCWSTR)(path.utf16().get_data()), &st) == 0) { if (!S_ISREG(st.st_mode)) { @@ -125,7 +132,7 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { // platforms), we only check for relative paths, or paths in res:// or user://, // other paths aren't likely to be portable anyway. if (p_mode_flags == READ && (p_path.is_relative_path() || get_access_type() != ACCESS_FILESYSTEM)) { - String base_path = path; + String base_path = p_path; String working_path; String proper_path; @@ -144,23 +151,17 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { } proper_path = "user://"; } + working_path = fix_path(working_path); WIN32_FIND_DATAW d; - Vector parts = base_path.split("/"); + Vector parts = base_path.simplify_path().split("/"); bool mismatch = false; for (const String &part : parts) { - working_path = working_path.path_join(part); - - // Skip if relative. - if (part == "." || part == "..") { - proper_path = proper_path.path_join(part); - continue; - } + working_path = working_path + "\\" + part; HANDLE fnd = FindFirstFileW((LPCWSTR)(working_path.utf16().get_data()), &d); - if (fnd == INVALID_HANDLE_VALUE) { mismatch = false; break; @@ -186,12 +187,22 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { if (is_backup_save_enabled() && p_mode_flags == WRITE) { save_path = path; // Create a temporary file in the same directory as the target file. - WCHAR tmpFileName[MAX_PATH]; - if (GetTempFileNameW((LPCWSTR)(path.get_base_dir().utf16().get_data()), (LPCWSTR)(path.get_file().utf16().get_data()), 0, tmpFileName) == 0) { - last_error = ERR_FILE_CANT_OPEN; - return last_error; + // Note: do not use GetTempFileNameW, it's not long path aware! + String tmpfile; + uint64_t id = OS::get_singleton()->get_ticks_usec(); + while (true) { + tmpfile = path + itos(id++) + ".tmp"; + HANDLE handle = CreateFileW((LPCWSTR)tmpfile.utf16().get_data(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + break; + } + if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_SHARING_VIOLATION) { + last_error = ERR_FILE_CANT_WRITE; + return FAILED; + } } - path = tmpFileName; + path = tmpfile; } f = _wfsopen((LPCWSTR)(path.utf16().get_data()), mode_string, is_backup_save_enabled() ? _SH_SECURE : _SH_DENYNO); @@ -235,7 +246,7 @@ void FileAccessWindows::_close() { } else { // Either the target exists and is locked (temporarily, hopefully) // or it doesn't exist; let's assume the latter before re-trying. - rename_error = _wrename((LPCWSTR)(path_utf16.get_data()), (LPCWSTR)(save_path_utf16.get_data())) != 0; + rename_error = MoveFileW((LPCWSTR)(path_utf16.get_data()), (LPCWSTR)(save_path_utf16.get_data())) == 0; } if (!rename_error) { @@ -262,7 +273,7 @@ String FileAccessWindows::get_path() const { } String FileAccessWindows::get_path_absolute() const { - return path; + return path.trim_prefix(R"(\\?\)").replace("\\", "/"); } bool FileAccessWindows::is_open() const { @@ -548,10 +559,11 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { return 0; } - String file = fix_path(p_file); + String file = p_file; if (file.ends_with("/") && file != "/") { file = file.substr(0, file.length() - 1); } + file = fix_path(file); struct _stat st; int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st); @@ -582,14 +594,15 @@ bool FileAccessWindows::_get_hidden_attribute(const String &p_file) { Error FileAccessWindows::_set_hidden_attribute(const String &p_file, bool p_hidden) { String file = fix_path(p_file); + const Char16String &file_utf16 = file.utf16(); - DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data()); + DWORD attrib = GetFileAttributesW((LPCWSTR)file_utf16.get_data()); ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file); BOOL ok; if (p_hidden) { - ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_HIDDEN); + ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib | FILE_ATTRIBUTE_HIDDEN); } else { - ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_HIDDEN); + ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib & ~FILE_ATTRIBUTE_HIDDEN); } ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file); @@ -606,14 +619,15 @@ bool FileAccessWindows::_get_read_only_attribute(const String &p_file) { Error FileAccessWindows::_set_read_only_attribute(const String &p_file, bool p_ro) { String file = fix_path(p_file); + const Char16String &file_utf16 = file.utf16(); - DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data()); + DWORD attrib = GetFileAttributesW((LPCWSTR)file_utf16.get_data()); ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file); BOOL ok; if (p_ro) { - ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_READONLY); + ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib | FILE_ATTRIBUTE_READONLY); } else { - ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_READONLY); + ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib & ~FILE_ATTRIBUTE_READONLY); } ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file); diff --git a/godot.manifest b/godot.manifest index 30b80aff25a..17b588cafd4 100644 --- a/godot.manifest +++ b/godot.manifest @@ -1,5 +1,10 @@ + + + true + + SetOptions(flags | FOS_FORCEFILESYSTEM); pfd->SetTitle((LPCWSTR)fd->title.utf16().ptr()); - String dir = fd->current_directory.replace("/", "\\"); + String dir = ProjectSettings::get_singleton()->globalize_path(fd->current_directory); + if (dir == ".") { + dir = OS::get_singleton()->get_executable_path().get_base_dir(); + } + if (dir.is_relative_path() || dir == ".") { + Char16String current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw()); + if (dir == ".") { + dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace("\\", "/"); + } else { + dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace("\\", "/").path_join(dir); + } + } + dir = dir.simplify_path(); + dir = dir.replace("/", "\\"); + if (!dir.is_network_share_path() && !dir.begins_with(R"(\\?\)")) { + dir = R"(\\?\)" + dir; + } IShellItem *shellitem = nullptr; hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem); @@ -585,7 +604,7 @@ void DisplayServerWindows::_thread_fd_monitor(void *p_ud) { PWSTR file_path = nullptr; hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); if (SUCCEEDED(hr)) { - file_names.push_back(String::utf16((const char16_t *)file_path)); + file_names.push_back(String::utf16((const char16_t *)file_path).replace("\\", "/").trim_prefix(R"(\\?\)")); CoTaskMemFree(file_path); } result->Release(); @@ -599,7 +618,7 @@ void DisplayServerWindows::_thread_fd_monitor(void *p_ud) { PWSTR file_path = nullptr; hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); if (SUCCEEDED(hr)) { - file_names.push_back(String::utf16((const char16_t *)file_path)); + file_names.push_back(String::utf16((const char16_t *)file_path).replace("\\", "/").trim_prefix(R"(\\?\)")); CoTaskMemFree(file_path); } result->Release(); diff --git a/platform/windows/godot_res_wrap.rc b/platform/windows/godot_res_wrap.rc index 27ad26cbc54..61e61004979 100644 --- a/platform/windows/godot_res_wrap.rc +++ b/platform/windows/godot_res_wrap.rc @@ -1,6 +1,11 @@ #include "core/version.h" +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif + GODOT_ICON ICON platform/windows/godot_console.ico +1 RT_MANIFEST "godot.manifest" 1 VERSIONINFO FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0 diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 95b98326198..bc42b234be4 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -90,6 +90,23 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #define GetProcAddress (void *)GetProcAddress #endif +static String fix_path(const String &p_path) { + String path = p_path; + if (p_path.is_relative_path()) { + Char16String current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw()); + path = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace("\\", "/").path_join(path); + } + path = path.simplify_path(); + path = path.replace("/", "\\"); + if (!path.is_network_share_path() && !path.begins_with(R"(\\?\)")) { + path = R"(\\?\)" + path; + } + return path; +} + static String format_error_message(DWORD id) { LPWSTR messageBuffer = nullptr; size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, @@ -300,7 +317,7 @@ Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) { } #ifdef DEBUG_ENABLED -void debug_dynamic_library_check_dependencies(const String &p_root_path, const String &p_path, HashSet &r_checked, HashSet &r_missing) { +void debug_dynamic_library_check_dependencies(const String &p_path, HashSet &r_checked, HashSet &r_missing) { if (r_checked.has(p_path)) { return; } @@ -342,15 +359,15 @@ void debug_dynamic_library_check_dependencies(const String &p_root_path, const S const IMAGE_IMPORT_DESCRIPTOR *import_desc = (const IMAGE_IMPORT_DESCRIPTOR *)ImageDirectoryEntryToData((HMODULE)loaded_image.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_IMPORT, &size); if (import_desc) { for (; import_desc->Name && import_desc->FirstThunk; import_desc++) { - char16_t full_name_wc[MAX_PATH]; + char16_t full_name_wc[32767]; const char *name_cs = (const char *)ImageRvaToVa(loaded_image.FileHeader, loaded_image.MappedAddress, import_desc->Name, nullptr); String name = String(name_cs); if (name.begins_with("api-ms-win-")) { r_checked.insert(name); - } else if (SearchPathW(nullptr, (LPCWSTR)name.utf16().get_data(), nullptr, MAX_PATH, (LPWSTR)full_name_wc, nullptr)) { - debug_dynamic_library_check_dependencies(p_root_path, String::utf16(full_name_wc), r_checked, r_missing); - } else if (SearchPathW((LPCWSTR)(p_path.get_base_dir().utf16().get_data()), (LPCWSTR)name.utf16().get_data(), nullptr, MAX_PATH, (LPWSTR)full_name_wc, nullptr)) { - debug_dynamic_library_check_dependencies(p_root_path, String::utf16(full_name_wc), r_checked, r_missing); + } else if (SearchPathW(nullptr, (LPCWSTR)name.utf16().get_data(), nullptr, 32767, (LPWSTR)full_name_wc, nullptr)) { + debug_dynamic_library_check_dependencies(String::utf16(full_name_wc), r_checked, r_missing); + } else if (SearchPathW((LPCWSTR)(p_path.get_base_dir().utf16().get_data()), (LPCWSTR)name.utf16().get_data(), nullptr, 32767, (LPWSTR)full_name_wc, nullptr)) { + debug_dynamic_library_check_dependencies(String::utf16(full_name_wc), r_checked, r_missing); } else { r_missing.insert(name); } @@ -367,7 +384,7 @@ void debug_dynamic_library_check_dependencies(const String &p_root_path, const S #endif Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data) { - String path = p_path.replace("/", "\\"); + String path = p_path; if (!FileAccess::exists(path)) { //this code exists so gdextension can load .dll files from within the executable path @@ -413,12 +430,13 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha bool has_dll_directory_api = ((add_dll_directory != nullptr) && (remove_dll_directory != nullptr)); DLL_DIRECTORY_COOKIE cookie = nullptr; + String dll_dir = ProjectSettings::get_singleton()->globalize_path(load_path.get_base_dir()); + String wpath = fix_path(dll_dir); if (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) { - String dll_dir = ProjectSettings::get_singleton()->globalize_path(load_path.get_base_dir()); - cookie = add_dll_directory((LPCWSTR)(dll_dir.utf16().get_data())); + cookie = add_dll_directory((LPCWSTR)(wpath.get_base_dir().utf16().get_data())); } - p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(load_path.utf16().get_data()), nullptr, (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); + p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(wpath.utf16().get_data()), nullptr, (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); if (!p_library_handle) { if (p_data != nullptr && p_data->generate_temp_files) { DirAccess::remove_absolute(load_path); @@ -429,7 +447,7 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha HashSet checked_libs; HashSet missing_libs; - debug_dynamic_library_check_dependencies(load_path, load_path, checked_libs, missing_libs); + debug_dynamic_library_check_dependencies(wpath, checked_libs, missing_libs); if (!missing_libs.is_empty()) { String missing; for (const String &E : missing_libs) { @@ -883,7 +901,7 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List= MAX_PATH) { + Char16String current_short_dir_name; + str_len = GetShortPathNameW((LPCWSTR)current_dir_name.ptr(), nullptr, 0); + current_short_dir_name.resize(str_len); + GetShortPathNameW((LPCWSTR)current_dir_name.ptr(), (LPWSTR)current_short_dir_name.ptrw(), current_short_dir_name.size()); + current_dir_name = current_short_dir_name; + } + + if (!CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, true, creation_flags, nullptr, (LPWSTR)current_dir_name.ptr(), si_w, &pi.pi)) { CLEAN_PIPES ERR_FAIL_V_MSG(ret, "Could not create child process: " + command); } @@ -962,7 +992,7 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { - String path = p_path.replace("/", "\\"); + String path = p_path.is_absolute_path() ? fix_path(p_path) : p_path; String command = _quote_command_line_argument(path); for (const String &E : p_arguments) { command += " " + _quote_command_line_argument(E); @@ -1000,7 +1030,19 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, creation_flags |= CREATE_NO_WINDOW; } - int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, inherit_handles, creation_flags, nullptr, nullptr, si_w, &pi.pi); + Char16String current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw()); + if (current_dir_name.size() >= MAX_PATH) { + Char16String current_short_dir_name; + str_len = GetShortPathNameW((LPCWSTR)current_dir_name.ptr(), nullptr, 0); + current_short_dir_name.resize(str_len); + GetShortPathNameW((LPCWSTR)current_dir_name.ptr(), (LPWSTR)current_short_dir_name.ptrw(), current_short_dir_name.size()); + current_dir_name = current_short_dir_name; + } + + int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, inherit_handles, creation_flags, nullptr, (LPWSTR)current_dir_name.ptr(), si_w, &pi.pi); if (!ret && r_pipe) { CloseHandle(pipe[0]); // Cleanup pipe handles. CloseHandle(pipe[1]); @@ -1064,7 +1106,7 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, } Error OS_Windows::create_process(const String &p_path, const List &p_arguments, ProcessID *r_child_id, bool p_open_console) { - String path = p_path.replace("/", "\\"); + String path = p_path.is_absolute_path() ? fix_path(p_path) : p_path; String command = _quote_command_line_argument(path); for (const String &E : p_arguments) { command += " " + _quote_command_line_argument(E); @@ -1083,7 +1125,19 @@ Error OS_Windows::create_process(const String &p_path, const List &p_arg creation_flags |= CREATE_NO_WINDOW; } - int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, creation_flags, nullptr, nullptr, si_w, &pi.pi); + Char16String current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw()); + if (current_dir_name.size() >= MAX_PATH) { + Char16String current_short_dir_name; + str_len = GetShortPathNameW((LPCWSTR)current_dir_name.ptr(), nullptr, 0); + current_short_dir_name.resize(str_len); + GetShortPathNameW((LPCWSTR)current_dir_name.ptr(), (LPWSTR)current_short_dir_name.ptrw(), current_short_dir_name.size()); + current_dir_name = current_short_dir_name; + } + + int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, creation_flags, nullptr, (LPWSTR)current_dir_name.ptr(), si_w, &pi.pi); ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command); ProcessID pid = pi.pi.dwProcessId; @@ -1451,8 +1505,8 @@ Vector OS_Windows::get_system_font_path_for_text(const String &p_font_na continue; } - WCHAR file_path[MAX_PATH]; - hr = loader->GetFilePathFromKey(reference_key, reference_key_size, &file_path[0], MAX_PATH); + WCHAR file_path[32767]; + hr = loader->GetFilePathFromKey(reference_key, reference_key_size, &file_path[0], 32767); if (FAILED(hr)) { continue; } @@ -1530,8 +1584,8 @@ String OS_Windows::get_system_font_path(const String &p_font_name, int p_weight, continue; } - WCHAR file_path[MAX_PATH]; - hr = loader->GetFilePathFromKey(reference_key, reference_key_size, &file_path[0], MAX_PATH); + WCHAR file_path[32767]; + hr = loader->GetFilePathFromKey(reference_key, reference_key_size, &file_path[0], 32767); if (FAILED(hr)) { continue; } @@ -1637,7 +1691,7 @@ Error OS_Windows::shell_show_in_file_manager(String p_path, bool p_open_folder) if (!p_path.is_quoted()) { p_path = p_path.quote(); } - p_path = p_path.replace("/", "\\"); + p_path = fix_path(p_path); INT_PTR ret = OK; if (open_folder) { @@ -1962,6 +2016,19 @@ String OS_Windows::get_system_ca_certificates() { OS_Windows::OS_Windows(HINSTANCE _hInstance) { hInstance = _hInstance; + // Reset CWD to ensure long path is used. + Char16String current_dir_name; + size_t str_len = GetCurrentDirectoryW(0, nullptr); + current_dir_name.resize(str_len + 1); + GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw()); + + Char16String new_current_dir_name; + str_len = GetLongPathNameW((LPCWSTR)current_dir_name.get_data(), nullptr, 0); + new_current_dir_name.resize(str_len + 1); + GetLongPathNameW((LPCWSTR)current_dir_name.get_data(), (LPWSTR)new_current_dir_name.ptrw(), new_current_dir_name.size()); + + SetCurrentDirectoryW((LPCWSTR)new_current_dir_name.get_data()); + #ifndef WINDOWS_SUBSYSTEM_CONSOLE RedirectIOToConsole(); #endif