diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index b86f94e9b..b0b8b5db6 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -43,7 +43,6 @@ #ifdef Q_OS_WIN #include -#include #include #endif @@ -878,153 +877,6 @@ void Preferences::disableRecursiveDownload(bool disable) } #ifdef Q_OS_WIN -namespace -{ - enum REG_SEARCH_TYPE - { - USER, - SYSTEM_32BIT, - SYSTEM_64BIT - }; - - QStringList getRegSubkeys(HKEY handle) - { - QStringList keys; - - DWORD cSubKeys = 0; - DWORD cMaxSubKeyLen = 0; - LONG res = ::RegQueryInfoKeyW(handle, NULL, NULL, NULL, &cSubKeys, &cMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); - - if (res == ERROR_SUCCESS) { - ++cMaxSubKeyLen; // For null character - LPWSTR lpName = new WCHAR[cMaxSubKeyLen]; - DWORD cName; - - for (DWORD i = 0; i < cSubKeys; ++i) { - cName = cMaxSubKeyLen; - res = ::RegEnumKeyExW(handle, i, lpName, &cName, NULL, NULL, NULL, NULL); - if (res == ERROR_SUCCESS) - keys.push_back(QString::fromWCharArray(lpName)); - } - - delete[] lpName; - } - - return keys; - } - - QString getRegValue(HKEY handle, const QString &name = QString()) - { - QString result; - - DWORD type = 0; - DWORD cbData = 0; - LPWSTR lpValueName = NULL; - if (!name.isEmpty()) { - lpValueName = new WCHAR[name.size() + 1]; - name.toWCharArray(lpValueName); - lpValueName[name.size()] = 0; - } - - // Discover the size of the value - ::RegQueryValueExW(handle, lpValueName, NULL, &type, NULL, &cbData); - DWORD cBuffer = (cbData / sizeof(WCHAR)) + 1; - LPWSTR lpData = new WCHAR[cBuffer]; - LONG res = ::RegQueryValueExW(handle, lpValueName, NULL, &type, (LPBYTE)lpData, &cbData); - if (lpValueName) - delete[] lpValueName; - - if (res == ERROR_SUCCESS) { - lpData[cBuffer - 1] = 0; - result = QString::fromWCharArray(lpData); - } - delete[] lpData; - - return result; - } - - QString pythonSearchReg(const REG_SEARCH_TYPE type) - { - HKEY hkRoot; - if (type == USER) - hkRoot = HKEY_CURRENT_USER; - else - hkRoot = HKEY_LOCAL_MACHINE; - - REGSAM samDesired = KEY_READ; - if (type == SYSTEM_32BIT) - samDesired |= KEY_WOW64_32KEY; - else if (type == SYSTEM_64BIT) - samDesired |= KEY_WOW64_64KEY; - - QString path; - LONG res = 0; - HKEY hkPythonCore; - res = ::RegOpenKeyExW(hkRoot, L"SOFTWARE\\Python\\PythonCore", 0, samDesired, &hkPythonCore); - - if (res == ERROR_SUCCESS) { - QStringList versions = getRegSubkeys(hkPythonCore); - qDebug("Python versions nb: %d", versions.size()); - versions.sort(); - - bool found = false; - while (!found && !versions.empty()) { - const QString version = versions.takeLast() + "\\InstallPath"; - LPWSTR lpSubkey = new WCHAR[version.size() + 1]; - version.toWCharArray(lpSubkey); - lpSubkey[version.size()] = 0; - - HKEY hkInstallPath; - res = ::RegOpenKeyExW(hkPythonCore, lpSubkey, 0, samDesired, &hkInstallPath); - delete[] lpSubkey; - - if (res == ERROR_SUCCESS) { - qDebug("Detected possible Python v%s location", qUtf8Printable(version)); - path = getRegValue(hkInstallPath); - ::RegCloseKey(hkInstallPath); - - if (!path.isEmpty() && QDir(path).exists("python.exe")) { - qDebug("Found python.exe at %s", qUtf8Printable(path)); - found = true; - } - } - } - - if (!found) - path = QString(); - - ::RegCloseKey(hkPythonCore); - } - - return path; - } -} - -QString Preferences::getPythonPath() -{ - QString path = pythonSearchReg(USER); - if (!path.isEmpty()) - return path; - - path = pythonSearchReg(SYSTEM_32BIT); - if (!path.isEmpty()) - return path; - - path = pythonSearchReg(SYSTEM_64BIT); - if (!path.isEmpty()) - return path; - - // Fallback: Detect python from default locations - const QStringList dirs = QDir("C:/").entryList(QStringList("Python*"), QDir::Dirs, QDir::Name | QDir::Reversed); - foreach (const QString &dir, dirs) { - const QString path("C:/" + dir + '/'); - if (QFile::exists(path + "python.exe")) - return path; - } - - return QString(); -} - bool Preferences::neverCheckFileAssoc() const { return value("Preferences/Win32/NeverCheckFileAssocation", false).toBool(); diff --git a/src/base/preferences.h b/src/base/preferences.h index 466305bb2..2c66ef04c 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -259,7 +259,6 @@ public: bool recursiveDownloadDisabled() const; void disableRecursiveDownload(bool disable = true); #ifdef Q_OS_WIN - static QString getPythonPath(); bool neverCheckFileAssoc() const; void setNeverCheckFileAssoc(bool check = true); static bool isTorrentFileAssocSet(); diff --git a/src/base/utils/foreignapps.cpp b/src/base/utils/foreignapps.cpp index 958b3186b..41ccf270e 100644 --- a/src/base/utils/foreignapps.cpp +++ b/src/base/utils/foreignapps.cpp @@ -29,11 +29,19 @@ #include "foreignapps.h" +#if defined(Q_OS_WIN) +#include +#endif + #include #include #include #include +#if defined(Q_OS_WIN) +#include +#endif + #include "base/logger.h" using namespace Utils::ForeignApps; @@ -75,6 +83,152 @@ namespace return false; } + +#if defined(Q_OS_WIN) + enum REG_SEARCH_TYPE + { + USER, + SYSTEM_32BIT, + SYSTEM_64BIT + }; + + QStringList getRegSubkeys(const HKEY handle) + { + QStringList keys; + + DWORD cSubKeys = 0; + DWORD cMaxSubKeyLen = 0; + LONG res = ::RegQueryInfoKeyW(handle, NULL, NULL, NULL, &cSubKeys, &cMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); + + if (res == ERROR_SUCCESS) { + ++cMaxSubKeyLen; // For null character + LPWSTR lpName = new WCHAR[cMaxSubKeyLen]; + DWORD cName; + + for (DWORD i = 0; i < cSubKeys; ++i) { + cName = cMaxSubKeyLen; + res = ::RegEnumKeyExW(handle, i, lpName, &cName, NULL, NULL, NULL, NULL); + if (res == ERROR_SUCCESS) + keys.push_back(QString::fromWCharArray(lpName)); + } + + delete[] lpName; + } + + return keys; + } + + QString getRegValue(const HKEY handle, const QString &name = QString()) + { + QString result; + + DWORD type = 0; + DWORD cbData = 0; + LPWSTR lpValueName = NULL; + if (!name.isEmpty()) { + lpValueName = new WCHAR[name.size() + 1]; + name.toWCharArray(lpValueName); + lpValueName[name.size()] = 0; + } + + // Discover the size of the value + ::RegQueryValueExW(handle, lpValueName, NULL, &type, NULL, &cbData); + DWORD cBuffer = (cbData / sizeof(WCHAR)) + 1; + LPWSTR lpData = new WCHAR[cBuffer]; + LONG res = ::RegQueryValueExW(handle, lpValueName, NULL, &type, (LPBYTE)lpData, &cbData); + if (lpValueName) + delete[] lpValueName; + + if (res == ERROR_SUCCESS) { + lpData[cBuffer - 1] = 0; + result = QString::fromWCharArray(lpData); + } + delete[] lpData; + + return result; + } + + QString pythonSearchReg(const REG_SEARCH_TYPE type) + { + HKEY hkRoot; + if (type == USER) + hkRoot = HKEY_CURRENT_USER; + else + hkRoot = HKEY_LOCAL_MACHINE; + + REGSAM samDesired = KEY_READ; + if (type == SYSTEM_32BIT) + samDesired |= KEY_WOW64_32KEY; + else if (type == SYSTEM_64BIT) + samDesired |= KEY_WOW64_64KEY; + + QString path; + LONG res = 0; + HKEY hkPythonCore; + res = ::RegOpenKeyExW(hkRoot, L"SOFTWARE\\Python\\PythonCore", 0, samDesired, &hkPythonCore); + + if (res == ERROR_SUCCESS) { + QStringList versions = getRegSubkeys(hkPythonCore); + qDebug("Python versions nb: %d", versions.size()); + versions.sort(); + + bool found = false; + while (!found && !versions.empty()) { + const QString version = versions.takeLast() + "\\InstallPath"; + LPWSTR lpSubkey = new WCHAR[version.size() + 1]; + version.toWCharArray(lpSubkey); + lpSubkey[version.size()] = 0; + + HKEY hkInstallPath; + res = ::RegOpenKeyExW(hkPythonCore, lpSubkey, 0, samDesired, &hkInstallPath); + delete[] lpSubkey; + + if (res == ERROR_SUCCESS) { + qDebug("Detected possible Python v%s location", qUtf8Printable(version)); + path = getRegValue(hkInstallPath); + ::RegCloseKey(hkInstallPath); + + if (!path.isEmpty() && QDir(path).exists("python.exe")) { + found = true; + path = QDir(path).filePath("python.exe"); + } + } + } + + if (!found) + path = QString(); + + ::RegCloseKey(hkPythonCore); + } + + return path; + } + + QString findPythonPath() + { + QString path = pythonSearchReg(USER); + if (!path.isEmpty()) + return path; + + path = pythonSearchReg(SYSTEM_32BIT); + if (!path.isEmpty()) + return path; + + path = pythonSearchReg(SYSTEM_64BIT); + if (!path.isEmpty()) + return path; + + // Fallback: Detect python from default locations + const QFileInfoList dirs = QDir("C:/").entryInfoList({"Python*"}, QDir::Dirs, (QDir::Name | QDir::Reversed)); + for (const QFileInfo &info : dirs) { + const QString path {info.absolutePath() + "/python.exe"}; + if (QFile::exists(path)) + return path; + } + + return QString(); + } +#endif } bool Utils::ForeignApps::PythonInfo::isValid() const @@ -99,6 +253,11 @@ PythonInfo Utils::ForeignApps::pythonInfo() if (testPythonInstallation("python", pyInfo)) return pyInfo; +#if defined(Q_OS_WIN) + if (testPythonInstallation(findPythonPath(), pyInfo)) + return pyInfo; +#endif + LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Python not detected"), Log::INFO); }