From 24aee49792c149f080eee37171ba049d0a618961 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 3 Jun 2019 14:51:12 -0400 Subject: remember the monitor number use it to display the splash screen on the same monitor as the main window instead of where the mouse cursor is --- src/main.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c012b037..6f304944 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -597,6 +597,17 @@ int runApplication(MOApplication &application, SingleInstance &instance, QPixmap pixmap(splashPath); QSplashScreen splash(pixmap); + + if (settings.contains("window_monitor")) { + const int monitor = settings.value("window_monitor").toInt(); + + if (monitor != -1) { + QDesktopWidget* desktop = QApplication::desktop(); + const QPoint center = desktop->availableGeometry(monitor).center(); + splash.move(center - splash.rect().center()); + } + } + splash.show(); splash.activateWindow(); -- cgit v1.3.1 From eab0ec298b81138c4c602259ebf930f583113b95 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 29 Jun 2019 15:26:04 -0400 Subject: refactored preloadSsl() into preloadDll() removed old HGID check moved formatSystemMessage() to uibase added Environment class, lists loaded modules, logged at startup --- src/main.cpp | 70 ++++++----- src/settings.cpp | 31 ----- src/shared/util.cpp | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/shared/util.h | 50 +++++++- 4 files changed, 418 insertions(+), 60 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6f304944..e1a6b853 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -411,27 +411,39 @@ void setupPath() ::SetEnvironmentVariableW(L"PATH", newPath.c_str()); } -static void preloadSsl() +void preloadDll(const QString& filename) { - QString appPath = QDir::toNativeSeparators(QCoreApplication::applicationDirPath()); - if (GetModuleHandleA("libeay32.dll")) - qWarning("libeay32.dll already loaded?!"); - else { - QString libeay32 = appPath + "\\libeay32.dll"; - if (!QFile::exists(libeay32)) - qWarning("libeay32.dll not found: %s", qUtf8Printable(libeay32)); - else if (!LoadLibraryW(libeay32.toStdWString().c_str())) - qWarning("failed to load: %s, %d", qUtf8Printable(libeay32), GetLastError()); + qDebug().nospace() << "preloading " << filename; + + if (!GetModuleHandleW(filename.toStdWString().c_str())) { + // already loaded, this can happen when "restarting" MO by switching + // instances, for example + return; } - if (GetModuleHandleA("ssleay32.dll")) - qWarning("ssleay32.dll already loaded?!"); - else { - QString ssleay32 = appPath + "\\ssleay32.dll"; - if (!QFile::exists(ssleay32)) - qWarning("ssleay32.dll not found: %s", qUtf8Printable(ssleay32)); - else if (!LoadLibraryW(ssleay32.toStdWString().c_str())) - qWarning("failed to load: %s, %d", qUtf8Printable(ssleay32), GetLastError()); + + const auto appPath = QDir::toNativeSeparators( + QCoreApplication::applicationDirPath()); + + const auto dllPath = appPath + "\\" + filename; + + if (!QFile::exists(dllPath)) { + qWarning().nospace() << dllPath << "not found"; + return; } + + if (!LoadLibraryW(dllPath.toStdWString().c_str())) { + const auto e = GetLastError(); + + qWarning().nospace() + << "failed to load " << dllPath << ": " + << formatSystemMessage(e); + } +} + +void preloadSsl() +{ + preloadDll("libeay32.dll"); + preloadDll("ssleay32.dll"); } static QString getVersionDisplayString() @@ -443,15 +455,9 @@ int runApplication(MOApplication &application, SingleInstance &instance, const QString &splashPath) { - qDebug("Starting Mod Organizer version %s revision %s", qUtf8Printable(getVersionDisplayString()), -#if defined(HGID) - HGID -#elif defined(GITID) - GITID -#else - "unknown" -#endif - ); + qDebug().nospace() + << "Starting Mod Organizer version " + << getVersionDisplayString() << " revision " << GITID; #if !defined(QT_NO_SSL) preloadSsl(); @@ -460,6 +466,16 @@ int runApplication(MOApplication &application, SingleInstance &instance, qDebug("non-ssl build"); #endif + { + Environment env; + + for (const auto& m : env.loadedModules()) { + qInfo().nospace().noquote() << " . " << m.toString(); + } + + return 0; + } + QString dataPath = application.property("dataPath").toString(); qDebug("data path: %s", qUtf8Printable(dataPath)); diff --git a/src/settings.cpp b/src/settings.cpp index b45912d4..59d3369a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -82,37 +82,6 @@ private: }; -QString formatSystemMessage(DWORD id) -{ - wchar_t* message = nullptr; - - const auto ret = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - id, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&message), - 0, NULL); - - QString s; - const QString idString = QString("0x%1").arg(id, 0, 16); - - if (ret == 0 || !message) { - s = idString; - } else { - s = QString("%1 (%2)") - .arg(QString::fromStdWString(message).trimmed()) - .arg(idString); - } - - LocalFree(message); - - return s; -} - - Settings *Settings::s_Instance = nullptr; diff --git a/src/shared/util.cpp b/src/shared/util.cpp index ed7c434e..16b373db 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -20,6 +20,7 @@ along with Mod Organizer. If not, see . #include "util.h" #include "windows_error.h" #include "error_report.h" +#include #include #include @@ -29,6 +30,8 @@ along with Mod Organizer. If not, see . #include #include +using MOBase::formatSystemMessage; + namespace MOShared { @@ -253,4 +256,328 @@ MOBase::VersionInfo createVersionInfo() } +struct HandleCloser +{ + using pointer = HANDLE; + void operator()(HANDLE h) + { + if (h != INVALID_HANDLE_VALUE) { + ::CloseHandle(h); + } + } +}; + + +Environment::Environment() +{ + getLoadedModules(); +} + +const std::vector& Environment::loadedModules() +{ + return m_modules; +} + +void Environment::getLoadedModules() +{ + std::unique_ptr snapshot(CreateToolhelp32Snapshot( + TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId())); + + if (snapshot.get() == INVALID_HANDLE_VALUE) + { + const auto e = GetLastError(); + + qCritical().nospace() + << "CreateToolhelp32Snapshot() failed, " + << formatSystemMessage(e); + + return; + } + + // Set the size of the structure before using it. + MODULEENTRY32 me = {}; + me.dwSize = sizeof(me); + + // Retrieve information about the first module, + // and exit if unsuccessful + if (!Module32First(snapshot.get(), &me)) + { + const auto e = GetLastError(); + + qCritical().nospace() + << "Module32First() failed, " << formatSystemMessage(e); + + return; + } + + // Now walk the module list of the process, + // and display information about each module + qInfo() << "modules loaded in process:"; + + for (;;) + { + const auto path = QString::fromWCharArray(me.szExePath); + + m_modules.push_back(Module(path, me.modBaseSize)); + + if (!Module32Next(snapshot.get(), &me)) { + const auto e = GetLastError(); + + if (e != ERROR_NO_MORE_FILES) { + qCritical() << "Module32Next() failed, " << formatSystemMessage(e); + } + + break; + } + } +} + + +Environment::Module::Module(QString path, std::size_t fileSize) + : m_path(std::move(path)), m_fileSize(fileSize) +{ + const auto fi = getFileInfo(); + + m_version = getVersion(fi.ffi); + m_timestamp = getTimestamp(fi.ffi); + m_versionString = fi.fileDescription; +} + +const QString& Environment::Module::path() const +{ + return m_path; +} + +std::size_t Environment::Module::fileSize() const +{ + return m_fileSize; +} + +const QString& Environment::Module::version() const +{ + return m_version; +} + +const QString& Environment::Module::versionString() const +{ + return m_versionString; +} + +QString Environment::Module::timestampString() const +{ + if (!m_timestamp.isValid()) { + return "(no timestamp)"; + } + + return m_timestamp.toString(Qt::DateFormat::ISODate); +} + +QString Environment::Module::toString() const +{ + QStringList sl; + + sl.push_back(m_path); + sl.push_back(QString("%1 B").arg(m_fileSize)); + + if (m_version.isEmpty() && m_versionString.isEmpty()) { + sl.push_back("(no version)"); + } else { + if (!m_version.isEmpty()) { + sl.push_back(m_version); + } + + if (m_versionString != m_version) { + sl.push_back(versionString()); + } + } + + if (m_timestamp.isValid()) { + sl.push_back(m_timestamp.toString(Qt::DateFormat::ISODate)); + } else { + sl.push_back("(no timestamp)"); + } + + return sl.join(", "); +} + +Environment::Module::FileInfo Environment::Module::getFileInfo() const +{ + const auto wspath = m_path.toStdWString(); + + DWORD dummy = 0; + const DWORD size = GetFileVersionInfoSizeW(wspath.c_str(), &dummy); + + if (size == 0) { + const auto e = GetLastError(); + + if (e == ERROR_RESOURCE_TYPE_NOT_FOUND) { + // not an error, no version information built into that module + return {}; + } + + qCritical().nospace().noquote() + << "GetFileVersionInfoSizeW() failed on '" << m_path << "', " + << formatSystemMessage(e); + + return {}; + } + + auto buffer = std::make_unique(size); + + if (!GetFileVersionInfoW(wspath.c_str(), 0, size, buffer.get())) { + const auto e = GetLastError(); + + qCritical().nospace().noquote() + << "GetFileVersionInfoW() failed on '" << m_path << "', " + << formatSystemMessage(e); + + return {}; + } + + + FileInfo fi; + fi.ffi = getFixedFileInfo(buffer.get()); + fi.fileDescription = getFileDescription(buffer.get()); + + return fi; +} + +VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const +{ + void* valuePointer = nullptr; + unsigned int valueSize = 0; + + const auto ret = VerQueryValueW(buffer, L"\\", &valuePointer, &valueSize); + + if (!ret || !valuePointer || valueSize == 0) { + // not an error, no fixed file info + return {}; + } + + const auto* fi = reinterpret_cast(valuePointer); + + if (fi->dwSignature != 0xfeef04bd) { + qCritical().nospace().noquote() + << "bad file info signature 0x" << hex << fi->dwSignature << " for " + << "'" << m_path << "'"; + + return {}; + } + + return *fi; +} + +QString Environment::Module::getFileDescription(std::byte* buffer) const +{ + struct LANGANDCODEPAGE + { + WORD wLanguage; + WORD wCodePage; + }; + + void* valuePointer = nullptr; + unsigned int valueSize = 0; + + auto ret = VerQueryValueW( + buffer, L"\\VarFileInfo\\Translation", &valuePointer, &valueSize); + + if (!ret || !valuePointer || valueSize == 0) { + qCritical().nospace().noquote() + << "VerQueryValueW() for translations failed on '" << m_path << "'"; + + return {}; + } + + const auto count = valueSize / sizeof(LANGANDCODEPAGE); + if (count == 0) { + return {}; + } + + const auto* lcp = reinterpret_cast(valuePointer); + + const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion") + .arg(lcp->wLanguage, 4, 16, QChar('0')) + .arg(lcp->wCodePage, 4, 16, QChar('0')); + + ret = VerQueryValueW( + buffer, subBlock.toStdWString().c_str(), &valuePointer, &valueSize); + + if (!ret || !valuePointer || valueSize == 0) { + // not an error, no file version + return {}; + } + + // valueSize includes the null terminator + return QString::fromWCharArray( + reinterpret_cast(valuePointer), valueSize - 1); +} + +QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const +{ + if (fi.dwSignature == 0) { + return {}; + } + + const DWORD major = (fi.dwFileVersionMS >> 16 ) & 0xffff; + const DWORD minor = (fi.dwFileVersionMS >> 0 ) & 0xffff; + const DWORD maintenance = (fi.dwFileVersionLS >> 16 ) & 0xffff; + const DWORD build = (fi.dwFileVersionLS >> 0 ) & 0xffff; + + if (major == 0 && minor == 0 && maintenance == 0 && build == 0) { + return {}; + } + + return QString("%1.%2.%3.%4") + .arg(major).arg(minor).arg(maintenance).arg(build); +} + +QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const +{ + FILETIME ft = {}; + + if (fi.dwSignature == 0 || (fi.dwFileDateMS == 0 && fi.dwFileDateLS == 0)) { + std::unique_ptr h(CreateFileW( + m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)); + + if (h.get() == INVALID_HANDLE_VALUE) { + const auto e = GetLastError(); + + qCritical() + << "can't open file '" << m_path << "' for timestamp, " + << formatSystemMessage(e); + + return {}; + } + + if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) { + const auto e = GetLastError(); + qCritical() + << "can't get file time for '" << m_path << "', " + << formatSystemMessage(e); + + return {}; + } + } else { + ft.dwHighDateTime = fi.dwFileDateMS; + ft.dwLowDateTime = fi.dwFileDateLS; + } + + + SYSTEMTIME utc = {}; + if (!FileTimeToSystemTime(&ft, &utc)) { + qCritical() + << "FileTimeToSystemTime() failed on timestamp " + << "high=0x" << hex << ft.dwHighDateTime << " " + << "low=0x" << hex << ft.dwLowDateTime << " for " + << "'" << m_path << "'"; + + return {}; + } + + return QDateTime( + QDate(utc.wYear, utc.wMonth, utc.wDay), + QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds)); +} + } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index 1fdfb089..5d88481c 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -46,8 +46,54 @@ std::wstring ToLower(const std::wstring &text); bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); -VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName); -std::wstring GetFileVersionString(const std::wstring &fileName); +class Environment +{ +public: + class Module + { + public: + explicit Module(QString path, std::size_t fileSize); + + const QString& path() const; + std::size_t fileSize() const; + const QString& version() const; + const QString& versionString() const; + QString timestampString() const; + + QString toString() const; + + private: + struct FileInfo + { + VS_FIXEDFILEINFO ffi; + QString fileDescription; + }; + + QString m_path; + std::size_t m_fileSize; + QString m_version; + QDateTime m_timestamp; + QString m_versionString; + + FileInfo getFileInfo() const; + + QString getVersion(const VS_FIXEDFILEINFO& fi) const; + QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const; + + VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const; + QString getFileDescription(std::byte* buffer) const; + }; + + Environment(); + + const std::vector& loadedModules(); + +private: + std::vector m_modules; + + void getLoadedModules(); +}; + MOBase::VersionInfo createVersionInfo(); } // namespace MOShared -- cgit v1.3.1 From fb6512b72ebf86d5273744774388deb14c8a21ae Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 29 Jun 2019 18:16:00 -0400 Subject: log windows version --- src/main.cpp | 6 +- src/shared/util.cpp | 211 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/shared/util.h | 94 +++++++++++++++-------- 3 files changed, 264 insertions(+), 47 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e1a6b853..75148c4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -467,8 +467,12 @@ int runApplication(MOApplication &application, SingleInstance &instance, #endif { - Environment env; + env::Environment env; + qInfo().nospace().noquote() + << "windows: " << env.windowsVersion().toString(); + + qInfo() << "modules loaded in process:"; for (const auto& m : env.loadedModules()) { qInfo().nospace().noquote() << " . " << m.toString(); } diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 772d3ca5..9337ebf6 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -256,9 +256,13 @@ MOBase::VersionInfo createVersionInfo() } +namespace env +{ + struct HandleCloser { using pointer = HANDLE; + void operator()(HANDLE h) { if (h != INVALID_HANDLE_VALUE) { @@ -267,17 +271,34 @@ struct HandleCloser } }; +struct LibraryFreer +{ + using pointer = HINSTANCE; + + void operator()(HINSTANCE h) + { + if (h != 0) { + ::FreeLibrary(h); + } + } +}; + Environment::Environment() { getLoadedModules(); } -const std::vector& Environment::loadedModules() +const std::vector& Environment::loadedModules() { return m_modules; } +const WindowsVersion& Environment::windowsVersion() const +{ + return m_windows; +} + void Environment::getLoadedModules() { std::unique_ptr snapshot(CreateToolhelp32Snapshot( @@ -312,7 +333,6 @@ void Environment::getLoadedModules() // Now walk the module list of the process, // and display information about each module - qInfo() << "modules loaded in process:"; for (;;) { @@ -338,7 +358,7 @@ void Environment::getLoadedModules() } -Environment::Module::Module(QString path, std::size_t fileSize) +Module::Module(QString path, std::size_t fileSize) : m_path(std::move(path)), m_fileSize(fileSize) { const auto fi = getFileInfo(); @@ -349,32 +369,32 @@ Environment::Module::Module(QString path, std::size_t fileSize) m_md5 = getMD5(); } -const QString& Environment::Module::path() const +const QString& Module::path() const { return m_path; } -QString Environment::Module::displayPath() const +QString Module::displayPath() const { return QDir::fromNativeSeparators(m_path.toLower()); } -std::size_t Environment::Module::fileSize() const +std::size_t Module::fileSize() const { return m_fileSize; } -const QString& Environment::Module::version() const +const QString& Module::version() const { return m_version; } -const QString& Environment::Module::versionString() const +const QString& Module::versionString() const { return m_versionString; } -QString Environment::Module::timestampString() const +QString Module::timestampString() const { if (!m_timestamp.isValid()) { return "(no timestamp)"; @@ -383,7 +403,7 @@ QString Environment::Module::timestampString() const return m_timestamp.toString(Qt::DateFormat::ISODate); } -QString Environment::Module::toString() const +QString Module::toString() const { QStringList sl; @@ -415,7 +435,7 @@ QString Environment::Module::toString() const return sl.join(", "); } -Environment::Module::FileInfo Environment::Module::getFileInfo() const +Module::FileInfo Module::getFileInfo() const { const auto wspath = m_path.toStdWString(); @@ -457,7 +477,7 @@ Environment::Module::FileInfo Environment::Module::getFileInfo() const return fi; } -VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const +VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const { void* valuePointer = nullptr; unsigned int valueSize = 0; @@ -482,7 +502,7 @@ VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const return *fi; } -QString Environment::Module::getFileDescription(std::byte* buffer) const +QString Module::getFileDescription(std::byte* buffer) const { struct LANGANDCODEPAGE { @@ -527,7 +547,7 @@ QString Environment::Module::getFileDescription(std::byte* buffer) const reinterpret_cast(valuePointer), valueSize - 1); } -QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const +QString Module::getVersion(const VS_FIXEDFILEINFO& fi) const { if (fi.dwSignature == 0) { return {}; @@ -546,7 +566,7 @@ QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const .arg(major).arg(minor).arg(maintenance).arg(build); } -QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const +QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const { FILETIME ft = {}; @@ -595,7 +615,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds)); } -QString Environment::Module::getMD5() const +QString Module::getMD5() const { if (m_path.contains("\\windows\\", Qt::CaseInsensitive)) { // don't calculate md5 for system files, it's not really relevant and @@ -623,4 +643,163 @@ QString Environment::Module::getMD5() const return hash.result().toHex(); } + +WindowsVersion::WindowsVersion() : + m_realMajor(0), m_realMinor(0), m_realBuild(0), + m_major(0), m_minor(0), m_build(0), m_UBR(0) +{ + getVersion(); + getRelease(); + getElevated(); +} + +QString WindowsVersion::toString() const +{ + QStringList sl; + + const QString version = QString("%1.%2.%3") + .arg(m_major).arg(m_minor).arg(m_build); + + const QString realVersion = QString("%1.%2.%3") + .arg(m_realMajor).arg(m_realMinor).arg(m_realBuild); + + sl.push_back("version: " + version); + + if (m_realMajor != m_major || m_realMinor != m_minor || m_realBuild != m_build) { + sl.push_back("real version: " + realVersion); + } + + if (!m_buildLab.isEmpty()) { + sl.push_back(m_buildLab); + } + + if (!m_productName.isEmpty()) { + sl.push_back(m_productName); + } + + if (!m_releaseID.isEmpty()) { + sl.push_back("build " + m_releaseID); + } + + if (m_UBR != 0) { + sl.push_back(QString("%1").arg(m_UBR)); + } + + QString elevated = "?"; + if (m_elevated.has_value()) { + elevated = (*m_elevated ? "yes" : "no"); + } + + sl.push_back("elevated: " + elevated); + + return sl.join(", "); +} + +void WindowsVersion::getVersion() +{ + std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll")); + + if (!ntdll) { + qCritical() << "failed to load ntdll.dll while getting version"; + return; + } + + getRealVersion(ntdll.get()); + getReportedVersion(ntdll.get()); +} + +void WindowsVersion::getRealVersion(HINSTANCE ntdll) +{ + using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*); + + auto* RtlGetNtVersionNumbers = reinterpret_cast( + GetProcAddress(ntdll, "RtlGetNtVersionNumbers")); + + if (RtlGetNtVersionNumbers) { + DWORD build = 0; + RtlGetNtVersionNumbers(&m_realMajor, &m_realMinor, &build); + + m_realBuild = 0x0fffffff & build; + } +} + +void WindowsVersion::getReportedVersion(HINSTANCE ntdll) +{ + using RtlGetVersionType = NTSTATUS (NTAPI)(PRTL_OSVERSIONINFOW); + + auto* RtlGetVersion = reinterpret_cast( + GetProcAddress(ntdll, "RtlGetVersion")); + + if (!RtlGetVersion) { + qCritical() << "RtlGetVersion() not found in ntdll.dll"; + return; + } + + OSVERSIONINFOEX vi = {}; + vi.dwOSVersionInfoSize = sizeof(vi); + + RtlGetVersion((RTL_OSVERSIONINFOW*)&vi); + + m_major = vi.dwMajorVersion; + m_minor = vi.dwMinorVersion; + m_build = vi.dwBuildNumber; +} + +void WindowsVersion::getRelease() +{ + QSettings settings( + R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)", + QSettings::NativeFormat); + + m_buildLab = settings.value("BuildLabEx", "").toString(); + if (m_buildLab.isEmpty()) { + m_buildLab = settings.value("BuildLab", "").toString(); + if (m_buildLab.isEmpty()) { + m_buildLab = settings.value("BuildBranch", "").toString(); + } + } + + m_productName = settings.value("ProductName", "").toString(); + m_releaseID = settings.value("ReleaseId", "").toString(); + m_UBR = settings.value("UBR", 0).toUInt(); +} + +void WindowsVersion::getElevated() +{ + std::unique_ptr token; + + { + HANDLE rawToken = 0; + + if (!OpenProcessToken(GetCurrentProcess( ), TOKEN_QUERY, &rawToken)) { + const auto e = GetLastError(); + + qCritical() + << "while trying to check if process is elevated, " + << "OpenProcessToken() failed: " << formatSystemMessage(e); + + return; + } + + token.reset(rawToken); + } + + TOKEN_ELEVATION e = {}; + DWORD size = sizeof(TOKEN_ELEVATION); + + if (!GetTokenInformation(token.get(), TokenElevation, &e, sizeof(e), &size)) { + const auto e = GetLastError(); + + qCritical() + << "while trying to check if process is elevated, " + << "GetTokenInformation() failed: " << formatSystemMessage(e); + + return; + } + + m_elevated = (e.TokenIsElevated != 0); +} + +} // namespace env + } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index 46bdea78..deaf6fcc 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -22,6 +22,8 @@ along with Mod Organizer. If not, see . #include +#include + #define WIN32_LEAN_AND_MEAN #include @@ -46,58 +48,90 @@ std::wstring ToLower(const std::wstring &text); bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); -class Environment + +namespace env +{ + +class Module { public: - class Module + explicit Module(QString path, std::size_t fileSize); + + const QString& path() const; + QString displayPath() const; + + std::size_t fileSize() const; + const QString& version() const; + const QString& versionString() const; + QString timestampString() const; + + QString toString() const; + +private: + struct FileInfo { - public: - explicit Module(QString path, std::size_t fileSize); + VS_FIXEDFILEINFO ffi; + QString fileDescription; + }; - const QString& path() const; - QString displayPath() const; + QString m_path; + std::size_t m_fileSize; + QString m_version; + QDateTime m_timestamp; + QString m_versionString; + QString m_md5; - std::size_t fileSize() const; - const QString& version() const; - const QString& versionString() const; - QString timestampString() const; + FileInfo getFileInfo() const; - QString toString() const; + QString getVersion(const VS_FIXEDFILEINFO& fi) const; + QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const; + QString getMD5() const; - private: - struct FileInfo - { - VS_FIXEDFILEINFO ffi; - QString fileDescription; - }; + VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const; + QString getFileDescription(std::byte* buffer) const; +}; - QString m_path; - std::size_t m_fileSize; - QString m_version; - QDateTime m_timestamp; - QString m_versionString; - QString m_md5; - FileInfo getFileInfo() const; +class WindowsVersion +{ +public: + WindowsVersion(); - QString getVersion(const VS_FIXEDFILEINFO& fi) const; - QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const; - QString getMD5() const; + QString toString() const; + +private: + DWORD m_realMajor, m_realMinor, m_realBuild; + DWORD m_major, m_minor, m_build; + QString m_buildLab, m_productName, m_releaseID; + DWORD m_UBR; + std::optional m_elevated; + + void getVersion(); + void getRealVersion(HINSTANCE ntdll); + void getReportedVersion(HINSTANCE ntdll); + void getRelease(); + void getElevated(); +}; - VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const; - QString getFileDescription(std::byte* buffer) const; - }; +class Environment +{ +public: Environment(); const std::vector& loadedModules(); + const WindowsVersion& windowsVersion() const; private: std::vector m_modules; + WindowsVersion m_windows; void getLoadedModules(); }; +} // namespace env + + MOBase::VersionInfo createVersionInfo(); } // namespace MOShared -- cgit v1.3.1 From 813ab32a2d16106f2799fba4e85c768a5b1c4850 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 29 Jun 2019 18:58:15 -0400 Subject: added a warning when running in compatibility mode --- src/main.cpp | 6 ++++-- src/shared/util.cpp | 14 +++++++++++++- src/shared/util.h | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 75148c4b..e43ec6d0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -472,12 +472,14 @@ int runApplication(MOApplication &application, SingleInstance &instance, qInfo().nospace().noquote() << "windows: " << env.windowsVersion().toString(); + if (env.windowsVersion().compatibilityMode()) { + qWarning() << "MO seems to be running in compatibility mode"; + } + qInfo() << "modules loaded in process:"; for (const auto& m : env.loadedModules()) { qInfo().nospace().noquote() << " . " << m.toString(); } - - return 0; } QString dataPath = application.property("dataPath").toString(); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 9337ebf6..33094eff 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -653,6 +653,18 @@ WindowsVersion::WindowsVersion() : getElevated(); } +bool WindowsVersion::compatibilityMode() const +{ + if (m_realMajor == 0 && m_realMinor == 0 && m_realBuild == 0) { + return false; + } + + return + m_realMajor != m_major || + m_realMinor != m_minor || + m_realBuild != m_build; +} + QString WindowsVersion::toString() const { QStringList sl; @@ -665,7 +677,7 @@ QString WindowsVersion::toString() const sl.push_back("version: " + version); - if (m_realMajor != m_major || m_realMinor != m_minor || m_realBuild != m_build) { + if (compatibilityMode()) { sl.push_back("real version: " + realVersion); } diff --git a/src/shared/util.h b/src/shared/util.h index deaf6fcc..232a97bb 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -97,6 +97,7 @@ class WindowsVersion public: WindowsVersion(); + bool compatibilityMode() const; QString toString() const; private: -- cgit v1.3.1 From 1aa174f1438d7b1c1a9dab47d69e912726578a06 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 29 Jun 2019 20:02:10 -0400 Subject: comments, some refactoring switched to qDebug() --- src/main.cpp | 10 +- src/shared/util.cpp | 259 +++++++++++++++++++++++++++++++++++----------------- src/shared/util.h | 159 +++++++++++++++++++++++++++++--- 3 files changed, 326 insertions(+), 102 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e43ec6d0..6c3e40be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -469,16 +469,16 @@ int runApplication(MOApplication &application, SingleInstance &instance, { env::Environment env; - qInfo().nospace().noquote() - << "windows: " << env.windowsVersion().toString(); + qDebug().nospace().noquote() + << "windows: " << env.windowsInfo().toString(); - if (env.windowsVersion().compatibilityMode()) { + if (env.windowsInfo().compatibilityMode()) { qWarning() << "MO seems to be running in compatibility mode"; } - qInfo() << "modules loaded in process:"; + qDebug() << "modules loaded in process:"; for (const auto& m : env.loadedModules()) { - qInfo().nospace().noquote() << " . " << m.toString(); + qDebug().nospace().noquote() << " . " << m.toString(); } } diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 33094eff..70adb791 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -294,7 +294,7 @@ const std::vector& Environment::loadedModules() return m_modules; } -const WindowsVersion& Environment::windowsVersion() const +const WindowsInfo& Environment::windowsInfo() const { return m_windows; } @@ -315,12 +315,10 @@ void Environment::getLoadedModules() return; } - // Set the size of the structure before using it. MODULEENTRY32 me = {}; me.dwSize = sizeof(me); - // Retrieve information about the first module, - // and exit if unsuccessful + // first module, this shouldn't fail because there's at least the executable if (!Module32First(snapshot.get(), &me)) { const auto e = GetLastError(); @@ -331,27 +329,29 @@ void Environment::getLoadedModules() return; } - // Now walk the module list of the process, - // and display information about each module - for (;;) { const auto path = QString::fromWCharArray(me.szExePath); m_modules.push_back(Module(path, me.modBaseSize)); + // next module if (!Module32Next(snapshot.get(), &me)) { const auto e = GetLastError(); - if (e != ERROR_NO_MORE_FILES) { - qCritical().nospace().noquote() - << "Module32Next() failed, " << formatSystemMessage(e); + if (e == ERROR_NO_MORE_FILES) { + // not an error + break; } + qCritical().nospace().noquote() + << "Module32Next() failed, " << formatSystemMessage(e); + break; } } + // sorting by display name std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) { return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0); }); @@ -394,6 +394,16 @@ const QString& Module::versionString() const return m_versionString; } +const QDateTime& Module::timestamp() const +{ + return m_timestamp; +} + +const QString& Module::md5() const +{ + return m_md5; +} + QString Module::timestampString() const { if (!m_timestamp.isValid()) { @@ -407,9 +417,11 @@ QString Module::toString() const { QStringList sl; + // file size sl.push_back(displayPath()); sl.push_back(QString("%1 B").arg(m_fileSize)); + // version if (m_version.isEmpty() && m_versionString.isEmpty()) { sl.push_back("(no version)"); } else { @@ -417,17 +429,19 @@ QString Module::toString() const sl.push_back(m_version); } - if (m_versionString != m_version) { + if (!m_versionString.isEmpty() && m_versionString != m_version) { sl.push_back(versionString()); } } + // timestamp if (m_timestamp.isValid()) { sl.push_back(m_timestamp.toString(Qt::DateFormat::ISODate)); } else { sl.push_back("(no timestamp)"); } + // md5 if (!m_md5.isEmpty()) { sl.push_back(m_md5); } @@ -439,6 +453,7 @@ Module::FileInfo Module::getFileInfo() const { const auto wspath = m_path.toStdWString(); + // getting version info size DWORD dummy = 0; const DWORD size = GetFileVersionInfoSizeW(wspath.c_str(), &dummy); @@ -457,6 +472,7 @@ Module::FileInfo Module::getFileInfo() const return {}; } + // getting version info auto buffer = std::make_unique(size); if (!GetFileVersionInfoW(wspath.c_str(), 0, size, buffer.get())) { @@ -469,6 +485,8 @@ Module::FileInfo Module::getFileInfo() const return {}; } + // the version info has two major parts: a fixed version and a localizable + // set of strings FileInfo fi; fi.ffi = getFixedFileInfo(buffer.get()); @@ -482,6 +500,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const void* valuePointer = nullptr; unsigned int valueSize = 0; + // the fixed version info is in the root const auto ret = VerQueryValueW(buffer, L"\\", &valuePointer, &valueSize); if (!ret || !valuePointer || valueSize == 0) { @@ -491,6 +510,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const const auto* fi = reinterpret_cast(valuePointer); + // signature is always 0xfeef04bd if (fi->dwSignature != 0xfeef04bd) { qCritical().nospace().noquote() << "bad file info signature 0x" << hex << fi->dwSignature << " for " @@ -513,6 +533,7 @@ QString Module::getFileDescription(std::byte* buffer) const void* valuePointer = nullptr; unsigned int valueSize = 0; + // getting list of available languages auto ret = VerQueryValueW( buffer, L"\\VarFileInfo\\Translation", &valuePointer, &valueSize); @@ -523,11 +544,13 @@ QString Module::getFileDescription(std::byte* buffer) const return {}; } + // number of languages const auto count = valueSize / sizeof(LANGANDCODEPAGE); if (count == 0) { return {}; } + // using the first language in the list to get FileVersion const auto* lcp = reinterpret_cast(valuePointer); const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion") @@ -571,6 +594,10 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const FILETIME ft = {}; if (fi.dwSignature == 0 || (fi.dwFileDateMS == 0 && fi.dwFileDateLS == 0)) { + // if the file info is invalid or doesn't have a date, use the creation + // time on the file + + // opening the file std::unique_ptr h(CreateFileW( m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)); @@ -585,6 +612,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const return {}; } + // getting the file time if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) { const auto e = GetLastError(); qCritical().nospace().noquote() @@ -594,12 +622,15 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const return {}; } } else { + // use the time from the file info ft.dwHighDateTime = fi.dwFileDateMS; ft.dwLowDateTime = fi.dwFileDateLS; } + // converting to SYSTEMTIME SYSTEMTIME utc = {}; + if (!FileTimeToSystemTime(&ft, &utc)) { qCritical().nospace().noquote() << "FileTimeToSystemTime() failed on timestamp " @@ -623,6 +654,7 @@ QString Module::getMD5() const return {}; } + // opening the file QFile f(m_path); if (!f.open(QFile::ReadOnly)) { @@ -632,6 +664,7 @@ QString Module::getMD5() const return {}; } + // hashing QCryptographicHash hash(QCryptographicHash::Md5); if (!hash.addData(&f)) { qCritical().nospace().noquote() @@ -644,59 +677,97 @@ QString Module::getMD5() const } -WindowsVersion::WindowsVersion() : - m_realMajor(0), m_realMinor(0), m_realBuild(0), - m_major(0), m_minor(0), m_build(0), m_UBR(0) +WindowsInfo::WindowsInfo() { - getVersion(); - getRelease(); - getElevated(); + // loading ntdll.dll, the functions will be found with GetProcAddress() + std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll")); + + if (!ntdll) { + qCritical() << "failed to load ntdll.dll while getting version"; + return; + } else { + m_reported = getReportedVersion(ntdll.get()); + m_real = getRealVersion(ntdll.get()); + } + + m_release = getRelease(); + m_elevated = getElevated(); } -bool WindowsVersion::compatibilityMode() const +bool WindowsInfo::compatibilityMode() const { - if (m_realMajor == 0 && m_realMinor == 0 && m_realBuild == 0) { + if (m_real == Version()) { + // don't know the real version, can't guess compatibility mode return false; } - return - m_realMajor != m_major || - m_realMinor != m_minor || - m_realBuild != m_build; + return (m_real != m_reported); } -QString WindowsVersion::toString() const +const WindowsInfo::Version& WindowsInfo::reportedVersion() const { - QStringList sl; + return m_reported; +} + +const WindowsInfo::Version& WindowsInfo::realVersion() const +{ + return m_real; +} + +const WindowsInfo::Release& WindowsInfo::release() const +{ + return m_release; +} + +std::optional WindowsInfo::isElevated() const +{ + return m_elevated; +} - const QString version = QString("%1.%2.%3") - .arg(m_major).arg(m_minor).arg(m_build); +QString WindowsInfo::toString() const +{ + QStringList sl; - const QString realVersion = QString("%1.%2.%3") - .arg(m_realMajor).arg(m_realMinor).arg(m_realBuild); + const QString reported = m_reported.toString(); + const QString real = m_real.toString(); - sl.push_back("version: " + version); + // version + sl.push_back("version: " + reported); + // real version if different if (compatibilityMode()) { - sl.push_back("real version: " + realVersion); + sl.push_back("real version: " + real); } - if (!m_buildLab.isEmpty()) { - sl.push_back(m_buildLab); + // build.UBR, such as 17763.557 + if (m_release.UBR != 0) { + DWORD build = 0; + + if (compatibilityMode()) { + build = m_real.build; + } else { + build = m_reported.build; + } + + sl.push_back(QString("%1.%2").arg(build).arg(m_release.UBR)); } - if (!m_productName.isEmpty()) { - sl.push_back(m_productName); + // release ID + if (!m_release.ID.isEmpty()) { + sl.push_back("release " + m_release.ID); } - if (!m_releaseID.isEmpty()) { - sl.push_back("build " + m_releaseID); + // buildlab string + if (!m_release.buildLab.isEmpty()) { + sl.push_back(m_release.buildLab); } - if (m_UBR != 0) { - sl.push_back(QString("%1").arg(m_UBR)); + // product name + if (!m_release.productName.isEmpty()) { + sl.push_back(m_release.productName); } + // elevated QString elevated = "?"; if (m_elevated.has_value()) { elevated = (*m_elevated ? "yes" : "no"); @@ -707,36 +778,14 @@ QString WindowsVersion::toString() const return sl.join(", "); } -void WindowsVersion::getVersion() -{ - std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll")); - - if (!ntdll) { - qCritical() << "failed to load ntdll.dll while getting version"; - return; - } - - getRealVersion(ntdll.get()); - getReportedVersion(ntdll.get()); -} - -void WindowsVersion::getRealVersion(HINSTANCE ntdll) +WindowsInfo::Version WindowsInfo::getReportedVersion(HINSTANCE ntdll) const { - using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*); - - auto* RtlGetNtVersionNumbers = reinterpret_cast( - GetProcAddress(ntdll, "RtlGetNtVersionNumbers")); + // windows has been deprecating pretty much all the functions having to do + // with getting version information because apparently, people keep misusing + // them for feature detection + // + // there's still RtlGetVersion() though - if (RtlGetNtVersionNumbers) { - DWORD build = 0; - RtlGetNtVersionNumbers(&m_realMajor, &m_realMinor, &build); - - m_realBuild = 0x0fffffff & build; - } -} - -void WindowsVersion::getReportedVersion(HINSTANCE ntdll) -{ using RtlGetVersionType = NTSTATUS (NTAPI)(PRTL_OSVERSIONINFOW); auto* RtlGetVersion = reinterpret_cast( @@ -744,39 +793,81 @@ void WindowsVersion::getReportedVersion(HINSTANCE ntdll) if (!RtlGetVersion) { qCritical() << "RtlGetVersion() not found in ntdll.dll"; - return; + return {}; } OSVERSIONINFOEX vi = {}; vi.dwOSVersionInfoSize = sizeof(vi); + // this apparently never fails RtlGetVersion((RTL_OSVERSIONINFOW*)&vi); - m_major = vi.dwMajorVersion; - m_minor = vi.dwMinorVersion; - m_build = vi.dwBuildNumber; + return {vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber}; +} + +WindowsInfo::Version WindowsInfo::getRealVersion(HINSTANCE ntdll) const +{ + // getting the actual windows version is more difficult because all the + // functions are lying when running in compatibility mode + // + // RtlGetNtVersionNumbers() is an undocumented function that seems to work + // fine, but it might not in the future + + using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*); + + auto* RtlGetNtVersionNumbers = reinterpret_cast( + GetProcAddress(ntdll, "RtlGetNtVersionNumbers")); + + if (!RtlGetNtVersionNumbers) { + qCritical() << "RtlGetNtVersionNumbers not found in ntdll.dll"; + return {}; + } + + DWORD major=0, minor=0, build=0; + RtlGetNtVersionNumbers(&major, &minor, &build); + + // for whatever reason, the build number has 0xf0000000 set + build = 0x0fffffff & build; + + return {major, minor, build}; } -void WindowsVersion::getRelease() +WindowsInfo::Release WindowsInfo::getRelease() const { + // there are several interesting items in the registry, but most of them + // are undocumented, not always available, and localizable + // + // most of them are used to provide as much information as possible in case + // any of the other versions fail to work + QSettings settings( R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)", QSettings::NativeFormat); - m_buildLab = settings.value("BuildLabEx", "").toString(); - if (m_buildLab.isEmpty()) { - m_buildLab = settings.value("BuildLab", "").toString(); - if (m_buildLab.isEmpty()) { - m_buildLab = settings.value("BuildBranch", "").toString(); + Release r; + + // buildlab seems to be an internal name from the build system + r.buildLab = settings.value("BuildLabEx", "").toString(); + if (r.buildLab.isEmpty()) { + r.buildLab = settings.value("BuildLab", "").toString(); + if (r.buildLab.isEmpty()) { + r.buildLab = settings.value("BuildBranch", "").toString(); } } - m_productName = settings.value("ProductName", "").toString(); - m_releaseID = settings.value("ReleaseId", "").toString(); - m_UBR = settings.value("UBR", 0).toUInt(); + // localized name of windows, such as "Windows 10 Pro" + r.productName = settings.value("ProductName", "").toString(); + + // release ID, such as 1803 + r.ID = settings.value("ReleaseId", "").toString(); + + // some other build number, shown in winver.exe + r.UBR = settings.value("UBR", 0).toUInt(); + + return r; } -void WindowsVersion::getElevated() +std::optional WindowsInfo::getElevated() const { std::unique_ptr token; @@ -790,7 +881,7 @@ void WindowsVersion::getElevated() << "while trying to check if process is elevated, " << "OpenProcessToken() failed: " << formatSystemMessage(e); - return; + return {}; } token.reset(rawToken); @@ -806,10 +897,10 @@ void WindowsVersion::getElevated() << "while trying to check if process is elevated, " << "GetTokenInformation() failed: " << formatSystemMessage(e); - return; + return {}; } - m_elevated = (e.TokenIsElevated != 0); + return (e.TokenIsElevated != 0); } } // namespace env diff --git a/src/shared/util.h b/src/shared/util.h index 232a97bb..3ec677f4 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -52,22 +52,55 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); namespace env { +// represents one module +// class Module { public: explicit Module(QString path, std::size_t fileSize); + // returns the module's path + // const QString& path() const; + + // returns the module's path in lowercase and using forward slashes + // QString displayPath() const; + // returns the size in bytes, may be 0 + // std::size_t fileSize() const; + + // returns the x.x.x.x version embedded from the version info, may be empty + // const QString& version() const; + + // returns the FileVersion entry from the resource file, returns + // "(no version)" if not available + // const QString& versionString() const; + + // returns the build date from the version info, or the creation time of the + // file on the filesystem, may be empty + // + const QDateTime& timestamp() const; + + // returns the md5 of the file, may be empty for system files + // + const QString& md5() const; + + // converts timestamp() to a string for display, returns "(no timestamp)" if + // not available + // QString timestampString() const; + // returns a string with all the above information on one line + // QString toString() const; private: + // contains the information from the version resource + // struct FileInfo { VS_FIXEDFILEINFO ffi; @@ -81,51 +114,151 @@ private: QString m_versionString; QString m_md5; + // returns information from the version resource + // FileInfo getFileInfo() const; + // uses VS_FIXEDFILEINFO to build the version string + // QString getVersion(const VS_FIXEDFILEINFO& fi) const; + + // uses the file date from VS_FIXEDFILEINFO if available, or gets the + // creation date on the file + // QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const; + + // returns the md5 hash unless the path contains "\windows\" + // QString getMD5() const; + // gets VS_FIXEDFILEINFO from the file version info buffer + // VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const; + + // gets FileVersion from the file version info buffer + // QString getFileDescription(std::byte* buffer) const; }; -class WindowsVersion +// a variety of information on windows +// +class WindowsInfo { public: - WindowsVersion(); + struct Version + { + DWORD major=0, minor=0, build=0; + + QString toString() const + { + return QString("%1.%2.%3").arg(major).arg(minor).arg(build); + } + + friend bool operator==(const Version& a, const Version& b) + { + return + a.major == b.major && + a.minor == b.minor && + a.build == b.build; + } + + friend bool operator!=(const Version& a, const Version& b) + { + return !(a == b); + } + }; + + struct Release + { + // the BuildLab entry from the registry, may be empty + QString buildLab; + + // product name such as "Windows 10 Pro", may not be in English, may be + // empty + QString productName; + + // release ID such as 1809, may be mepty + QString ID; + // some sub-build number, undocumented, may be empty + DWORD UBR; + + Release() + : UBR(0) + { + } + }; + + + WindowsInfo(); + + // tries to guess whether this process is running in compatibility mode + // bool compatibilityMode() const; + + // returns the Windows version, may not correspond to the actual version + // if the process is running in compatibility mode + // + const Version& reportedVersion() const; + + // tries to guess the real Windows version that's running, can be empty + // + const Version& realVersion() const; + + // various information about the current release + // + const Release& release() const; + + // whether this process is running as administrator, may be empty if the + // information is not available + std::optional isElevated() const; + + // returns a string with all the above information on one line + // QString toString() const; private: - DWORD m_realMajor, m_realMinor, m_realBuild; - DWORD m_major, m_minor, m_build; - QString m_buildLab, m_productName, m_releaseID; - DWORD m_UBR; + Version m_reported, m_real; + Release m_release; std::optional m_elevated; - void getVersion(); - void getRealVersion(HINSTANCE ntdll); - void getReportedVersion(HINSTANCE ntdll); - void getRelease(); - void getElevated(); + // uses RtlGetVersion() to get the version number as reported by Windows + // + Version getReportedVersion(HINSTANCE ntdll) const; + + // uses RtlGetNtVersionNumbers() to get the real version number + // + Version getRealVersion(HINSTANCE ntdll) const; + + // gets various information from the registry + // + Release getRelease() const; + + // gets whether the process is elevated + // + std::optional getElevated() const; }; +// represents the process's environment +// class Environment { public: Environment(); + // list of loaded modules in the current process + // const std::vector& loadedModules(); - const WindowsVersion& windowsVersion() const; + + // information about the operating system + // + const WindowsInfo& windowsInfo() const; private: std::vector m_modules; - WindowsVersion m_windows; + WindowsInfo m_windows; void getLoadedModules(); }; -- cgit v1.3.1 From 5a74b02442302fb484b1db68cf0ce1736af4911c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 1 Jul 2019 03:41:32 -0400 Subject: security products --- src/main.cpp | 7 ++ src/shared/util.cpp | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/shared/util.h | 21 ++++ 3 files changed, 308 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6c3e40be..518d31a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -476,10 +476,17 @@ int runApplication(MOApplication &application, SingleInstance &instance, qWarning() << "MO seems to be running in compatibility mode"; } + qDebug().nospace().noquote() << "security features:"; + for (const auto& sf : env.securityFeatures()) { + qDebug().nospace().noquote() << " . " << sf.toString(); + } + qDebug() << "modules loaded in process:"; for (const auto& m : env.loadedModules()) { qDebug().nospace().noquote() << " . " << m.toString(); } + + return 0; } QString dataPath = application.property("dataPath").toString(); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 70adb791..aa2e8d0f 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -30,6 +30,11 @@ along with Mod Organizer. If not, see . #include #include +#include +#include +#include +#pragma comment(lib, "Wbemuuid.lib") + using MOBase::formatSystemMessage; namespace MOShared { @@ -283,10 +288,153 @@ struct LibraryFreer } }; +struct COMReleaser +{ + void operator()(IUnknown* p) + { + if (p) { + p->Release(); + } + } +}; + + +class WMI +{ +public: + class failed {}; + + WMI(const std::string& ns) + { + try + { + createLocator(); + createService(ns); + setSecurity(); + } + catch(failed&) + { + } + } + + template + void query(const std::string& q, F&& f) + { + if (!m_locator || !m_service) { + return; + } + + auto enumerator = getEnumerator(q); + + for (;;) + { + std::unique_ptr object; + + { + IWbemClassObject* rawObject = nullptr; + ULONG count = 0; + auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count); + + if (count == 0) { + break; + } + + object.reset(rawObject); + } + + f(object.get()); + } + } + + std::unique_ptr getEnumerator( + const std::string& query) + { + IEnumWbemClassObject* rawEnumerator = NULL; + + auto ret = m_service->ExecQuery( + bstr_t("WQL"), + bstr_t(query.c_str()), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &rawEnumerator); + + if (FAILED(ret)) + { + qCritical() + << "query '" << QString::fromStdString(query) << "' failed, " + << formatSystemMessage(ret); + + return {}; + } + + return std::unique_ptr(rawEnumerator); + } + +private: + std::unique_ptr m_locator; + std::unique_ptr m_service; + + void createLocator() + { + void* rawLocator = nullptr; + + const auto ret = CoCreateInstance( + CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, + IID_IWbemLocator, &rawLocator); + + if (FAILED(ret)) { + qCritical() + << "CoCreateInstance for WbemLocator failed, " + << formatSystemMessage(ret); + + throw failed(); + } + + m_locator.reset(static_cast(rawLocator)); + } + + void createService(const std::string& ns) + { + IWbemServices* rawService = nullptr; + + const auto res = m_locator->ConnectServer( + _bstr_t(ns.c_str()), + nullptr, nullptr, nullptr, 0, nullptr, nullptr, + &rawService); + + if (FAILED(res)) { + qCritical() + << "locator->ConnectServer() failed for namespace " + << "'" << QString::fromStdString(ns) << "', " + << formatSystemMessage(res); + + throw failed(); + } + + m_service.reset(rawService); + } + + void setSecurity() + { + auto ret = CoSetProxyBlanket( + m_service.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE); + + if (FAILED(ret)) + { + qCritical() + << "CoSetProxyBlanket() failed, " << formatSystemMessage(ret); + + throw failed(); + } + } +}; + Environment::Environment() { getLoadedModules(); + getSecurityFeatures(); } const std::vector& Environment::loadedModules() @@ -299,6 +447,11 @@ const WindowsInfo& Environment::windowsInfo() const return m_windows; } +const std::vector& Environment::securityFeatures() const +{ + return m_security; +} + void Environment::getLoadedModules() { std::unique_ptr snapshot(CreateToolhelp32Snapshot( @@ -357,6 +510,59 @@ void Environment::getLoadedModules() }); } +void Environment::getSecurityFeatures() +{ + WMI wmi("root\\SecurityCenter2"); + std::map map; + + auto handleProduct = [&](auto* o) { + VARIANT prop; + + auto ret = o->Get(L"displayName", 0, &prop, 0, 0); + if (FAILED(ret)) { + qCritical() << "failed to get displayName, " << formatSystemMessage(ret); + return; + } + + const std::wstring name = prop.bstrVal; + VariantClear(&prop); + + ret = o->Get(L"productState", 0, &prop, 0, 0); + if (FAILED(ret)) { + qCritical() << "failed to get productState, " << formatSystemMessage(ret); + return; + } + + const DWORD state = prop.ulVal; + VariantClear(&prop); + + ret = o->Get(L"instanceGuid", 0, &prop, 0, 0); + if (FAILED(ret)) { + qCritical() << "failed to get instanceGuid, " << formatSystemMessage(ret); + return; + } + + const QUuid guid(QString::fromWCharArray(prop.bstrVal)); + VariantClear(&prop); + + + auto itor = map.find(guid); + + if (itor == map.end()) { + map.insert({ + guid, SecurityFeature(QString::fromStdWString(name), state)}); + } + }; + + wmi.query("select * from AntivirusProduct", handleProduct); + wmi.query("select * from FirewallProduct", handleProduct); + wmi.query("select * from AntiSpywareProduct", handleProduct); + + for (auto&& p : map) { + m_security.push_back(p.second); + } +} + Module::Module(QString path, std::size_t fileSize) : m_path(std::move(path)), m_fileSize(fileSize) @@ -508,7 +714,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const return {}; } - const auto* fi = reinterpret_cast(valuePointer); + const auto* fi = static_cast(valuePointer); // signature is always 0xfeef04bd if (fi->dwSignature != 0xfeef04bd) { @@ -551,7 +757,7 @@ QString Module::getFileDescription(std::byte* buffer) const } // using the first language in the list to get FileVersion - const auto* lcp = reinterpret_cast(valuePointer); + const auto* lcp = static_cast(valuePointer); const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion") .arg(lcp->wLanguage, 4, 16, QChar('0')) @@ -567,7 +773,7 @@ QString Module::getFileDescription(std::byte* buffer) const // valueSize includes the null terminator return QString::fromWCharArray( - reinterpret_cast(valuePointer), valueSize - 1); + static_cast(valuePointer), valueSize - 1); } QString Module::getVersion(const VS_FIXEDFILEINFO& fi) const @@ -903,6 +1109,77 @@ std::optional WindowsInfo::getElevated() const return (e.TokenIsElevated != 0); } + +SecurityFeature::SecurityFeature(QString name, DWORD state) + : m_name(std::move(name)), m_state(state) +{ +} + +const QString& SecurityFeature::name() const +{ + return m_name; +} + +QString SecurityFeature::toString() const +{ + QString s; + + s += m_name + " "; + + const auto provider = (m_state >> 16) & 0xff; + const auto scanner = (m_state >> 8) & 0xff; + const auto definitions = m_state & 0xff; + + QStringList ps; + if (provider & WSC_SECURITY_PROVIDER_FIREWALL) { + ps.push_back("firewall"); + } + + if (provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) { + ps.push_back("autoupdate"); + } + + if (provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) { + ps.push_back("antivirus"); + } + + if (provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) { + ps.push_back("antispyware"); + } + + if (provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) { + ps.push_back("settings"); + } + + if (provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) { + ps.push_back("uac"); + } + + if (provider & WSC_SECURITY_PROVIDER_SERVICE) { + ps.push_back("service"); + } + + if (ps.empty()) { + s += "(doesn't provide anything)"; + } else { + s += "(" + ps.join("|") + ")"; + } + + if (scanner & 0x10) { + s += ", active"; + } else { + s += ", inactive"; + } + + if (definitions == 0) { + s += ", definitions up to date"; + } else { + s += ", definitions outdated"; + } + + return s; +} + } // namespace env } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index 3ec677f4..6b842f8c 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -241,6 +241,21 @@ private: }; +class SecurityFeature +{ +public: + SecurityFeature(QString name, DWORD state); + + const QString& name() const; + + QString toString() const; + +private: + QString m_name; + DWORD m_state; +}; + + // represents the process's environment // class Environment @@ -256,11 +271,17 @@ public: // const WindowsInfo& windowsInfo() const; + // information about the installed antivirus + // + const std::vector& securityFeatures() const; + private: std::vector m_modules; WindowsInfo m_windows; + std::vector m_security; void getLoadedModules(); + void getSecurityFeatures(); }; } // namespace env -- cgit v1.3.1 From dfe8093c1ad16e1611c12e88d52d1ac38371d3f6 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 1 Jul 2019 08:42:58 -0400 Subject: added --crashdump to generate dumps of a running MO process added dump_running_process.bat to start another instance of MO with that flag --- CMakeLists.txt | 2 + dump_running_process.bat | 2 + src/main.cpp | 45 +++++- src/settings.cpp | 10 +- src/shared/util.cpp | 392 ++++++++++++++++++++++++++++++++++++++++++++--- src/shared/util.h | 17 ++ 6 files changed, 443 insertions(+), 25 deletions(-) create mode 100644 dump_running_process.bat (limited to 'src/main.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index ced097e1..94c76373 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,3 +29,5 @@ if(NOT EXISTS ${vcxproj_user_file}) "\n" "\n") endif() + +INSTALL(FILES dump_running_process.bat DESTINATION bin) diff --git a/dump_running_process.bat b/dump_running_process.bat new file mode 100644 index 00000000..4697fe5e --- /dev/null +++ b/dump_running_process.bat @@ -0,0 +1,2 @@ +pushd "%~dp0" +start ModOrganizer.exe --crashdump diff --git a/src/main.cpp b/src/main.cpp index 518d31a0..f74a9cf4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -485,8 +485,6 @@ int runApplication(MOApplication &application, SingleInstance &instance, for (const auto& m : env.loadedModules()) { qDebug().nospace().noquote() << " . " << m.toString(); } - - return 0; } QString dataPath = application.property("dataPath").toString(); @@ -682,9 +680,52 @@ int runApplication(MOApplication &application, SingleInstance &instance, } } +int doCoreDump(env::CoreDumpTypes type) +{ + // open a console + AllocConsole(); + + // redirect stdin, stdout and stderr to it + FILE* in=nullptr; + FILE* out=nullptr; + FILE* err=nullptr; + freopen_s(&in, "CONIN$", "r", stdin); + freopen_s(&out, "CONOUT$", "w", stdout); + freopen_s(&err, "CONOUT$", "w", stderr); + + // dump + const auto b = env::coredumpOther(type); + if (!b) { + std::wcerr << L"\n>>>> a minidump file was not written\n\n"; + } + + std::wcerr << L"Press enter to continue..."; + std::wcin.get(); + + // close redirected handles + std::fclose(err); + std::fclose(out); + std::fclose(in); + + // close console + FreeConsole(); + + return (b ? 0 : 1); +} int main(int argc, char *argv[]) { + // handle --crashdump first + for (int i=1; i. #include #include #include -#include #include +#include + +#include #include #include @@ -36,6 +38,8 @@ along with Mod Organizer. If not, see . #pragma comment(lib, "Wbemuuid.lib") using MOBase::formatSystemMessage; +using MOBase::formatSystemMessageQ; +namespace fs = std::filesystem; namespace MOShared { @@ -276,6 +280,9 @@ struct HandleCloser } }; +using HandlePtr = std::unique_ptr; + + struct LibraryFreer { using pointer = HINSTANCE; @@ -362,7 +369,7 @@ public: { qCritical() << "query '" << QString::fromStdString(query) << "' failed, " - << formatSystemMessage(ret); + << formatSystemMessageQ(ret); return {}; } @@ -385,7 +392,7 @@ private: if (FAILED(ret)) { qCritical() << "CoCreateInstance for WbemLocator failed, " - << formatSystemMessage(ret); + << formatSystemMessageQ(ret); throw failed(); } @@ -406,7 +413,7 @@ private: qCritical() << "locator->ConnectServer() failed for namespace " << "'" << QString::fromStdString(ns) << "', " - << formatSystemMessage(res); + << formatSystemMessageQ(res); throw failed(); } @@ -423,7 +430,7 @@ private: if (FAILED(ret)) { qCritical() - << "CoSetProxyBlanket() failed, " << formatSystemMessage(ret); + << "CoSetProxyBlanket() failed, " << formatSystemMessageQ(ret); throw failed(); } @@ -454,7 +461,7 @@ const std::vector& Environment::securityFeatures() const void Environment::getLoadedModules() { - std::unique_ptr snapshot(CreateToolhelp32Snapshot( + HandlePtr snapshot(CreateToolhelp32Snapshot( TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId())); if (snapshot.get() == INVALID_HANDLE_VALUE) @@ -463,7 +470,7 @@ void Environment::getLoadedModules() qCritical().nospace().noquote() << "CreateToolhelp32Snapshot() failed, " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return; } @@ -477,7 +484,7 @@ void Environment::getLoadedModules() const auto e = GetLastError(); qCritical().nospace().noquote() - << "Module32First() failed, " << formatSystemMessage(e); + << "Module32First() failed, " << formatSystemMessageQ(e); return; } @@ -498,7 +505,7 @@ void Environment::getLoadedModules() } qCritical().nospace().noquote() - << "Module32Next() failed, " << formatSystemMessage(e); + << "Module32Next() failed, " << formatSystemMessageQ(e); break; } @@ -520,7 +527,7 @@ void Environment::getSecurityFeatures() auto ret = o->Get(L"displayName", 0, &prop, 0, 0); if (FAILED(ret)) { - qCritical() << "failed to get displayName, " << formatSystemMessage(ret); + qCritical() << "failed to get displayName, " << formatSystemMessageQ(ret); return; } @@ -529,7 +536,10 @@ void Environment::getSecurityFeatures() ret = o->Get(L"productState", 0, &prop, 0, 0); if (FAILED(ret)) { - qCritical() << "failed to get productState, " << formatSystemMessage(ret); + qCritical() + << "failed to get productState, " + << formatSystemMessageQ(ret); + return; } @@ -538,7 +548,10 @@ void Environment::getSecurityFeatures() ret = o->Get(L"instanceGuid", 0, &prop, 0, 0); if (FAILED(ret)) { - qCritical() << "failed to get instanceGuid, " << formatSystemMessage(ret); + qCritical() + << "failed to get instanceGuid, " + << formatSystemMessageQ(ret); + return; } @@ -673,7 +686,7 @@ Module::FileInfo Module::getFileInfo() const qCritical().nospace().noquote() << "GetFileVersionInfoSizeW() failed on '" << m_path << "', " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -686,7 +699,7 @@ Module::FileInfo Module::getFileInfo() const qCritical().nospace().noquote() << "GetFileVersionInfoW() failed on '" << m_path << "', " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -804,7 +817,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const // time on the file // opening the file - std::unique_ptr h(CreateFileW( + HandlePtr h(CreateFileW( m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)); @@ -813,7 +826,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const qCritical().nospace().noquote() << "can't open file '" << m_path << "' for timestamp, " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -823,7 +836,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const const auto e = GetLastError(); qCritical().nospace().noquote() << "can't get file time for '" << m_path << "', " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -1075,7 +1088,7 @@ WindowsInfo::Release WindowsInfo::getRelease() const std::optional WindowsInfo::getElevated() const { - std::unique_ptr token; + HandlePtr token; { HANDLE rawToken = 0; @@ -1085,7 +1098,7 @@ std::optional WindowsInfo::getElevated() const qCritical() << "while trying to check if process is elevated, " - << "OpenProcessToken() failed: " << formatSystemMessage(e); + << "OpenProcessToken() failed: " << formatSystemMessageQ(e); return {}; } @@ -1101,7 +1114,7 @@ std::optional WindowsInfo::getElevated() const qCritical() << "while trying to check if process is elevated, " - << "GetTokenInformation() failed: " << formatSystemMessage(e); + << "GetTokenInformation() failed: " << formatSystemMessageQ(e); return {}; } @@ -1180,6 +1193,345 @@ QString SecurityFeature::toString() const return s; } + +struct Process +{ + std::wstring filename; + DWORD pid; + + Process(std::wstring f, DWORD id) + : filename(std::move(f)), pid(id) + { + } +}; + +std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE) +{ + DWORD bufferSize = MAX_PATH; + + for (int tries=0; tries<10; ++tries) + { + auto buffer = std::make_unique(bufferSize + 1); + std::fill(buffer.get(), buffer.get() + bufferSize + 1, 0); + + DWORD writtenSize = 0; + + if (process == INVALID_HANDLE_VALUE) { + // query this process + writtenSize = GetModuleFileNameW(0, buffer.get(), bufferSize); + } else { + // query another process + writtenSize = GetModuleBaseNameW(process, 0, buffer.get(), bufferSize); + } + + if (writtenSize == 0) { + const auto e = GetLastError(); + std::wcerr << formatSystemMessage(e) << L"\n"; + break; + } else if (writtenSize >= bufferSize) { + // buffer is too small, try again + bufferSize *= 2; + } else { + // if GetModuleFileName() works, `writtenSize` does not include the null + // terminator + const std::wstring s(buffer.get(), writtenSize); + const fs::path path(s); + + return path.filename().native(); + } + } + + + std::wstring what; + if (process == INVALID_HANDLE_VALUE) { + what = L"the current process"; + } else { + what = L"pid " + std::to_wstring(reinterpret_cast(process)); + } + + std::wcerr << L"failed to get filename for " << what << L"\n"; + return {}; +} + +std::vector runningProcessesIds() +{ + // initial size of 300 processes, unlikely to be more than that + std::size_t size = 300; + + for (int tries=0; tries<10; ++tries) { + auto ids = std::make_unique(size); + std::fill(ids.get(), ids.get() + size, 0); + + DWORD bytesGiven = static_cast(size * sizeof(ids[0])); + DWORD bytesWritten = 0; + + if (!EnumProcesses(ids.get(), bytesGiven, &bytesWritten)) + { + const auto e = GetLastError(); + + std::wcerr + << L"failed to enumerate processes, " + << formatSystemMessage(e) << L"\n"; + + return {}; + } + + if (bytesWritten == bytesGiven) { + size *= 2; + continue; + } + + const auto count = bytesWritten / sizeof(ids[0]); + return std::vector(ids.get(), ids.get() + count); + } + + std::cerr << L"too many processes to enumerate"; + return {}; +} + +std::vector runningProcesses() +{ + const auto pids = runningProcessesIds(); + std::vector v; + + for (const auto& pid : pids) { + if (pid == 0) { + // the idle process seems to be picked up by EnumProcesses() + continue; + } + + HandlePtr h(OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); + + if (!h) { + const auto e = GetLastError(); + + if (e != ERROR_ACCESS_DENIED) { + // don't log access denied, will happen a lot when not elevated + std::wcerr + << L"failed to open process " << pid << L", " + << formatSystemMessage(e) << L"\n"; + } + + continue; + } + + auto filename = processFilename(h.get()); + if (!filename.empty()) { + v.emplace_back(std::move(filename), pid); + } + } + + return v; +} + +DWORD findOtherPid() +{ + const std::wstring defaultName = L"ModOrganizer.exe"; + + std::wclog << L"looking for the other process...\n"; + + const auto thisPid = GetCurrentProcessId(); + std::wclog << L"this process id is " << thisPid << L"\n"; + + auto filename = processFilename(); + if (filename.empty()) { + std::wcerr + << L"can't get current process filename, defaulting to " + << defaultName << L"\n"; + + filename = defaultName; + } else { + std::wclog << L"this process filename is " << filename << L"\n"; + } + + const auto processes = runningProcesses(); + std::wclog << L"there are " << processes.size() << L" processes running\n"; + + for (const auto& p : processes) { + if (p.filename == filename) { + if (p.pid != thisPid) { + return p.pid; + } + } + } + + std::wclog + << L"no process with this filename\n" + << L"MO may not be running, or it may be running as administrator\n" + << L"you can try running this again as administrator\n"; + + return 0; +} + + +std::wstring tempDir() +{ + const DWORD bufferSize = MAX_PATH + 1; + wchar_t buffer[bufferSize + 1] = {}; + + const auto written = GetTempPathW(bufferSize, buffer); + if (written == 0) { + const auto e = GetLastError(); + + std::wcerr + << L"failed to get temp path, " << formatSystemMessage(e) << L"\n"; + + return {}; + } + + // `written` does not include the null terminator + return std::wstring(buffer, buffer + written); +} + +HandlePtr tempFile(const std::wstring dir) +{ + const auto now = std::time(0); + const auto tm = std::gmtime(&now); + + std::wostringstream oss; + oss + << L"ModOrganizer-" + << std::setw(4) << (1900 + tm->tm_year) + << std::setw(2) << std::setfill(L'0') << (tm->tm_mon + 1) + << std::setw(2) << std::setfill(L'0') << tm->tm_mday << "T" + << std::setw(2) << std::setfill(L'0') << tm->tm_hour + << std::setw(2) << std::setfill(L'0') << tm->tm_min + << std::setw(2) << std::setfill(L'0') << tm->tm_sec; + + const std::wstring prefix = oss.str(); + const std::wstring ext = L".dmp"; + + std::wstring path = dir + L"\\" + prefix + ext; + for (int i=0; i<100; ++i) { + std::wclog << L"trying file '" << path << L"'\n"; + + HandlePtr h (CreateFileW( + path.c_str(), GENERIC_WRITE, 0, nullptr, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr)); + + if (h.get() != INVALID_HANDLE_VALUE) { + return h; + } + + const auto e = GetLastError(); + if (e != ERROR_FILE_EXISTS) { + // probably no write access + std::wcerr + << L"failed to create dump file, " << formatSystemMessage(e) << L"\n"; + + return {}; + } + + path = dir + L"\\" + prefix + L"-" + std::to_wstring(i + 1) + ext; + } + + std::wcerr << L"can't create dump file, ran out of filenames\n"; + return {}; +} + +HandlePtr dumpFile() +{ + // try the current directory + HandlePtr h = tempFile(L"."); + if (h.get() != INVALID_HANDLE_VALUE) { + return h; + } + + std::wclog << L"cannot write dump file in current directory\n"; + + // try the temp directory + const auto dir = tempDir(); + + if (dir.empty()) { + std::wclog << L"can't get the temp directory\n"; + } else { + h = tempFile(dir.c_str()); + if (h.get() != INVALID_HANDLE_VALUE) { + return h; + } + } + + std::wcerr << L"nowhere to write the dump file\n"; + return {}; +} + +bool createMiniDump(HANDLE process, CoreDumpTypes type) +{ + const DWORD pid = GetProcessId(process); + + const HandlePtr file = dumpFile(); + if (!file) { + return false; + } + + auto flags = _MINIDUMP_TYPE( + MiniDumpNormal | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithProcessThreadData); + + if (type == CoreDumpTypes::Data) { + std::wclog << L"writing minidump with data\n"; + flags = _MINIDUMP_TYPE(flags | MiniDumpWithDataSegs); + } else if (type == CoreDumpTypes::Full) { + std::wclog << L"writing full minidump\n"; + flags = _MINIDUMP_TYPE(flags | MiniDumpWithFullMemory); + } else { + std::wclog << L"writing mini minidump\n"; + } + + const auto ret = MiniDumpWriteDump( + process, pid, file.get(), flags, nullptr, nullptr, nullptr); + + if (!ret) { + const auto e = GetLastError(); + + std::wcerr + << L"failed to write mini dump, " << formatSystemMessage(e) << L"\n"; + + return false; + } + + std::wclog << L"minidump written correctly\n"; + return true; +} + + +bool coredump(CoreDumpTypes type) +{ + std::wclog << L"creating minidump for the current process\n"; + return createMiniDump(GetCurrentProcess(), type); +} + +bool coredumpOther(CoreDumpTypes type) +{ + std::wclog << L"creating minidump for an running process\n"; + + const auto pid = findOtherPid(); + if (pid == 0) { + std::wcerr << L"no other process found\n"; + return false; + } + + std::wclog << L"found other process with pid " << pid << L"\n"; + + HandlePtr handle(OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); + + if (!handle) { + const auto e = GetLastError(); + + std::wcerr + << L"failed to open process " << pid << L", " + << formatSystemMessage(e) << L"\n"; + + return false; + } + + return createMiniDump(handle.get(), type); +} + } // namespace env } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index 6b842f8c..2296651c 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -284,6 +284,23 @@ private: void getSecurityFeatures(); }; + +enum class CoreDumpTypes +{ + Mini = 1, + Data, + Full +}; + +// creates a minidump file for the given process +// +bool coredump(CoreDumpTypes type); + +// finds another process with the same name as this one and creates a minidump +// file for it +// +bool coredumpOther(CoreDumpTypes type); + } // namespace env -- cgit v1.3.1 From 553a4d12a7891a463b87406ec1550b874518b001 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 1 Jul 2019 09:55:42 -0400 Subject: show supported games in error dialog when browsing for a binary --- src/main.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f74a9cf4..f08e5ad3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -371,8 +371,25 @@ MOBase::IPluginGame *determineCurrentGame(QString const &moPath, QSettings &sett } else if(possibleGames.count() == 1) { return selectGame(settings, gameDir, possibleGames[0]); } else { - reportError(gameConfigured ? QObject::tr("%1 not identified in \"%2\". The directory is required to contain the game binary.").arg(gameName).arg(gamePath) - : QObject::tr("No game identified in \"%1\". The directory is required to contain the game binary.").arg(gamePath)); + if (gameConfigured) { + reportError(QObject::tr("%1 not identified in \"%2\". The directory is required to contain the game binary.").arg(gameName).arg(gamePath)); + } else { + QString supportedGames; + + for (IPluginGame * const game : plugins.plugins()) { + supportedGames += "
  • " + game->gameName() + "
  • "; + } + + QString text = QObject::tr( + "No game identified in \"%1\". The directory is required to " + "contain the game binary.

    " + "These are the games supported by Mod Organizer:" + "
      %2
    ") + .arg(gamePath) + .arg(supportedGames); + + reportError(text); + } } } } -- cgit v1.3.1 From 7c1ce768ef38dc194583bd3c522596bd124890f2 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 1 Jul 2019 12:38:35 -0400 Subject: now handles windows firewall, which apparently doens't report itself to wmi some more error checking --- src/main.cpp | 6 +- src/shared/util.cpp | 294 ++++++++++++++++++++++++++++++++++++++-------------- src/shared/util.h | 26 +++-- 3 files changed, 239 insertions(+), 87 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f08e5ad3..c6c87a64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -493,9 +493,9 @@ int runApplication(MOApplication &application, SingleInstance &instance, qWarning() << "MO seems to be running in compatibility mode"; } - qDebug().nospace().noquote() << "security features:"; - for (const auto& sf : env.securityFeatures()) { - qDebug().nospace().noquote() << " . " << sf.toString(); + qDebug().nospace().noquote() << "security products:"; + for (const auto& sp : env.securityProducts()) { + qDebug().nospace().noquote() << " . " << sp.toString(); } qDebug() << "modules loaded in process:"; diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 36800d5b..072cee2d 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -35,6 +35,8 @@ along with Mod Organizer. If not, see . #include #include #include +#include + #pragma comment(lib, "Wbemuuid.lib") using MOBase::formatSystemMessage; @@ -305,6 +307,9 @@ struct COMReleaser } }; +template +using COMPtr = std::unique_ptr; + class WMI { @@ -332,17 +337,26 @@ public: } auto enumerator = getEnumerator(q); + if (!enumerator) { + return; + } for (;;) { - std::unique_ptr object; + COMPtr object; { IWbemClassObject* rawObject = nullptr; ULONG count = 0; auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count); - if (count == 0) { + if (count == 0 || !rawObject) { + break; + } + + if (FAILED(ret)) { + qCritical() + << "enumerator->next() failed, " << formatSystemMessageQ(ret); break; } @@ -353,33 +367,9 @@ public: } } - std::unique_ptr getEnumerator( - const std::string& query) - { - IEnumWbemClassObject* rawEnumerator = NULL; - - auto ret = m_service->ExecQuery( - bstr_t("WQL"), - bstr_t(query.c_str()), - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, - NULL, - &rawEnumerator); - - if (FAILED(ret)) - { - qCritical() - << "query '" << QString::fromStdString(query) << "' failed, " - << formatSystemMessageQ(ret); - - return {}; - } - - return std::unique_ptr(rawEnumerator); - } - private: - std::unique_ptr m_locator; - std::unique_ptr m_service; + COMPtr m_locator; + COMPtr m_service; void createLocator() { @@ -389,7 +379,7 @@ private: CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, IID_IWbemLocator, &rawLocator); - if (FAILED(ret)) { + if (FAILED(ret) || !rawLocator) { qCritical() << "CoCreateInstance for WbemLocator failed, " << formatSystemMessageQ(ret); @@ -409,7 +399,7 @@ private: nullptr, nullptr, nullptr, 0, nullptr, nullptr, &rawService); - if (FAILED(res)) { + if (FAILED(res) || !rawService) { qCritical() << "locator->ConnectServer() failed for namespace " << "'" << QString::fromStdString(ns) << "', " @@ -435,13 +425,37 @@ private: throw failed(); } } + + COMPtr getEnumerator( + const std::string& query) + { + IEnumWbemClassObject* rawEnumerator = NULL; + + auto ret = m_service->ExecQuery( + bstr_t("WQL"), + bstr_t(query.c_str()), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &rawEnumerator); + + if (FAILED(ret) || !rawEnumerator) + { + qCritical() + << "query '" << QString::fromStdString(query) << "' failed, " + << formatSystemMessageQ(ret); + + return {}; + } + + return COMPtr(rawEnumerator); + } }; Environment::Environment() { - getLoadedModules(); - getSecurityFeatures(); + m_modules = getLoadedModules(); + m_security = getSecurityProducts(); } const std::vector& Environment::loadedModules() @@ -454,12 +468,12 @@ const WindowsInfo& Environment::windowsInfo() const return m_windows; } -const std::vector& Environment::securityFeatures() const +const std::vector& Environment::securityProducts() const { return m_security; } -void Environment::getLoadedModules() +std::vector Environment::getLoadedModules() const { HandlePtr snapshot(CreateToolhelp32Snapshot( TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId())); @@ -472,7 +486,7 @@ void Environment::getLoadedModules() << "CreateToolhelp32Snapshot() failed, " << formatSystemMessageQ(e); - return; + return {}; } MODULEENTRY32 me = {}; @@ -486,54 +500,88 @@ void Environment::getLoadedModules() qCritical().nospace().noquote() << "Module32First() failed, " << formatSystemMessageQ(e); - return; + return {}; } + std::vector v; + for (;;) { const auto path = QString::fromWCharArray(me.szExePath); - - m_modules.push_back(Module(path, me.modBaseSize)); + if (!path.isEmpty()) { + v.push_back(Module(path, me.modBaseSize)); + } // next module if (!Module32Next(snapshot.get(), &me)) { const auto e = GetLastError(); - if (e == ERROR_NO_MORE_FILES) { - // not an error - break; - } - + // no more modules is not an error + if (e != ERROR_NO_MORE_FILES) { qCritical().nospace().noquote() << "Module32Next() failed, " << formatSystemMessageQ(e); + } break; } } // sorting by display name - std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) { + std::sort(v.begin(), v.end(), [](auto&& a, auto&& b) { return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0); }); + + return v; +} + +std::vector Environment::getSecurityProducts() const +{ + std::vector v; + + { + auto fromWMI = getSecurityProductsFromWMI(); + v.insert( + v.end(), + std::make_move_iterator(fromWMI.begin()), + std::make_move_iterator(fromWMI.end())); + } + + if (auto p=getWindowsFirewall()) { + v.push_back(std::move(*p)); + } + + return v; } -void Environment::getSecurityFeatures() +std::vector Environment::getSecurityProductsFromWMI() const { - WMI wmi("root\\SecurityCenter2"); - std::map map; + // some products may be present in multiple queries, such as a product marked + // as both antivirus and antispyware, but they'll have the same GUID, so use + // that to avoid duplicating entries + std::map map; auto handleProduct = [&](auto* o) { VARIANT prop; + // display name auto ret = o->Get(L"displayName", 0, &prop, 0, 0); if (FAILED(ret)) { - qCritical() << "failed to get displayName, " << formatSystemMessageQ(ret); + qCritical() + << "failed to get displayName, " + << formatSystemMessageQ(ret); + + return; + } + + if (prop.vt != VT_BSTR) { + qCritical() << "displayName is a " << prop.vt << ", not a bstr"; return; } const std::wstring name = prop.bstrVal; VariantClear(&prop); + // product state ret = o->Get(L"productState", 0, &prop, 0, 0); if (FAILED(ret)) { qCritical() @@ -543,9 +591,21 @@ void Environment::getSecurityFeatures() return; } - const DWORD state = prop.ulVal; + if (prop.vt != VT_UI4 && prop.vt != VT_I4) { + qCritical() << "productState is a " << prop.vt << ", is not a VT_UI4"; + return; + } + + DWORD state = 0; + if (prop.vt == VT_I4) { + state = prop.lVal; + } else { + state = prop.ulVal; + } + VariantClear(&prop); + // guid ret = o->Get(L"instanceGuid", 0, &prop, 0, 0); if (FAILED(ret)) { qCritical() @@ -555,25 +615,94 @@ void Environment::getSecurityFeatures() return; } + if (prop.vt != VT_BSTR) { + qCritical() << "instanceGuid is a " << prop.vt << ", is not a bstr"; + return; + } + const QUuid guid(QString::fromWCharArray(prop.bstrVal)); VariantClear(&prop); + const auto provider = static_cast((state >> 16) & 0xff); + const auto scanner = (state >> 8) & 0xff; + const auto definitions = state & 0xff; - auto itor = map.find(guid); + const bool active = ((scanner & 0x10) != 0); + const bool upToDate = (definitions == 0); - if (itor == map.end()) { - map.insert({ - guid, SecurityFeature(QString::fromStdWString(name), state)}); - } + map.insert({ + guid, + {QString::fromStdWString(name), provider, active, upToDate}}); }; - wmi.query("select * from AntivirusProduct", handleProduct); - wmi.query("select * from FirewallProduct", handleProduct); - wmi.query("select * from AntiSpywareProduct", handleProduct); + { + WMI wmi("root\\SecurityCenter2"); + wmi.query("select * from AntivirusProduct", handleProduct); + wmi.query("select * from FirewallProduct", handleProduct); + wmi.query("select * from AntiSpywareProduct", handleProduct); + } + + { + WMI wmi("root\\SecurityCenter"); + wmi.query("select * from AntivirusProduct", handleProduct); + wmi.query("select * from FirewallProduct", handleProduct); + wmi.query("select * from AntiSpywareProduct", handleProduct); + } + + std::vector v; for (auto&& p : map) { - m_security.push_back(p.second); + v.push_back(p.second); } + + return v; +} + +std::optional Environment::getWindowsFirewall() const +{ + HRESULT hr = 0; + + COMPtr policy; + + { + void* rawPolicy = nullptr; + + hr = CoCreateInstance( + __uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, + __uuidof(INetFwPolicy2), &rawPolicy); + + if (FAILED(hr) || !rawPolicy) { + qCritical() + << "CoCreateInstance for NetFwPolicy2 failed, " + << formatSystemMessage(hr); + + return {}; + } + + policy.reset(static_cast(rawPolicy)); + } + + VARIANT_BOOL enabledVariant; + + if (policy) { + hr = policy->get_FirewallEnabled(NET_FW_PROFILE2_PUBLIC, &enabledVariant); + if (FAILED(hr)) + { + qCritical() + << "get_FirewallEnabled failed, " + << formatSystemMessage(hr); + + return {}; + } + } + + const auto enabled = (enabledVariant != VARIANT_FALSE); + if (!enabled) { + return {}; + } + + return SecurityProduct( + "Windows Firewall", WSC_SECURITY_PROVIDER_FIREWALL, true, true); } @@ -1123,52 +1252,67 @@ std::optional WindowsInfo::getElevated() const } -SecurityFeature::SecurityFeature(QString name, DWORD state) - : m_name(std::move(name)), m_state(state) +SecurityProduct::SecurityProduct( + QString name, int provider, + bool active, bool upToDate) : + m_name(std::move(name)), m_provider(provider), + m_active(active), m_upToDate(upToDate) { } -const QString& SecurityFeature::name() const +const QString& SecurityProduct::name() const { return m_name; } -QString SecurityFeature::toString() const +int SecurityProduct::provider() const +{ + return m_provider; +} + +bool SecurityProduct::active() const +{ + return m_active; +} + +bool SecurityProduct::upToDate() const +{ + return m_upToDate; +} + +QString SecurityProduct::toString() const { QString s; s += m_name + " "; - const auto provider = (m_state >> 16) & 0xff; - const auto scanner = (m_state >> 8) & 0xff; - const auto definitions = m_state & 0xff; QStringList ps; - if (provider & WSC_SECURITY_PROVIDER_FIREWALL) { + if (m_provider & WSC_SECURITY_PROVIDER_FIREWALL) { ps.push_back("firewall"); } - if (provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) { + if (m_provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) { ps.push_back("autoupdate"); } - if (provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) { + if (m_provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) { ps.push_back("antivirus"); } - if (provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) { + if (m_provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) { ps.push_back("antispyware"); } - if (provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) { + if (m_provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) { ps.push_back("settings"); } - if (provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) { + if (m_provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) { ps.push_back("uac"); } - if (provider & WSC_SECURITY_PROVIDER_SERVICE) { + if (m_provider & WSC_SECURITY_PROVIDER_SERVICE) { ps.push_back("service"); } @@ -1178,15 +1322,13 @@ QString SecurityFeature::toString() const s += "(" + ps.join("|") + ")"; } - if (scanner & 0x10) { + if (m_active) { s += ", active"; } else { s += ", inactive"; } - if (definitions == 0) { - s += ", definitions up to date"; - } else { + if (!m_upToDate) { s += ", definitions outdated"; } diff --git a/src/shared/util.h b/src/shared/util.h index 2296651c..e0da8a99 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -241,18 +241,25 @@ private: }; -class SecurityFeature +class SecurityProduct { public: - SecurityFeature(QString name, DWORD state); + SecurityProduct( + QString name, int provider, + bool active, bool upToDate); const QString& name() const; + int provider() const; + bool active() const; + bool upToDate() const; QString toString() const; private: QString m_name; - DWORD m_state; + int m_provider; + bool m_active; + bool m_upToDate; }; @@ -271,17 +278,20 @@ public: // const WindowsInfo& windowsInfo() const; - // information about the installed antivirus + // information about the installed security products // - const std::vector& securityFeatures() const; + const std::vector& securityProducts() const; private: std::vector m_modules; WindowsInfo m_windows; - std::vector m_security; + std::vector m_security; - void getLoadedModules(); - void getSecurityFeatures(); + std::vector getLoadedModules() const; + std::vector getSecurityProducts() const; + + std::vector getSecurityProductsFromWMI() const; + std::optional getWindowsFirewall() const; }; -- cgit v1.3.1 From 41c9dd9b722b5792e4540a830f5ae55b130fa7f5 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 3 Jul 2019 17:26:08 -0400 Subject: preloadDll() would always think dlls were already loaded --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c6c87a64..b7176ee1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -432,7 +432,7 @@ void preloadDll(const QString& filename) { qDebug().nospace() << "preloading " << filename; - if (!GetModuleHandleW(filename.toStdWString().c_str())) { + if (GetModuleHandleW(filename.toStdWString().c_str())) { // already loaded, this can happen when "restarting" MO by switching // instances, for example return; -- cgit v1.3.1 From dfd34bf9c0ad055e76c1b6f272c21eb0d79536ce Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 3 Jul 2019 19:12:54 -0500 Subject: Update openssl library names --- src/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b7176ee1..4a2f6035 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -459,8 +459,13 @@ void preloadDll(const QString& filename) void preloadSsl() { - preloadDll("libeay32.dll"); - preloadDll("ssleay32.dll"); +#if Q_PROCESSOR_WORDSIZE == 8 + preloadDll("libcrypto-1_1-x64.dll"); + preloadDll("libssl-1_1-x64.dll"); +#elif Q_PROCESSOR_WORDSIZE == 4 + preloadDll("libcrypto-1_1.dll"); + preloadDll("libssl-1_1.dll"); +#endif } static QString getVersionDisplayString() -- cgit v1.3.1 From 37aa5e07f473e0377ef59d6e2f53f7373b11aecd Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 8 Jul 2019 13:19:52 -0500 Subject: Add some basic checks for symlinks --- src/main.cpp | 18 +++++++++++++++++- src/organizercore.cpp | 17 ++++++++++++++++- src/organizercore.h | 1 + src/selectiondialog.cpp | 8 ++++++++ src/selectiondialog.h | 1 + 5 files changed, 43 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4a2f6035..f2571685 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -303,6 +303,11 @@ MOBase::IPluginGame *determineCurrentGame(QString const &moPath, QSettings &sett gamePath = game->gameDirectory().absolutePath(); } QDir gameDir(gamePath); + QFileInfo directoryInfo(gameDir.path()); + if (directoryInfo.isSymLink()) { + reportError(QObject::tr("The configured path to the game directory (%1) appears to be a symbolic (or other) link. " + "This setup is incompatible with MO2's VFS and will not run correctly.").arg(gamePath)); + } if (game->looksValid(gameDir)) { return selectGame(settings, gameDir, game); } @@ -335,15 +340,26 @@ MOBase::IPluginGame *determineCurrentGame(QString const &moPath, QSettings &sett while (selection.exec() != QDialog::Rejected) { IPluginGame * game = selection.getChoiceData().value(); + QString gamePath = selection.getChoiceDescription(); + QFileInfo directoryInfo(gamePath); + if (directoryInfo.isSymLink()) { + reportError(QObject::tr("The configured path to the game directory (%1) appears to be a symbolic (or other) link. " + "This setup is incompatible with MO2's VFS and will not run correctly.").arg(gamePath)); + } if (game != nullptr) { return selectGame(settings, game->gameDirectory(), game); } - QString gamePath = QFileDialog::getExistingDirectory(nullptr, gameConfigured ? QObject::tr("Please select the installation of %1 to manage").arg(gameName) + gamePath = QFileDialog::getExistingDirectory(nullptr, gameConfigured ? QObject::tr("Please select the installation of %1 to manage").arg(gameName) : QObject::tr("Please select the game to manage"), QString(), QFileDialog::ShowDirsOnly); if (!gamePath.isEmpty()) { QDir gameDir(gamePath); + QFileInfo directoryInfo(gamePath); + if (directoryInfo.isSymLink()) { + reportError(QObject::tr("The configured path to the game directory (%1) appears to be a symbolic (or other) link. " + "This setup is incompatible with MO2's VFS and will not run correctly.").arg(gamePath)); + } QList possibleGames; for (IPluginGame * const game : plugins.plugins()) { //If a game is already configured, skip any plugins that are not for that game diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 99ddda1d..81ec7f43 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -691,12 +691,27 @@ bool OrganizerCore::createDirectory(const QString &path) { } } +bool OrganizerCore::checkPathSymlinks() { + bool hasSymlink = (QFileInfo(m_Settings.getProfileDirectory()).isSymLink() || + QFileInfo(m_Settings.getModDirectory()).isSymLink() || + QFileInfo(m_Settings.getOverwriteDirectory()).isSymLink()); + if (hasSymlink) { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("One of the configured MO2 directories (profiles, mods, or overwrite) " + "is on a path containing a symbolic (or other) link. This is incompatible " + "with MO2's VFS system.")); + return false; + } + return true; +} + bool OrganizerCore::bootstrap() { return createDirectory(m_Settings.getProfileDirectory()) && createDirectory(m_Settings.getModDirectory()) && createDirectory(m_Settings.getDownloadDirectory()) && createDirectory(m_Settings.getOverwriteDirectory()) && - createDirectory(QString::fromStdWString(crashDumpsPath())) && cycleDiagnostics(); + createDirectory(QString::fromStdWString(crashDumpsPath())) && + checkPathSymlinks() && cycleDiagnostics(); } void OrganizerCore::createDefaultProfile() diff --git a/src/organizercore.h b/src/organizercore.h index 4dd11831..99b1c5f2 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -183,6 +183,7 @@ public: void loginFailedUpdate(const QString &message); static bool createAndMakeWritable(const QString &path); + bool checkPathSymlinks(); bool bootstrap(); void createDefaultProfile(); diff --git a/src/selectiondialog.cpp b/src/selectiondialog.cpp index 55728751..089760f9 100644 --- a/src/selectiondialog.cpp +++ b/src/selectiondialog.cpp @@ -79,6 +79,14 @@ QString SelectionDialog::getChoiceString() } } +QString SelectionDialog::getChoiceDescription() +{ + if (m_Choice == nullptr) + return QString(); + else + return m_Choice->accessibleDescription(); +} + void SelectionDialog::disableCancel() { ui->cancelButton->setEnabled(false); diff --git a/src/selectiondialog.h b/src/selectiondialog.h index 3c4b25df..5a70aa5e 100644 --- a/src/selectiondialog.h +++ b/src/selectiondialog.h @@ -52,6 +52,7 @@ public: QVariant getChoiceData(); QString getChoiceString(); + QString getChoiceDescription(); void disableCancel(); -- cgit v1.3.1