From 54d98e291701f2187174a67c186f1ea762c6b959 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Tue, 16 Jul 2019 05:38:32 -0400
Subject: removed unused or redundant stuff in error_report.h renamed log() to
vlog() for now extracted console creation to Console class rewrote LogBuffer
to work with logging from uibase, renamed to LogModel added fmt dependency
---
src/shared/directoryentry.cpp | 12 ++++++------
src/shared/error_report.cpp | 45 -------------------------------------------
src/shared/error_report.h | 21 ++------------------
3 files changed, 8 insertions(+), 70 deletions(-)
(limited to 'src/shared')
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index bde515a9..9d9edd85 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -103,7 +103,7 @@ public:
m_OriginsNameMap.erase(iter);
m_OriginsNameMap[newName] = idx;
} else {
- log("failed to change name lookup from %ls to %ls", oldName.c_str(), newName.c_str());
+ vlog("failed to change name lookup from %ls to %ls", oldName.c_str(), newName.c_str());
}
}
@@ -714,12 +714,12 @@ void DirectoryEntry::removeFile(FileEntry::Index index)
if (iter != m_Files.end()) {
m_Files.erase(iter);
} else {
- log("file \"%ls\" not in directory \"%ls\"",
+ vlog("file \"%ls\" not in directory \"%ls\"",
m_FileRegister->getFile(index)->getName().c_str(),
this->getName().c_str());
}
} else {
- log("file \"%ls\" not in directory \"%ls\", directory empty",
+ vlog("file \"%ls\" not in directory \"%ls\", directory empty",
m_FileRegister->getFile(index)->getName().c_str(),
this->getName().c_str());
}
@@ -844,7 +844,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const
DirectoryEntry *temp = findSubDirectory(pathComponent);
if (temp != nullptr) {
if (len >= path.size()) {
- log("unexpected end of path");
+ vlog("unexpected end of path");
return FileEntry::Ptr();
}
return temp->searchFile(path.substr(len + 1), directory);
@@ -988,7 +988,7 @@ bool FileRegister::removeFile(FileEntry::Index index)
m_Files.erase(index);
return true;
} else {
- log("invalid file index for remove: %lu", index);
+ vlog("invalid file index for remove: %lu", index);
return false;
}
}
@@ -1002,7 +1002,7 @@ void FileRegister::removeOrigin(FileEntry::Index index, int originID)
m_Files.erase(iter);
}
} else {
- log("invalid file index for remove (for origin): %lu", index);
+ vlog("invalid file index for remove (for origin): %lu", index);
}
}
diff --git a/src/shared/error_report.cpp b/src/shared/error_report.cpp
index 6d091630..4185b544 100644
--- a/src/shared/error_report.cpp
+++ b/src/shared/error_report.cpp
@@ -23,7 +23,6 @@ along with Mod Organizer. If not, see .
namespace MOShared {
-
void reportError(LPCSTR format, ...)
{
char buffer[1025];
@@ -52,48 +51,4 @@ void reportError(LPCWSTR format, ...)
MessageBoxW(nullptr, buffer, L"Error", MB_OK | MB_ICONERROR);
}
-
-std::string getCurrentErrorStringA()
-{
- LPSTR buffer = nullptr;
-
- DWORD errorCode = ::GetLastError();
-
- if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, nullptr) == 0) {
- ::SetLastError(errorCode);
- return std::string();
- } else {
- LPSTR lastChar = buffer + strlen(buffer) - 2;
- *lastChar = '\0';
-
- std::string result(buffer);
-
- LocalFree(buffer);
- ::SetLastError(errorCode);
- return result;
- }
-}
-
-std::wstring getCurrentErrorStringW()
-{
- LPWSTR buffer = nullptr;
-
- DWORD errorCode = ::GetLastError();
-
- if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buffer, 0, nullptr) == 0) {
- ::SetLastError(errorCode);
- return std::wstring();
- } else {
- LPWSTR lastChar = buffer + wcslen(buffer) - 2;
- *lastChar = '\0';
-
- std::wstring result(buffer);
-
- LocalFree(buffer);
- ::SetLastError(errorCode);
- return result;
- }
-}
} // namespace MOShared
diff --git a/src/shared/error_report.h b/src/shared/error_report.h
index c09ad75b..a003ee09 100644
--- a/src/shared/error_report.h
+++ b/src/shared/error_report.h
@@ -24,28 +24,11 @@ along with Mod Organizer. If not, see .
#include
#include
-namespace std {
-#ifdef UNICODE
-typedef wstring tstring;
-#else
-typedef string tstring;
-#endif
-}
-
-extern void log(const char* format, ...);
-
namespace MOShared {
void reportError(LPCSTR format, ...);
void reportError(LPCWSTR format, ...);
-std::string getCurrentErrorStringA();
-std::wstring getCurrentErrorStringW();
-
-#ifdef UNICODE
-#define getCurrentErrorString getCurrentErrorStringW
-#else
-#define getCurrentErrorString getCurrentErrorStringA
-#endif
-
} // namespace MOShared
+
+void vlog(const char* format, ...);
--
cgit v1.3.1
From ad77e315f5c53994d75056608df0f9ff0a390530 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Wed, 17 Jul 2019 09:10:14 -0400
Subject: moved Console to util
---
src/main.cpp | 39 +--------------------------------------
src/shared/util.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++
src/shared/util.h | 14 ++++++++++++++
3 files changed, 58 insertions(+), 38 deletions(-)
(limited to 'src/shared')
diff --git a/src/main.cpp b/src/main.cpp
index 8b5648c9..65f4bd05 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -732,46 +732,9 @@ int runApplication(MOApplication &application, SingleInstance &instance,
}
}
-class Console
-{
-public:
- Console()
- {
- // open a console
- AllocConsole();
-
- // redirect stdin, stdout and stderr to it
- freopen_s(&m_in, "CONIN$", "r", stdin);
- freopen_s(&m_out, "CONOUT$", "w", stdout);
- freopen_s(&m_err, "CONOUT$", "w", stderr);
- }
-
- ~Console()
- {
- // close redirected handles
- std::fclose(m_err);
- std::fclose(m_out);
- std::fclose(m_in);
-
- // close console
- FreeConsole();
-
- // redirect stdin, stdout and stderr to NUL, don't bother closing the
- // handles
- freopen_s(&m_in, "NUL", "r", stdin);
- freopen_s(&m_out, "NUL", "w", stdout);
- freopen_s(&m_err, "NUL", "w", stderr);
- }
-
-private:
- FILE* m_in = nullptr;
- FILE* m_out = nullptr;
- FILE* m_err = nullptr;
-};
-
int doCoreDump(env::CoreDumpTypes type)
{
- Console c;
+ env::Console c;
// dump
const auto b = env::coredumpOther(type);
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 17df3b92..29e52f40 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -441,6 +441,49 @@ private:
};
+Console::Console()
+ : m_hasConsole(false), m_in(nullptr), m_out(nullptr), m_err(nullptr)
+{
+ // open a console
+ if (!AllocConsole()) {
+ // failed, ignore
+ }
+
+ m_hasConsole = true;
+
+ // redirect stdin, stdout and stderr to it
+ freopen_s(&m_in, "CONIN$", "r", stdin);
+ freopen_s(&m_out, "CONOUT$", "w", stdout);
+ freopen_s(&m_err, "CONOUT$", "w", stderr);
+}
+
+Console::~Console()
+{
+ // close redirected handles and redirect standard stream to NUL in case
+ // they're used after this
+
+ if (m_err) {
+ std::fclose(m_err);
+ freopen_s(&m_err, "NUL", "w", stderr);
+ }
+
+ if (m_out) {
+ std::fclose(m_out);
+ freopen_s(&m_out, "NUL", "w", stdout);
+ }
+
+ if (m_in) {
+ std::fclose(m_in);
+ freopen_s(&m_in, "NUL", "r", stdin);
+ }
+
+ // close console
+ if (m_hasConsole) {
+ FreeConsole();
+ }
+}
+
+
Shortcut::Shortcut()
: m_iconIndex(0)
{
diff --git a/src/shared/util.h b/src/shared/util.h
index c4a2ed7d..a5d096ac 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -54,6 +54,20 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
namespace env
{
+class Console
+{
+public:
+ Console();
+ ~Console();
+
+private:
+ bool m_hasConsole;
+ FILE* m_in;
+ FILE* m_out;
+ FILE* m_err;
+};
+
+
// an application shortcut that can be either on the desktop or the start menu
//
class Shortcut
--
cgit v1.3.1
From 3bf7717c0a8507c9befce6b74c84c4dbdcac99de Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Wed, 17 Jul 2019 11:41:09 -0400
Subject: moved Settings out of OrganizerCore so it can be created by itself to
access settings early set log level on startup replaced more qDebug()
---
src/main.cpp | 100 +++++++++++++++++++++++++-------------------------
src/organizercore.cpp | 6 +--
src/organizercore.h | 4 +-
src/shared/util.cpp | 4 +-
4 files changed, 57 insertions(+), 57 deletions(-)
(limited to 'src/shared')
diff --git a/src/main.cpp b/src/main.cpp
index f55c32f2..aa842ead 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -425,8 +425,6 @@ void setupPath()
{
static const int BUFSIZE = 4096;
- log::debug("MO at {}", QCoreApplication::applicationDirPath());
-
QCoreApplication::setLibraryPaths(QStringList(QCoreApplication::applicationDirPath() + "/dlls") + QCoreApplication::libraryPaths());
boost::scoped_array oldPath(new TCHAR[BUFSIZE]);
@@ -446,8 +444,6 @@ void setupPath()
void preloadDll(const QString& filename)
{
- log::debug("preloading {}", filename);
-
if (GetModuleHandleW(filename.toStdWString().c_str())) {
// already loaded, this can happen when "restarting" MO by switching
// instances, for example
@@ -513,62 +509,68 @@ void dumpEnvironment()
int runApplication(MOApplication &application, SingleInstance &instance,
const QString &splashPath)
{
-
log::info(
- "Starting Mod Organizer version {} revision {}",
- getVersionDisplayString(), GITID);
+ "Starting Mod Organizer version {} revision {} in {}",
+ getVersionDisplayString(), GITID, QCoreApplication::applicationDirPath());
-#if !defined(QT_NO_SSL)
preloadSsl();
- log::info("ssl support: {}", QSslSocket::supportsSsl());
-#else
- log::info("non-ssl build");
-#endif
-
- dumpEnvironment();
+ if (!QSslSocket::supportsSsl()) {
+ log::warn("no ssl support");
+ }
QString dataPath = application.property("dataPath").toString();
- qDebug("data path: %s", qUtf8Printable(dataPath));
+ log::info("data path: {}", dataPath);
if (!bootstrap()) {
reportError("failed to set up data paths");
return 1;
}
- QWindowsWindowFunctions::setWindowActivationBehavior(QWindowsWindowFunctions::AlwaysActivateWindow);
+ QWindowsWindowFunctions::setWindowActivationBehavior(
+ QWindowsWindowFunctions::AlwaysActivateWindow);
QStringList arguments = application.arguments();
try {
- qDebug("Working directory: %s", qUtf8Printable(QDir::toNativeSeparators(QDir::currentPath())));
+ log::info("working directory: {}", QDir::currentPath());
- QSettings settings(dataPath + "/"
- + QString::fromStdWString(AppConfig::iniFileName()),
- QSettings::IniFormat);
+ QSettings initSettings(
+ dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName()),
+ QSettings::IniFormat);
- // global crashDumpType sits in OrganizerCore to make a bit less ugly to update it when the settings are changed during runtime
- OrganizerCore::setGlobalCrashDumpsType(settings.value("Settings/crash_dumps_type", static_cast(CrashDumpsType::Mini)).toInt());
+ Settings settings(initSettings);
+ log::getDefault().setLevel(settings.logLevel());
- qDebug("Loaded settings:");
- settings.beginGroup("Settings");
- for (auto k : settings.allKeys())
- if (!k.contains("username") && !k.contains("password") && !k.contains("nexus_api_key"))
- qDebug(" %s=%s", k.toUtf8().data(), settings.value(k).toString().toUtf8().data());
- settings.endGroup();
+ dumpEnvironment();
+ // global crashDumpType sits in OrganizerCore to make a bit less ugly to
+ // update it when the settings are changed during runtime
+ OrganizerCore::setGlobalCrashDumpsType(settings.crashDumpsType());
- qDebug("initializing core");
+ log::debug("Loaded settings:");
+
+ initSettings.beginGroup("Settings");
+ for (auto k : initSettings.allKeys()) {
+ if (!k.contains("username") && !k.contains("password") && !k.contains("nexus_api_key")) {
+ log::debug(" {}={}", k, initSettings.value(k).toString());
+ }
+ }
+ initSettings.endGroup();
+
+
+ log::debug("initializing core");
OrganizerCore organizer(settings);
if (!organizer.bootstrap()) {
reportError("failed to set up data paths");
return 1;
}
- qDebug("initialize plugins");
+
+ log::debug("initializing plugins");
PluginContainer pluginContainer(&organizer);
pluginContainer.loadPlugins();
MOBase::IPluginGame *game = determineCurrentGame(
- application.applicationDirPath(), settings, pluginContainer);
+ application.applicationDirPath(), initSettings, pluginContainer);
if (game == nullptr) {
InstanceManager &instance = InstanceManager::instance();
QString instanceName = instance.currentInstance();
@@ -586,14 +588,14 @@ int runApplication(MOApplication &application, SingleInstance &instance,
if (!image.isNull()) {
image.save(dataPath + "/splash.png");
} else {
- qDebug("no plugin splash");
+ log::debug("no plugin splash");
}
}
organizer.setManagedGame(game);
organizer.createDefaultProfile();
- if (!settings.contains("game_edition")) {
+ if (!initSettings.contains("game_edition")) {
QStringList editions = game->gameVariants();
if (editions.size() > 1) {
SelectionDialog selection(
@@ -609,18 +611,17 @@ int runApplication(MOApplication &application, SingleInstance &instance,
if (selection.exec() == QDialog::Rejected) {
return 1;
} else {
- settings.setValue("game_edition", selection.getChoiceString());
+ initSettings.setValue("game_edition", selection.getChoiceString());
}
}
}
- game->setGameVariant(settings.value("game_edition").toString());
+ game->setGameVariant(initSettings.value("game_edition").toString());
- qDebug("managing game at %s", qUtf8Printable(QDir::toNativeSeparators(
- game->gameDirectory().absolutePath())));
+ log::info("managing game at {}", game->gameDirectory().absolutePath());
- organizer.updateExecutablesList(settings);
+ organizer.updateExecutablesList(initSettings);
- QString selectedProfileName = determineProfile(arguments, settings);
+ QString selectedProfileName = determineProfile(arguments, initSettings);
organizer.setCurrentProfile(selectedProfileName);
// if we have a command line parameter, it is either a nxm link or
@@ -640,13 +641,12 @@ int runApplication(MOApplication &application, SingleInstance &instance,
}
}
else if (OrganizerCore::isNxmLink(arguments.at(1))) {
- qDebug("starting download from command line: %s",
- qUtf8Printable(arguments.at(1)));
+ log::debug("starting download from command line: {}", arguments.at(1));
organizer.externalMessage(arguments.at(1));
}
else {
QString exeName = arguments.at(1);
- qDebug("starting %s from command line", qUtf8Printable(exeName));
+ log::debug("starting {} from command line", exeName);
arguments.removeFirst(); // remove application name (ModOrganizer.exe)
arguments.removeFirst(); // remove binary name
// pass the remaining parameters to the binary
@@ -665,8 +665,8 @@ 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 (initSettings.contains("window_monitor")) {
+ const int monitor = initSettings.value("window_monitor").toInt();
if (monitor != -1) {
QDesktopWidget* desktop = QApplication::desktop();
@@ -683,21 +683,21 @@ int runApplication(MOApplication &application, SingleInstance &instance,
NexusInterface::instance(&pluginContainer)->getAccessManager()->apiCheck(apiKey);
}
- qDebug("initializing tutorials");
+ log::debug("initializing tutorials");
TutorialManager::init(
qApp->applicationDirPath() + "/"
+ QString::fromStdWString(AppConfig::tutorialsPath()) + "/",
&organizer);
- if (!application.setStyleFile(settings.value("Settings/style", "").toString())) {
+ if (!application.setStyleFile(initSettings.value("Settings/style", "").toString())) {
// disable invalid stylesheet
- settings.setValue("Settings/style", "");
+ initSettings.setValue("Settings/style", "");
}
int res = 1;
{ // scope to control lifetime of mainwindow
// set up main window and its data structures
- MainWindow mainWindow(settings, organizer, pluginContainer);
+ MainWindow mainWindow(initSettings, organizer, pluginContainer);
NexusInterface::instance(&pluginContainer)
->getAccessManager()->setTopLevelWidget(&mainWindow);
@@ -714,7 +714,7 @@ int runApplication(MOApplication &application, SingleInstance &instance,
mainWindow.readSettings();
- qDebug("displaying main window");
+ log::debug("displaying main window");
mainWindow.show();
mainWindow.activateWindow();
@@ -857,7 +857,7 @@ int main(int argc, char *argv[])
if (moshortcut ||
arguments.size() > 1 && OrganizerCore::isNxmLink(arguments.at(1)))
{
- qDebug("not primary instance, sending shortcut/download message");
+ log::debug("not primary instance, sending shortcut/download message");
instance.sendMessage(arguments.at(1));
return 0;
} else if (arguments.size() == 1) {
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 25fbc7cd..400f5391 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -268,12 +268,12 @@ bool checkService()
}
-OrganizerCore::OrganizerCore(const QSettings &initSettings)
+OrganizerCore::OrganizerCore(Settings &settings)
: m_UserInterface(nullptr)
, m_PluginContainer(nullptr)
, m_GameName()
, m_CurrentProfile(nullptr)
- , m_Settings(initSettings)
+ , m_Settings(settings)
, m_Updater(NexusInterface::instance(m_PluginContainer))
, m_AboutToRun()
, m_FinishedRun()
@@ -294,7 +294,7 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings)
NexusInterface::instance(m_PluginContainer)->setCacheDirectory(m_Settings.getCacheDirectory());
- MOBase::QuestionBoxMemory::init(initSettings.fileName());
+ MOBase::QuestionBoxMemory::init(m_Settings.directInterface().fileName());
m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
m_InstallationManager.setDownloadDirectory(m_Settings.getDownloadDirectory());
diff --git a/src/organizercore.h b/src/organizercore.h
index ef1a4133..c368d101 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -97,7 +97,7 @@ public:
static bool isNxmLink(const QString &link) { return link.startsWith("nxm://", Qt::CaseInsensitive); }
- OrganizerCore(const QSettings &initSettings);
+ OrganizerCore(Settings &settings);
~OrganizerCore();
@@ -336,7 +336,7 @@ private:
Profile *m_CurrentProfile;
- Settings m_Settings;
+ Settings& m_Settings;
SelfUpdater m_Updater;
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 29e52f40..8d8c2000 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -1492,11 +1492,11 @@ QString WindowsInfo::toString() const
const QString real = m_real.toString();
// version
- sl.push_back("version: " + reported);
+ sl.push_back("version " + reported);
// real version if different
if (compatibilityMode()) {
- sl.push_back("real version: " + real);
+ sl.push_back("real version " + real);
}
// build.UBR, such as 17763.557
--
cgit v1.3.1
From 2ef32d12306ac21c0c900ff8f353ff0b573e67ae Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Wed, 17 Jul 2019 12:39:01 -0400
Subject: moved environment dump to member function added check for nahimic
---
src/main.cpp | 51 +++++++++++++++++++++++++++------------------------
src/shared/util.cpp | 25 ++++++++++++++++++++++---
src/shared/util.h | 6 +++++-
3 files changed, 54 insertions(+), 28 deletions(-)
(limited to 'src/shared')
diff --git a/src/main.cpp b/src/main.cpp
index 15d36428..ef698dd4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -482,27 +482,6 @@ static QString getVersionDisplayString()
return createVersionInfo().displayString(3);
}
-void dumpEnvironment()
-{
- env::Environment env;
-
- log::debug("windows: {}", env.windowsInfo().toString());
-
- if (env.windowsInfo().compatibilityMode()) {
- log::warn("MO seems to be running in compatibility mode");
- }
-
- log::debug("security products:");
- for (const auto& sp : env.securityProducts()) {
- log::debug(" . {}", sp.toString());
- }
-
- log::debug("modules loaded in process:");
- for (const auto& m : env.loadedModules()) {
- log::debug(" . {}", m.toString());
- }
-}
-
void dumpSettings(QSettings& settings)
{
static const QStringList ignore({
@@ -524,7 +503,7 @@ void dumpSettings(QSettings& settings)
settings.endGroup();
}
-void sanityChecks()
+void checkMissingFiles()
{
// files that are likely to be eaten
static const QStringList files({
@@ -545,6 +524,28 @@ void sanityChecks()
}
}
+void checkNahimic(const env::Environment& e)
+{
+ for (auto&& m : e.loadedModules()) {
+ const QFileInfo file(m.path());
+
+ if (file.fileName().compare("NahimicOSD.dll", Qt::CaseInsensitive)) {
+ log::warn(
+ "NahimicOSD.dll is loaded. Nahimic is known to cause issues with "
+ "Mod Organizer, such as freezing or blank windows. Consider "
+ "uninstalling it.");
+
+ break;
+ }
+ }
+}
+
+void sanityChecks(const env::Environment& e)
+{
+ checkMissingFiles();
+ checkNahimic(e);
+}
+
int runApplication(MOApplication &application, SingleInstance &instance,
const QString &splashPath)
@@ -585,9 +586,11 @@ int runApplication(MOApplication &application, SingleInstance &instance,
// update it when the settings are changed during runtime
OrganizerCore::setGlobalCrashDumpsType(settings.crashDumpsType());
- dumpEnvironment();
+ env::Environment env;
+
+ env.dump();
dumpSettings(initSettings);
- sanityChecks();
+ sanityChecks(env);
log::debug("initializing core");
OrganizerCore organizer(settings);
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 8d8c2000..eacd1f88 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -23,6 +23,7 @@ along with Mod Organizer. If not, see .
#include "executableslist.h"
#include "instancemanager.h"
#include
+#include
#include
#include
@@ -41,8 +42,7 @@ along with Mod Organizer. If not, see .
#pragma comment(lib, "Wbemuuid.lib")
-using MOBase::formatSystemMessage;
-using MOBase::formatSystemMessageQ;
+using namespace MOBase;
namespace fs = std::filesystem;
namespace MOShared {
@@ -870,7 +870,7 @@ Environment::Environment()
m_security = getSecurityProducts();
}
-const std::vector& Environment::loadedModules()
+const std::vector& Environment::loadedModules() const
{
return m_modules;
}
@@ -885,6 +885,25 @@ const std::vector& Environment::securityProducts() const
return m_security;
}
+void Environment::dump() const
+{
+ log::debug("windows: {}", windowsInfo().toString());
+
+ if (windowsInfo().compatibilityMode()) {
+ log::warn("MO seems to be running in compatibility mode");
+ }
+
+ log::debug("security products:");
+ for (const auto& sp : securityProducts()) {
+ log::debug(" . {}", sp.toString());
+ }
+
+ log::debug("modules loaded in process:");
+ for (const auto& m : loadedModules()) {
+ log::debug(" . {}", m.toString());
+ }
+}
+
std::vector Environment::getLoadedModules() const
{
HandlePtr snapshot(CreateToolhelp32Snapshot(
diff --git a/src/shared/util.h b/src/shared/util.h
index a5d096ac..267bb780 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -410,7 +410,7 @@ public:
// list of loaded modules in the current process
//
- const std::vector& loadedModules();
+ const std::vector& loadedModules() const;
// information about the operating system
//
@@ -420,6 +420,10 @@ public:
//
const std::vector& securityProducts() const;
+ // logs the environment
+ //
+ void dump() const;
+
private:
std::vector m_modules;
WindowsInfo m_windows;
--
cgit v1.3.1
From 20ac714bf880ab7e3762428c7154d1c81b5188ba Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Wed, 17 Jul 2019 19:00:33 -0400
Subject: fixed bad compare for nahimic log displays on startup
---
src/main.cpp | 2 +-
src/shared/util.cpp | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/util.h | 27 +++++++
3 files changed, 251 insertions(+), 1 deletion(-)
(limited to 'src/shared')
diff --git a/src/main.cpp b/src/main.cpp
index ef698dd4..09da9408 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -529,7 +529,7 @@ void checkNahimic(const env::Environment& e)
for (auto&& m : e.loadedModules()) {
const QFileInfo file(m.path());
- if (file.fileName().compare("NahimicOSD.dll", Qt::CaseInsensitive)) {
+ if (file.fileName().compare("NahimicOSD.dll", Qt::CaseInsensitive) == 0) {
log::warn(
"NahimicOSD.dll is loaded. Nahimic is known to cause issues with "
"Mod Organizer, such as freezing or blank windows. Consider "
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index eacd1f88..8c4a3f17 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -39,6 +39,7 @@ along with Mod Organizer. If not, see .
#include
#include
#include
+#include
#pragma comment(lib, "Wbemuuid.lib")
@@ -864,6 +865,196 @@ private:
};
+class DisplayEnumerator
+{
+public:
+ DisplayEnumerator()
+ : m_GetDpiForMonitor(nullptr)
+ {
+ m_shcore.reset(LoadLibraryW(L"Shcore.dll"));
+
+ if (m_shcore) {
+ // windows 8.1+ only
+ m_GetDpiForMonitor = reinterpret_cast(
+ GetProcAddress(m_shcore.get(), "GetDpiForMonitor"));
+ }
+
+ // gets all monitors and the device they're running on
+ getDisplayDevices();
+ }
+
+ std::vector&& displays() &&
+ {
+ return std::move(m_displays);
+ }
+
+ const std::vector& displays() const &
+ {
+ return m_displays;
+ }
+
+private:
+ using GetDpiForMonitorFunction =
+ HRESULT WINAPI (HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
+
+ std::unique_ptr m_shcore;
+ GetDpiForMonitorFunction* m_GetDpiForMonitor;
+ std::vector m_displays;
+
+ void getDisplayDevices()
+ {
+ // don't bother if it goes over 100
+ for (int i=0; i<100; ++i) {
+ DISPLAY_DEVICEW device = {};
+ device.cb = sizeof(device);
+
+ if (!EnumDisplayDevicesW(nullptr, i, &device, 0)) {
+ // no more
+ break;
+ }
+
+ // EnumDisplayDevices() seems to be returning a lot of devices that are
+ // not actually monitors, but those don't have the
+ // DISPLAY_DEVICE_ATTACHED_TO_DESKTOP bit set
+ if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) {
+ continue;
+ }
+
+ m_displays.push_back(createDisplay(device));
+ }
+ }
+
+ Metrics::Display createDisplay(const DISPLAY_DEVICEW& device)
+ {
+ Metrics::Display d;
+
+ d.adapter = QString::fromWCharArray(device.DeviceString);
+ d.monitor = QString::fromWCharArray(device.DeviceName);
+ d.primary = (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);
+
+ getDisplaySettings(device.DeviceName, d);
+ getDpi(d);
+
+ return d;
+ }
+
+ void getDisplaySettings(const wchar_t* monitorName, Metrics::Display& d)
+ {
+ DEVMODEW dm = {};
+ dm.dmSize = sizeof(dm);
+
+ if (!EnumDisplaySettingsW(monitorName, ENUM_CURRENT_SETTINGS, &dm)) {
+ log::error("EnumDisplaySettings() failed for '{}'", d.monitor);
+ return;
+ }
+
+ // all these fields should be available
+
+ if (dm.dmFields & DM_DISPLAYFREQUENCY) {
+ d.refreshRate = dm.dmDisplayFrequency;
+ }
+
+ if (dm.dmFields & DM_PELSWIDTH) {
+ d.resX = dm.dmPelsWidth;
+ }
+
+ if (dm.dmFields & DM_PELSHEIGHT) {
+ d.resY = dm.dmPelsHeight;
+ }
+ }
+
+ void getDpi(Metrics::Display& d)
+ {
+ if (!m_GetDpiForMonitor) {
+ // this happens on windows 7, get the desktop dpi instead
+ getDesktopDpi(d);
+ return;
+ }
+
+ // there's no way to get an HMONITOR from a device name, so all monitors
+ // will have to be enumerated and their name checked
+ HMONITOR hm = findMonitor(d.monitor);
+ if (!hm) {
+ log::error("can't get dpi for monitor '{}', not found", d.monitor);
+ return;
+ }
+
+ UINT dpiX=0, dpiY=0;
+ const auto r = m_GetDpiForMonitor(hm, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
+
+ if (FAILED(r)) {
+ log::error(
+ "GetDpiForMonitor() failed for '{}', {}",
+ d.monitor, formatSystemMessageQ(r));
+
+ return;
+ }
+
+ // dpiX and dpiY are always identical, as per the documentation
+ d.dpi = dpiX;
+ }
+
+ void getDesktopDpi(Metrics::Display& d)
+ {
+ // desktop dc
+ HDC dc = GetDC(0);
+
+ if (!dc) {
+ const auto e = GetLastError();
+ log::error("can't get desktop DC, {}", formatSystemMessageQ(e));
+ return;
+ }
+
+ d.dpi = GetDeviceCaps(dc, LOGPIXELSX);
+
+ ReleaseDC(0, dc);
+ }
+
+ HMONITOR findMonitor(const QString& name)
+ {
+ // passed to the enumeration callback
+ struct Data
+ {
+ DisplayEnumerator* self;
+ QString name;
+ HMONITOR hm;
+ };
+
+ Data data = {this, name, 0};
+
+ // for each monitor
+ EnumDisplayMonitors(0, nullptr, [](HMONITOR hm, HDC, RECT*, LPARAM lp) {
+ auto& data = *reinterpret_cast(lp);
+
+ MONITORINFOEX mi = {};
+ mi.cbSize = sizeof(mi);
+
+ // monitor info will include the name
+ if (!GetMonitorInfoW(hm, &mi)) {
+ const auto e = GetLastError();
+ log::error(
+ "GetMonitorInfo() failed for '{}', {}",
+ data.name, formatSystemMessageQ(e));
+
+ // error for this monitor, but continue
+ return TRUE;
+ }
+
+ if (QString::fromWCharArray(mi.szDevice) == data.name) {
+ // found, stop
+ data.hm = hm;
+ return FALSE;
+ }
+
+ // not found, continue to the next monitor
+ return TRUE;
+ }, reinterpret_cast(&data));
+
+ return data.hm;
+ }
+};
+
+
Environment::Environment()
{
m_modules = getLoadedModules();
@@ -885,6 +1076,11 @@ const std::vector& Environment::securityProducts() const
return m_security;
}
+const Metrics& Environment::metrics() const
+{
+ return m_metrics;
+}
+
void Environment::dump() const
{
log::debug("windows: {}", windowsInfo().toString());
@@ -902,6 +1098,11 @@ void Environment::dump() const
for (const auto& m : loadedModules()) {
log::debug(" . {}", m.toString());
}
+
+ log::debug("displays:");
+ for (const auto& d : m_metrics.displays()) {
+ log::debug(" . {}", d.toString());
+ }
}
std::vector Environment::getLoadedModules() const
@@ -1137,6 +1338,28 @@ std::optional Environment::getWindowsFirewall() const
}
+Metrics::Metrics()
+{
+ m_displays = DisplayEnumerator().displays();
+}
+
+const std::vector& Metrics::displays() const
+{
+ return m_displays;
+}
+
+QString Metrics::Display::toString() const
+{
+ return QString("%1*%2 %3hz dpi=%4 on %5%6")
+ .arg(resX)
+ .arg(resY)
+ .arg(refreshRate)
+ .arg(dpi)
+ .arg(adapter)
+ .arg(primary ? " (primary)" : "");
+}
+
+
Module::Module(QString path, std::size_t fileSize)
: m_path(std::move(path)), m_fileSize(fileSize)
{
diff --git a/src/shared/util.h b/src/shared/util.h
index 267bb780..fc2028db 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -401,6 +401,28 @@ private:
};
+class Metrics
+{
+public:
+ struct Display
+ {
+ int resX=0, resY=0, dpi=0;
+ bool primary=false;
+ int refreshRate = 0;
+ QString monitor, adapter;
+
+ QString toString() const;
+ };
+
+ Metrics();
+
+ const std::vector& displays() const;
+
+private:
+ std::vector m_displays;
+};
+
+
// represents the process's environment
//
class Environment
@@ -420,6 +442,10 @@ public:
//
const std::vector& securityProducts() const;
+ // information about displays
+ //
+ const Metrics& metrics() const;
+
// logs the environment
//
void dump() const;
@@ -428,6 +454,7 @@ private:
std::vector m_modules;
WindowsInfo m_windows;
std::vector m_security;
+ Metrics m_metrics;
std::vector getLoadedModules() const;
std::vector getSecurityProducts() const;
--
cgit v1.3.1
From c84240b75906fdc1f2ef8b41f4f3c00421dc61fa Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Wed, 17 Jul 2019 19:01:58 -0400
Subject: only display "inactive" for security products
---
src/shared/util.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
(limited to 'src/shared')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 8c4a3f17..441b64bd 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -1976,9 +1976,7 @@ QString SecurityProduct::toString() const
s += "(" + ps.join("|") + ")";
}
- if (m_active) {
- s += ", active";
- } else {
+ if (!m_active) {
s += ", inactive";
}
--
cgit v1.3.1
From f95479b981b41f51a3ecf055c73f42440766e5d7 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 18 Jul 2019 22:35:12 -0400
Subject: log guid for security products
---
src/shared/util.cpp | 41 ++++++++++++++++++++++++-----------------
src/shared/util.h | 6 +++++-
2 files changed, 29 insertions(+), 18 deletions(-)
(limited to 'src/shared')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 441b64bd..4ee4b766 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -1264,7 +1264,7 @@ std::vector Environment::getSecurityProductsFromWMI() const
map.insert({
guid,
- {QString::fromStdWString(name), provider, active, upToDate}});
+ {guid, QString::fromStdWString(name), provider, active, upToDate}});
};
{
@@ -1334,7 +1334,7 @@ std::optional Environment::getWindowsFirewall() const
}
return SecurityProduct(
- "Windows Firewall", WSC_SECURITY_PROVIDER_FIREWALL, true, true);
+ {}, "Windows Firewall", WSC_SECURITY_PROVIDER_FIREWALL, true, true);
}
@@ -1907,9 +1907,9 @@ std::optional WindowsInfo::getElevated() const
SecurityProduct::SecurityProduct(
- QString name, int provider,
+ QUuid guid, QString name, int provider,
bool active, bool upToDate) :
- m_name(std::move(name)), m_provider(provider),
+ m_guid(std::move(guid)), m_name(std::move(name)), m_provider(provider),
m_active(active), m_upToDate(upToDate)
{
}
@@ -1938,10 +1938,27 @@ QString SecurityProduct::toString() const
{
QString s;
- s += m_name + " ";
+ s += m_name + " (" + providerToString() + ")";
+ if (!m_active) {
+ s += ", inactive";
+ }
+
+ if (!m_upToDate) {
+ s += ", definitions outdated";
+ }
+
+ if (!m_guid.isNull()) {
+ s += ", " + m_guid.toString(QUuid::QUuid::WithoutBraces);
+ }
+
+ return s;
+}
+QString SecurityProduct::providerToString() const
+{
QStringList ps;
+
if (m_provider & WSC_SECURITY_PROVIDER_FIREWALL) {
ps.push_back("firewall");
}
@@ -1971,20 +1988,10 @@ QString SecurityProduct::toString() const
}
if (ps.empty()) {
- s += "(doesn't provide anything)";
- } else {
- s += "(" + ps.join("|") + ")";
- }
-
- if (!m_active) {
- s += ", inactive";
- }
-
- if (!m_upToDate) {
- s += ", definitions outdated";
+ return "doesn't provider anything";
}
- return s;
+ return ps.join("|");
}
diff --git a/src/shared/util.h b/src/shared/util.h
index fc2028db..46764367 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -28,6 +28,7 @@ along with Mod Organizer. If not, see .
#include
#include
+#include
class Executable;
@@ -370,7 +371,7 @@ class SecurityProduct
{
public:
SecurityProduct(
- QString name, int provider,
+ QUuid guid, QString name, int provider,
bool active, bool upToDate);
// display name of the product
@@ -394,10 +395,13 @@ public:
QString toString() const;
private:
+ QUuid m_guid;
QString m_name;
int m_provider;
bool m_active;
bool m_upToDate;
+
+ QString providerToString() const;
};
--
cgit v1.3.1
From b2a1e1391fdd6bdee1c5e8d337b273447c70a506 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 18 Jul 2019 23:13:57 -0400
Subject: split env
---
src/CMakeLists.txt | 25 +-
src/env.cpp | 479 ++++++++++++
src/env.h | 117 +++
src/envmetrics.cpp | 224 ++++++
src/envmetrics.h | 28 +
src/envmodule.cpp | 390 ++++++++++
src/envmodule.h | 98 +++
src/envsecurity.cpp | 418 ++++++++++
src/envsecurity.h | 49 ++
src/envshortcut.cpp | 376 +++++++++
src/envshortcut.h | 114 +++
src/envwindows.cpp | 236 ++++++
src/envwindows.h | 106 +++
src/main.cpp | 2 +
src/mainwindow.cpp | 1 +
src/shared/util.cpp | 2123 +--------------------------------------------------
src/shared/util.h | 443 -----------
17 files changed, 2663 insertions(+), 2566 deletions(-)
create mode 100644 src/env.cpp
create mode 100644 src/env.h
create mode 100644 src/envmetrics.cpp
create mode 100644 src/envmetrics.h
create mode 100644 src/envmodule.cpp
create mode 100644 src/envmodule.h
create mode 100644 src/envsecurity.cpp
create mode 100644 src/envsecurity.h
create mode 100644 src/envshortcut.cpp
create mode 100644 src/envshortcut.h
create mode 100644 src/envwindows.cpp
create mode 100644 src/envwindows.h
(limited to 'src/shared')
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9dbab132..9785dc3d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -129,6 +129,12 @@ SET(organizer_SRCS
filerenamer.cpp
texteditor.cpp
expanderwidget.cpp
+ env.cpp
+ envmetrics.cpp
+ envmodule.cpp
+ envsecurity.cpp
+ envshortcut.cpp
+ envwindows.cpp
shared/windows_error.cpp
shared/error_report.cpp
@@ -239,6 +245,12 @@ SET(organizer_HDRS
filerenamer.h
texteditor.h
expanderwidget.h
+ env.h
+ envmetrics.h
+ envmodule.h
+ envsecurity.h
+ envshortcut.h
+ envwindows.h
shared/windows_error.h
shared/error_report.h
@@ -350,6 +362,15 @@ set(downloads
downloadmanager
)
+set(env
+ env
+ envmetrics
+ envmodule
+ envsecurity
+ envshortcut
+ envwindows
+)
+
set(executables
executableslist
editexecutablesdialog
@@ -447,8 +468,8 @@ set(widgets
)
set(src_filters
- application core browser dialogs downloads executables locking modinfo modinfo\\dialog
- modlist plugins previews profiles settings utilities widgets
+ application core browser dialogs downloads env executables locking modinfo
+ modinfo\\dialog modlist plugins previews profiles settings utilities widgets
)
foreach(filter in list ${src_filters})
diff --git a/src/env.cpp b/src/env.cpp
new file mode 100644
index 00000000..641eb4a7
--- /dev/null
+++ b/src/env.cpp
@@ -0,0 +1,479 @@
+#include "env.h"
+#include "envmetrics.h"
+#include "envmodule.h"
+#include "envsecurity.h"
+#include "envshortcut.h"
+#include "envwindows.h"
+#include
+#include
+
+namespace env
+{
+
+using namespace MOBase;
+
+Console::Console()
+ : m_hasConsole(false), m_in(nullptr), m_out(nullptr), m_err(nullptr)
+{
+ // open a console
+ if (!AllocConsole()) {
+ // failed, ignore
+ }
+
+ m_hasConsole = true;
+
+ // redirect stdin, stdout and stderr to it
+ freopen_s(&m_in, "CONIN$", "r", stdin);
+ freopen_s(&m_out, "CONOUT$", "w", stdout);
+ freopen_s(&m_err, "CONOUT$", "w", stderr);
+}
+
+Console::~Console()
+{
+ // close redirected handles and redirect standard stream to NUL in case
+ // they're used after this
+
+ if (m_err) {
+ std::fclose(m_err);
+ freopen_s(&m_err, "NUL", "w", stderr);
+ }
+
+ if (m_out) {
+ std::fclose(m_out);
+ freopen_s(&m_out, "NUL", "w", stdout);
+ }
+
+ if (m_in) {
+ std::fclose(m_in);
+ freopen_s(&m_in, "NUL", "r", stdin);
+ }
+
+ // close console
+ if (m_hasConsole) {
+ FreeConsole();
+ }
+}
+
+
+Environment::Environment()
+ : m_windows(new WindowsInfo), m_metrics(new Metrics)
+{
+ m_modules = getLoadedModules();
+ m_security = getSecurityProducts();
+}
+
+// anchor
+Environment::~Environment() = default;
+
+const std::vector& Environment::loadedModules() const
+{
+ return m_modules;
+}
+
+const WindowsInfo& Environment::windowsInfo() const
+{
+ return *m_windows;
+}
+
+const std::vector& Environment::securityProducts() const
+{
+ return m_security;
+}
+
+const Metrics& Environment::metrics() const
+{
+ return *m_metrics;
+}
+
+void Environment::dump() const
+{
+ log::debug("windows: {}", m_windows->toString());
+
+ if (m_windows->compatibilityMode()) {
+ log::warn("MO seems to be running in compatibility mode");
+ }
+
+ log::debug("security products:");
+ for (const auto& sp : m_security) {
+ log::debug(" . {}", sp.toString());
+ }
+
+ log::debug("modules loaded in process:");
+ for (const auto& m : m_modules) {
+ log::debug(" . {}", m.toString());
+ }
+
+ log::debug("displays:");
+ for (const auto& d : m_metrics->displays()) {
+ log::debug(" . {}", d.toString());
+ }
+}
+
+
+struct Process
+{
+ std::wstring filename;
+ DWORD pid;
+
+ Process(std::wstring f, DWORD id)
+ : filename(std::move(f)), pid(id)
+ {
+ }
+};
+
+
+// returns the filename of the given process or the current one
+//
+std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
+{
+ // double the buffer size 10 times
+ const int MaxTries = 10;
+
+ DWORD bufferSize = MAX_PATH;
+
+ for (int tries=0; tries(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) {
+ // hard failure
+ 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 std::filesystem::path path(s);
+
+ return path.filename().native();
+ }
+ }
+
+ // something failed or the path is way too long to make sense
+
+ 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()
+{
+ // double the buffer size 10 times
+ const int MaxTries = 10;
+
+ // initial size of 300 processes, unlikely to be more than that
+ std::size_t size = 300;
+
+ for (int tries=0; tries(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) {
+ // no way to distinguish between an exact fit and not enough space,
+ // just try again
+ 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 has pid 0 and 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 for system processes, even
+ // when 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";
+
+ // used to skip the current process below
+ const auto thisPid = GetCurrentProcessId();
+ std::wclog << L"this process id is " << thisPid << L"\n";
+
+ // getting the filename for this process, assumes the other process has the
+ // smae one
+ 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";
+ }
+
+ // getting all running processes
+ const auto processes = runningProcesses();
+ std::wclog << L"there are " << processes.size() << L" processes running\n";
+
+ // going through processes, trying to find one with the same name and a
+ // different pid than this process has
+ 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)
+{
+ // maximum tries of incrementing the counter
+ const int MaxTries = 100;
+
+ // UTC time and date will be in the filename
+ const auto now = std::time(0);
+ const auto tm = std::gmtime(&now);
+
+ // "ModOrganizer-YYYYMMDDThhmmss.dmp", with a possible "-i" appended, where
+ // i can go until MaxTries
+ 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";
+
+ // first path to try, without counter in it
+ std::wstring path = dir + L"\\" + prefix + ext;
+
+ for (int i=0; i;
+
+
+struct LibraryFreer
+{
+ using pointer = HINSTANCE;
+
+ void operator()(HINSTANCE h)
+ {
+ if (h != 0) {
+ ::FreeLibrary(h);
+ }
+ }
+};
+
+struct COMReleaser
+{
+ void operator()(IUnknown* p)
+ {
+ if (p) {
+ p->Release();
+ }
+ }
+};
+
+
+template
+using COMPtr = std::unique_ptr;
+
+
+class Console
+{
+public:
+ Console();
+ ~Console();
+
+private:
+ bool m_hasConsole;
+ FILE* m_in;
+ FILE* m_out;
+ FILE* m_err;
+};
+
+
+// represents the process's environment
+//
+class Environment
+{
+public:
+ Environment();
+ ~Environment();
+
+ // list of loaded modules in the current process
+ //
+ const std::vector& loadedModules() const;
+
+ // information about the operating system
+ //
+ const WindowsInfo& windowsInfo() const;
+
+ // information about the installed security products
+ //
+ const std::vector& securityProducts() const;
+
+ // information about displays
+ //
+ const Metrics& metrics() const;
+
+ // logs the environment
+ //
+ void dump() const;
+
+private:
+ std::vector m_modules;
+ std::unique_ptr m_windows;
+ std::vector m_security;
+ std::unique_ptr m_metrics;
+};
+
+
+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
diff --git a/src/envmetrics.cpp b/src/envmetrics.cpp
new file mode 100644
index 00000000..a6988909
--- /dev/null
+++ b/src/envmetrics.cpp
@@ -0,0 +1,224 @@
+#include "envmetrics.h"
+#include "env.h"
+#include
+#include
+#include
+#include
+
+namespace env
+{
+
+using namespace MOBase;
+
+class DisplayEnumerator
+{
+public:
+ DisplayEnumerator()
+ : m_GetDpiForMonitor(nullptr)
+ {
+ m_shcore.reset(LoadLibraryW(L"Shcore.dll"));
+
+ if (m_shcore) {
+ // windows 8.1+ only
+ m_GetDpiForMonitor = reinterpret_cast(
+ GetProcAddress(m_shcore.get(), "GetDpiForMonitor"));
+ }
+
+ // gets all monitors and the device they're running on
+ getDisplayDevices();
+ }
+
+ std::vector&& displays() &&
+ {
+ return std::move(m_displays);
+ }
+
+ const std::vector& displays() const &
+ {
+ return m_displays;
+ }
+
+private:
+ using GetDpiForMonitorFunction =
+ HRESULT WINAPI (HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
+
+ std::unique_ptr m_shcore;
+ GetDpiForMonitorFunction* m_GetDpiForMonitor;
+ std::vector m_displays;
+
+ void getDisplayDevices()
+ {
+ // don't bother if it goes over 100
+ for (int i=0; i<100; ++i) {
+ DISPLAY_DEVICEW device = {};
+ device.cb = sizeof(device);
+
+ if (!EnumDisplayDevicesW(nullptr, i, &device, 0)) {
+ // no more
+ break;
+ }
+
+ // EnumDisplayDevices() seems to be returning a lot of devices that are
+ // not actually monitors, but those don't have the
+ // DISPLAY_DEVICE_ATTACHED_TO_DESKTOP bit set
+ if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) {
+ continue;
+ }
+
+ m_displays.push_back(createDisplay(device));
+ }
+ }
+
+ Metrics::Display createDisplay(const DISPLAY_DEVICEW& device)
+ {
+ Metrics::Display d;
+
+ d.adapter = QString::fromWCharArray(device.DeviceString);
+ d.monitor = QString::fromWCharArray(device.DeviceName);
+ d.primary = (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);
+
+ getDisplaySettings(device.DeviceName, d);
+ getDpi(d);
+
+ return d;
+ }
+
+ void getDisplaySettings(const wchar_t* monitorName, Metrics::Display& d)
+ {
+ DEVMODEW dm = {};
+ dm.dmSize = sizeof(dm);
+
+ if (!EnumDisplaySettingsW(monitorName, ENUM_CURRENT_SETTINGS, &dm)) {
+ log::error("EnumDisplaySettings() failed for '{}'", d.monitor);
+ return;
+ }
+
+ // all these fields should be available
+
+ if (dm.dmFields & DM_DISPLAYFREQUENCY) {
+ d.refreshRate = dm.dmDisplayFrequency;
+ }
+
+ if (dm.dmFields & DM_PELSWIDTH) {
+ d.resX = dm.dmPelsWidth;
+ }
+
+ if (dm.dmFields & DM_PELSHEIGHT) {
+ d.resY = dm.dmPelsHeight;
+ }
+ }
+
+ void getDpi(Metrics::Display& d)
+ {
+ if (!m_GetDpiForMonitor) {
+ // this happens on windows 7, get the desktop dpi instead
+ getDesktopDpi(d);
+ return;
+ }
+
+ // there's no way to get an HMONITOR from a device name, so all monitors
+ // will have to be enumerated and their name checked
+ HMONITOR hm = findMonitor(d.monitor);
+ if (!hm) {
+ log::error("can't get dpi for monitor '{}', not found", d.monitor);
+ return;
+ }
+
+ UINT dpiX=0, dpiY=0;
+ const auto r = m_GetDpiForMonitor(hm, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
+
+ if (FAILED(r)) {
+ log::error(
+ "GetDpiForMonitor() failed for '{}', {}",
+ d.monitor, formatSystemMessageQ(r));
+
+ return;
+ }
+
+ // dpiX and dpiY are always identical, as per the documentation
+ d.dpi = dpiX;
+ }
+
+ void getDesktopDpi(Metrics::Display& d)
+ {
+ // desktop dc
+ HDC dc = GetDC(0);
+
+ if (!dc) {
+ const auto e = GetLastError();
+ log::error("can't get desktop DC, {}", formatSystemMessageQ(e));
+ return;
+ }
+
+ d.dpi = GetDeviceCaps(dc, LOGPIXELSX);
+
+ ReleaseDC(0, dc);
+ }
+
+ HMONITOR findMonitor(const QString& name)
+ {
+ // passed to the enumeration callback
+ struct Data
+ {
+ DisplayEnumerator* self;
+ QString name;
+ HMONITOR hm;
+ };
+
+ Data data = {this, name, 0};
+
+ // for each monitor
+ EnumDisplayMonitors(0, nullptr, [](HMONITOR hm, HDC, RECT*, LPARAM lp) {
+ auto& data = *reinterpret_cast(lp);
+
+ MONITORINFOEX mi = {};
+ mi.cbSize = sizeof(mi);
+
+ // monitor info will include the name
+ if (!GetMonitorInfoW(hm, &mi)) {
+ const auto e = GetLastError();
+ log::error(
+ "GetMonitorInfo() failed for '{}', {}",
+ data.name, formatSystemMessageQ(e));
+
+ // error for this monitor, but continue
+ return TRUE;
+ }
+
+ if (QString::fromWCharArray(mi.szDevice) == data.name) {
+ // found, stop
+ data.hm = hm;
+ return FALSE;
+ }
+
+ // not found, continue to the next monitor
+ return TRUE;
+ }, reinterpret_cast(&data));
+
+ return data.hm;
+ }
+};
+
+
+Metrics::Metrics()
+{
+ m_displays = DisplayEnumerator().displays();
+}
+
+const std::vector& Metrics::displays() const
+{
+ return m_displays;
+}
+
+QString Metrics::Display::toString() const
+{
+ return QString("%1*%2 %3hz dpi=%4 on %5%6")
+ .arg(resX)
+ .arg(resY)
+ .arg(refreshRate)
+ .arg(dpi)
+ .arg(adapter)
+ .arg(primary ? " (primary)" : "");
+}
+
+} // namespace
diff --git a/src/envmetrics.h b/src/envmetrics.h
new file mode 100644
index 00000000..62fc8c49
--- /dev/null
+++ b/src/envmetrics.h
@@ -0,0 +1,28 @@
+#include
+#include
+
+namespace env
+{
+
+class Metrics
+{
+public:
+ struct Display
+ {
+ int resX=0, resY=0, dpi=0;
+ bool primary=false;
+ int refreshRate = 0;
+ QString monitor, adapter;
+
+ QString toString() const;
+ };
+
+ Metrics();
+
+ const std::vector& displays() const;
+
+private:
+ std::vector m_displays;
+};
+
+} // namespace
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
new file mode 100644
index 00000000..1717da15
--- /dev/null
+++ b/src/envmodule.cpp
@@ -0,0 +1,390 @@
+#include "envmodule.h"
+#include "env.h"
+#include
+
+namespace env
+{
+
+using namespace MOBase;
+
+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;
+ m_md5 = getMD5();
+}
+
+const QString& Module::path() const
+{
+ return m_path;
+}
+
+QString Module::displayPath() const
+{
+ return QDir::fromNativeSeparators(m_path.toLower());
+}
+
+std::size_t Module::fileSize() const
+{
+ return m_fileSize;
+}
+
+const QString& Module::version() const
+{
+ return m_version;
+}
+
+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()) {
+ return "(no timestamp)";
+ }
+
+ return m_timestamp.toString(Qt::DateFormat::ISODate);
+}
+
+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 {
+ if (!m_version.isEmpty()) {
+ sl.push_back(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);
+ }
+
+ return sl.join(", ");
+}
+
+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);
+
+ 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 << "', "
+ << formatSystemMessageQ(e);
+
+ return {};
+ }
+
+ // getting version info
+ 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 << "', "
+ << formatSystemMessageQ(e);
+
+ return {};
+ }
+
+ // the version info has two major parts: a fixed version and a localizable
+ // set of strings
+
+ FileInfo fi;
+ fi.ffi = getFixedFileInfo(buffer.get());
+ fi.fileDescription = getFileDescription(buffer.get());
+
+ return fi;
+}
+
+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) {
+ // not an error, no fixed file info
+ return {};
+ }
+
+ const auto* fi = static_cast(valuePointer);
+
+ // signature is always 0xfeef04bd
+ if (fi->dwSignature != 0xfeef04bd) {
+ qCritical().nospace().noquote()
+ << "bad file info signature 0x" << hex << fi->dwSignature << " for "
+ << "'" << m_path << "'";
+
+ return {};
+ }
+
+ return *fi;
+}
+
+QString Module::getFileDescription(std::byte* buffer) const
+{
+ struct LANGANDCODEPAGE
+ {
+ WORD wLanguage;
+ WORD wCodePage;
+ };
+
+ void* valuePointer = nullptr;
+ unsigned int valueSize = 0;
+
+ // getting list of available languages
+ 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 {};
+ }
+
+ // 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 = static_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(
+ static_cast(valuePointer), valueSize - 1);
+}
+
+QString 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 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
+ HandlePtr 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().nospace().noquote()
+ << "can't open file '" << m_path << "' for timestamp, "
+ << formatSystemMessageQ(e);
+
+ return {};
+ }
+
+ // getting the file time
+ if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
+ const auto e = GetLastError();
+ qCritical().nospace().noquote()
+ << "can't get file time for '" << m_path << "', "
+ << formatSystemMessageQ(e);
+
+ 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 "
+ << "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));
+}
+
+QString Module::getMD5() const
+{
+ if (m_path.contains("\\windows\\", Qt::CaseInsensitive)) {
+ // don't calculate md5 for system files, it's not really relevant and
+ // it takes a while
+ return {};
+ }
+
+ // opening the file
+ QFile f(m_path);
+
+ if (!f.open(QFile::ReadOnly)) {
+ qCritical().nospace().noquote()
+ << "failed to open file '" << m_path << "' for md5";
+
+ return {};
+ }
+
+ // hashing
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (!hash.addData(&f)) {
+ qCritical().nospace().noquote()
+ << "failed to calculate md5 for '" << m_path << "'";
+
+ return {};
+ }
+
+ return hash.result().toHex();
+}
+
+
+std::vector getLoadedModules()
+{
+ HandlePtr snapshot(CreateToolhelp32Snapshot(
+ TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
+
+ if (snapshot.get() == INVALID_HANDLE_VALUE)
+ {
+ const auto e = GetLastError();
+
+ qCritical().nospace().noquote()
+ << "CreateToolhelp32Snapshot() failed, "
+ << formatSystemMessageQ(e);
+
+ return {};
+ }
+
+ MODULEENTRY32 me = {};
+ me.dwSize = sizeof(me);
+
+ // first module, this shouldn't fail because there's at least the executable
+ if (!Module32First(snapshot.get(), &me))
+ {
+ const auto e = GetLastError();
+
+ qCritical().nospace().noquote()
+ << "Module32First() failed, " << formatSystemMessageQ(e);
+
+ return {};
+ }
+
+ std::vector v;
+
+ for (;;)
+ {
+ const auto path = QString::fromWCharArray(me.szExePath);
+ if (!path.isEmpty()) {
+ v.push_back(Module(path, me.modBaseSize));
+ }
+
+ // next module
+ if (!Module32Next(snapshot.get(), &me)) {
+ const auto e = GetLastError();
+
+ // 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(v.begin(), v.end(), [](auto&& a, auto&& b) {
+ return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0);
+ });
+
+ return v;
+}
+
+} // namespace
diff --git a/src/envmodule.h b/src/envmodule.h
new file mode 100644
index 00000000..ea1156bd
--- /dev/null
+++ b/src/envmodule.h
@@ -0,0 +1,98 @@
+#include
+#include
+
+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;
+ QString fileDescription;
+ };
+
+ QString m_path;
+ std::size_t m_fileSize;
+ QString m_version;
+ QDateTime m_timestamp;
+ 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;
+};
+
+
+std::vector getLoadedModules();
+
+} // namespace env
diff --git a/src/envsecurity.cpp b/src/envsecurity.cpp
new file mode 100644
index 00000000..559ce4ad
--- /dev/null
+++ b/src/envsecurity.cpp
@@ -0,0 +1,418 @@
+#include "envsecurity.h"
+#include "env.h"
+#include
+
+#include
+#include
+#include
+#include
+#pragma comment(lib, "Wbemuuid.lib")
+
+namespace env
+{
+
+using namespace MOBase;
+
+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);
+ if (!enumerator) {
+ return;
+ }
+
+ for (;;)
+ {
+ COMPtr object;
+
+ {
+ IWbemClassObject* rawObject = nullptr;
+ ULONG count = 0;
+ auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count);
+
+ if (count == 0 || !rawObject) {
+ break;
+ }
+
+ if (FAILED(ret)) {
+ qCritical()
+ << "enumerator->next() failed, " << formatSystemMessageQ(ret);
+ break;
+ }
+
+ object.reset(rawObject);
+ }
+
+ f(object.get());
+ }
+ }
+
+private:
+ COMPtr m_locator;
+ COMPtr m_service;
+
+ void createLocator()
+ {
+ void* rawLocator = nullptr;
+
+ const auto ret = CoCreateInstance(
+ CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator, &rawLocator);
+
+ if (FAILED(ret) || !rawLocator) {
+ qCritical()
+ << "CoCreateInstance for WbemLocator failed, "
+ << formatSystemMessageQ(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) || !rawService) {
+ qCritical()
+ << "locator->ConnectServer() failed for namespace "
+ << "'" << QString::fromStdString(ns) << "', "
+ << formatSystemMessageQ(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, " << formatSystemMessageQ(ret);
+
+ 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);
+ }
+};
+
+
+SecurityProduct::SecurityProduct(
+ QUuid guid, QString name, int provider,
+ bool active, bool upToDate) :
+ m_guid(std::move(guid)), m_name(std::move(name)), m_provider(provider),
+ m_active(active), m_upToDate(upToDate)
+{
+}
+
+const QString& SecurityProduct::name() const
+{
+ return m_name;
+}
+
+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 + " (" + providerToString() + ")";
+
+ if (!m_active) {
+ s += ", inactive";
+ }
+
+ if (!m_upToDate) {
+ s += ", definitions outdated";
+ }
+
+ if (!m_guid.isNull()) {
+ s += ", " + m_guid.toString(QUuid::QUuid::WithoutBraces);
+ }
+
+ return s;
+}
+
+QString SecurityProduct::providerToString() const
+{
+ QStringList ps;
+
+ if (m_provider & WSC_SECURITY_PROVIDER_FIREWALL) {
+ ps.push_back("firewall");
+ }
+
+ if (m_provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
+ ps.push_back("autoupdate");
+ }
+
+ if (m_provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
+ ps.push_back("antivirus");
+ }
+
+ if (m_provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
+ ps.push_back("antispyware");
+ }
+
+ if (m_provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
+ ps.push_back("settings");
+ }
+
+ if (m_provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
+ ps.push_back("uac");
+ }
+
+ if (m_provider & WSC_SECURITY_PROVIDER_SERVICE) {
+ ps.push_back("service");
+ }
+
+ if (ps.empty()) {
+ return "doesn't provider anything";
+ }
+
+ return ps.join("|");
+}
+
+
+std::vector getSecurityProductsFromWMI()
+{
+ // 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);
+
+ 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()
+ << "failed to get productState, "
+ << formatSystemMessageQ(ret);
+
+ return;
+ }
+
+ 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()
+ << "failed to get instanceGuid, "
+ << formatSystemMessageQ(ret);
+
+ 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;
+
+ const bool active = ((scanner & 0x10) != 0);
+ const bool upToDate = (definitions == 0);
+
+ map.insert({
+ guid,
+ {guid, QString::fromStdWString(name), provider, active, upToDate}});
+ };
+
+ {
+ 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) {
+ v.push_back(p.second);
+ }
+
+ return v;
+}
+
+std::optional getWindowsFirewall()
+{
+ 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, "
+ << formatSystemMessageQ(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, "
+ << formatSystemMessageQ(hr);
+
+ return {};
+ }
+ }
+
+ const auto enabled = (enabledVariant != VARIANT_FALSE);
+ if (!enabled) {
+ return {};
+ }
+
+ return SecurityProduct(
+ {}, "Windows Firewall", WSC_SECURITY_PROVIDER_FIREWALL, true, true);
+}
+
+
+std::vector getSecurityProducts()
+{
+ 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;
+}
+
+} // namespace
diff --git a/src/envsecurity.h b/src/envsecurity.h
new file mode 100644
index 00000000..200cb531
--- /dev/null
+++ b/src/envsecurity.h
@@ -0,0 +1,49 @@
+#include
+#include
+
+namespace env
+{
+
+// represents a security product, such as an antivirus or a firewall
+//
+class SecurityProduct
+{
+public:
+ SecurityProduct(
+ QUuid guid, QString name, int provider,
+ bool active, bool upToDate);
+
+ // display name of the product
+ //
+ const QString& name() const;
+
+ // a bunch of _WSC_SECURITY_PROVIDER flags
+ //
+ int provider() const;
+
+ // whether the product is active
+ //
+ bool active() const;
+
+ // whether its definitions are up-to-date
+ //
+ bool upToDate() const;
+
+ // string representation of the above
+ //
+ QString toString() const;
+
+private:
+ QUuid m_guid;
+ QString m_name;
+ int m_provider;
+ bool m_active;
+ bool m_upToDate;
+
+ QString providerToString() const;
+};
+
+
+std::vector getSecurityProducts();
+
+} // namespace env
diff --git a/src/envshortcut.cpp b/src/envshortcut.cpp
new file mode 100644
index 00000000..30ef4633
--- /dev/null
+++ b/src/envshortcut.cpp
@@ -0,0 +1,376 @@
+#include "envshortcut.h"
+#include "env.h"
+#include "executableslist.h"
+#include "instancemanager.h"
+#include
+
+namespace env
+{
+
+using namespace MOBase;
+
+class ShellLinkException
+{
+public:
+ ShellLinkException(QString s)
+ : m_what(std::move(s))
+ {
+ }
+
+ const QString& what() const
+ {
+ return m_what;
+ }
+
+private:
+ QString m_what;
+};
+
+// just a wrapper around IShellLink operations that throws ShellLinkException
+// on errors
+//
+class ShellLinkWrapper
+{
+public:
+ ShellLinkWrapper()
+ {
+ m_link = createShellLink();
+ m_file = createPersistFile();
+ }
+
+ void setPath(const QString& s)
+ {
+ if (s.isEmpty()) {
+ throw ShellLinkException("path cannot be empty");
+ }
+
+ const auto r = m_link->SetPath(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set target path '%1'").arg(s));
+ }
+
+ void setArguments(const QString& s)
+ {
+ const auto r = m_link->SetArguments(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set arguments '%1'").arg(s));
+ }
+
+ void setDescription(const QString& s)
+ {
+ if (s.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetDescription(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set description '%1'").arg(s));
+ }
+
+ void setIcon(const QString& file, int i)
+ {
+ if (file.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetIconLocation(file.toStdWString().c_str(), i);
+ throwOnFail(r, QString("failed to set icon '%1' @ %2").arg(file).arg(i));
+ }
+
+ void setWorkingDirectory(const QString& s)
+ {
+ if (s.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetWorkingDirectory(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set working directory '%1'").arg(s));
+ }
+
+ void save(const QString& path)
+ {
+ const auto r = m_file->Save(path.toStdWString().c_str(), TRUE);
+ throwOnFail(r, QString("failed to save link '%1'").arg(path));
+ }
+
+private:
+ COMPtr m_link;
+ COMPtr m_file;
+
+ void throwOnFail(HRESULT r, const QString& s)
+ {
+ if (FAILED(r)) {
+ throw ShellLinkException(QString("%1, %2")
+ .arg(s)
+ .arg(formatSystemMessageQ(r)));
+ }
+ }
+
+ COMPtr createShellLink()
+ {
+ void* link = nullptr;
+
+ const auto r = CoCreateInstance(
+ CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IShellLink, &link);
+
+ throwOnFail(r, "failed to create IShellLink instance");
+
+ if (!link) {
+ throw ShellLinkException("creating IShellLink worked, pointer is null");
+ }
+
+ return COMPtr(static_cast(link));
+ }
+
+ COMPtr createPersistFile()
+ {
+ void* file = nullptr;
+
+ const auto r = m_link->QueryInterface(IID_IPersistFile, &file);
+ throwOnFail(r, "failed to get IPersistFile interface");
+
+ if (!file) {
+ throw ShellLinkException("querying IPersistFile worked, pointer is null");
+ }
+
+ return COMPtr(static_cast(file));
+ }
+};
+
+
+Shortcut::Shortcut()
+ : m_iconIndex(0)
+{
+}
+
+Shortcut::Shortcut(const Executable& exe)
+ : Shortcut()
+{
+ m_name = exe.title();
+ m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
+
+ m_arguments = QString("\"moshortcut://%1:%2\"")
+ .arg(InstanceManager::instance().currentInstance())
+ .arg(exe.title());
+
+ m_description = QString("Run %1 with ModOrganizer").arg(exe.title());
+
+ if (exe.usesOwnIcon()) {
+ m_icon = exe.binaryInfo().absoluteFilePath();
+ }
+
+ m_workingDirectory = qApp->applicationDirPath();
+}
+
+Shortcut& Shortcut::name(const QString& s)
+{
+ m_name = s;
+ return *this;
+}
+
+Shortcut& Shortcut::target(const QString& s)
+{
+ m_target = s;
+ return *this;
+}
+
+Shortcut& Shortcut::arguments(const QString& s)
+{
+ m_arguments = s;
+ return *this;
+}
+
+Shortcut& Shortcut::description(const QString& s)
+{
+ m_description = s;
+ return *this;
+}
+
+Shortcut& Shortcut::icon(const QString& s, int index)
+{
+ m_icon = s;
+ m_iconIndex = index;
+ return *this;
+}
+
+Shortcut& Shortcut::workingDirectory(const QString& s)
+{
+ m_workingDirectory = s;
+ return *this;
+}
+
+bool Shortcut::exists(Locations loc) const
+{
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ return QFileInfo(path).exists();
+}
+
+bool Shortcut::toggle(Locations loc)
+{
+ if (exists(loc)) {
+ return remove(loc);
+ } else {
+ return add(loc);
+ }
+}
+
+bool Shortcut::add(Locations loc)
+{
+ debug()
+ << "adding shortcut to " << toString(loc) << ":\n"
+ << " . name: '" << m_name << "'\n"
+ << " . target: '" << m_target << "'\n"
+ << " . arguments: '" << m_arguments << "'\n"
+ << " . description: '" << m_description << "'\n"
+ << " . icon: '" << m_icon << "' @ " << m_iconIndex << "\n"
+ << " . working directory: '" << m_workingDirectory << "'";
+
+ if (m_target.isEmpty()) {
+ critical() << "target is empty";
+ return false;
+ }
+
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ debug() << "shorcut file will be saved at '" << path << "'";
+
+ try
+ {
+ ShellLinkWrapper link;
+
+ link.setPath(m_target);
+ link.setArguments(m_arguments);
+ link.setDescription(m_description);
+ link.setIcon(m_icon, m_iconIndex);
+ link.setWorkingDirectory(m_workingDirectory);
+
+ link.save(path);
+
+ return true;
+ }
+ catch(ShellLinkException& e)
+ {
+ critical() << e.what() << "\nshortcut file was not saved";
+ }
+
+ return false;
+}
+
+bool Shortcut::remove(Locations loc)
+{
+ debug() << "removing shortcut for '" << m_name << "' from " << toString(loc);
+
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ debug() << "path to shortcut file is '" << path << "'";
+
+ if (!QFile::exists(path)) {
+ critical() << "can't remove '" << path << "', file not found";
+ return false;
+ }
+
+ if (!MOBase::shellDelete({path})) {
+ const auto e = ::GetLastError();
+
+ critical()
+ << "failed to remove '" << path << "', "
+ << formatSystemMessageQ(e);
+
+ return false;
+ }
+
+ return true;
+}
+
+QString Shortcut::shortcutPath(Locations loc) const
+{
+ const auto dir = shortcutDirectory(loc);
+ if (dir.isEmpty()) {
+ return {};
+ }
+
+ const auto file = shortcutFilename();
+ if (file.isEmpty()) {
+ return {};
+ }
+
+ return dir + QDir::separator() + file;
+}
+
+QString Shortcut::shortcutDirectory(Locations loc) const
+{
+ QString dir;
+
+ try
+ {
+ switch (loc)
+ {
+ case Desktop:
+ dir = MOBase::getDesktopDirectory();
+ break;
+
+ case StartMenu:
+ dir = MOBase::getStartMenuDirectory();
+ break;
+
+ case None:
+ default:
+ critical() << "bad location " << loc;
+ break;
+ }
+ }
+ catch(std::exception&)
+ {
+ }
+
+ return QDir::toNativeSeparators(dir);
+}
+
+QString Shortcut::shortcutFilename() const
+{
+ if (m_name.isEmpty()) {
+ critical() << "name is empty";
+ return {};
+ }
+
+ return m_name + ".lnk";
+}
+
+QDebug Shortcut::debug() const
+{
+ return qDebug().noquote().nospace() << "system shortcut: ";
+}
+
+QDebug Shortcut::critical() const
+{
+ return qCritical().noquote().nospace() << "system shortcut: ";
+}
+
+
+QString toString(Shortcut::Locations loc)
+{
+ switch (loc)
+ {
+ case Shortcut::None:
+ return "none";
+
+ case Shortcut::Desktop:
+ return "desktop";
+
+ case Shortcut::StartMenu:
+ return "start menu";
+
+ default:
+ return QString("? (%1)").arg(static_cast(loc));
+ }
+}
+
+} // namespace
diff --git a/src/envshortcut.h b/src/envshortcut.h
new file mode 100644
index 00000000..904b3ab7
--- /dev/null
+++ b/src/envshortcut.h
@@ -0,0 +1,114 @@
+#include
+
+class Executable;
+
+namespace env
+{
+
+// an application shortcut that can be either on the desktop or the start menu
+//
+class Shortcut
+{
+public:
+ // location of a shortcut
+ //
+ enum Locations
+ {
+ None = 0,
+
+ // on the desktop
+ Desktop,
+
+ // in the start menu
+ StartMenu
+ };
+
+
+ // empty shortcut
+ //
+ Shortcut();
+
+ // shortcut from an executable
+ //
+ explicit Shortcut(const Executable& exe);
+
+ // sets the name of the shortcut, shown on icons and start menu entries
+ //
+ Shortcut& name(const QString& s);
+
+ // the program to start
+ //
+ Shortcut& target(const QString& s);
+
+ // arguments to pass
+ //
+ Shortcut& arguments(const QString& s);
+
+ // shows in the status bar of explorer, for example
+ //
+ Shortcut& description(const QString& s);
+
+ // path to a binary that contains the icon and its index
+ //
+ Shortcut& icon(const QString& s, int index=0);
+
+ // "start in" option for this shortcut
+ //
+ Shortcut& workingDirectory(const QString& s);
+
+
+ // returns whether this shortcut already exists at the given location; this
+ // does not check whether the shortcut parameters are different, it merely if
+ // the .lnk file exists
+ //
+ bool exists(Locations loc) const;
+
+ // calls remove() if exists(), or add()
+ //
+ bool toggle(Locations loc);
+
+ // adds the shortcut to the given location
+ //
+ bool add(Locations loc);
+
+ // removes the shortcut from the given location
+ //
+ bool remove(Locations loc);
+
+private:
+ QString m_name;
+ QString m_target;
+ QString m_arguments;
+ QString m_description;
+ QString m_icon;
+ int m_iconIndex;
+ QString m_workingDirectory;
+
+ // returns a qCritical() logger with a prefix already logged
+ //
+ QDebug critical() const;
+
+ // returns a qDebug() logger with a prefix already logged
+ //
+ QDebug debug() const;
+
+
+ // returns the path where the shortcut file should be saved
+ //
+ QString shortcutPath(Locations loc) const;
+
+ // returns the directory where the shortcut file should be saved
+ //
+ QString shortcutDirectory(Locations loc) const;
+
+ // returns the filename of the shortcut file that should be used when saving
+ //
+ QString shortcutFilename() const;
+};
+
+
+// returns a string representation of the given location
+//
+QString toString(Shortcut::Locations loc);
+
+} // namespace
diff --git a/src/envwindows.cpp b/src/envwindows.cpp
new file mode 100644
index 00000000..718cf2ce
--- /dev/null
+++ b/src/envwindows.cpp
@@ -0,0 +1,236 @@
+#include "envwindows.h"
+#include "env.h"
+#include
+
+namespace env
+{
+
+using namespace MOBase;
+
+WindowsInfo::WindowsInfo()
+{
+ // 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 WindowsInfo::compatibilityMode() const
+{
+ if (m_real == Version()) {
+ // don't know the real version, can't guess compatibility mode
+ return false;
+ }
+
+ return (m_real != m_reported);
+}
+
+const WindowsInfo::Version& WindowsInfo::reportedVersion() const
+{
+ 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;
+}
+
+QString WindowsInfo::toString() const
+{
+ QStringList sl;
+
+ const QString reported = m_reported.toString();
+ const QString real = m_real.toString();
+
+ // version
+ sl.push_back("version " + reported);
+
+ // real version if different
+ if (compatibilityMode()) {
+ sl.push_back("real version " + real);
+ }
+
+ // 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));
+ }
+
+ // release ID
+ if (!m_release.ID.isEmpty()) {
+ sl.push_back("release " + m_release.ID);
+ }
+
+ // buildlab string
+ if (!m_release.buildLab.isEmpty()) {
+ sl.push_back(m_release.buildLab);
+ }
+
+ // 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");
+ }
+
+ sl.push_back("elevated: " + elevated);
+
+ return sl.join(", ");
+}
+
+WindowsInfo::Version WindowsInfo::getReportedVersion(HINSTANCE ntdll) const
+{
+ // 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
+
+ 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);
+
+ // this apparently never fails
+ RtlGetVersion((RTL_OSVERSIONINFOW*)&vi);
+
+ 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};
+}
+
+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);
+
+ 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();
+ }
+ }
+
+ // 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;
+}
+
+std::optional WindowsInfo::getElevated() const
+{
+ HandlePtr 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: " << formatSystemMessageQ(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: " << formatSystemMessageQ(e);
+
+ return {};
+ }
+
+ return (e.TokenIsElevated != 0);
+}
+
+} // namespace
diff --git a/src/envwindows.h b/src/envwindows.h
new file mode 100644
index 00000000..c23f99f4
--- /dev/null
+++ b/src/envwindows.h
@@ -0,0 +1,106 @@
+#include
+#include
+
+namespace env
+{
+
+// a variety of information on windows
+//
+class WindowsInfo
+{
+public:
+ 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:
+ Version m_reported, m_real;
+ Release m_release;
+ std::optional m_elevated;
+
+ // 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;
+};
+
+} // namespace
diff --git a/src/main.cpp b/src/main.cpp
index 09da9408..5c5ce945 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,6 +47,8 @@ along with Mod Organizer. If not, see .
#include "instancemanager.h"
#include "moshortcut.h"
#include "organizercore.h"
+#include "env.h"
+#include "envmodule.h"
#include
#include
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 6c2ab389..87ee2c8e 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -86,6 +86,7 @@ along with Mod Organizer. If not, see .
#include
#include "localsavegames.h"
#include "listdialog.h"
+#include "envshortcut.h"
#include
#include
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 4ee4b766..07983e12 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -19,35 +19,9 @@ along with Mod Organizer. If not, see .
#include "util.h"
#include "windows_error.h"
-#include "error_report.h"
-#include "executableslist.h"
-#include "instancemanager.h"
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#pragma comment(lib, "Wbemuuid.lib")
-
-using namespace MOBase;
-namespace fs = std::filesystem;
-
-namespace MOShared {
+namespace MOShared
+{
bool FileExists(const std::string &filename)
{
@@ -269,2097 +243,4 @@ MOBase::VersionInfo createVersionInfo()
}
}
-
-namespace env
-{
-
-struct HandleCloser
-{
- using pointer = HANDLE;
-
- void operator()(HANDLE h)
- {
- if (h != INVALID_HANDLE_VALUE) {
- ::CloseHandle(h);
- }
- }
-};
-
-using HandlePtr = std::unique_ptr;
-
-
-struct LibraryFreer
-{
- using pointer = HINSTANCE;
-
- void operator()(HINSTANCE h)
- {
- if (h != 0) {
- ::FreeLibrary(h);
- }
- }
-};
-
-struct COMReleaser
-{
- void operator()(IUnknown* p)
- {
- if (p) {
- p->Release();
- }
- }
-};
-
-
-template
-using COMPtr = std::unique_ptr;
-
-
-class ShellLinkException
-{
-public:
- ShellLinkException(QString s)
- : m_what(std::move(s))
- {
- }
-
- const QString& what() const
- {
- return m_what;
- }
-
-private:
- QString m_what;
-};
-
-// just a wrapper around IShellLink operations that throws ShellLinkException
-// on errors
-//
-class ShellLinkWrapper
-{
-public:
- ShellLinkWrapper()
- {
- m_link = createShellLink();
- m_file = createPersistFile();
- }
-
- void setPath(const QString& s)
- {
- if (s.isEmpty()) {
- throw ShellLinkException("path cannot be empty");
- }
-
- const auto r = m_link->SetPath(s.toStdWString().c_str());
- throwOnFail(r, QString("failed to set target path '%1'").arg(s));
- }
-
- void setArguments(const QString& s)
- {
- const auto r = m_link->SetArguments(s.toStdWString().c_str());
- throwOnFail(r, QString("failed to set arguments '%1'").arg(s));
- }
-
- void setDescription(const QString& s)
- {
- if (s.isEmpty()) {
- return;
- }
-
- const auto r = m_link->SetDescription(s.toStdWString().c_str());
- throwOnFail(r, QString("failed to set description '%1'").arg(s));
- }
-
- void setIcon(const QString& file, int i)
- {
- if (file.isEmpty()) {
- return;
- }
-
- const auto r = m_link->SetIconLocation(file.toStdWString().c_str(), i);
- throwOnFail(r, QString("failed to set icon '%1' @ %2").arg(file).arg(i));
- }
-
- void setWorkingDirectory(const QString& s)
- {
- if (s.isEmpty()) {
- return;
- }
-
- const auto r = m_link->SetWorkingDirectory(s.toStdWString().c_str());
- throwOnFail(r, QString("failed to set working directory '%1'").arg(s));
- }
-
- void save(const QString& path)
- {
- const auto r = m_file->Save(path.toStdWString().c_str(), TRUE);
- throwOnFail(r, QString("failed to save link '%1'").arg(path));
- }
-
-private:
- COMPtr m_link;
- COMPtr m_file;
-
- void throwOnFail(HRESULT r, const QString& s)
- {
- if (FAILED(r)) {
- throw ShellLinkException(QString("%1, %2")
- .arg(s)
- .arg(formatSystemMessageQ(r)));
- }
- }
-
- COMPtr createShellLink()
- {
- void* link = nullptr;
-
- const auto r = CoCreateInstance(
- CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
- IID_IShellLink, &link);
-
- throwOnFail(r, "failed to create IShellLink instance");
-
- if (!link) {
- throw ShellLinkException("creating IShellLink worked, pointer is null");
- }
-
- return COMPtr(static_cast(link));
- }
-
- COMPtr createPersistFile()
- {
- void* file = nullptr;
-
- const auto r = m_link->QueryInterface(IID_IPersistFile, &file);
- throwOnFail(r, "failed to get IPersistFile interface");
-
- if (!file) {
- throw ShellLinkException("querying IPersistFile worked, pointer is null");
- }
-
- return COMPtr(static_cast(file));
- }
-};
-
-
-Console::Console()
- : m_hasConsole(false), m_in(nullptr), m_out(nullptr), m_err(nullptr)
-{
- // open a console
- if (!AllocConsole()) {
- // failed, ignore
- }
-
- m_hasConsole = true;
-
- // redirect stdin, stdout and stderr to it
- freopen_s(&m_in, "CONIN$", "r", stdin);
- freopen_s(&m_out, "CONOUT$", "w", stdout);
- freopen_s(&m_err, "CONOUT$", "w", stderr);
-}
-
-Console::~Console()
-{
- // close redirected handles and redirect standard stream to NUL in case
- // they're used after this
-
- if (m_err) {
- std::fclose(m_err);
- freopen_s(&m_err, "NUL", "w", stderr);
- }
-
- if (m_out) {
- std::fclose(m_out);
- freopen_s(&m_out, "NUL", "w", stdout);
- }
-
- if (m_in) {
- std::fclose(m_in);
- freopen_s(&m_in, "NUL", "r", stdin);
- }
-
- // close console
- if (m_hasConsole) {
- FreeConsole();
- }
-}
-
-
-Shortcut::Shortcut()
- : m_iconIndex(0)
-{
-}
-
-Shortcut::Shortcut(const Executable& exe)
- : Shortcut()
-{
- m_name = exe.title();
- m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
-
- m_arguments = QString("\"moshortcut://%1:%2\"")
- .arg(InstanceManager::instance().currentInstance())
- .arg(exe.title());
-
- m_description = QString("Run %1 with ModOrganizer").arg(exe.title());
-
- if (exe.usesOwnIcon()) {
- m_icon = exe.binaryInfo().absoluteFilePath();
- }
-
- m_workingDirectory = qApp->applicationDirPath();
-}
-
-Shortcut& Shortcut::name(const QString& s)
-{
- m_name = s;
- return *this;
-}
-
-Shortcut& Shortcut::target(const QString& s)
-{
- m_target = s;
- return *this;
-}
-
-Shortcut& Shortcut::arguments(const QString& s)
-{
- m_arguments = s;
- return *this;
-}
-
-Shortcut& Shortcut::description(const QString& s)
-{
- m_description = s;
- return *this;
-}
-
-Shortcut& Shortcut::icon(const QString& s, int index)
-{
- m_icon = s;
- m_iconIndex = index;
- return *this;
-}
-
-Shortcut& Shortcut::workingDirectory(const QString& s)
-{
- m_workingDirectory = s;
- return *this;
-}
-
-bool Shortcut::exists(Locations loc) const
-{
- const auto path = shortcutPath(loc);
- if (path.isEmpty()) {
- return false;
- }
-
- return QFileInfo(path).exists();
-}
-
-bool Shortcut::toggle(Locations loc)
-{
- if (exists(loc)) {
- return remove(loc);
- } else {
- return add(loc);
- }
-}
-
-bool Shortcut::add(Locations loc)
-{
- debug()
- << "adding shortcut to " << toString(loc) << ":\n"
- << " . name: '" << m_name << "'\n"
- << " . target: '" << m_target << "'\n"
- << " . arguments: '" << m_arguments << "'\n"
- << " . description: '" << m_description << "'\n"
- << " . icon: '" << m_icon << "' @ " << m_iconIndex << "\n"
- << " . working directory: '" << m_workingDirectory << "'";
-
- if (m_target.isEmpty()) {
- critical() << "target is empty";
- return false;
- }
-
- const auto path = shortcutPath(loc);
- if (path.isEmpty()) {
- return false;
- }
-
- debug() << "shorcut file will be saved at '" << path << "'";
-
- try
- {
- ShellLinkWrapper link;
-
- link.setPath(m_target);
- link.setArguments(m_arguments);
- link.setDescription(m_description);
- link.setIcon(m_icon, m_iconIndex);
- link.setWorkingDirectory(m_workingDirectory);
-
- link.save(path);
-
- return true;
- }
- catch(ShellLinkException& e)
- {
- critical() << e.what() << "\nshortcut file was not saved";
- }
-
- return false;
-}
-
-bool Shortcut::remove(Locations loc)
-{
- debug() << "removing shortcut for '" << m_name << "' from " << toString(loc);
-
- const auto path = shortcutPath(loc);
- if (path.isEmpty()) {
- return false;
- }
-
- debug() << "path to shortcut file is '" << path << "'";
-
- if (!QFile::exists(path)) {
- critical() << "can't remove '" << path << "', file not found";
- return false;
- }
-
- if (!MOBase::shellDelete({path})) {
- const auto e = ::GetLastError();
-
- critical()
- << "failed to remove '" << path << "', "
- << formatSystemMessageQ(e);
-
- return false;
- }
-
- return true;
-}
-
-QString Shortcut::shortcutPath(Locations loc) const
-{
- const auto dir = shortcutDirectory(loc);
- if (dir.isEmpty()) {
- return {};
- }
-
- const auto file = shortcutFilename();
- if (file.isEmpty()) {
- return {};
- }
-
- return dir + QDir::separator() + file;
-}
-
-QString Shortcut::shortcutDirectory(Locations loc) const
-{
- QString dir;
-
- try
- {
- switch (loc)
- {
- case Desktop:
- dir = MOBase::getDesktopDirectory();
- break;
-
- case StartMenu:
- dir = MOBase::getStartMenuDirectory();
- break;
-
- case None:
- default:
- critical() << "bad location " << loc;
- break;
- }
- }
- catch(std::exception&)
- {
- }
-
- return QDir::toNativeSeparators(dir);
-}
-
-QString Shortcut::shortcutFilename() const
-{
- if (m_name.isEmpty()) {
- critical() << "name is empty";
- return {};
- }
-
- return m_name + ".lnk";
-}
-
-QDebug Shortcut::debug() const
-{
- return qDebug().noquote().nospace() << "system shortcut: ";
-}
-
-QDebug Shortcut::critical() const
-{
- return qCritical().noquote().nospace() << "system shortcut: ";
-}
-
-
-QString toString(Shortcut::Locations loc)
-{
- switch (loc)
- {
- case Shortcut::None:
- return "none";
-
- case Shortcut::Desktop:
- return "desktop";
-
- case Shortcut::StartMenu:
- return "start menu";
-
- default:
- return QString("? (%1)").arg(static_cast(loc));
- }
-}
-
-
-
-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);
- if (!enumerator) {
- return;
- }
-
- for (;;)
- {
- COMPtr object;
-
- {
- IWbemClassObject* rawObject = nullptr;
- ULONG count = 0;
- auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count);
-
- if (count == 0 || !rawObject) {
- break;
- }
-
- if (FAILED(ret)) {
- qCritical()
- << "enumerator->next() failed, " << formatSystemMessageQ(ret);
- break;
- }
-
- object.reset(rawObject);
- }
-
- f(object.get());
- }
- }
-
-private:
- COMPtr m_locator;
- COMPtr m_service;
-
- void createLocator()
- {
- void* rawLocator = nullptr;
-
- const auto ret = CoCreateInstance(
- CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IWbemLocator, &rawLocator);
-
- if (FAILED(ret) || !rawLocator) {
- qCritical()
- << "CoCreateInstance for WbemLocator failed, "
- << formatSystemMessageQ(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) || !rawService) {
- qCritical()
- << "locator->ConnectServer() failed for namespace "
- << "'" << QString::fromStdString(ns) << "', "
- << formatSystemMessageQ(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, " << formatSystemMessageQ(ret);
-
- 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);
- }
-};
-
-
-class DisplayEnumerator
-{
-public:
- DisplayEnumerator()
- : m_GetDpiForMonitor(nullptr)
- {
- m_shcore.reset(LoadLibraryW(L"Shcore.dll"));
-
- if (m_shcore) {
- // windows 8.1+ only
- m_GetDpiForMonitor = reinterpret_cast(
- GetProcAddress(m_shcore.get(), "GetDpiForMonitor"));
- }
-
- // gets all monitors and the device they're running on
- getDisplayDevices();
- }
-
- std::vector&& displays() &&
- {
- return std::move(m_displays);
- }
-
- const std::vector& displays() const &
- {
- return m_displays;
- }
-
-private:
- using GetDpiForMonitorFunction =
- HRESULT WINAPI (HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
-
- std::unique_ptr m_shcore;
- GetDpiForMonitorFunction* m_GetDpiForMonitor;
- std::vector m_displays;
-
- void getDisplayDevices()
- {
- // don't bother if it goes over 100
- for (int i=0; i<100; ++i) {
- DISPLAY_DEVICEW device = {};
- device.cb = sizeof(device);
-
- if (!EnumDisplayDevicesW(nullptr, i, &device, 0)) {
- // no more
- break;
- }
-
- // EnumDisplayDevices() seems to be returning a lot of devices that are
- // not actually monitors, but those don't have the
- // DISPLAY_DEVICE_ATTACHED_TO_DESKTOP bit set
- if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) {
- continue;
- }
-
- m_displays.push_back(createDisplay(device));
- }
- }
-
- Metrics::Display createDisplay(const DISPLAY_DEVICEW& device)
- {
- Metrics::Display d;
-
- d.adapter = QString::fromWCharArray(device.DeviceString);
- d.monitor = QString::fromWCharArray(device.DeviceName);
- d.primary = (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);
-
- getDisplaySettings(device.DeviceName, d);
- getDpi(d);
-
- return d;
- }
-
- void getDisplaySettings(const wchar_t* monitorName, Metrics::Display& d)
- {
- DEVMODEW dm = {};
- dm.dmSize = sizeof(dm);
-
- if (!EnumDisplaySettingsW(monitorName, ENUM_CURRENT_SETTINGS, &dm)) {
- log::error("EnumDisplaySettings() failed for '{}'", d.monitor);
- return;
- }
-
- // all these fields should be available
-
- if (dm.dmFields & DM_DISPLAYFREQUENCY) {
- d.refreshRate = dm.dmDisplayFrequency;
- }
-
- if (dm.dmFields & DM_PELSWIDTH) {
- d.resX = dm.dmPelsWidth;
- }
-
- if (dm.dmFields & DM_PELSHEIGHT) {
- d.resY = dm.dmPelsHeight;
- }
- }
-
- void getDpi(Metrics::Display& d)
- {
- if (!m_GetDpiForMonitor) {
- // this happens on windows 7, get the desktop dpi instead
- getDesktopDpi(d);
- return;
- }
-
- // there's no way to get an HMONITOR from a device name, so all monitors
- // will have to be enumerated and their name checked
- HMONITOR hm = findMonitor(d.monitor);
- if (!hm) {
- log::error("can't get dpi for monitor '{}', not found", d.monitor);
- return;
- }
-
- UINT dpiX=0, dpiY=0;
- const auto r = m_GetDpiForMonitor(hm, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
-
- if (FAILED(r)) {
- log::error(
- "GetDpiForMonitor() failed for '{}', {}",
- d.monitor, formatSystemMessageQ(r));
-
- return;
- }
-
- // dpiX and dpiY are always identical, as per the documentation
- d.dpi = dpiX;
- }
-
- void getDesktopDpi(Metrics::Display& d)
- {
- // desktop dc
- HDC dc = GetDC(0);
-
- if (!dc) {
- const auto e = GetLastError();
- log::error("can't get desktop DC, {}", formatSystemMessageQ(e));
- return;
- }
-
- d.dpi = GetDeviceCaps(dc, LOGPIXELSX);
-
- ReleaseDC(0, dc);
- }
-
- HMONITOR findMonitor(const QString& name)
- {
- // passed to the enumeration callback
- struct Data
- {
- DisplayEnumerator* self;
- QString name;
- HMONITOR hm;
- };
-
- Data data = {this, name, 0};
-
- // for each monitor
- EnumDisplayMonitors(0, nullptr, [](HMONITOR hm, HDC, RECT*, LPARAM lp) {
- auto& data = *reinterpret_cast(lp);
-
- MONITORINFOEX mi = {};
- mi.cbSize = sizeof(mi);
-
- // monitor info will include the name
- if (!GetMonitorInfoW(hm, &mi)) {
- const auto e = GetLastError();
- log::error(
- "GetMonitorInfo() failed for '{}', {}",
- data.name, formatSystemMessageQ(e));
-
- // error for this monitor, but continue
- return TRUE;
- }
-
- if (QString::fromWCharArray(mi.szDevice) == data.name) {
- // found, stop
- data.hm = hm;
- return FALSE;
- }
-
- // not found, continue to the next monitor
- return TRUE;
- }, reinterpret_cast(&data));
-
- return data.hm;
- }
-};
-
-
-Environment::Environment()
-{
- m_modules = getLoadedModules();
- m_security = getSecurityProducts();
-}
-
-const std::vector& Environment::loadedModules() const
-{
- return m_modules;
-}
-
-const WindowsInfo& Environment::windowsInfo() const
-{
- return m_windows;
-}
-
-const std::vector& Environment::securityProducts() const
-{
- return m_security;
-}
-
-const Metrics& Environment::metrics() const
-{
- return m_metrics;
-}
-
-void Environment::dump() const
-{
- log::debug("windows: {}", windowsInfo().toString());
-
- if (windowsInfo().compatibilityMode()) {
- log::warn("MO seems to be running in compatibility mode");
- }
-
- log::debug("security products:");
- for (const auto& sp : securityProducts()) {
- log::debug(" . {}", sp.toString());
- }
-
- log::debug("modules loaded in process:");
- for (const auto& m : loadedModules()) {
- log::debug(" . {}", m.toString());
- }
-
- log::debug("displays:");
- for (const auto& d : m_metrics.displays()) {
- log::debug(" . {}", d.toString());
- }
-}
-
-std::vector Environment::getLoadedModules() const
-{
- HandlePtr snapshot(CreateToolhelp32Snapshot(
- TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
-
- if (snapshot.get() == INVALID_HANDLE_VALUE)
- {
- const auto e = GetLastError();
-
- qCritical().nospace().noquote()
- << "CreateToolhelp32Snapshot() failed, "
- << formatSystemMessageQ(e);
-
- return {};
- }
-
- MODULEENTRY32 me = {};
- me.dwSize = sizeof(me);
-
- // first module, this shouldn't fail because there's at least the executable
- if (!Module32First(snapshot.get(), &me))
- {
- const auto e = GetLastError();
-
- qCritical().nospace().noquote()
- << "Module32First() failed, " << formatSystemMessageQ(e);
-
- return {};
- }
-
- std::vector v;
-
- for (;;)
- {
- const auto path = QString::fromWCharArray(me.szExePath);
- if (!path.isEmpty()) {
- v.push_back(Module(path, me.modBaseSize));
- }
-
- // next module
- if (!Module32Next(snapshot.get(), &me)) {
- const auto e = GetLastError();
-
- // 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(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;
-}
-
-std::vector Environment::getSecurityProductsFromWMI() const
-{
- // 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);
-
- 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()
- << "failed to get productState, "
- << formatSystemMessageQ(ret);
-
- return;
- }
-
- 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()
- << "failed to get instanceGuid, "
- << formatSystemMessageQ(ret);
-
- 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;
-
- const bool active = ((scanner & 0x10) != 0);
- const bool upToDate = (definitions == 0);
-
- map.insert({
- guid,
- {guid, QString::fromStdWString(name), provider, active, upToDate}});
- };
-
- {
- 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) {
- 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, "
- << formatSystemMessageQ(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, "
- << formatSystemMessageQ(hr);
-
- return {};
- }
- }
-
- const auto enabled = (enabledVariant != VARIANT_FALSE);
- if (!enabled) {
- return {};
- }
-
- return SecurityProduct(
- {}, "Windows Firewall", WSC_SECURITY_PROVIDER_FIREWALL, true, true);
-}
-
-
-Metrics::Metrics()
-{
- m_displays = DisplayEnumerator().displays();
-}
-
-const std::vector& Metrics::displays() const
-{
- return m_displays;
-}
-
-QString Metrics::Display::toString() const
-{
- return QString("%1*%2 %3hz dpi=%4 on %5%6")
- .arg(resX)
- .arg(resY)
- .arg(refreshRate)
- .arg(dpi)
- .arg(adapter)
- .arg(primary ? " (primary)" : "");
-}
-
-
-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;
- m_md5 = getMD5();
-}
-
-const QString& Module::path() const
-{
- return m_path;
-}
-
-QString Module::displayPath() const
-{
- return QDir::fromNativeSeparators(m_path.toLower());
-}
-
-std::size_t Module::fileSize() const
-{
- return m_fileSize;
-}
-
-const QString& Module::version() const
-{
- return m_version;
-}
-
-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()) {
- return "(no timestamp)";
- }
-
- return m_timestamp.toString(Qt::DateFormat::ISODate);
-}
-
-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 {
- if (!m_version.isEmpty()) {
- sl.push_back(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);
- }
-
- return sl.join(", ");
-}
-
-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);
-
- 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 << "', "
- << formatSystemMessageQ(e);
-
- return {};
- }
-
- // getting version info
- 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 << "', "
- << formatSystemMessageQ(e);
-
- return {};
- }
-
- // the version info has two major parts: a fixed version and a localizable
- // set of strings
-
- FileInfo fi;
- fi.ffi = getFixedFileInfo(buffer.get());
- fi.fileDescription = getFileDescription(buffer.get());
-
- return fi;
-}
-
-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) {
- // not an error, no fixed file info
- return {};
- }
-
- const auto* fi = static_cast(valuePointer);
-
- // signature is always 0xfeef04bd
- if (fi->dwSignature != 0xfeef04bd) {
- qCritical().nospace().noquote()
- << "bad file info signature 0x" << hex << fi->dwSignature << " for "
- << "'" << m_path << "'";
-
- return {};
- }
-
- return *fi;
-}
-
-QString Module::getFileDescription(std::byte* buffer) const
-{
- struct LANGANDCODEPAGE
- {
- WORD wLanguage;
- WORD wCodePage;
- };
-
- void* valuePointer = nullptr;
- unsigned int valueSize = 0;
-
- // getting list of available languages
- 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 {};
- }
-
- // 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 = static_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(
- static_cast(valuePointer), valueSize - 1);
-}
-
-QString 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 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
- HandlePtr 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().nospace().noquote()
- << "can't open file '" << m_path << "' for timestamp, "
- << formatSystemMessageQ(e);
-
- return {};
- }
-
- // getting the file time
- if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
- const auto e = GetLastError();
- qCritical().nospace().noquote()
- << "can't get file time for '" << m_path << "', "
- << formatSystemMessageQ(e);
-
- 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 "
- << "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));
-}
-
-QString Module::getMD5() const
-{
- if (m_path.contains("\\windows\\", Qt::CaseInsensitive)) {
- // don't calculate md5 for system files, it's not really relevant and
- // it takes a while
- return {};
- }
-
- // opening the file
- QFile f(m_path);
-
- if (!f.open(QFile::ReadOnly)) {
- qCritical().nospace().noquote()
- << "failed to open file '" << m_path << "' for md5";
-
- return {};
- }
-
- // hashing
- QCryptographicHash hash(QCryptographicHash::Md5);
- if (!hash.addData(&f)) {
- qCritical().nospace().noquote()
- << "failed to calculate md5 for '" << m_path << "'";
-
- return {};
- }
-
- return hash.result().toHex();
-}
-
-
-WindowsInfo::WindowsInfo()
-{
- // 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 WindowsInfo::compatibilityMode() const
-{
- if (m_real == Version()) {
- // don't know the real version, can't guess compatibility mode
- return false;
- }
-
- return (m_real != m_reported);
-}
-
-const WindowsInfo::Version& WindowsInfo::reportedVersion() const
-{
- 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;
-}
-
-QString WindowsInfo::toString() const
-{
- QStringList sl;
-
- const QString reported = m_reported.toString();
- const QString real = m_real.toString();
-
- // version
- sl.push_back("version " + reported);
-
- // real version if different
- if (compatibilityMode()) {
- sl.push_back("real version " + real);
- }
-
- // 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));
- }
-
- // release ID
- if (!m_release.ID.isEmpty()) {
- sl.push_back("release " + m_release.ID);
- }
-
- // buildlab string
- if (!m_release.buildLab.isEmpty()) {
- sl.push_back(m_release.buildLab);
- }
-
- // 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");
- }
-
- sl.push_back("elevated: " + elevated);
-
- return sl.join(", ");
-}
-
-WindowsInfo::Version WindowsInfo::getReportedVersion(HINSTANCE ntdll) const
-{
- // 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
-
- 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);
-
- // this apparently never fails
- RtlGetVersion((RTL_OSVERSIONINFOW*)&vi);
-
- 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};
-}
-
-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);
-
- 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();
- }
- }
-
- // 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;
-}
-
-std::optional WindowsInfo::getElevated() const
-{
- HandlePtr 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: " << formatSystemMessageQ(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: " << formatSystemMessageQ(e);
-
- return {};
- }
-
- return (e.TokenIsElevated != 0);
-}
-
-
-SecurityProduct::SecurityProduct(
- QUuid guid, QString name, int provider,
- bool active, bool upToDate) :
- m_guid(std::move(guid)), m_name(std::move(name)), m_provider(provider),
- m_active(active), m_upToDate(upToDate)
-{
-}
-
-const QString& SecurityProduct::name() const
-{
- return m_name;
-}
-
-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 + " (" + providerToString() + ")";
-
- if (!m_active) {
- s += ", inactive";
- }
-
- if (!m_upToDate) {
- s += ", definitions outdated";
- }
-
- if (!m_guid.isNull()) {
- s += ", " + m_guid.toString(QUuid::QUuid::WithoutBraces);
- }
-
- return s;
-}
-
-QString SecurityProduct::providerToString() const
-{
- QStringList ps;
-
- if (m_provider & WSC_SECURITY_PROVIDER_FIREWALL) {
- ps.push_back("firewall");
- }
-
- if (m_provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
- ps.push_back("autoupdate");
- }
-
- if (m_provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
- ps.push_back("antivirus");
- }
-
- if (m_provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
- ps.push_back("antispyware");
- }
-
- if (m_provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
- ps.push_back("settings");
- }
-
- if (m_provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
- ps.push_back("uac");
- }
-
- if (m_provider & WSC_SECURITY_PROVIDER_SERVICE) {
- ps.push_back("service");
- }
-
- if (ps.empty()) {
- return "doesn't provider anything";
- }
-
- return ps.join("|");
-}
-
-
-struct Process
-{
- std::wstring filename;
- DWORD pid;
-
- Process(std::wstring f, DWORD id)
- : filename(std::move(f)), pid(id)
- {
- }
-};
-
-// returns the filename of the given process or the current one
-//
-std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
-{
- // double the buffer size 10 times
- const int MaxTries = 10;
-
- DWORD bufferSize = MAX_PATH;
-
- for (int tries=0; tries(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) {
- // hard failure
- 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();
- }
- }
-
- // something failed or the path is way too long to make sense
-
- 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()
-{
- // double the buffer size 10 times
- const int MaxTries = 10;
-
- // initial size of 300 processes, unlikely to be more than that
- std::size_t size = 300;
-
- for (int tries=0; tries(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) {
- // no way to distinguish between an exact fit and not enough space,
- // just try again
- 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 has pid 0 and 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 for system processes, even
- // when 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";
-
- // used to skip the current process below
- const auto thisPid = GetCurrentProcessId();
- std::wclog << L"this process id is " << thisPid << L"\n";
-
- // getting the filename for this process, assumes the other process has the
- // smae one
- 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";
- }
-
- // getting all running processes
- const auto processes = runningProcesses();
- std::wclog << L"there are " << processes.size() << L" processes running\n";
-
- // going through processes, trying to find one with the same name and a
- // different pid than this process has
- 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)
-{
- // maximum tries of incrementing the counter
- const int MaxTries = 100;
-
- // UTC time and date will be in the filename
- const auto now = std::time(0);
- const auto tm = std::gmtime(&now);
-
- // "ModOrganizer-YYYYMMDDThhmmss.dmp", with a possible "-i" appended, where
- // i can go until MaxTries
- 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";
-
- // first path to try, without counter in it
- std::wstring path = dir + L"\\" + prefix + ext;
-
- for (int i=0; i.
#ifndef UTIL_H
#define UTIL_H
-
#include
-#include
-
-#define WIN32_LEAN_AND_MEAN
-#include
-
#include
-#include
class Executable;
@@ -51,442 +44,6 @@ std::wstring ToLower(const std::wstring &text);
bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
-
-namespace env
-{
-
-class Console
-{
-public:
- Console();
- ~Console();
-
-private:
- bool m_hasConsole;
- FILE* m_in;
- FILE* m_out;
- FILE* m_err;
-};
-
-
-// an application shortcut that can be either on the desktop or the start menu
-//
-class Shortcut
-{
-public:
- // location of a shortcut
- //
- enum Locations
- {
- None = 0,
-
- // on the desktop
- Desktop,
-
- // in the start menu
- StartMenu
- };
-
-
- // empty shortcut
- //
- Shortcut();
-
- // shortcut from an executable
- //
- explicit Shortcut(const Executable& exe);
-
- // sets the name of the shortcut, shown on icons and start menu entries
- //
- Shortcut& name(const QString& s);
-
- // the program to start
- //
- Shortcut& target(const QString& s);
-
- // arguments to pass
- //
- Shortcut& arguments(const QString& s);
-
- // shows in the status bar of explorer, for example
- //
- Shortcut& description(const QString& s);
-
- // path to a binary that contains the icon and its index
- //
- Shortcut& icon(const QString& s, int index=0);
-
- // "start in" option for this shortcut
- //
- Shortcut& workingDirectory(const QString& s);
-
-
- // returns whether this shortcut already exists at the given location; this
- // does not check whether the shortcut parameters are different, it merely if
- // the .lnk file exists
- //
- bool exists(Locations loc) const;
-
- // calls remove() if exists(), or add()
- //
- bool toggle(Locations loc);
-
- // adds the shortcut to the given location
- //
- bool add(Locations loc);
-
- // removes the shortcut from the given location
- //
- bool remove(Locations loc);
-
-private:
- QString m_name;
- QString m_target;
- QString m_arguments;
- QString m_description;
- QString m_icon;
- int m_iconIndex;
- QString m_workingDirectory;
-
- // returns a qCritical() logger with a prefix already logged
- //
- QDebug critical() const;
-
- // returns a qDebug() logger with a prefix already logged
- //
- QDebug debug() const;
-
-
- // returns the path where the shortcut file should be saved
- //
- QString shortcutPath(Locations loc) const;
-
- // returns the directory where the shortcut file should be saved
- //
- QString shortcutDirectory(Locations loc) const;
-
- // returns the filename of the shortcut file that should be used when saving
- //
- QString shortcutFilename() const;
-};
-
-
-// returns a string representation of the given location
-//
-QString toString(Shortcut::Locations loc);
-
-
-// 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;
- QString fileDescription;
- };
-
- QString m_path;
- std::size_t m_fileSize;
- QString m_version;
- QDateTime m_timestamp;
- 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;
-};
-
-
-// a variety of information on windows
-//
-class WindowsInfo
-{
-public:
- 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:
- Version m_reported, m_real;
- Release m_release;
- std::optional m_elevated;
-
- // 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 a security product, such as an antivirus or a firewall
-//
-class SecurityProduct
-{
-public:
- SecurityProduct(
- QUuid guid, QString name, int provider,
- bool active, bool upToDate);
-
- // display name of the product
- //
- const QString& name() const;
-
- // a bunch of _WSC_SECURITY_PROVIDER flags
- //
- int provider() const;
-
- // whether the product is active
- //
- bool active() const;
-
- // whether its definitions are up-to-date
- //
- bool upToDate() const;
-
- // string representation of the above
- //
- QString toString() const;
-
-private:
- QUuid m_guid;
- QString m_name;
- int m_provider;
- bool m_active;
- bool m_upToDate;
-
- QString providerToString() const;
-};
-
-
-class Metrics
-{
-public:
- struct Display
- {
- int resX=0, resY=0, dpi=0;
- bool primary=false;
- int refreshRate = 0;
- QString monitor, adapter;
-
- QString toString() const;
- };
-
- Metrics();
-
- const std::vector& displays() const;
-
-private:
- std::vector m_displays;
-};
-
-
-// represents the process's environment
-//
-class Environment
-{
-public:
- Environment();
-
- // list of loaded modules in the current process
- //
- const std::vector& loadedModules() const;
-
- // information about the operating system
- //
- const WindowsInfo& windowsInfo() const;
-
- // information about the installed security products
- //
- const std::vector& securityProducts() const;
-
- // information about displays
- //
- const Metrics& metrics() const;
-
- // logs the environment
- //
- void dump() const;
-
-private:
- std::vector m_modules;
- WindowsInfo m_windows;
- std::vector m_security;
- Metrics m_metrics;
-
- std::vector getLoadedModules() const;
- std::vector getSecurityProducts() const;
-
- std::vector getSecurityProductsFromWMI() const;
- std::optional getWindowsFirewall() const;
-};
-
-
-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
-
-
MOBase::VersionInfo createVersionInfo();
} // namespace MOShared
--
cgit v1.3.1
From e071dfdfaa369a475a2d93df623c1696feee56ba Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Fri, 19 Jul 2019 02:47:13 -0400
Subject: changed qCritical() to log::error() removed now unused vlog()
---
src/browserdialog.cpp | 12 ++++----
src/categories.cpp | 10 +++----
src/downloadlist.cpp | 5 ++--
src/downloadmanager.cpp | 10 +++++--
src/envmodule.cpp | 66 +++++++++++++++++-------------------------
src/envsecurity.cpp | 58 +++++++++++++------------------------
src/envshortcut.cpp | 56 +++++++++++++++++------------------
src/envshortcut.h | 9 ------
src/envwindows.cpp | 19 ++++++------
src/executableslist.cpp | 10 +++----
src/filerenamer.cpp | 2 +-
src/filterwidget.cpp | 5 +++-
src/forcedloaddialogwidget.cpp | 9 +++---
src/installationmanager.cpp | 7 ++---
src/loglist.cpp | 18 ------------
src/mainwindow.cpp | 34 +++++++++++-----------
src/moapplication.cpp | 10 ++++---
src/modinfo.cpp | 5 +---
src/modinfodialog.cpp | 12 ++++----
src/modinfodialogconflicts.cpp | 6 ++--
src/modinfodialogfiletree.cpp | 9 +++---
src/modinfodialogimages.cpp | 9 +++---
src/modinforegular.cpp | 20 ++++++-------
src/modlist.cpp | 12 ++++----
src/modlistsortproxy.cpp | 2 +-
src/nexusinterface.cpp | 28 +++++++++---------
src/organizercore.cpp | 22 +++++++-------
src/overwriteinfodialog.cpp | 6 ++--
src/persistentcookiejar.cpp | 8 +++--
src/plugincontainer.cpp | 5 ++--
src/pluginlist.cpp | 10 +++----
src/profile.cpp | 4 +--
src/settings.cpp | 14 ++-------
src/settingsdialog.cpp | 1 -
src/shared/directoryentry.cpp | 23 ++++++++-------
src/shared/error_report.h | 2 --
src/syncoverwritedialog.cpp | 3 +-
src/texteditor.cpp | 7 +++--
src/transfersavesdialog.cpp | 13 ++++-----
39 files changed, 251 insertions(+), 310 deletions(-)
(limited to 'src/shared')
diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp
index e186ad63..1fde7f15 100644
--- a/src/browserdialog.cpp
+++ b/src/browserdialog.cpp
@@ -24,9 +24,10 @@ along with Mod Organizer. If not, see .
#include "messagedialog.h"
#include "report.h"
#include "persistentcookiejar.h"
+#include "settings.h"
#include
-#include "settings.h"
+#include
#include
#include
@@ -38,6 +39,7 @@ along with Mod Organizer. If not, see .
#include
#include
+using namespace MOBase;
BrowserDialog::BrowserDialog(QWidget *parent)
@@ -192,12 +194,12 @@ void BrowserDialog::unsupportedContent(QNetworkReply *reply)
try {
QWebEnginePage *page = qobject_cast(sender());
if (page == nullptr) {
- qCritical("sender not a page");
+ log::error("sender not a page");
return;
}
BrowserView *view = qobject_cast(page->view());
if (view == nullptr) {
- qCritical("no view?");
+ log::error("no view?");
return;
}
@@ -206,14 +208,14 @@ void BrowserDialog::unsupportedContent(QNetworkReply *reply)
if (isVisible()) {
MessageDialog::showMessage(tr("failed to start download"), this);
}
- qCritical("exception downloading unsupported content: %s", e.what());
+ log::error("exception downloading unsupported content: {}", e.what());
}
}
void BrowserDialog::downloadRequested(const QNetworkRequest &request)
{
- qCritical("download request %s ignored", request.url().toString().toUtf8().constData());
+ log::error("download request {} ignored", request.url().toString());
}
diff --git a/src/categories.cpp b/src/categories.cpp
index 8f9d3ad8..7acf6ff5 100644
--- a/src/categories.cpp
+++ b/src/categories.cpp
@@ -62,8 +62,9 @@ void CategoryFactory::loadCategories()
++lineNum;
QList cells = line.split('|');
if (cells.count() != 4) {
- qCritical("invalid category line %d: %s (%d cells)",
- lineNum, line.constData(), cells.count());
+ log::error(
+ "invalid category line {}: {} ({} cells)",
+ lineNum, line.constData(), cells.count());
} else {
std::vector nexusIDs;
if (cells[2].length() > 0) {
@@ -73,7 +74,7 @@ void CategoryFactory::loadCategories()
bool ok = false;
int temp = iter->toInt(&ok);
if (!ok) {
- qCritical("invalid category id %s", iter->constData());
+ log::error("invalid category id {}", iter->constData());
}
nexusIDs.push_back(temp);
}
@@ -83,8 +84,7 @@ void CategoryFactory::loadCategories()
int id = cells[0].toInt(&cell0Ok);
int parentID = cells[3].trimmed().toInt(&cell3Ok);
if (!cell0Ok || !cell3Ok) {
- qCritical("invalid category line %d: %s",
- lineNum, line.constData());
+ log::error("invalid category line {}: {}", lineNum, line.constData());
}
addCategory(id, QString::fromUtf8(cells[1].constData()), nexusIDs, parentID);
}
diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp
index 5e698e0e..36bc2b7f 100644
--- a/src/downloadlist.cpp
+++ b/src/downloadlist.cpp
@@ -19,12 +19,13 @@ along with Mod Organizer. If not, see .
#include "downloadlist.h"
#include "downloadmanager.h"
+#include
#include
#include
#include
-
#include
+using namespace MOBase;
DownloadList::DownloadList(DownloadManager *manager, QObject *parent)
: QAbstractTableModel(parent), m_Manager(manager)
@@ -192,7 +193,7 @@ void DownloadList::update(int row)
else if (row < this->rowCount())
emit dataChanged(this->index(row, 0, QModelIndex()), this->index(row, this->columnCount(QModelIndex())-1, QModelIndex()));
else
- qCritical("invalid row %d in download list, update failed", row);
+ log::error("invalid row {} in download list, update failed", row);
}
QString DownloadList::sizeFormat(quint64 size) const
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
index e3ceb261..348b2108 100644
--- a/src/downloadmanager.cpp
+++ b/src/downloadmanager.cpp
@@ -660,7 +660,7 @@ void DownloadManager::removeFile(int index, bool deleteFile)
if ((download->m_State == STATE_STARTED) ||
(download->m_State == STATE_DOWNLOADING)) {
// shouldn't have been possible
- qCritical("tried to remove active download");
+ log::error("tried to remove active download");
endDisableDirWatcher();
return;
}
@@ -798,7 +798,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile)
emit update(-1);
endDisableDirWatcher();
} catch (const std::exception &e) {
- qCritical("failed to remove download: %s", e.what());
+ log::error("failed to remove download: {}", e.what());
}
refreshList();
}
@@ -2069,7 +2069,11 @@ void DownloadManager::writeData(DownloadInfo *info)
if (ret < info->m_Reply->size()) {
QString fileName = info->m_FileName; // m_FileName may be destroyed after setState
setState(info, DownloadState::STATE_CANCELED);
- qCritical(QString("Unable to write download \"%2\" to drive (return %1)").arg(ret).arg(info->m_FileName).toLocal8Bit());
+
+ log::error(
+ "Unable to write download \"{}\" to drive (return {})",
+ info->m_FileName, ret);
+
reportError(tr("Unable to write download to drive (return %1).\n"
"Check the drive's available storage.\n\n"
"Canceling download \"%2\"...").arg(ret).arg(fileName));
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
index 1717da15..aae4e0b1 100644
--- a/src/envmodule.cpp
+++ b/src/envmodule.cpp
@@ -1,6 +1,7 @@
#include "envmodule.h"
#include "env.h"
#include
+#include
namespace env
{
@@ -114,9 +115,9 @@ Module::FileInfo Module::getFileInfo() const
return {};
}
- qCritical().nospace().noquote()
- << "GetFileVersionInfoSizeW() failed on '" << m_path << "', "
- << formatSystemMessageQ(e);
+ log::error(
+ "GetFileVersionInfoSizeW() failed on '{}', {}",
+ m_path, formatSystemMessageQ(e));
return {};
}
@@ -127,9 +128,9 @@ Module::FileInfo Module::getFileInfo() const
if (!GetFileVersionInfoW(wspath.c_str(), 0, size, buffer.get())) {
const auto e = GetLastError();
- qCritical().nospace().noquote()
- << "GetFileVersionInfoW() failed on '" << m_path << "', "
- << formatSystemMessageQ(e);
+ log::error(
+ "GetFileVersionInfoW() failed on '{}', {}",
+ m_path, formatSystemMessageQ(e));
return {};
}
@@ -161,9 +162,9 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
// signature is always 0xfeef04bd
if (fi->dwSignature != 0xfeef04bd) {
- qCritical().nospace().noquote()
- << "bad file info signature 0x" << hex << fi->dwSignature << " for "
- << "'" << m_path << "'";
+ log::error(
+ "bad file info signature {:#x} for '{}'",
+ fi->dwSignature, m_path);
return {};
}
@@ -187,9 +188,7 @@ QString Module::getFileDescription(std::byte* buffer) const
buffer, L"\\VarFileInfo\\Translation", &valuePointer, &valueSize);
if (!ret || !valuePointer || valueSize == 0) {
- qCritical().nospace().noquote()
- << "VerQueryValueW() for translations failed on '" << m_path << "'";
-
+ log::error("VerQueryValueW() for translations failed on '{}'", m_path);
return {};
}
@@ -254,9 +253,9 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
if (h.get() == INVALID_HANDLE_VALUE) {
const auto e = GetLastError();
- qCritical().nospace().noquote()
- << "can't open file '" << m_path << "' for timestamp, "
- << formatSystemMessageQ(e);
+ log::error(
+ "can't open file '{}' for timestamp, {}",
+ m_path, formatSystemMessageQ(e));
return {};
}
@@ -264,9 +263,10 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
// getting the file time
if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
const auto e = GetLastError();
- qCritical().nospace().noquote()
- << "can't get file time for '" << m_path << "', "
- << formatSystemMessageQ(e);
+
+ log::error(
+ "can't get file time for '{}', {}",
+ m_path, formatSystemMessageQ(e));
return {};
}
@@ -281,11 +281,9 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
SYSTEMTIME utc = {};
if (!FileTimeToSystemTime(&ft, &utc)) {
- qCritical().nospace().noquote()
- << "FileTimeToSystemTime() failed on timestamp "
- << "high=0x" << hex << ft.dwHighDateTime << " "
- << "low=0x" << hex << ft.dwLowDateTime << " for "
- << "'" << m_path << "'";
+ log::error(
+ "FileTimeToSystemTime() failed on timestamp high={:#x} low={:#x} for '{}'",
+ ft.dwHighDateTime, ft.dwLowDateTime, m_path);
return {};
}
@@ -307,18 +305,14 @@ QString Module::getMD5() const
QFile f(m_path);
if (!f.open(QFile::ReadOnly)) {
- qCritical().nospace().noquote()
- << "failed to open file '" << m_path << "' for md5";
-
+ log::error("failed to open file '{}' for md5", m_path);
return {};
}
// hashing
QCryptographicHash hash(QCryptographicHash::Md5);
if (!hash.addData(&f)) {
- qCritical().nospace().noquote()
- << "failed to calculate md5 for '" << m_path << "'";
-
+ log::error("failed to calculate md5 for '{}'", m_path);
return {};
}
@@ -334,11 +328,7 @@ std::vector getLoadedModules()
if (snapshot.get() == INVALID_HANDLE_VALUE)
{
const auto e = GetLastError();
-
- qCritical().nospace().noquote()
- << "CreateToolhelp32Snapshot() failed, "
- << formatSystemMessageQ(e);
-
+ log::error("CreateToolhelp32Snapshot() failed, {}", formatSystemMessageQ(e));
return {};
}
@@ -349,10 +339,7 @@ std::vector getLoadedModules()
if (!Module32First(snapshot.get(), &me))
{
const auto e = GetLastError();
-
- qCritical().nospace().noquote()
- << "Module32First() failed, " << formatSystemMessageQ(e);
-
+ log::error("Module32First() failed, {}", formatSystemMessageQ(e));
return {};
}
@@ -371,8 +358,7 @@ std::vector getLoadedModules()
// no more modules is not an error
if (e != ERROR_NO_MORE_FILES) {
- qCritical().nospace().noquote()
- << "Module32Next() failed, " << formatSystemMessageQ(e);
+ log::error("Module32Next() failed, {}", formatSystemMessageQ(e));
}
break;
diff --git a/src/envsecurity.cpp b/src/envsecurity.cpp
index 559ce4ad..015e4000 100644
--- a/src/envsecurity.cpp
+++ b/src/envsecurity.cpp
@@ -1,6 +1,7 @@
#include "envsecurity.h"
#include "env.h"
#include
+#include
#include