Merge pull request #8393 from CookiePLMonster/long-paths

Support Windows 10 long paths
This commit is contained in:
Léo Lam
2019-11-09 21:10:16 +01:00
committed by GitHub
12 changed files with 105 additions and 91 deletions

View File

@ -26,6 +26,7 @@
#include <IOKit/storage/IOMedia.h> #include <IOKit/storage/IOMedia.h>
#include <paths.h> #include <paths.h>
#else #else
#include <climits>
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -211,7 +212,7 @@ bool IsCDROMDevice(std::string device)
#ifndef _WIN32 #ifndef _WIN32
// Resolve symbolic links. This allows symbolic links to valid // Resolve symbolic links. This allows symbolic links to valid
// drives to be passed from the command line with the -e flag. // drives to be passed from the command line with the -e flag.
char resolved_path[MAX_PATH]; char resolved_path[PATH_MAX];
char* devname = realpath(device.c_str(), resolved_path); char* devname = realpath(device.c_str(), resolved_path);
if (!devname) if (!devname)
return false; return false;

View File

@ -30,12 +30,6 @@ struct CrtDebugBreak
#endif #endif
// Windows compatibility
#ifndef _WIN32
#include <limits.h>
#define MAX_PATH PATH_MAX
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
#define __getcwd _getcwd #define __getcwd _getcwd
#define __chdir _chdir #define __chdir _chdir

View File

@ -49,4 +49,27 @@ std::string GetLastErrorString()
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, BUFFER_SIZE, nullptr); MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, BUFFER_SIZE, nullptr);
return std::string(error_message); return std::string(error_message);
} }
// Obtains a full path to the specified module.
std::optional<std::wstring> GetModuleName(void* hInstance)
{
DWORD max_size = 50; // Start with space for 50 characters and grow if needed
std::wstring name(max_size, L'\0');
DWORD size;
while ((size = GetModuleFileNameW(static_cast<HMODULE>(hInstance), name.data(), max_size)) ==
max_size &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
max_size *= 2;
name.resize(max_size);
}
if (size == 0)
{
return std::nullopt;
}
name.resize(size);
return name;
}
#endif #endif

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <optional>
#include <string> #include <string>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -47,4 +48,7 @@ std::string LastStrerrorString();
// Wrapper function to get GetLastError() string. // Wrapper function to get GetLastError() string.
// This function might change the error code. // This function might change the error code.
std::string GetLastErrorString(); std::string GetLastErrorString();
// Obtains a full path to the specified module.
std::optional<std::wstring> GetModuleName(void* hInstance);
#endif #endif

View File

@ -4,12 +4,14 @@
#include <Windows.h> #include <Windows.h>
#include <functional> #include <functional>
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <winternl.h> #include <winternl.h>
#include <fmt/format.h> #include <fmt/format.h>
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/LdrWatcher.h" #include "Common/LdrWatcher.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -162,37 +164,26 @@ struct Version
} }
}; };
static bool GetModulePath(const wchar_t* name, std::wstring* path) static std::optional<std::wstring> GetModulePath(const wchar_t* name)
{ {
auto module = GetModuleHandleW(name); auto module = GetModuleHandleW(name);
if (module == nullptr) if (module == nullptr)
return false; return std::nullopt;
DWORD path_len = MAX_PATH;
retry: return GetModuleName(module);
path->resize(path_len);
path_len = GetModuleFileNameW(module, const_cast<wchar_t*>(path->data()),
static_cast<DWORD>(path->size()));
if (!path_len)
return false;
auto error = GetLastError();
if (error == ERROR_SUCCESS)
return true;
if (error == ERROR_INSUFFICIENT_BUFFER)
goto retry;
return false;
} }
static bool GetModuleVersion(const wchar_t* name, Version* version) static bool GetModuleVersion(const wchar_t* name, Version* version)
{ {
std::wstring path; auto path = GetModulePath(name);
if (!GetModulePath(name, &path)) if (!path)
return false; return false;
DWORD handle; DWORD handle;
DWORD data_len = GetFileVersionInfoSizeW(path.c_str(), &handle); DWORD data_len = GetFileVersionInfoSizeW(path->c_str(), &handle);
if (!data_len) if (!data_len)
return false; return false;
std::vector<u8> block(data_len); std::vector<u8> block(data_len);
if (!GetFileVersionInfoW(path.c_str(), handle, data_len, block.data())) if (!GetFileVersionInfoW(path->c_str(), handle, data_len, block.data()))
return false; return false;
void* buf; void* buf;
UINT buf_len; UINT buf_len;

View File

@ -639,19 +639,21 @@ std::string CreateTempDir()
#endif #endif
} }
std::string GetTempFilenameForAtomicWrite(const std::string& path) std::string GetTempFilenameForAtomicWrite(std::string path)
{ {
std::string abs = path;
#ifdef _WIN32 #ifdef _WIN32
TCHAR absbuf[MAX_PATH]; std::unique_ptr<TCHAR[], decltype(&std::free)> absbuf{
if (_tfullpath(absbuf, UTF8ToTStr(path).c_str(), MAX_PATH) != nullptr) _tfullpath(nullptr, UTF8ToTStr(path).c_str(), 0), std::free};
abs = TStrToUTF8(absbuf); if (absbuf != nullptr)
{
path = TStrToUTF8(absbuf.get());
}
#else #else
char absbuf[PATH_MAX]; char absbuf[PATH_MAX];
if (realpath(path.c_str(), absbuf) != nullptr) if (realpath(path.c_str(), absbuf) != nullptr)
abs = absbuf; path = absbuf;
#endif #endif
return abs + ".xxx"; return std::move(path) + ".xxx";
} }
#if defined(__APPLE__) #if defined(__APPLE__)
@ -672,22 +674,26 @@ std::string GetBundleDirectory()
std::string GetExePath() std::string GetExePath()
{ {
static std::string dolphin_path; static const std::string dolphin_path = [] {
if (dolphin_path.empty()) std::string result;
{
#ifdef _WIN32 #ifdef _WIN32
TCHAR dolphin_exe_path[2048]; auto dolphin_exe_path = GetModuleName(nullptr);
TCHAR dolphin_exe_expanded_path[MAX_PATH]; if (dolphin_exe_path)
GetModuleFileName(nullptr, dolphin_exe_path, ARRAYSIZE(dolphin_exe_path)); {
if (_tfullpath(dolphin_exe_expanded_path, dolphin_exe_path, std::unique_ptr<TCHAR[], decltype(&std::free)> dolphin_exe_expanded_path{
ARRAYSIZE(dolphin_exe_expanded_path)) != nullptr) _tfullpath(nullptr, dolphin_exe_path->c_str(), 0), std::free};
dolphin_path = TStrToUTF8(dolphin_exe_expanded_path); if (dolphin_exe_expanded_path)
else {
dolphin_path = TStrToUTF8(dolphin_exe_path); result = TStrToUTF8(dolphin_exe_expanded_path.get());
}
else
{
result = TStrToUTF8(*dolphin_exe_path);
}
}
#elif defined(__APPLE__) #elif defined(__APPLE__)
dolphin_path = GetBundleDirectory(); result = GetBundleDirectory();
dolphin_path = result = result.substr(0, result.find_last_of("Dolphin.app/Contents/MacOS") + 1);
dolphin_path.substr(0, dolphin_path.find_last_of("Dolphin.app/Contents/MacOS") + 1);
#else #else
char dolphin_exe_path[PATH_MAX]; char dolphin_exe_path[PATH_MAX];
ssize_t len = ::readlink("/proc/self/exe", dolphin_exe_path, sizeof(dolphin_exe_path)); ssize_t len = ::readlink("/proc/self/exe", dolphin_exe_path, sizeof(dolphin_exe_path));
@ -696,9 +702,10 @@ std::string GetExePath()
len = 0; len = 0;
} }
dolphin_exe_path[len] = '\0'; dolphin_exe_path[len] = '\0';
dolphin_path = dolphin_exe_path; result = dolphin_exe_path;
#endif #endif
} return result;
}();
return dolphin_path; return dolphin_path;
} }

View File

@ -174,7 +174,7 @@ bool SetCurrentDir(const std::string& directory);
std::string CreateTempDir(); std::string CreateTempDir();
// Get a filename that can hopefully be atomically renamed to the given path. // Get a filename that can hopefully be atomically renamed to the given path.
std::string GetTempFilenameForAtomicWrite(const std::string& path); std::string GetTempFilenameForAtomicWrite(std::string path);
// Gets a set user directory path // Gets a set user directory path
// Don't call prior to setting the base user directory // Don't call prior to setting the base user directory

View File

@ -4,6 +4,8 @@
#include "Core/PowerPC/Jit64/JitAsm.h" #include "Core/PowerPC/Jit64/JitAsm.h"
#include <climits>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/JitRegister.h" #include "Common/JitRegister.h"
#include "Common/x64ABI.h" #include "Common/x64ABI.h"

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings> <windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> <dpiAware>true</dpiAware>
<longPathAware>true</longPathAware>
</windowsSettings> </windowsSettings>
</application> </application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">

View File

@ -226,46 +226,57 @@ void SetUserDirectory(const std::string& custom_path)
// -> Use GetExeDirectory()\User // -> Use GetExeDirectory()\User
// Check our registry keys // Check our registry keys
// TODO: Maybe use WIL when it's available?
HKEY hkey; HKEY hkey;
DWORD local = 0; DWORD local = 0;
TCHAR configPath[MAX_PATH] = {0}; std::unique_ptr<TCHAR[]> configPath;
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Dolphin Emulator"), 0, KEY_QUERY_VALUE, if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Dolphin Emulator"), 0, KEY_QUERY_VALUE,
&hkey) == ERROR_SUCCESS) &hkey) == ERROR_SUCCESS)
{ {
DWORD size = 4; DWORD size = 4;
if (RegQueryValueEx(hkey, TEXT("LocalUserConfig"), nullptr, nullptr, if (RegQueryValueEx(hkey, TEXT("LocalUserConfig"), nullptr, nullptr,
reinterpret_cast<LPBYTE>(&local), &size) != ERROR_SUCCESS) reinterpret_cast<LPBYTE>(&local), &size) != ERROR_SUCCESS)
{
local = 0; local = 0;
}
size = 0;
RegQueryValueEx(hkey, TEXT("UserConfigPath"), nullptr, nullptr, nullptr, &size);
configPath = std::make_unique<TCHAR[]>(size / sizeof(TCHAR));
if (RegQueryValueEx(hkey, TEXT("UserConfigPath"), nullptr, nullptr,
reinterpret_cast<LPBYTE>(configPath.get()), &size) != ERROR_SUCCESS)
{
configPath.reset();
}
size = MAX_PATH;
if (RegQueryValueEx(hkey, TEXT("UserConfigPath"), nullptr, nullptr, (LPBYTE)configPath,
&size) != ERROR_SUCCESS)
configPath[0] = 0;
RegCloseKey(hkey); RegCloseKey(hkey);
} }
local = local || File::Exists(File::GetExeDirectory() + DIR_SEP "portable.txt"); local = local != 0 || File::Exists(File::GetExeDirectory() + DIR_SEP "portable.txt");
// Get Program Files path in case we need it. // Get Documents path in case we need it.
TCHAR my_documents[MAX_PATH]; // TODO: Maybe use WIL when it's available?
bool my_documents_found = SUCCEEDED( PWSTR my_documents = nullptr;
SHGetFolderPath(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, my_documents)); bool my_documents_found =
SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, nullptr, &my_documents));
if (local) // Case 1-2 if (local) // Case 1-2
user_path = File::GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; user_path = File::GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
else if (configPath[0]) // Case 3 else if (configPath) // Case 3
user_path = TStrToUTF8(configPath); user_path = TStrToUTF8(configPath.get());
else if (my_documents_found) // Case 4 else if (my_documents_found) // Case 4
user_path = TStrToUTF8(my_documents) + DIR_SEP "Dolphin Emulator" DIR_SEP; user_path = TStrToUTF8(my_documents) + DIR_SEP "Dolphin Emulator" DIR_SEP;
else // Case 5 else // Case 5
user_path = File::GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; user_path = File::GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
CoTaskMemFree(my_documents);
// Prettify the path: it will be displayed in some places, we don't want a mix // Prettify the path: it will be displayed in some places, we don't want a mix
// of \ and /. // of \ and /.
user_path = ReplaceAll(user_path, "\\", DIR_SEP); user_path = ReplaceAll(std::move(user_path), "\\", DIR_SEP);
// Make sure it ends in DIR_SEP. // Make sure it ends in DIR_SEP.
if (*user_path.rbegin() != DIR_SEP_CHR) if (user_path.back() != DIR_SEP_CHR)
user_path += DIR_SEP; user_path += DIR_SEP;
#else #else
@ -340,7 +351,7 @@ void SetUserDirectory(const std::string& custom_path)
#endif #endif
} }
#endif #endif
File::SetUserPath(D_USER_IDX, user_path); File::SetUserPath(D_USER_IDX, std::move(user_path));
} }
void SaveWiimoteSources() void SaveWiimoteSources()

View File

@ -10,6 +10,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "Common/CommonFuncs.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "UpdaterCommon/UI.h" #include "UpdaterCommon/UI.h"
@ -33,28 +34,6 @@ std::vector<std::string> CommandLineToUtf8Argv(PCWSTR command_line)
LocalFree(tokenized); LocalFree(tokenized);
return argv; return argv;
} }
std::optional<std::wstring> GetModuleName(HINSTANCE hInstance)
{
std::wstring name;
DWORD max_size = 50; // Start with space for 50 characters and grow if needed
name.resize(max_size);
DWORD size;
while ((size = GetModuleFileNameW(hInstance, name.data(), max_size)) == max_size &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
max_size *= 2;
name.resize(max_size);
}
if (size == 0)
{
return {};
}
name.resize(size);
return name;
}
}; // namespace }; // namespace
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)

View File

@ -29,8 +29,9 @@
</compatibility> </compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings <asmv3:windowsSettings
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAware>True</dpiAware> <dpiAware>true</dpiAware>
<longPathAware>true</longPathAware>
</asmv3:windowsSettings> </asmv3:windowsSettings>
</asmv3:application> </asmv3:application>
</assembly> </assembly>