From eab0ec298b81138c4c602259ebf930f583113b95 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 15:26:04 -0400
Subject: refactored preloadSsl() into preloadDll() removed old HGID check
moved formatSystemMessage() to uibase added Environment class, lists loaded
modules, logged at startup
---
src/shared/util.cpp | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 327 insertions(+)
(limited to 'src/shared/util.cpp')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index ed7c434e..16b373db 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -20,6 +20,7 @@ along with Mod Organizer. If not, see .
#include "util.h"
#include "windows_error.h"
#include "error_report.h"
+#include
#include
#include
@@ -29,6 +30,8 @@ along with Mod Organizer. If not, see .
#include
#include
+using MOBase::formatSystemMessage;
+
namespace MOShared {
@@ -253,4 +256,328 @@ MOBase::VersionInfo createVersionInfo()
}
+struct HandleCloser
+{
+ using pointer = HANDLE;
+ void operator()(HANDLE h)
+ {
+ if (h != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(h);
+ }
+ }
+};
+
+
+Environment::Environment()
+{
+ getLoadedModules();
+}
+
+const std::vector& Environment::loadedModules()
+{
+ return m_modules;
+}
+
+void Environment::getLoadedModules()
+{
+ std::unique_ptr snapshot(CreateToolhelp32Snapshot(
+ TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
+
+ if (snapshot.get() == INVALID_HANDLE_VALUE)
+ {
+ const auto e = GetLastError();
+
+ qCritical().nospace()
+ << "CreateToolhelp32Snapshot() failed, "
+ << formatSystemMessage(e);
+
+ return;
+ }
+
+ // Set the size of the structure before using it.
+ MODULEENTRY32 me = {};
+ me.dwSize = sizeof(me);
+
+ // Retrieve information about the first module,
+ // and exit if unsuccessful
+ if (!Module32First(snapshot.get(), &me))
+ {
+ const auto e = GetLastError();
+
+ qCritical().nospace()
+ << "Module32First() failed, " << formatSystemMessage(e);
+
+ return;
+ }
+
+ // Now walk the module list of the process,
+ // and display information about each module
+ qInfo() << "modules loaded in process:";
+
+ for (;;)
+ {
+ const auto path = QString::fromWCharArray(me.szExePath);
+
+ m_modules.push_back(Module(path, me.modBaseSize));
+
+ if (!Module32Next(snapshot.get(), &me)) {
+ const auto e = GetLastError();
+
+ if (e != ERROR_NO_MORE_FILES) {
+ qCritical() << "Module32Next() failed, " << formatSystemMessage(e);
+ }
+
+ break;
+ }
+ }
+}
+
+
+Environment::Module::Module(QString path, std::size_t fileSize)
+ : m_path(std::move(path)), m_fileSize(fileSize)
+{
+ const auto fi = getFileInfo();
+
+ m_version = getVersion(fi.ffi);
+ m_timestamp = getTimestamp(fi.ffi);
+ m_versionString = fi.fileDescription;
+}
+
+const QString& Environment::Module::path() const
+{
+ return m_path;
+}
+
+std::size_t Environment::Module::fileSize() const
+{
+ return m_fileSize;
+}
+
+const QString& Environment::Module::version() const
+{
+ return m_version;
+}
+
+const QString& Environment::Module::versionString() const
+{
+ return m_versionString;
+}
+
+QString Environment::Module::timestampString() const
+{
+ if (!m_timestamp.isValid()) {
+ return "(no timestamp)";
+ }
+
+ return m_timestamp.toString(Qt::DateFormat::ISODate);
+}
+
+QString Environment::Module::toString() const
+{
+ QStringList sl;
+
+ sl.push_back(m_path);
+ sl.push_back(QString("%1 B").arg(m_fileSize));
+
+ if (m_version.isEmpty() && m_versionString.isEmpty()) {
+ sl.push_back("(no version)");
+ } else {
+ if (!m_version.isEmpty()) {
+ sl.push_back(m_version);
+ }
+
+ if (m_versionString != m_version) {
+ sl.push_back(versionString());
+ }
+ }
+
+ if (m_timestamp.isValid()) {
+ sl.push_back(m_timestamp.toString(Qt::DateFormat::ISODate));
+ } else {
+ sl.push_back("(no timestamp)");
+ }
+
+ return sl.join(", ");
+}
+
+Environment::Module::FileInfo Environment::Module::getFileInfo() const
+{
+ const auto wspath = m_path.toStdWString();
+
+ DWORD dummy = 0;
+ const DWORD size = GetFileVersionInfoSizeW(wspath.c_str(), &dummy);
+
+ if (size == 0) {
+ const auto e = GetLastError();
+
+ if (e == ERROR_RESOURCE_TYPE_NOT_FOUND) {
+ // not an error, no version information built into that module
+ return {};
+ }
+
+ qCritical().nospace().noquote()
+ << "GetFileVersionInfoSizeW() failed on '" << m_path << "', "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+
+ auto buffer = std::make_unique(size);
+
+ if (!GetFileVersionInfoW(wspath.c_str(), 0, size, buffer.get())) {
+ const auto e = GetLastError();
+
+ qCritical().nospace().noquote()
+ << "GetFileVersionInfoW() failed on '" << m_path << "', "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+
+
+ FileInfo fi;
+ fi.ffi = getFixedFileInfo(buffer.get());
+ fi.fileDescription = getFileDescription(buffer.get());
+
+ return fi;
+}
+
+VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const
+{
+ void* valuePointer = nullptr;
+ unsigned int valueSize = 0;
+
+ const auto ret = VerQueryValueW(buffer, L"\\", &valuePointer, &valueSize);
+
+ if (!ret || !valuePointer || valueSize == 0) {
+ // not an error, no fixed file info
+ return {};
+ }
+
+ const auto* fi = reinterpret_cast(valuePointer);
+
+ if (fi->dwSignature != 0xfeef04bd) {
+ qCritical().nospace().noquote()
+ << "bad file info signature 0x" << hex << fi->dwSignature << " for "
+ << "'" << m_path << "'";
+
+ return {};
+ }
+
+ return *fi;
+}
+
+QString Environment::Module::getFileDescription(std::byte* buffer) const
+{
+ struct LANGANDCODEPAGE
+ {
+ WORD wLanguage;
+ WORD wCodePage;
+ };
+
+ void* valuePointer = nullptr;
+ unsigned int valueSize = 0;
+
+ auto ret = VerQueryValueW(
+ buffer, L"\\VarFileInfo\\Translation", &valuePointer, &valueSize);
+
+ if (!ret || !valuePointer || valueSize == 0) {
+ qCritical().nospace().noquote()
+ << "VerQueryValueW() for translations failed on '" << m_path << "'";
+
+ return {};
+ }
+
+ const auto count = valueSize / sizeof(LANGANDCODEPAGE);
+ if (count == 0) {
+ return {};
+ }
+
+ const auto* lcp = reinterpret_cast(valuePointer);
+
+ const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion")
+ .arg(lcp->wLanguage, 4, 16, QChar('0'))
+ .arg(lcp->wCodePage, 4, 16, QChar('0'));
+
+ ret = VerQueryValueW(
+ buffer, subBlock.toStdWString().c_str(), &valuePointer, &valueSize);
+
+ if (!ret || !valuePointer || valueSize == 0) {
+ // not an error, no file version
+ return {};
+ }
+
+ // valueSize includes the null terminator
+ return QString::fromWCharArray(
+ reinterpret_cast(valuePointer), valueSize - 1);
+}
+
+QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const
+{
+ if (fi.dwSignature == 0) {
+ return {};
+ }
+
+ const DWORD major = (fi.dwFileVersionMS >> 16 ) & 0xffff;
+ const DWORD minor = (fi.dwFileVersionMS >> 0 ) & 0xffff;
+ const DWORD maintenance = (fi.dwFileVersionLS >> 16 ) & 0xffff;
+ const DWORD build = (fi.dwFileVersionLS >> 0 ) & 0xffff;
+
+ if (major == 0 && minor == 0 && maintenance == 0 && build == 0) {
+ return {};
+ }
+
+ return QString("%1.%2.%3.%4")
+ .arg(major).arg(minor).arg(maintenance).arg(build);
+}
+
+QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
+{
+ FILETIME ft = {};
+
+ if (fi.dwSignature == 0 || (fi.dwFileDateMS == 0 && fi.dwFileDateLS == 0)) {
+ std::unique_ptr h(CreateFileW(
+ m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
+
+ if (h.get() == INVALID_HANDLE_VALUE) {
+ const auto e = GetLastError();
+
+ qCritical()
+ << "can't open file '" << m_path << "' for timestamp, "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+
+ if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
+ const auto e = GetLastError();
+ qCritical()
+ << "can't get file time for '" << m_path << "', "
+ << formatSystemMessage(e);
+
+ return {};
+ }
+ } else {
+ ft.dwHighDateTime = fi.dwFileDateMS;
+ ft.dwLowDateTime = fi.dwFileDateLS;
+ }
+
+
+ SYSTEMTIME utc = {};
+ if (!FileTimeToSystemTime(&ft, &utc)) {
+ qCritical()
+ << "FileTimeToSystemTime() failed on timestamp "
+ << "high=0x" << hex << ft.dwHighDateTime << " "
+ << "low=0x" << hex << ft.dwLowDateTime << " for "
+ << "'" << m_path << "'";
+
+ return {};
+ }
+
+ return QDateTime(
+ QDate(utc.wYear, utc.wMonth, utc.wDay),
+ QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds));
+}
+
} // namespace MOShared
--
cgit v1.3.1
From 7152b8e97b060e34e5a5bfa8f36c94722007d6aa Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 16:13:55 -0400
Subject: lower case paths, sorted list, md5 for some files
---
src/shared/util.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++-------
src/shared/util.h | 4 ++++
2 files changed, 54 insertions(+), 7 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 16b373db..772d3ca5 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -287,7 +287,7 @@ void Environment::getLoadedModules()
{
const auto e = GetLastError();
- qCritical().nospace()
+ qCritical().nospace().noquote()
<< "CreateToolhelp32Snapshot() failed, "
<< formatSystemMessage(e);
@@ -304,7 +304,7 @@ void Environment::getLoadedModules()
{
const auto e = GetLastError();
- qCritical().nospace()
+ qCritical().nospace().noquote()
<< "Module32First() failed, " << formatSystemMessage(e);
return;
@@ -324,12 +324,17 @@ void Environment::getLoadedModules()
const auto e = GetLastError();
if (e != ERROR_NO_MORE_FILES) {
- qCritical() << "Module32Next() failed, " << formatSystemMessage(e);
+ qCritical().nospace().noquote()
+ << "Module32Next() failed, " << formatSystemMessage(e);
}
break;
}
}
+
+ std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) {
+ return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0);
+ });
}
@@ -341,6 +346,7 @@ Environment::Module::Module(QString path, std::size_t fileSize)
m_version = getVersion(fi.ffi);
m_timestamp = getTimestamp(fi.ffi);
m_versionString = fi.fileDescription;
+ m_md5 = getMD5();
}
const QString& Environment::Module::path() const
@@ -348,6 +354,11 @@ const QString& Environment::Module::path() const
return m_path;
}
+QString Environment::Module::displayPath() const
+{
+ return QDir::fromNativeSeparators(m_path.toLower());
+}
+
std::size_t Environment::Module::fileSize() const
{
return m_fileSize;
@@ -376,7 +387,7 @@ QString Environment::Module::toString() const
{
QStringList sl;
- sl.push_back(m_path);
+ sl.push_back(displayPath());
sl.push_back(QString("%1 B").arg(m_fileSize));
if (m_version.isEmpty() && m_versionString.isEmpty()) {
@@ -397,6 +408,10 @@ QString Environment::Module::toString() const
sl.push_back("(no timestamp)");
}
+ if (!m_md5.isEmpty()) {
+ sl.push_back(m_md5);
+ }
+
return sl.join(", ");
}
@@ -543,7 +558,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
if (h.get() == INVALID_HANDLE_VALUE) {
const auto e = GetLastError();
- qCritical()
+ qCritical().nospace().noquote()
<< "can't open file '" << m_path << "' for timestamp, "
<< formatSystemMessage(e);
@@ -552,7 +567,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
const auto e = GetLastError();
- qCritical()
+ qCritical().nospace().noquote()
<< "can't get file time for '" << m_path << "', "
<< formatSystemMessage(e);
@@ -566,7 +581,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
SYSTEMTIME utc = {};
if (!FileTimeToSystemTime(&ft, &utc)) {
- qCritical()
+ qCritical().nospace().noquote()
<< "FileTimeToSystemTime() failed on timestamp "
<< "high=0x" << hex << ft.dwHighDateTime << " "
<< "low=0x" << hex << ft.dwLowDateTime << " for "
@@ -580,4 +595,32 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds));
}
+QString Environment::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 {};
+ }
+
+ QFile f(m_path);
+
+ if (!f.open(QFile::ReadOnly)) {
+ qCritical().nospace().noquote()
+ << "failed to open file '" << m_path << "' for md5";
+
+ return {};
+ }
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (!hash.addData(&f)) {
+ qCritical().nospace().noquote()
+ << "failed to calculate md5 for '" << m_path << "'";
+
+ return {};
+ }
+
+ return hash.result().toHex();
+}
+
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 5d88481c..46bdea78 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -55,6 +55,8 @@ public:
explicit Module(QString path, std::size_t fileSize);
const QString& path() const;
+ QString displayPath() const;
+
std::size_t fileSize() const;
const QString& version() const;
const QString& versionString() const;
@@ -74,11 +76,13 @@ public:
QString m_version;
QDateTime m_timestamp;
QString m_versionString;
+ QString m_md5;
FileInfo getFileInfo() const;
QString getVersion(const VS_FIXEDFILEINFO& fi) const;
QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
+ QString getMD5() const;
VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
QString getFileDescription(std::byte* buffer) const;
--
cgit v1.3.1
From fb6512b72ebf86d5273744774388deb14c8a21ae Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 18:16:00 -0400
Subject: log windows version
---
src/main.cpp | 6 +-
src/shared/util.cpp | 211 ++++++++++++++++++++++++++++++++++++++++++++++++----
src/shared/util.h | 94 +++++++++++++++--------
3 files changed, 264 insertions(+), 47 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index e1a6b853..75148c4b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -467,8 +467,12 @@ int runApplication(MOApplication &application, SingleInstance &instance,
#endif
{
- Environment env;
+ env::Environment env;
+ qInfo().nospace().noquote()
+ << "windows: " << env.windowsVersion().toString();
+
+ qInfo() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
qInfo().nospace().noquote() << " . " << m.toString();
}
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 772d3ca5..9337ebf6 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -256,9 +256,13 @@ MOBase::VersionInfo createVersionInfo()
}
+namespace env
+{
+
struct HandleCloser
{
using pointer = HANDLE;
+
void operator()(HANDLE h)
{
if (h != INVALID_HANDLE_VALUE) {
@@ -267,17 +271,34 @@ struct HandleCloser
}
};
+struct LibraryFreer
+{
+ using pointer = HINSTANCE;
+
+ void operator()(HINSTANCE h)
+ {
+ if (h != 0) {
+ ::FreeLibrary(h);
+ }
+ }
+};
+
Environment::Environment()
{
getLoadedModules();
}
-const std::vector& Environment::loadedModules()
+const std::vector& Environment::loadedModules()
{
return m_modules;
}
+const WindowsVersion& Environment::windowsVersion() const
+{
+ return m_windows;
+}
+
void Environment::getLoadedModules()
{
std::unique_ptr snapshot(CreateToolhelp32Snapshot(
@@ -312,7 +333,6 @@ void Environment::getLoadedModules()
// Now walk the module list of the process,
// and display information about each module
- qInfo() << "modules loaded in process:";
for (;;)
{
@@ -338,7 +358,7 @@ void Environment::getLoadedModules()
}
-Environment::Module::Module(QString path, std::size_t fileSize)
+Module::Module(QString path, std::size_t fileSize)
: m_path(std::move(path)), m_fileSize(fileSize)
{
const auto fi = getFileInfo();
@@ -349,32 +369,32 @@ Environment::Module::Module(QString path, std::size_t fileSize)
m_md5 = getMD5();
}
-const QString& Environment::Module::path() const
+const QString& Module::path() const
{
return m_path;
}
-QString Environment::Module::displayPath() const
+QString Module::displayPath() const
{
return QDir::fromNativeSeparators(m_path.toLower());
}
-std::size_t Environment::Module::fileSize() const
+std::size_t Module::fileSize() const
{
return m_fileSize;
}
-const QString& Environment::Module::version() const
+const QString& Module::version() const
{
return m_version;
}
-const QString& Environment::Module::versionString() const
+const QString& Module::versionString() const
{
return m_versionString;
}
-QString Environment::Module::timestampString() const
+QString Module::timestampString() const
{
if (!m_timestamp.isValid()) {
return "(no timestamp)";
@@ -383,7 +403,7 @@ QString Environment::Module::timestampString() const
return m_timestamp.toString(Qt::DateFormat::ISODate);
}
-QString Environment::Module::toString() const
+QString Module::toString() const
{
QStringList sl;
@@ -415,7 +435,7 @@ QString Environment::Module::toString() const
return sl.join(", ");
}
-Environment::Module::FileInfo Environment::Module::getFileInfo() const
+Module::FileInfo Module::getFileInfo() const
{
const auto wspath = m_path.toStdWString();
@@ -457,7 +477,7 @@ Environment::Module::FileInfo Environment::Module::getFileInfo() const
return fi;
}
-VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const
+VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
{
void* valuePointer = nullptr;
unsigned int valueSize = 0;
@@ -482,7 +502,7 @@ VS_FIXEDFILEINFO Environment::Module::getFixedFileInfo(std::byte* buffer) const
return *fi;
}
-QString Environment::Module::getFileDescription(std::byte* buffer) const
+QString Module::getFileDescription(std::byte* buffer) const
{
struct LANGANDCODEPAGE
{
@@ -527,7 +547,7 @@ QString Environment::Module::getFileDescription(std::byte* buffer) const
reinterpret_cast(valuePointer), valueSize - 1);
}
-QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const
+QString Module::getVersion(const VS_FIXEDFILEINFO& fi) const
{
if (fi.dwSignature == 0) {
return {};
@@ -546,7 +566,7 @@ QString Environment::Module::getVersion(const VS_FIXEDFILEINFO& fi) const
.arg(major).arg(minor).arg(maintenance).arg(build);
}
-QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
+QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
{
FILETIME ft = {};
@@ -595,7 +615,7 @@ QDateTime Environment::Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
QTime(utc.wHour, utc.wMinute, utc.wSecond, utc.wMilliseconds));
}
-QString Environment::Module::getMD5() const
+QString Module::getMD5() const
{
if (m_path.contains("\\windows\\", Qt::CaseInsensitive)) {
// don't calculate md5 for system files, it's not really relevant and
@@ -623,4 +643,163 @@ QString Environment::Module::getMD5() const
return hash.result().toHex();
}
+
+WindowsVersion::WindowsVersion() :
+ m_realMajor(0), m_realMinor(0), m_realBuild(0),
+ m_major(0), m_minor(0), m_build(0), m_UBR(0)
+{
+ getVersion();
+ getRelease();
+ getElevated();
+}
+
+QString WindowsVersion::toString() const
+{
+ QStringList sl;
+
+ const QString version = QString("%1.%2.%3")
+ .arg(m_major).arg(m_minor).arg(m_build);
+
+ const QString realVersion = QString("%1.%2.%3")
+ .arg(m_realMajor).arg(m_realMinor).arg(m_realBuild);
+
+ sl.push_back("version: " + version);
+
+ if (m_realMajor != m_major || m_realMinor != m_minor || m_realBuild != m_build) {
+ sl.push_back("real version: " + realVersion);
+ }
+
+ if (!m_buildLab.isEmpty()) {
+ sl.push_back(m_buildLab);
+ }
+
+ if (!m_productName.isEmpty()) {
+ sl.push_back(m_productName);
+ }
+
+ if (!m_releaseID.isEmpty()) {
+ sl.push_back("build " + m_releaseID);
+ }
+
+ if (m_UBR != 0) {
+ sl.push_back(QString("%1").arg(m_UBR));
+ }
+
+ QString elevated = "?";
+ if (m_elevated.has_value()) {
+ elevated = (*m_elevated ? "yes" : "no");
+ }
+
+ sl.push_back("elevated: " + elevated);
+
+ return sl.join(", ");
+}
+
+void WindowsVersion::getVersion()
+{
+ std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll"));
+
+ if (!ntdll) {
+ qCritical() << "failed to load ntdll.dll while getting version";
+ return;
+ }
+
+ getRealVersion(ntdll.get());
+ getReportedVersion(ntdll.get());
+}
+
+void WindowsVersion::getRealVersion(HINSTANCE ntdll)
+{
+ using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*);
+
+ auto* RtlGetNtVersionNumbers = reinterpret_cast(
+ GetProcAddress(ntdll, "RtlGetNtVersionNumbers"));
+
+ if (RtlGetNtVersionNumbers) {
+ DWORD build = 0;
+ RtlGetNtVersionNumbers(&m_realMajor, &m_realMinor, &build);
+
+ m_realBuild = 0x0fffffff & build;
+ }
+}
+
+void WindowsVersion::getReportedVersion(HINSTANCE ntdll)
+{
+ using RtlGetVersionType = NTSTATUS (NTAPI)(PRTL_OSVERSIONINFOW);
+
+ auto* RtlGetVersion = reinterpret_cast(
+ GetProcAddress(ntdll, "RtlGetVersion"));
+
+ if (!RtlGetVersion) {
+ qCritical() << "RtlGetVersion() not found in ntdll.dll";
+ return;
+ }
+
+ OSVERSIONINFOEX vi = {};
+ vi.dwOSVersionInfoSize = sizeof(vi);
+
+ RtlGetVersion((RTL_OSVERSIONINFOW*)&vi);
+
+ m_major = vi.dwMajorVersion;
+ m_minor = vi.dwMinorVersion;
+ m_build = vi.dwBuildNumber;
+}
+
+void WindowsVersion::getRelease()
+{
+ QSettings settings(
+ R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
+ QSettings::NativeFormat);
+
+ m_buildLab = settings.value("BuildLabEx", "").toString();
+ if (m_buildLab.isEmpty()) {
+ m_buildLab = settings.value("BuildLab", "").toString();
+ if (m_buildLab.isEmpty()) {
+ m_buildLab = settings.value("BuildBranch", "").toString();
+ }
+ }
+
+ m_productName = settings.value("ProductName", "").toString();
+ m_releaseID = settings.value("ReleaseId", "").toString();
+ m_UBR = settings.value("UBR", 0).toUInt();
+}
+
+void WindowsVersion::getElevated()
+{
+ std::unique_ptr token;
+
+ {
+ HANDLE rawToken = 0;
+
+ if (!OpenProcessToken(GetCurrentProcess( ), TOKEN_QUERY, &rawToken)) {
+ const auto e = GetLastError();
+
+ qCritical()
+ << "while trying to check if process is elevated, "
+ << "OpenProcessToken() failed: " << formatSystemMessage(e);
+
+ return;
+ }
+
+ token.reset(rawToken);
+ }
+
+ TOKEN_ELEVATION e = {};
+ DWORD size = sizeof(TOKEN_ELEVATION);
+
+ if (!GetTokenInformation(token.get(), TokenElevation, &e, sizeof(e), &size)) {
+ const auto e = GetLastError();
+
+ qCritical()
+ << "while trying to check if process is elevated, "
+ << "GetTokenInformation() failed: " << formatSystemMessage(e);
+
+ return;
+ }
+
+ m_elevated = (e.TokenIsElevated != 0);
+}
+
+} // namespace env
+
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 46bdea78..deaf6fcc 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -22,6 +22,8 @@ along with Mod Organizer. If not, see .
#include
+#include
+
#define WIN32_LEAN_AND_MEAN
#include
@@ -46,58 +48,90 @@ std::wstring ToLower(const std::wstring &text);
bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
-class Environment
+
+namespace env
+{
+
+class Module
{
public:
- class Module
+ explicit Module(QString path, std::size_t fileSize);
+
+ const QString& path() const;
+ QString displayPath() const;
+
+ std::size_t fileSize() const;
+ const QString& version() const;
+ const QString& versionString() const;
+ QString timestampString() const;
+
+ QString toString() const;
+
+private:
+ struct FileInfo
{
- public:
- explicit Module(QString path, std::size_t fileSize);
+ VS_FIXEDFILEINFO ffi;
+ QString fileDescription;
+ };
- const QString& path() const;
- QString displayPath() const;
+ QString m_path;
+ std::size_t m_fileSize;
+ QString m_version;
+ QDateTime m_timestamp;
+ QString m_versionString;
+ QString m_md5;
- std::size_t fileSize() const;
- const QString& version() const;
- const QString& versionString() const;
- QString timestampString() const;
+ FileInfo getFileInfo() const;
- QString toString() const;
+ QString getVersion(const VS_FIXEDFILEINFO& fi) const;
+ QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
+ QString getMD5() const;
- private:
- struct FileInfo
- {
- VS_FIXEDFILEINFO ffi;
- QString fileDescription;
- };
+ VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
+ QString getFileDescription(std::byte* buffer) const;
+};
- QString m_path;
- std::size_t m_fileSize;
- QString m_version;
- QDateTime m_timestamp;
- QString m_versionString;
- QString m_md5;
- FileInfo getFileInfo() const;
+class WindowsVersion
+{
+public:
+ WindowsVersion();
- QString getVersion(const VS_FIXEDFILEINFO& fi) const;
- QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
- QString getMD5() const;
+ QString toString() const;
+
+private:
+ DWORD m_realMajor, m_realMinor, m_realBuild;
+ DWORD m_major, m_minor, m_build;
+ QString m_buildLab, m_productName, m_releaseID;
+ DWORD m_UBR;
+ std::optional m_elevated;
+
+ void getVersion();
+ void getRealVersion(HINSTANCE ntdll);
+ void getReportedVersion(HINSTANCE ntdll);
+ void getRelease();
+ void getElevated();
+};
- VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
- QString getFileDescription(std::byte* buffer) const;
- };
+class Environment
+{
+public:
Environment();
const std::vector& loadedModules();
+ const WindowsVersion& windowsVersion() const;
private:
std::vector m_modules;
+ WindowsVersion m_windows;
void getLoadedModules();
};
+} // namespace env
+
+
MOBase::VersionInfo createVersionInfo();
} // namespace MOShared
--
cgit v1.3.1
From 813ab32a2d16106f2799fba4e85c768a5b1c4850 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 18:58:15 -0400
Subject: added a warning when running in compatibility mode
---
src/main.cpp | 6 ++++--
src/shared/util.cpp | 14 +++++++++++++-
src/shared/util.h | 1 +
3 files changed, 18 insertions(+), 3 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index 75148c4b..e43ec6d0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -472,12 +472,14 @@ int runApplication(MOApplication &application, SingleInstance &instance,
qInfo().nospace().noquote()
<< "windows: " << env.windowsVersion().toString();
+ if (env.windowsVersion().compatibilityMode()) {
+ qWarning() << "MO seems to be running in compatibility mode";
+ }
+
qInfo() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
qInfo().nospace().noquote() << " . " << m.toString();
}
-
- return 0;
}
QString dataPath = application.property("dataPath").toString();
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 9337ebf6..33094eff 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -653,6 +653,18 @@ WindowsVersion::WindowsVersion() :
getElevated();
}
+bool WindowsVersion::compatibilityMode() const
+{
+ if (m_realMajor == 0 && m_realMinor == 0 && m_realBuild == 0) {
+ return false;
+ }
+
+ return
+ m_realMajor != m_major ||
+ m_realMinor != m_minor ||
+ m_realBuild != m_build;
+}
+
QString WindowsVersion::toString() const
{
QStringList sl;
@@ -665,7 +677,7 @@ QString WindowsVersion::toString() const
sl.push_back("version: " + version);
- if (m_realMajor != m_major || m_realMinor != m_minor || m_realBuild != m_build) {
+ if (compatibilityMode()) {
sl.push_back("real version: " + realVersion);
}
diff --git a/src/shared/util.h b/src/shared/util.h
index deaf6fcc..232a97bb 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -97,6 +97,7 @@ class WindowsVersion
public:
WindowsVersion();
+ bool compatibilityMode() const;
QString toString() const;
private:
--
cgit v1.3.1
From 1aa174f1438d7b1c1a9dab47d69e912726578a06 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 29 Jun 2019 20:02:10 -0400
Subject: comments, some refactoring switched to qDebug()
---
src/main.cpp | 10 +-
src/shared/util.cpp | 259 +++++++++++++++++++++++++++++++++++-----------------
src/shared/util.h | 159 +++++++++++++++++++++++++++++---
3 files changed, 326 insertions(+), 102 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index e43ec6d0..6c3e40be 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -469,16 +469,16 @@ int runApplication(MOApplication &application, SingleInstance &instance,
{
env::Environment env;
- qInfo().nospace().noquote()
- << "windows: " << env.windowsVersion().toString();
+ qDebug().nospace().noquote()
+ << "windows: " << env.windowsInfo().toString();
- if (env.windowsVersion().compatibilityMode()) {
+ if (env.windowsInfo().compatibilityMode()) {
qWarning() << "MO seems to be running in compatibility mode";
}
- qInfo() << "modules loaded in process:";
+ qDebug() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
- qInfo().nospace().noquote() << " . " << m.toString();
+ qDebug().nospace().noquote() << " . " << m.toString();
}
}
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 33094eff..70adb791 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -294,7 +294,7 @@ const std::vector& Environment::loadedModules()
return m_modules;
}
-const WindowsVersion& Environment::windowsVersion() const
+const WindowsInfo& Environment::windowsInfo() const
{
return m_windows;
}
@@ -315,12 +315,10 @@ void Environment::getLoadedModules()
return;
}
- // Set the size of the structure before using it.
MODULEENTRY32 me = {};
me.dwSize = sizeof(me);
- // Retrieve information about the first module,
- // and exit if unsuccessful
+ // first module, this shouldn't fail because there's at least the executable
if (!Module32First(snapshot.get(), &me))
{
const auto e = GetLastError();
@@ -331,27 +329,29 @@ void Environment::getLoadedModules()
return;
}
- // Now walk the module list of the process,
- // and display information about each module
-
for (;;)
{
const auto path = QString::fromWCharArray(me.szExePath);
m_modules.push_back(Module(path, me.modBaseSize));
+ // next module
if (!Module32Next(snapshot.get(), &me)) {
const auto e = GetLastError();
- if (e != ERROR_NO_MORE_FILES) {
- qCritical().nospace().noquote()
- << "Module32Next() failed, " << formatSystemMessage(e);
+ if (e == ERROR_NO_MORE_FILES) {
+ // not an error
+ break;
}
+ qCritical().nospace().noquote()
+ << "Module32Next() failed, " << formatSystemMessage(e);
+
break;
}
}
+ // sorting by display name
std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) {
return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0);
});
@@ -394,6 +394,16 @@ const QString& Module::versionString() const
return m_versionString;
}
+const QDateTime& Module::timestamp() const
+{
+ return m_timestamp;
+}
+
+const QString& Module::md5() const
+{
+ return m_md5;
+}
+
QString Module::timestampString() const
{
if (!m_timestamp.isValid()) {
@@ -407,9 +417,11 @@ QString Module::toString() const
{
QStringList sl;
+ // file size
sl.push_back(displayPath());
sl.push_back(QString("%1 B").arg(m_fileSize));
+ // version
if (m_version.isEmpty() && m_versionString.isEmpty()) {
sl.push_back("(no version)");
} else {
@@ -417,17 +429,19 @@ QString Module::toString() const
sl.push_back(m_version);
}
- if (m_versionString != m_version) {
+ if (!m_versionString.isEmpty() && m_versionString != m_version) {
sl.push_back(versionString());
}
}
+ // timestamp
if (m_timestamp.isValid()) {
sl.push_back(m_timestamp.toString(Qt::DateFormat::ISODate));
} else {
sl.push_back("(no timestamp)");
}
+ // md5
if (!m_md5.isEmpty()) {
sl.push_back(m_md5);
}
@@ -439,6 +453,7 @@ Module::FileInfo Module::getFileInfo() const
{
const auto wspath = m_path.toStdWString();
+ // getting version info size
DWORD dummy = 0;
const DWORD size = GetFileVersionInfoSizeW(wspath.c_str(), &dummy);
@@ -457,6 +472,7 @@ Module::FileInfo Module::getFileInfo() const
return {};
}
+ // getting version info
auto buffer = std::make_unique(size);
if (!GetFileVersionInfoW(wspath.c_str(), 0, size, buffer.get())) {
@@ -469,6 +485,8 @@ Module::FileInfo Module::getFileInfo() const
return {};
}
+ // the version info has two major parts: a fixed version and a localizable
+ // set of strings
FileInfo fi;
fi.ffi = getFixedFileInfo(buffer.get());
@@ -482,6 +500,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
void* valuePointer = nullptr;
unsigned int valueSize = 0;
+ // the fixed version info is in the root
const auto ret = VerQueryValueW(buffer, L"\\", &valuePointer, &valueSize);
if (!ret || !valuePointer || valueSize == 0) {
@@ -491,6 +510,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
const auto* fi = reinterpret_cast(valuePointer);
+ // signature is always 0xfeef04bd
if (fi->dwSignature != 0xfeef04bd) {
qCritical().nospace().noquote()
<< "bad file info signature 0x" << hex << fi->dwSignature << " for "
@@ -513,6 +533,7 @@ QString Module::getFileDescription(std::byte* buffer) const
void* valuePointer = nullptr;
unsigned int valueSize = 0;
+ // getting list of available languages
auto ret = VerQueryValueW(
buffer, L"\\VarFileInfo\\Translation", &valuePointer, &valueSize);
@@ -523,11 +544,13 @@ QString Module::getFileDescription(std::byte* buffer) const
return {};
}
+ // number of languages
const auto count = valueSize / sizeof(LANGANDCODEPAGE);
if (count == 0) {
return {};
}
+ // using the first language in the list to get FileVersion
const auto* lcp = reinterpret_cast(valuePointer);
const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion")
@@ -571,6 +594,10 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
FILETIME ft = {};
if (fi.dwSignature == 0 || (fi.dwFileDateMS == 0 && fi.dwFileDateLS == 0)) {
+ // if the file info is invalid or doesn't have a date, use the creation
+ // time on the file
+
+ // opening the file
std::unique_ptr h(CreateFileW(
m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
@@ -585,6 +612,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
return {};
}
+ // getting the file time
if (!GetFileTime(h.get(), &ft, nullptr, nullptr)) {
const auto e = GetLastError();
qCritical().nospace().noquote()
@@ -594,12 +622,15 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
return {};
}
} else {
+ // use the time from the file info
ft.dwHighDateTime = fi.dwFileDateMS;
ft.dwLowDateTime = fi.dwFileDateLS;
}
+ // converting to SYSTEMTIME
SYSTEMTIME utc = {};
+
if (!FileTimeToSystemTime(&ft, &utc)) {
qCritical().nospace().noquote()
<< "FileTimeToSystemTime() failed on timestamp "
@@ -623,6 +654,7 @@ QString Module::getMD5() const
return {};
}
+ // opening the file
QFile f(m_path);
if (!f.open(QFile::ReadOnly)) {
@@ -632,6 +664,7 @@ QString Module::getMD5() const
return {};
}
+ // hashing
QCryptographicHash hash(QCryptographicHash::Md5);
if (!hash.addData(&f)) {
qCritical().nospace().noquote()
@@ -644,59 +677,97 @@ QString Module::getMD5() const
}
-WindowsVersion::WindowsVersion() :
- m_realMajor(0), m_realMinor(0), m_realBuild(0),
- m_major(0), m_minor(0), m_build(0), m_UBR(0)
+WindowsInfo::WindowsInfo()
{
- getVersion();
- getRelease();
- getElevated();
+ // loading ntdll.dll, the functions will be found with GetProcAddress()
+ std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll"));
+
+ if (!ntdll) {
+ qCritical() << "failed to load ntdll.dll while getting version";
+ return;
+ } else {
+ m_reported = getReportedVersion(ntdll.get());
+ m_real = getRealVersion(ntdll.get());
+ }
+
+ m_release = getRelease();
+ m_elevated = getElevated();
}
-bool WindowsVersion::compatibilityMode() const
+bool WindowsInfo::compatibilityMode() const
{
- if (m_realMajor == 0 && m_realMinor == 0 && m_realBuild == 0) {
+ if (m_real == Version()) {
+ // don't know the real version, can't guess compatibility mode
return false;
}
- return
- m_realMajor != m_major ||
- m_realMinor != m_minor ||
- m_realBuild != m_build;
+ return (m_real != m_reported);
}
-QString WindowsVersion::toString() const
+const WindowsInfo::Version& WindowsInfo::reportedVersion() const
{
- QStringList sl;
+ return m_reported;
+}
+
+const WindowsInfo::Version& WindowsInfo::realVersion() const
+{
+ return m_real;
+}
+
+const WindowsInfo::Release& WindowsInfo::release() const
+{
+ return m_release;
+}
+
+std::optional WindowsInfo::isElevated() const
+{
+ return m_elevated;
+}
- const QString version = QString("%1.%2.%3")
- .arg(m_major).arg(m_minor).arg(m_build);
+QString WindowsInfo::toString() const
+{
+ QStringList sl;
- const QString realVersion = QString("%1.%2.%3")
- .arg(m_realMajor).arg(m_realMinor).arg(m_realBuild);
+ const QString reported = m_reported.toString();
+ const QString real = m_real.toString();
- sl.push_back("version: " + version);
+ // version
+ sl.push_back("version: " + reported);
+ // real version if different
if (compatibilityMode()) {
- sl.push_back("real version: " + realVersion);
+ sl.push_back("real version: " + real);
}
- if (!m_buildLab.isEmpty()) {
- sl.push_back(m_buildLab);
+ // build.UBR, such as 17763.557
+ if (m_release.UBR != 0) {
+ DWORD build = 0;
+
+ if (compatibilityMode()) {
+ build = m_real.build;
+ } else {
+ build = m_reported.build;
+ }
+
+ sl.push_back(QString("%1.%2").arg(build).arg(m_release.UBR));
}
- if (!m_productName.isEmpty()) {
- sl.push_back(m_productName);
+ // release ID
+ if (!m_release.ID.isEmpty()) {
+ sl.push_back("release " + m_release.ID);
}
- if (!m_releaseID.isEmpty()) {
- sl.push_back("build " + m_releaseID);
+ // buildlab string
+ if (!m_release.buildLab.isEmpty()) {
+ sl.push_back(m_release.buildLab);
}
- if (m_UBR != 0) {
- sl.push_back(QString("%1").arg(m_UBR));
+ // product name
+ if (!m_release.productName.isEmpty()) {
+ sl.push_back(m_release.productName);
}
+ // elevated
QString elevated = "?";
if (m_elevated.has_value()) {
elevated = (*m_elevated ? "yes" : "no");
@@ -707,36 +778,14 @@ QString WindowsVersion::toString() const
return sl.join(", ");
}
-void WindowsVersion::getVersion()
-{
- std::unique_ptr ntdll(LoadLibraryW(L"ntdll.dll"));
-
- if (!ntdll) {
- qCritical() << "failed to load ntdll.dll while getting version";
- return;
- }
-
- getRealVersion(ntdll.get());
- getReportedVersion(ntdll.get());
-}
-
-void WindowsVersion::getRealVersion(HINSTANCE ntdll)
+WindowsInfo::Version WindowsInfo::getReportedVersion(HINSTANCE ntdll) const
{
- using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*);
-
- auto* RtlGetNtVersionNumbers = reinterpret_cast(
- GetProcAddress(ntdll, "RtlGetNtVersionNumbers"));
+ // windows has been deprecating pretty much all the functions having to do
+ // with getting version information because apparently, people keep misusing
+ // them for feature detection
+ //
+ // there's still RtlGetVersion() though
- if (RtlGetNtVersionNumbers) {
- DWORD build = 0;
- RtlGetNtVersionNumbers(&m_realMajor, &m_realMinor, &build);
-
- m_realBuild = 0x0fffffff & build;
- }
-}
-
-void WindowsVersion::getReportedVersion(HINSTANCE ntdll)
-{
using RtlGetVersionType = NTSTATUS (NTAPI)(PRTL_OSVERSIONINFOW);
auto* RtlGetVersion = reinterpret_cast(
@@ -744,39 +793,81 @@ void WindowsVersion::getReportedVersion(HINSTANCE ntdll)
if (!RtlGetVersion) {
qCritical() << "RtlGetVersion() not found in ntdll.dll";
- return;
+ return {};
}
OSVERSIONINFOEX vi = {};
vi.dwOSVersionInfoSize = sizeof(vi);
+ // this apparently never fails
RtlGetVersion((RTL_OSVERSIONINFOW*)&vi);
- m_major = vi.dwMajorVersion;
- m_minor = vi.dwMinorVersion;
- m_build = vi.dwBuildNumber;
+ return {vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber};
+}
+
+WindowsInfo::Version WindowsInfo::getRealVersion(HINSTANCE ntdll) const
+{
+ // getting the actual windows version is more difficult because all the
+ // functions are lying when running in compatibility mode
+ //
+ // RtlGetNtVersionNumbers() is an undocumented function that seems to work
+ // fine, but it might not in the future
+
+ using RtlGetNtVersionNumbersType = void (NTAPI)(DWORD*, DWORD*, DWORD*);
+
+ auto* RtlGetNtVersionNumbers = reinterpret_cast(
+ GetProcAddress(ntdll, "RtlGetNtVersionNumbers"));
+
+ if (!RtlGetNtVersionNumbers) {
+ qCritical() << "RtlGetNtVersionNumbers not found in ntdll.dll";
+ return {};
+ }
+
+ DWORD major=0, minor=0, build=0;
+ RtlGetNtVersionNumbers(&major, &minor, &build);
+
+ // for whatever reason, the build number has 0xf0000000 set
+ build = 0x0fffffff & build;
+
+ return {major, minor, build};
}
-void WindowsVersion::getRelease()
+WindowsInfo::Release WindowsInfo::getRelease() const
{
+ // there are several interesting items in the registry, but most of them
+ // are undocumented, not always available, and localizable
+ //
+ // most of them are used to provide as much information as possible in case
+ // any of the other versions fail to work
+
QSettings settings(
R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
QSettings::NativeFormat);
- m_buildLab = settings.value("BuildLabEx", "").toString();
- if (m_buildLab.isEmpty()) {
- m_buildLab = settings.value("BuildLab", "").toString();
- if (m_buildLab.isEmpty()) {
- m_buildLab = settings.value("BuildBranch", "").toString();
+ Release r;
+
+ // buildlab seems to be an internal name from the build system
+ r.buildLab = settings.value("BuildLabEx", "").toString();
+ if (r.buildLab.isEmpty()) {
+ r.buildLab = settings.value("BuildLab", "").toString();
+ if (r.buildLab.isEmpty()) {
+ r.buildLab = settings.value("BuildBranch", "").toString();
}
}
- m_productName = settings.value("ProductName", "").toString();
- m_releaseID = settings.value("ReleaseId", "").toString();
- m_UBR = settings.value("UBR", 0).toUInt();
+ // localized name of windows, such as "Windows 10 Pro"
+ r.productName = settings.value("ProductName", "").toString();
+
+ // release ID, such as 1803
+ r.ID = settings.value("ReleaseId", "").toString();
+
+ // some other build number, shown in winver.exe
+ r.UBR = settings.value("UBR", 0).toUInt();
+
+ return r;
}
-void WindowsVersion::getElevated()
+std::optional WindowsInfo::getElevated() const
{
std::unique_ptr token;
@@ -790,7 +881,7 @@ void WindowsVersion::getElevated()
<< "while trying to check if process is elevated, "
<< "OpenProcessToken() failed: " << formatSystemMessage(e);
- return;
+ return {};
}
token.reset(rawToken);
@@ -806,10 +897,10 @@ void WindowsVersion::getElevated()
<< "while trying to check if process is elevated, "
<< "GetTokenInformation() failed: " << formatSystemMessage(e);
- return;
+ return {};
}
- m_elevated = (e.TokenIsElevated != 0);
+ return (e.TokenIsElevated != 0);
}
} // namespace env
diff --git a/src/shared/util.h b/src/shared/util.h
index 232a97bb..3ec677f4 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -52,22 +52,55 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
namespace env
{
+// represents one module
+//
class Module
{
public:
explicit Module(QString path, std::size_t fileSize);
+ // returns the module's path
+ //
const QString& path() const;
+
+ // returns the module's path in lowercase and using forward slashes
+ //
QString displayPath() const;
+ // returns the size in bytes, may be 0
+ //
std::size_t fileSize() const;
+
+ // returns the x.x.x.x version embedded from the version info, may be empty
+ //
const QString& version() const;
+
+ // returns the FileVersion entry from the resource file, returns
+ // "(no version)" if not available
+ //
const QString& versionString() const;
+
+ // returns the build date from the version info, or the creation time of the
+ // file on the filesystem, may be empty
+ //
+ const QDateTime& timestamp() const;
+
+ // returns the md5 of the file, may be empty for system files
+ //
+ const QString& md5() const;
+
+ // converts timestamp() to a string for display, returns "(no timestamp)" if
+ // not available
+ //
QString timestampString() const;
+ // returns a string with all the above information on one line
+ //
QString toString() const;
private:
+ // contains the information from the version resource
+ //
struct FileInfo
{
VS_FIXEDFILEINFO ffi;
@@ -81,51 +114,151 @@ private:
QString m_versionString;
QString m_md5;
+ // returns information from the version resource
+ //
FileInfo getFileInfo() const;
+ // uses VS_FIXEDFILEINFO to build the version string
+ //
QString getVersion(const VS_FIXEDFILEINFO& fi) const;
+
+ // uses the file date from VS_FIXEDFILEINFO if available, or gets the
+ // creation date on the file
+ //
QDateTime getTimestamp(const VS_FIXEDFILEINFO& fi) const;
+
+ // returns the md5 hash unless the path contains "\windows\"
+ //
QString getMD5() const;
+ // gets VS_FIXEDFILEINFO from the file version info buffer
+ //
VS_FIXEDFILEINFO getFixedFileInfo(std::byte* buffer) const;
+
+ // gets FileVersion from the file version info buffer
+ //
QString getFileDescription(std::byte* buffer) const;
};
-class WindowsVersion
+// a variety of information on windows
+//
+class WindowsInfo
{
public:
- WindowsVersion();
+ struct Version
+ {
+ DWORD major=0, minor=0, build=0;
+
+ QString toString() const
+ {
+ return QString("%1.%2.%3").arg(major).arg(minor).arg(build);
+ }
+
+ friend bool operator==(const Version& a, const Version& b)
+ {
+ return
+ a.major == b.major &&
+ a.minor == b.minor &&
+ a.build == b.build;
+ }
+
+ friend bool operator!=(const Version& a, const Version& b)
+ {
+ return !(a == b);
+ }
+ };
+
+ struct Release
+ {
+ // the BuildLab entry from the registry, may be empty
+ QString buildLab;
+
+ // product name such as "Windows 10 Pro", may not be in English, may be
+ // empty
+ QString productName;
+
+ // release ID such as 1809, may be mepty
+ QString ID;
+ // some sub-build number, undocumented, may be empty
+ DWORD UBR;
+
+ Release()
+ : UBR(0)
+ {
+ }
+ };
+
+
+ WindowsInfo();
+
+ // tries to guess whether this process is running in compatibility mode
+ //
bool compatibilityMode() const;
+
+ // returns the Windows version, may not correspond to the actual version
+ // if the process is running in compatibility mode
+ //
+ const Version& reportedVersion() const;
+
+ // tries to guess the real Windows version that's running, can be empty
+ //
+ const Version& realVersion() const;
+
+ // various information about the current release
+ //
+ const Release& release() const;
+
+ // whether this process is running as administrator, may be empty if the
+ // information is not available
+ std::optional isElevated() const;
+
+ // returns a string with all the above information on one line
+ //
QString toString() const;
private:
- DWORD m_realMajor, m_realMinor, m_realBuild;
- DWORD m_major, m_minor, m_build;
- QString m_buildLab, m_productName, m_releaseID;
- DWORD m_UBR;
+ Version m_reported, m_real;
+ Release m_release;
std::optional m_elevated;
- void getVersion();
- void getRealVersion(HINSTANCE ntdll);
- void getReportedVersion(HINSTANCE ntdll);
- void getRelease();
- void getElevated();
+ // uses RtlGetVersion() to get the version number as reported by Windows
+ //
+ Version getReportedVersion(HINSTANCE ntdll) const;
+
+ // uses RtlGetNtVersionNumbers() to get the real version number
+ //
+ Version getRealVersion(HINSTANCE ntdll) const;
+
+ // gets various information from the registry
+ //
+ Release getRelease() const;
+
+ // gets whether the process is elevated
+ //
+ std::optional getElevated() const;
};
+// represents the process's environment
+//
class Environment
{
public:
Environment();
+ // list of loaded modules in the current process
+ //
const std::vector& loadedModules();
- const WindowsVersion& windowsVersion() const;
+
+ // information about the operating system
+ //
+ const WindowsInfo& windowsInfo() const;
private:
std::vector m_modules;
- WindowsVersion m_windows;
+ WindowsInfo m_windows;
void getLoadedModules();
};
--
cgit v1.3.1
From 5a74b02442302fb484b1db68cf0ce1736af4911c Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 1 Jul 2019 03:41:32 -0400
Subject: security products
---
src/main.cpp | 7 ++
src/shared/util.cpp | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++-
src/shared/util.h | 21 ++++
3 files changed, 308 insertions(+), 3 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index 6c3e40be..518d31a0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -476,10 +476,17 @@ int runApplication(MOApplication &application, SingleInstance &instance,
qWarning() << "MO seems to be running in compatibility mode";
}
+ qDebug().nospace().noquote() << "security features:";
+ for (const auto& sf : env.securityFeatures()) {
+ qDebug().nospace().noquote() << " . " << sf.toString();
+ }
+
qDebug() << "modules loaded in process:";
for (const auto& m : env.loadedModules()) {
qDebug().nospace().noquote() << " . " << m.toString();
}
+
+ return 0;
}
QString dataPath = application.property("dataPath").toString();
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 70adb791..aa2e8d0f 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -30,6 +30,11 @@ along with Mod Organizer. If not, see .
#include
#include
+#include
+#include
+#include
+#pragma comment(lib, "Wbemuuid.lib")
+
using MOBase::formatSystemMessage;
namespace MOShared {
@@ -283,10 +288,153 @@ struct LibraryFreer
}
};
+struct COMReleaser
+{
+ void operator()(IUnknown* p)
+ {
+ if (p) {
+ p->Release();
+ }
+ }
+};
+
+
+class WMI
+{
+public:
+ class failed {};
+
+ WMI(const std::string& ns)
+ {
+ try
+ {
+ createLocator();
+ createService(ns);
+ setSecurity();
+ }
+ catch(failed&)
+ {
+ }
+ }
+
+ template
+ void query(const std::string& q, F&& f)
+ {
+ if (!m_locator || !m_service) {
+ return;
+ }
+
+ auto enumerator = getEnumerator(q);
+
+ for (;;)
+ {
+ std::unique_ptr object;
+
+ {
+ IWbemClassObject* rawObject = nullptr;
+ ULONG count = 0;
+ auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count);
+
+ if (count == 0) {
+ break;
+ }
+
+ object.reset(rawObject);
+ }
+
+ f(object.get());
+ }
+ }
+
+ std::unique_ptr getEnumerator(
+ const std::string& query)
+ {
+ IEnumWbemClassObject* rawEnumerator = NULL;
+
+ auto ret = m_service->ExecQuery(
+ bstr_t("WQL"),
+ bstr_t(query.c_str()),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL,
+ &rawEnumerator);
+
+ if (FAILED(ret))
+ {
+ qCritical()
+ << "query '" << QString::fromStdString(query) << "' failed, "
+ << formatSystemMessage(ret);
+
+ return {};
+ }
+
+ return std::unique_ptr(rawEnumerator);
+ }
+
+private:
+ std::unique_ptr m_locator;
+ std::unique_ptr m_service;
+
+ void createLocator()
+ {
+ void* rawLocator = nullptr;
+
+ const auto ret = CoCreateInstance(
+ CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator, &rawLocator);
+
+ if (FAILED(ret)) {
+ qCritical()
+ << "CoCreateInstance for WbemLocator failed, "
+ << formatSystemMessage(ret);
+
+ throw failed();
+ }
+
+ m_locator.reset(static_cast(rawLocator));
+ }
+
+ void createService(const std::string& ns)
+ {
+ IWbemServices* rawService = nullptr;
+
+ const auto res = m_locator->ConnectServer(
+ _bstr_t(ns.c_str()),
+ nullptr, nullptr, nullptr, 0, nullptr, nullptr,
+ &rawService);
+
+ if (FAILED(res)) {
+ qCritical()
+ << "locator->ConnectServer() failed for namespace "
+ << "'" << QString::fromStdString(ns) << "', "
+ << formatSystemMessage(res);
+
+ throw failed();
+ }
+
+ m_service.reset(rawService);
+ }
+
+ void setSecurity()
+ {
+ auto ret = CoSetProxyBlanket(
+ m_service.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
+ RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE);
+
+ if (FAILED(ret))
+ {
+ qCritical()
+ << "CoSetProxyBlanket() failed, " << formatSystemMessage(ret);
+
+ throw failed();
+ }
+ }
+};
+
Environment::Environment()
{
getLoadedModules();
+ getSecurityFeatures();
}
const std::vector& Environment::loadedModules()
@@ -299,6 +447,11 @@ const WindowsInfo& Environment::windowsInfo() const
return m_windows;
}
+const std::vector& Environment::securityFeatures() const
+{
+ return m_security;
+}
+
void Environment::getLoadedModules()
{
std::unique_ptr snapshot(CreateToolhelp32Snapshot(
@@ -357,6 +510,59 @@ void Environment::getLoadedModules()
});
}
+void Environment::getSecurityFeatures()
+{
+ WMI wmi("root\\SecurityCenter2");
+ std::map map;
+
+ auto handleProduct = [&](auto* o) {
+ VARIANT prop;
+
+ auto ret = o->Get(L"displayName", 0, &prop, 0, 0);
+ if (FAILED(ret)) {
+ qCritical() << "failed to get displayName, " << formatSystemMessage(ret);
+ return;
+ }
+
+ const std::wstring name = prop.bstrVal;
+ VariantClear(&prop);
+
+ ret = o->Get(L"productState", 0, &prop, 0, 0);
+ if (FAILED(ret)) {
+ qCritical() << "failed to get productState, " << formatSystemMessage(ret);
+ return;
+ }
+
+ const DWORD state = prop.ulVal;
+ VariantClear(&prop);
+
+ ret = o->Get(L"instanceGuid", 0, &prop, 0, 0);
+ if (FAILED(ret)) {
+ qCritical() << "failed to get instanceGuid, " << formatSystemMessage(ret);
+ return;
+ }
+
+ const QUuid guid(QString::fromWCharArray(prop.bstrVal));
+ VariantClear(&prop);
+
+
+ auto itor = map.find(guid);
+
+ if (itor == map.end()) {
+ map.insert({
+ guid, SecurityFeature(QString::fromStdWString(name), state)});
+ }
+ };
+
+ wmi.query("select * from AntivirusProduct", handleProduct);
+ wmi.query("select * from FirewallProduct", handleProduct);
+ wmi.query("select * from AntiSpywareProduct", handleProduct);
+
+ for (auto&& p : map) {
+ m_security.push_back(p.second);
+ }
+}
+
Module::Module(QString path, std::size_t fileSize)
: m_path(std::move(path)), m_fileSize(fileSize)
@@ -508,7 +714,7 @@ VS_FIXEDFILEINFO Module::getFixedFileInfo(std::byte* buffer) const
return {};
}
- const auto* fi = reinterpret_cast(valuePointer);
+ const auto* fi = static_cast(valuePointer);
// signature is always 0xfeef04bd
if (fi->dwSignature != 0xfeef04bd) {
@@ -551,7 +757,7 @@ QString Module::getFileDescription(std::byte* buffer) const
}
// using the first language in the list to get FileVersion
- const auto* lcp = reinterpret_cast(valuePointer);
+ const auto* lcp = static_cast(valuePointer);
const auto subBlock = QString("\\StringFileInfo\\%1%2\\FileVersion")
.arg(lcp->wLanguage, 4, 16, QChar('0'))
@@ -567,7 +773,7 @@ QString Module::getFileDescription(std::byte* buffer) const
// valueSize includes the null terminator
return QString::fromWCharArray(
- reinterpret_cast(valuePointer), valueSize - 1);
+ static_cast(valuePointer), valueSize - 1);
}
QString Module::getVersion(const VS_FIXEDFILEINFO& fi) const
@@ -903,6 +1109,77 @@ std::optional WindowsInfo::getElevated() const
return (e.TokenIsElevated != 0);
}
+
+SecurityFeature::SecurityFeature(QString name, DWORD state)
+ : m_name(std::move(name)), m_state(state)
+{
+}
+
+const QString& SecurityFeature::name() const
+{
+ return m_name;
+}
+
+QString SecurityFeature::toString() const
+{
+ QString s;
+
+ s += m_name + " ";
+
+ const auto provider = (m_state >> 16) & 0xff;
+ const auto scanner = (m_state >> 8) & 0xff;
+ const auto definitions = m_state & 0xff;
+
+ QStringList ps;
+ if (provider & WSC_SECURITY_PROVIDER_FIREWALL) {
+ ps.push_back("firewall");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
+ ps.push_back("autoupdate");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
+ ps.push_back("antivirus");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
+ ps.push_back("antispyware");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
+ ps.push_back("settings");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
+ ps.push_back("uac");
+ }
+
+ if (provider & WSC_SECURITY_PROVIDER_SERVICE) {
+ ps.push_back("service");
+ }
+
+ if (ps.empty()) {
+ s += "(doesn't provide anything)";
+ } else {
+ s += "(" + ps.join("|") + ")";
+ }
+
+ if (scanner & 0x10) {
+ s += ", active";
+ } else {
+ s += ", inactive";
+ }
+
+ if (definitions == 0) {
+ s += ", definitions up to date";
+ } else {
+ s += ", definitions outdated";
+ }
+
+ return s;
+}
+
} // namespace env
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 3ec677f4..6b842f8c 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -241,6 +241,21 @@ private:
};
+class SecurityFeature
+{
+public:
+ SecurityFeature(QString name, DWORD state);
+
+ const QString& name() const;
+
+ QString toString() const;
+
+private:
+ QString m_name;
+ DWORD m_state;
+};
+
+
// represents the process's environment
//
class Environment
@@ -256,11 +271,17 @@ public:
//
const WindowsInfo& windowsInfo() const;
+ // information about the installed antivirus
+ //
+ const std::vector& securityFeatures() const;
+
private:
std::vector m_modules;
WindowsInfo m_windows;
+ std::vector m_security;
void getLoadedModules();
+ void getSecurityFeatures();
};
} // namespace env
--
cgit v1.3.1
From dfe8093c1ad16e1611c12e88d52d1ac38371d3f6 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 1 Jul 2019 08:42:58 -0400
Subject: added --crashdump to generate dumps of a running MO process added
dump_running_process.bat to start another instance of MO with that flag
---
CMakeLists.txt | 2 +
dump_running_process.bat | 2 +
src/main.cpp | 45 +++++-
src/settings.cpp | 10 +-
src/shared/util.cpp | 392 ++++++++++++++++++++++++++++++++++++++++++++---
src/shared/util.h | 17 ++
6 files changed, 443 insertions(+), 25 deletions(-)
create mode 100644 dump_running_process.bat
(limited to 'src/shared/util.cpp')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ced097e1..94c76373 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,3 +29,5 @@ if(NOT EXISTS ${vcxproj_user_file})
"\n"
"\n")
endif()
+
+INSTALL(FILES dump_running_process.bat DESTINATION bin)
diff --git a/dump_running_process.bat b/dump_running_process.bat
new file mode 100644
index 00000000..4697fe5e
--- /dev/null
+++ b/dump_running_process.bat
@@ -0,0 +1,2 @@
+pushd "%~dp0"
+start ModOrganizer.exe --crashdump
diff --git a/src/main.cpp b/src/main.cpp
index 518d31a0..f74a9cf4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -485,8 +485,6 @@ int runApplication(MOApplication &application, SingleInstance &instance,
for (const auto& m : env.loadedModules()) {
qDebug().nospace().noquote() << " . " << m.toString();
}
-
- return 0;
}
QString dataPath = application.property("dataPath").toString();
@@ -682,9 +680,52 @@ int runApplication(MOApplication &application, SingleInstance &instance,
}
}
+int doCoreDump(env::CoreDumpTypes type)
+{
+ // open a console
+ AllocConsole();
+
+ // redirect stdin, stdout and stderr to it
+ FILE* in=nullptr;
+ FILE* out=nullptr;
+ FILE* err=nullptr;
+ freopen_s(&in, "CONIN$", "r", stdin);
+ freopen_s(&out, "CONOUT$", "w", stdout);
+ freopen_s(&err, "CONOUT$", "w", stderr);
+
+ // dump
+ const auto b = env::coredumpOther(type);
+ if (!b) {
+ std::wcerr << L"\n>>>> a minidump file was not written\n\n";
+ }
+
+ std::wcerr << L"Press enter to continue...";
+ std::wcin.get();
+
+ // close redirected handles
+ std::fclose(err);
+ std::fclose(out);
+ std::fclose(in);
+
+ // close console
+ FreeConsole();
+
+ return (b ? 0 : 1);
+}
int main(int argc, char *argv[])
{
+ // handle --crashdump first
+ for (int i=1; i.
#include
#include
#include
-#include
#include
+#include
+
+#include
#include
#include
@@ -36,6 +38,8 @@ along with Mod Organizer. If not, see .
#pragma comment(lib, "Wbemuuid.lib")
using MOBase::formatSystemMessage;
+using MOBase::formatSystemMessageQ;
+namespace fs = std::filesystem;
namespace MOShared {
@@ -276,6 +280,9 @@ struct HandleCloser
}
};
+using HandlePtr = std::unique_ptr;
+
+
struct LibraryFreer
{
using pointer = HINSTANCE;
@@ -362,7 +369,7 @@ public:
{
qCritical()
<< "query '" << QString::fromStdString(query) << "' failed, "
- << formatSystemMessage(ret);
+ << formatSystemMessageQ(ret);
return {};
}
@@ -385,7 +392,7 @@ private:
if (FAILED(ret)) {
qCritical()
<< "CoCreateInstance for WbemLocator failed, "
- << formatSystemMessage(ret);
+ << formatSystemMessageQ(ret);
throw failed();
}
@@ -406,7 +413,7 @@ private:
qCritical()
<< "locator->ConnectServer() failed for namespace "
<< "'" << QString::fromStdString(ns) << "', "
- << formatSystemMessage(res);
+ << formatSystemMessageQ(res);
throw failed();
}
@@ -423,7 +430,7 @@ private:
if (FAILED(ret))
{
qCritical()
- << "CoSetProxyBlanket() failed, " << formatSystemMessage(ret);
+ << "CoSetProxyBlanket() failed, " << formatSystemMessageQ(ret);
throw failed();
}
@@ -454,7 +461,7 @@ const std::vector& Environment::securityFeatures() const
void Environment::getLoadedModules()
{
- std::unique_ptr snapshot(CreateToolhelp32Snapshot(
+ HandlePtr snapshot(CreateToolhelp32Snapshot(
TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
if (snapshot.get() == INVALID_HANDLE_VALUE)
@@ -463,7 +470,7 @@ void Environment::getLoadedModules()
qCritical().nospace().noquote()
<< "CreateToolhelp32Snapshot() failed, "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return;
}
@@ -477,7 +484,7 @@ void Environment::getLoadedModules()
const auto e = GetLastError();
qCritical().nospace().noquote()
- << "Module32First() failed, " << formatSystemMessage(e);
+ << "Module32First() failed, " << formatSystemMessageQ(e);
return;
}
@@ -498,7 +505,7 @@ void Environment::getLoadedModules()
}
qCritical().nospace().noquote()
- << "Module32Next() failed, " << formatSystemMessage(e);
+ << "Module32Next() failed, " << formatSystemMessageQ(e);
break;
}
@@ -520,7 +527,7 @@ void Environment::getSecurityFeatures()
auto ret = o->Get(L"displayName", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get displayName, " << formatSystemMessage(ret);
+ qCritical() << "failed to get displayName, " << formatSystemMessageQ(ret);
return;
}
@@ -529,7 +536,10 @@ void Environment::getSecurityFeatures()
ret = o->Get(L"productState", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get productState, " << formatSystemMessage(ret);
+ qCritical()
+ << "failed to get productState, "
+ << formatSystemMessageQ(ret);
+
return;
}
@@ -538,7 +548,10 @@ void Environment::getSecurityFeatures()
ret = o->Get(L"instanceGuid", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get instanceGuid, " << formatSystemMessage(ret);
+ qCritical()
+ << "failed to get instanceGuid, "
+ << formatSystemMessageQ(ret);
+
return;
}
@@ -673,7 +686,7 @@ Module::FileInfo Module::getFileInfo() const
qCritical().nospace().noquote()
<< "GetFileVersionInfoSizeW() failed on '" << m_path << "', "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -686,7 +699,7 @@ Module::FileInfo Module::getFileInfo() const
qCritical().nospace().noquote()
<< "GetFileVersionInfoW() failed on '" << m_path << "', "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -804,7 +817,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
// time on the file
// opening the file
- std::unique_ptr h(CreateFileW(
+ HandlePtr h(CreateFileW(
m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
@@ -813,7 +826,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
qCritical().nospace().noquote()
<< "can't open file '" << m_path << "' for timestamp, "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -823,7 +836,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
const auto e = GetLastError();
qCritical().nospace().noquote()
<< "can't get file time for '" << m_path << "', "
- << formatSystemMessage(e);
+ << formatSystemMessageQ(e);
return {};
}
@@ -1075,7 +1088,7 @@ WindowsInfo::Release WindowsInfo::getRelease() const
std::optional WindowsInfo::getElevated() const
{
- std::unique_ptr token;
+ HandlePtr token;
{
HANDLE rawToken = 0;
@@ -1085,7 +1098,7 @@ std::optional WindowsInfo::getElevated() const
qCritical()
<< "while trying to check if process is elevated, "
- << "OpenProcessToken() failed: " << formatSystemMessage(e);
+ << "OpenProcessToken() failed: " << formatSystemMessageQ(e);
return {};
}
@@ -1101,7 +1114,7 @@ std::optional WindowsInfo::getElevated() const
qCritical()
<< "while trying to check if process is elevated, "
- << "GetTokenInformation() failed: " << formatSystemMessage(e);
+ << "GetTokenInformation() failed: " << formatSystemMessageQ(e);
return {};
}
@@ -1180,6 +1193,345 @@ QString SecurityFeature::toString() const
return s;
}
+
+struct Process
+{
+ std::wstring filename;
+ DWORD pid;
+
+ Process(std::wstring f, DWORD id)
+ : filename(std::move(f)), pid(id)
+ {
+ }
+};
+
+std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
+{
+ DWORD bufferSize = MAX_PATH;
+
+ for (int tries=0; tries<10; ++tries)
+ {
+ auto buffer = std::make_unique(bufferSize + 1);
+ std::fill(buffer.get(), buffer.get() + bufferSize + 1, 0);
+
+ DWORD writtenSize = 0;
+
+ if (process == INVALID_HANDLE_VALUE) {
+ // query this process
+ writtenSize = GetModuleFileNameW(0, buffer.get(), bufferSize);
+ } else {
+ // query another process
+ writtenSize = GetModuleBaseNameW(process, 0, buffer.get(), bufferSize);
+ }
+
+ if (writtenSize == 0) {
+ const auto e = GetLastError();
+ std::wcerr << formatSystemMessage(e) << L"\n";
+ break;
+ } else if (writtenSize >= bufferSize) {
+ // buffer is too small, try again
+ bufferSize *= 2;
+ } else {
+ // if GetModuleFileName() works, `writtenSize` does not include the null
+ // terminator
+ const std::wstring s(buffer.get(), writtenSize);
+ const fs::path path(s);
+
+ return path.filename().native();
+ }
+ }
+
+
+ std::wstring what;
+ if (process == INVALID_HANDLE_VALUE) {
+ what = L"the current process";
+ } else {
+ what = L"pid " + std::to_wstring(reinterpret_cast(process));
+ }
+
+ std::wcerr << L"failed to get filename for " << what << L"\n";
+ return {};
+}
+
+std::vector runningProcessesIds()
+{
+ // initial size of 300 processes, unlikely to be more than that
+ std::size_t size = 300;
+
+ for (int tries=0; tries<10; ++tries) {
+ auto ids = std::make_unique(size);
+ std::fill(ids.get(), ids.get() + size, 0);
+
+ DWORD bytesGiven = static_cast(size * sizeof(ids[0]));
+ DWORD bytesWritten = 0;
+
+ if (!EnumProcesses(ids.get(), bytesGiven, &bytesWritten))
+ {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to enumerate processes, "
+ << formatSystemMessage(e) << L"\n";
+
+ return {};
+ }
+
+ if (bytesWritten == bytesGiven) {
+ size *= 2;
+ continue;
+ }
+
+ const auto count = bytesWritten / sizeof(ids[0]);
+ return std::vector(ids.get(), ids.get() + count);
+ }
+
+ std::cerr << L"too many processes to enumerate";
+ return {};
+}
+
+std::vector runningProcesses()
+{
+ const auto pids = runningProcessesIds();
+ std::vector v;
+
+ for (const auto& pid : pids) {
+ if (pid == 0) {
+ // the idle process seems to be picked up by EnumProcesses()
+ continue;
+ }
+
+ HandlePtr h(OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
+
+ if (!h) {
+ const auto e = GetLastError();
+
+ if (e != ERROR_ACCESS_DENIED) {
+ // don't log access denied, will happen a lot when not elevated
+ std::wcerr
+ << L"failed to open process " << pid << L", "
+ << formatSystemMessage(e) << L"\n";
+ }
+
+ continue;
+ }
+
+ auto filename = processFilename(h.get());
+ if (!filename.empty()) {
+ v.emplace_back(std::move(filename), pid);
+ }
+ }
+
+ return v;
+}
+
+DWORD findOtherPid()
+{
+ const std::wstring defaultName = L"ModOrganizer.exe";
+
+ std::wclog << L"looking for the other process...\n";
+
+ const auto thisPid = GetCurrentProcessId();
+ std::wclog << L"this process id is " << thisPid << L"\n";
+
+ auto filename = processFilename();
+ if (filename.empty()) {
+ std::wcerr
+ << L"can't get current process filename, defaulting to "
+ << defaultName << L"\n";
+
+ filename = defaultName;
+ } else {
+ std::wclog << L"this process filename is " << filename << L"\n";
+ }
+
+ const auto processes = runningProcesses();
+ std::wclog << L"there are " << processes.size() << L" processes running\n";
+
+ for (const auto& p : processes) {
+ if (p.filename == filename) {
+ if (p.pid != thisPid) {
+ return p.pid;
+ }
+ }
+ }
+
+ std::wclog
+ << L"no process with this filename\n"
+ << L"MO may not be running, or it may be running as administrator\n"
+ << L"you can try running this again as administrator\n";
+
+ return 0;
+}
+
+
+std::wstring tempDir()
+{
+ const DWORD bufferSize = MAX_PATH + 1;
+ wchar_t buffer[bufferSize + 1] = {};
+
+ const auto written = GetTempPathW(bufferSize, buffer);
+ if (written == 0) {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to get temp path, " << formatSystemMessage(e) << L"\n";
+
+ return {};
+ }
+
+ // `written` does not include the null terminator
+ return std::wstring(buffer, buffer + written);
+}
+
+HandlePtr tempFile(const std::wstring dir)
+{
+ const auto now = std::time(0);
+ const auto tm = std::gmtime(&now);
+
+ std::wostringstream oss;
+ oss
+ << L"ModOrganizer-"
+ << std::setw(4) << (1900 + tm->tm_year)
+ << std::setw(2) << std::setfill(L'0') << (tm->tm_mon + 1)
+ << std::setw(2) << std::setfill(L'0') << tm->tm_mday << "T"
+ << std::setw(2) << std::setfill(L'0') << tm->tm_hour
+ << std::setw(2) << std::setfill(L'0') << tm->tm_min
+ << std::setw(2) << std::setfill(L'0') << tm->tm_sec;
+
+ const std::wstring prefix = oss.str();
+ const std::wstring ext = L".dmp";
+
+ std::wstring path = dir + L"\\" + prefix + ext;
+ for (int i=0; i<100; ++i) {
+ std::wclog << L"trying file '" << path << L"'\n";
+
+ HandlePtr h (CreateFileW(
+ path.c_str(), GENERIC_WRITE, 0, nullptr,
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr));
+
+ if (h.get() != INVALID_HANDLE_VALUE) {
+ return h;
+ }
+
+ const auto e = GetLastError();
+ if (e != ERROR_FILE_EXISTS) {
+ // probably no write access
+ std::wcerr
+ << L"failed to create dump file, " << formatSystemMessage(e) << L"\n";
+
+ return {};
+ }
+
+ path = dir + L"\\" + prefix + L"-" + std::to_wstring(i + 1) + ext;
+ }
+
+ std::wcerr << L"can't create dump file, ran out of filenames\n";
+ return {};
+}
+
+HandlePtr dumpFile()
+{
+ // try the current directory
+ HandlePtr h = tempFile(L".");
+ if (h.get() != INVALID_HANDLE_VALUE) {
+ return h;
+ }
+
+ std::wclog << L"cannot write dump file in current directory\n";
+
+ // try the temp directory
+ const auto dir = tempDir();
+
+ if (dir.empty()) {
+ std::wclog << L"can't get the temp directory\n";
+ } else {
+ h = tempFile(dir.c_str());
+ if (h.get() != INVALID_HANDLE_VALUE) {
+ return h;
+ }
+ }
+
+ std::wcerr << L"nowhere to write the dump file\n";
+ return {};
+}
+
+bool createMiniDump(HANDLE process, CoreDumpTypes type)
+{
+ const DWORD pid = GetProcessId(process);
+
+ const HandlePtr file = dumpFile();
+ if (!file) {
+ return false;
+ }
+
+ auto flags = _MINIDUMP_TYPE(
+ MiniDumpNormal |
+ MiniDumpWithHandleData |
+ MiniDumpWithUnloadedModules |
+ MiniDumpWithProcessThreadData);
+
+ if (type == CoreDumpTypes::Data) {
+ std::wclog << L"writing minidump with data\n";
+ flags = _MINIDUMP_TYPE(flags | MiniDumpWithDataSegs);
+ } else if (type == CoreDumpTypes::Full) {
+ std::wclog << L"writing full minidump\n";
+ flags = _MINIDUMP_TYPE(flags | MiniDumpWithFullMemory);
+ } else {
+ std::wclog << L"writing mini minidump\n";
+ }
+
+ const auto ret = MiniDumpWriteDump(
+ process, pid, file.get(), flags, nullptr, nullptr, nullptr);
+
+ if (!ret) {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to write mini dump, " << formatSystemMessage(e) << L"\n";
+
+ return false;
+ }
+
+ std::wclog << L"minidump written correctly\n";
+ return true;
+}
+
+
+bool coredump(CoreDumpTypes type)
+{
+ std::wclog << L"creating minidump for the current process\n";
+ return createMiniDump(GetCurrentProcess(), type);
+}
+
+bool coredumpOther(CoreDumpTypes type)
+{
+ std::wclog << L"creating minidump for an running process\n";
+
+ const auto pid = findOtherPid();
+ if (pid == 0) {
+ std::wcerr << L"no other process found\n";
+ return false;
+ }
+
+ std::wclog << L"found other process with pid " << pid << L"\n";
+
+ HandlePtr handle(OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
+
+ if (!handle) {
+ const auto e = GetLastError();
+
+ std::wcerr
+ << L"failed to open process " << pid << L", "
+ << formatSystemMessage(e) << L"\n";
+
+ return false;
+ }
+
+ return createMiniDump(handle.get(), type);
+}
+
} // namespace env
} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 6b842f8c..2296651c 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -284,6 +284,23 @@ private:
void getSecurityFeatures();
};
+
+enum class CoreDumpTypes
+{
+ Mini = 1,
+ Data,
+ Full
+};
+
+// creates a minidump file for the given process
+//
+bool coredump(CoreDumpTypes type);
+
+// finds another process with the same name as this one and creates a minidump
+// file for it
+//
+bool coredumpOther(CoreDumpTypes type);
+
} // namespace env
--
cgit v1.3.1
From 5e27a96a27a351701182d493c64de698aaac4ae8 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 1 Jul 2019 08:55:24 -0400
Subject: a few comments
---
src/shared/util.cpp | 47 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 37 insertions(+), 10 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index b14a02b1..36800d5b 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -1205,11 +1205,16 @@ struct Process
}
};
+// 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<10; ++tries)
+ for (int tries=0; tries(bufferSize + 1);
std::fill(buffer.get(), buffer.get() + bufferSize + 1, 0);
@@ -1225,6 +1230,7 @@ std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
}
if (writtenSize == 0) {
+ // hard failure
const auto e = GetLastError();
std::wcerr << formatSystemMessage(e) << L"\n";
break;
@@ -1241,6 +1247,7 @@ std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
}
}
+ // something failed or the path is way too long to make sense
std::wstring what;
if (process == INVALID_HANDLE_VALUE) {
@@ -1255,10 +1262,13 @@ std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
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<10; ++tries) {
+ for (int tries=0; tries(size);
std::fill(ids.get(), ids.get() + size, 0);
@@ -1277,6 +1287,8 @@ std::vector runningProcessesIds()
}
if (bytesWritten == bytesGiven) {
+ // no way to distinguish between an exact fit and not enough space,
+ // just try again
size *= 2;
continue;
}
@@ -1296,7 +1308,7 @@ std::vector runningProcesses()
for (const auto& pid : pids) {
if (pid == 0) {
- // the idle process seems to be picked up by EnumProcesses()
+ // the idle process has pid 0 and seems to be picked up by EnumProcesses()
continue;
}
@@ -1307,7 +1319,8 @@ std::vector runningProcesses()
const auto e = GetLastError();
if (e != ERROR_ACCESS_DENIED) {
- // don't log access denied, will happen a lot when not elevated
+ // 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";
@@ -1331,9 +1344,12 @@ DWORD findOtherPid()
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
@@ -1345,9 +1361,12 @@ DWORD findOtherPid()
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) {
@@ -1364,7 +1383,6 @@ DWORD findOtherPid()
return 0;
}
-
std::wstring tempDir()
{
const DWORD bufferSize = MAX_PATH + 1;
@@ -1386,9 +1404,15 @@ std::wstring tempDir()
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-"
@@ -1402,8 +1426,10 @@ HandlePtr tempFile(const std::wstring dir)
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<100; ++i) {
+
+ for (int i=0; i
Date: Mon, 1 Jul 2019 12:38:35 -0400
Subject: now handles windows firewall, which apparently doens't report itself
to wmi some more error checking
---
src/main.cpp | 6 +-
src/shared/util.cpp | 294 ++++++++++++++++++++++++++++++++++++++--------------
src/shared/util.h | 26 +++--
3 files changed, 239 insertions(+), 87 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/main.cpp b/src/main.cpp
index f08e5ad3..c6c87a64 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -493,9 +493,9 @@ int runApplication(MOApplication &application, SingleInstance &instance,
qWarning() << "MO seems to be running in compatibility mode";
}
- qDebug().nospace().noquote() << "security features:";
- for (const auto& sf : env.securityFeatures()) {
- qDebug().nospace().noquote() << " . " << sf.toString();
+ qDebug().nospace().noquote() << "security products:";
+ for (const auto& sp : env.securityProducts()) {
+ qDebug().nospace().noquote() << " . " << sp.toString();
}
qDebug() << "modules loaded in process:";
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 36800d5b..072cee2d 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -35,6 +35,8 @@ along with Mod Organizer. If not, see .
#include
#include
#include
+#include
+
#pragma comment(lib, "Wbemuuid.lib")
using MOBase::formatSystemMessage;
@@ -305,6 +307,9 @@ struct COMReleaser
}
};
+template
+using COMPtr = std::unique_ptr;
+
class WMI
{
@@ -332,17 +337,26 @@ public:
}
auto enumerator = getEnumerator(q);
+ if (!enumerator) {
+ return;
+ }
for (;;)
{
- std::unique_ptr object;
+ COMPtr object;
{
IWbemClassObject* rawObject = nullptr;
ULONG count = 0;
auto ret = enumerator->Next(WBEM_INFINITE, 1, &rawObject, &count);
- if (count == 0) {
+ if (count == 0 || !rawObject) {
+ break;
+ }
+
+ if (FAILED(ret)) {
+ qCritical()
+ << "enumerator->next() failed, " << formatSystemMessageQ(ret);
break;
}
@@ -353,33 +367,9 @@ public:
}
}
- std::unique_ptr getEnumerator(
- const std::string& query)
- {
- IEnumWbemClassObject* rawEnumerator = NULL;
-
- auto ret = m_service->ExecQuery(
- bstr_t("WQL"),
- bstr_t(query.c_str()),
- WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
- NULL,
- &rawEnumerator);
-
- if (FAILED(ret))
- {
- qCritical()
- << "query '" << QString::fromStdString(query) << "' failed, "
- << formatSystemMessageQ(ret);
-
- return {};
- }
-
- return std::unique_ptr(rawEnumerator);
- }
-
private:
- std::unique_ptr m_locator;
- std::unique_ptr m_service;
+ COMPtr m_locator;
+ COMPtr m_service;
void createLocator()
{
@@ -389,7 +379,7 @@ private:
CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
IID_IWbemLocator, &rawLocator);
- if (FAILED(ret)) {
+ if (FAILED(ret) || !rawLocator) {
qCritical()
<< "CoCreateInstance for WbemLocator failed, "
<< formatSystemMessageQ(ret);
@@ -409,7 +399,7 @@ private:
nullptr, nullptr, nullptr, 0, nullptr, nullptr,
&rawService);
- if (FAILED(res)) {
+ if (FAILED(res) || !rawService) {
qCritical()
<< "locator->ConnectServer() failed for namespace "
<< "'" << QString::fromStdString(ns) << "', "
@@ -435,13 +425,37 @@ private:
throw failed();
}
}
+
+ COMPtr getEnumerator(
+ const std::string& query)
+ {
+ IEnumWbemClassObject* rawEnumerator = NULL;
+
+ auto ret = m_service->ExecQuery(
+ bstr_t("WQL"),
+ bstr_t(query.c_str()),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL,
+ &rawEnumerator);
+
+ if (FAILED(ret) || !rawEnumerator)
+ {
+ qCritical()
+ << "query '" << QString::fromStdString(query) << "' failed, "
+ << formatSystemMessageQ(ret);
+
+ return {};
+ }
+
+ return COMPtr(rawEnumerator);
+ }
};
Environment::Environment()
{
- getLoadedModules();
- getSecurityFeatures();
+ m_modules = getLoadedModules();
+ m_security = getSecurityProducts();
}
const std::vector& Environment::loadedModules()
@@ -454,12 +468,12 @@ const WindowsInfo& Environment::windowsInfo() const
return m_windows;
}
-const std::vector& Environment::securityFeatures() const
+const std::vector& Environment::securityProducts() const
{
return m_security;
}
-void Environment::getLoadedModules()
+std::vector Environment::getLoadedModules() const
{
HandlePtr snapshot(CreateToolhelp32Snapshot(
TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId()));
@@ -472,7 +486,7 @@ void Environment::getLoadedModules()
<< "CreateToolhelp32Snapshot() failed, "
<< formatSystemMessageQ(e);
- return;
+ return {};
}
MODULEENTRY32 me = {};
@@ -486,54 +500,88 @@ void Environment::getLoadedModules()
qCritical().nospace().noquote()
<< "Module32First() failed, " << formatSystemMessageQ(e);
- return;
+ return {};
}
+ std::vector v;
+
for (;;)
{
const auto path = QString::fromWCharArray(me.szExePath);
-
- m_modules.push_back(Module(path, me.modBaseSize));
+ if (!path.isEmpty()) {
+ v.push_back(Module(path, me.modBaseSize));
+ }
// next module
if (!Module32Next(snapshot.get(), &me)) {
const auto e = GetLastError();
- if (e == ERROR_NO_MORE_FILES) {
- // not an error
- break;
- }
-
+ // no more modules is not an error
+ if (e != ERROR_NO_MORE_FILES) {
qCritical().nospace().noquote()
<< "Module32Next() failed, " << formatSystemMessageQ(e);
+ }
break;
}
}
// sorting by display name
- std::sort(m_modules.begin(), m_modules.end(), [](auto&& a, auto&& b) {
+ std::sort(v.begin(), v.end(), [](auto&& a, auto&& b) {
return (a.displayPath().compare(b.displayPath(), Qt::CaseInsensitive) < 0);
});
+
+ return v;
+}
+
+std::vector Environment::getSecurityProducts() const
+{
+ std::vector v;
+
+ {
+ auto fromWMI = getSecurityProductsFromWMI();
+ v.insert(
+ v.end(),
+ std::make_move_iterator(fromWMI.begin()),
+ std::make_move_iterator(fromWMI.end()));
+ }
+
+ if (auto p=getWindowsFirewall()) {
+ v.push_back(std::move(*p));
+ }
+
+ return v;
}
-void Environment::getSecurityFeatures()
+std::vector Environment::getSecurityProductsFromWMI() const
{
- WMI wmi("root\\SecurityCenter2");
- std::map map;
+ // some products may be present in multiple queries, such as a product marked
+ // as both antivirus and antispyware, but they'll have the same GUID, so use
+ // that to avoid duplicating entries
+ std::map map;
auto handleProduct = [&](auto* o) {
VARIANT prop;
+ // display name
auto ret = o->Get(L"displayName", 0, &prop, 0, 0);
if (FAILED(ret)) {
- qCritical() << "failed to get displayName, " << formatSystemMessageQ(ret);
+ qCritical()
+ << "failed to get displayName, "
+ << formatSystemMessageQ(ret);
+
+ return;
+ }
+
+ if (prop.vt != VT_BSTR) {
+ qCritical() << "displayName is a " << prop.vt << ", not a bstr";
return;
}
const std::wstring name = prop.bstrVal;
VariantClear(&prop);
+ // product state
ret = o->Get(L"productState", 0, &prop, 0, 0);
if (FAILED(ret)) {
qCritical()
@@ -543,9 +591,21 @@ void Environment::getSecurityFeatures()
return;
}
- const DWORD state = prop.ulVal;
+ if (prop.vt != VT_UI4 && prop.vt != VT_I4) {
+ qCritical() << "productState is a " << prop.vt << ", is not a VT_UI4";
+ return;
+ }
+
+ DWORD state = 0;
+ if (prop.vt == VT_I4) {
+ state = prop.lVal;
+ } else {
+ state = prop.ulVal;
+ }
+
VariantClear(&prop);
+ // guid
ret = o->Get(L"instanceGuid", 0, &prop, 0, 0);
if (FAILED(ret)) {
qCritical()
@@ -555,25 +615,94 @@ void Environment::getSecurityFeatures()
return;
}
+ if (prop.vt != VT_BSTR) {
+ qCritical() << "instanceGuid is a " << prop.vt << ", is not a bstr";
+ return;
+ }
+
const QUuid guid(QString::fromWCharArray(prop.bstrVal));
VariantClear(&prop);
+ const auto provider = static_cast((state >> 16) & 0xff);
+ const auto scanner = (state >> 8) & 0xff;
+ const auto definitions = state & 0xff;
- auto itor = map.find(guid);
+ const bool active = ((scanner & 0x10) != 0);
+ const bool upToDate = (definitions == 0);
- if (itor == map.end()) {
- map.insert({
- guid, SecurityFeature(QString::fromStdWString(name), state)});
- }
+ map.insert({
+ guid,
+ {QString::fromStdWString(name), provider, active, upToDate}});
};
- wmi.query("select * from AntivirusProduct", handleProduct);
- wmi.query("select * from FirewallProduct", handleProduct);
- wmi.query("select * from AntiSpywareProduct", handleProduct);
+ {
+ WMI wmi("root\\SecurityCenter2");
+ wmi.query("select * from AntivirusProduct", handleProduct);
+ wmi.query("select * from FirewallProduct", handleProduct);
+ wmi.query("select * from AntiSpywareProduct", handleProduct);
+ }
+
+ {
+ WMI wmi("root\\SecurityCenter");
+ wmi.query("select * from AntivirusProduct", handleProduct);
+ wmi.query("select * from FirewallProduct", handleProduct);
+ wmi.query("select * from AntiSpywareProduct", handleProduct);
+ }
+
+ std::vector v;
for (auto&& p : map) {
- m_security.push_back(p.second);
+ v.push_back(p.second);
}
+
+ return v;
+}
+
+std::optional Environment::getWindowsFirewall() const
+{
+ HRESULT hr = 0;
+
+ COMPtr policy;
+
+ {
+ void* rawPolicy = nullptr;
+
+ hr = CoCreateInstance(
+ __uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER,
+ __uuidof(INetFwPolicy2), &rawPolicy);
+
+ if (FAILED(hr) || !rawPolicy) {
+ qCritical()
+ << "CoCreateInstance for NetFwPolicy2 failed, "
+ << formatSystemMessage(hr);
+
+ return {};
+ }
+
+ policy.reset(static_cast(rawPolicy));
+ }
+
+ VARIANT_BOOL enabledVariant;
+
+ if (policy) {
+ hr = policy->get_FirewallEnabled(NET_FW_PROFILE2_PUBLIC, &enabledVariant);
+ if (FAILED(hr))
+ {
+ qCritical()
+ << "get_FirewallEnabled failed, "
+ << formatSystemMessage(hr);
+
+ return {};
+ }
+ }
+
+ const auto enabled = (enabledVariant != VARIANT_FALSE);
+ if (!enabled) {
+ return {};
+ }
+
+ return SecurityProduct(
+ "Windows Firewall", WSC_SECURITY_PROVIDER_FIREWALL, true, true);
}
@@ -1123,52 +1252,67 @@ std::optional WindowsInfo::getElevated() const
}
-SecurityFeature::SecurityFeature(QString name, DWORD state)
- : m_name(std::move(name)), m_state(state)
+SecurityProduct::SecurityProduct(
+ QString name, int provider,
+ bool active, bool upToDate) :
+ m_name(std::move(name)), m_provider(provider),
+ m_active(active), m_upToDate(upToDate)
{
}
-const QString& SecurityFeature::name() const
+const QString& SecurityProduct::name() const
{
return m_name;
}
-QString SecurityFeature::toString() const
+int SecurityProduct::provider() const
+{
+ return m_provider;
+}
+
+bool SecurityProduct::active() const
+{
+ return m_active;
+}
+
+bool SecurityProduct::upToDate() const
+{
+ return m_upToDate;
+}
+
+QString SecurityProduct::toString() const
{
QString s;
s += m_name + " ";
- const auto provider = (m_state >> 16) & 0xff;
- const auto scanner = (m_state >> 8) & 0xff;
- const auto definitions = m_state & 0xff;
QStringList ps;
- if (provider & WSC_SECURITY_PROVIDER_FIREWALL) {
+ if (m_provider & WSC_SECURITY_PROVIDER_FIREWALL) {
ps.push_back("firewall");
}
- if (provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
+ if (m_provider & WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS) {
ps.push_back("autoupdate");
}
- if (provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
+ if (m_provider & WSC_SECURITY_PROVIDER_ANTIVIRUS) {
ps.push_back("antivirus");
}
- if (provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
+ if (m_provider & WSC_SECURITY_PROVIDER_ANTISPYWARE) {
ps.push_back("antispyware");
}
- if (provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
+ if (m_provider & WSC_SECURITY_PROVIDER_INTERNET_SETTINGS) {
ps.push_back("settings");
}
- if (provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
+ if (m_provider & WSC_SECURITY_PROVIDER_USER_ACCOUNT_CONTROL) {
ps.push_back("uac");
}
- if (provider & WSC_SECURITY_PROVIDER_SERVICE) {
+ if (m_provider & WSC_SECURITY_PROVIDER_SERVICE) {
ps.push_back("service");
}
@@ -1178,15 +1322,13 @@ QString SecurityFeature::toString() const
s += "(" + ps.join("|") + ")";
}
- if (scanner & 0x10) {
+ if (m_active) {
s += ", active";
} else {
s += ", inactive";
}
- if (definitions == 0) {
- s += ", definitions up to date";
- } else {
+ if (!m_upToDate) {
s += ", definitions outdated";
}
diff --git a/src/shared/util.h b/src/shared/util.h
index 2296651c..e0da8a99 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -241,18 +241,25 @@ private:
};
-class SecurityFeature
+class SecurityProduct
{
public:
- SecurityFeature(QString name, DWORD state);
+ SecurityProduct(
+ QString name, int provider,
+ bool active, bool upToDate);
const QString& name() const;
+ int provider() const;
+ bool active() const;
+ bool upToDate() const;
QString toString() const;
private:
QString m_name;
- DWORD m_state;
+ int m_provider;
+ bool m_active;
+ bool m_upToDate;
};
@@ -271,17 +278,20 @@ public:
//
const WindowsInfo& windowsInfo() const;
- // information about the installed antivirus
+ // information about the installed security products
//
- const std::vector& securityFeatures() const;
+ const std::vector& securityProducts() const;
private:
std::vector m_modules;
WindowsInfo m_windows;
- std::vector m_security;
+ std::vector m_security;
- void getLoadedModules();
- void getSecurityFeatures();
+ std::vector getLoadedModules() const;
+ std::vector getSecurityProducts() const;
+
+ std::vector getSecurityProductsFromWMI() const;
+ std::optional getWindowsFirewall() const;
};
--
cgit v1.3.1
From 9166bd9c5bf02484bd7486374e508ca304111f5a Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 6 Jul 2019 18:50:28 -0400
Subject: added new Shortcut class, moved stuff that was in MainWindow into it
added more error handling and logging, some was missing removed some
redundancy of setting the menu icons for add/remove, they're all set when
clicking the button anyway
---
src/mainwindow.cpp | 177 ++++-------------------------
src/mainwindow.h | 12 +-
src/shared/util.cpp | 320 +++++++++++++++++++++++++++++++++++++++++++++++++++-
src/shared/util.h | 96 ++++++++++++++++
4 files changed, 442 insertions(+), 163 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 7681b482..f9e4138e 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -220,6 +220,9 @@ MainWindow::MainWindow(QSettings &initSettings
, m_PluginContainer(pluginContainer)
, m_DidUpdateMasterList(false)
, m_ArchiveListWriter(std::bind(&MainWindow::saveArchiveList, this))
+ , m_LinkToolbar(nullptr)
+ , m_LinkDesktop(nullptr)
+ , m_LinkStartMenu(nullptr)
{
QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
QWebEngineProfile::defaultProfile()->setHttpCacheMaximumSize(52428800);
@@ -335,9 +338,9 @@ MainWindow::MainWindow(QSettings &initSettings
resizeLists(modListAdjusted, pluginListAdjusted);
QMenu *linkMenu = new QMenu(this);
- linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Toolbar and Menu"), this, SLOT(linkToolbar()));
- linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Desktop"), this, SLOT(linkDesktop()));
- linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu()));
+ m_LinkToolbar = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Toolbar and Menu"), this, SLOT(linkToolbar()));
+ m_LinkDesktop = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Desktop"), this, SLOT(linkDesktop()));
+ m_LinkStartMenu = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu()));
ui->linkButton->setMenu(linkMenu);
QMenu *listOptionsMenu = new QMenu(ui->listOptionsBtn);
@@ -2397,88 +2400,6 @@ void MainWindow::on_startButton_clicked() {
ui->startButton->setEnabled(true);
}
-static HRESULT CreateShortcut(LPCWSTR targetFileName, LPCWSTR arguments,
- LPCSTR linkFileName, LPCWSTR description,
- LPCTSTR iconFileName, int iconNumber,
- LPCWSTR currentDirectory)
-{
- HRESULT result = E_INVALIDARG;
- if ((targetFileName != nullptr) && (wcslen(targetFileName) > 0) &&
- (arguments != nullptr) &&
- (linkFileName != nullptr) && (strlen(linkFileName) > 0) &&
- (description != nullptr) &&
- (currentDirectory != nullptr)) {
-
- IShellLink* shellLink;
- result = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
- IID_IShellLink, (LPVOID*)&shellLink);
-
- if (!SUCCEEDED(result)) {
- qCritical("failed to create IShellLink instance");
- return result;
- }
-
- result = shellLink->SetPath(targetFileName);
- if (!SUCCEEDED(result)) {
- qCritical("failed to set target path %ls", targetFileName);
- shellLink->Release();
- return result;
- }
-
- result = shellLink->SetArguments(arguments);
- if (!SUCCEEDED(result)) {
- qCritical("failed to set arguments: %ls", arguments);
- shellLink->Release();
- return result;
- }
-
- if (wcslen(description) > 0) {
- result = shellLink->SetDescription(description);
- if (!SUCCEEDED(result)) {
- qCritical("failed to set description: %ls", description);
- shellLink->Release();
- return result;
- }
- }
-
- if (wcslen(currentDirectory) > 0) {
- result = shellLink->SetWorkingDirectory(currentDirectory);
- if (!SUCCEEDED(result)) {
- qCritical("failed to set working directory: %ls", currentDirectory);
- shellLink->Release();
- return result;
- }
- }
-
- if (iconFileName != nullptr) {
- result = shellLink->SetIconLocation(iconFileName, iconNumber);
- if (!SUCCEEDED(result)) {
- qCritical("failed to load program icon: %ls %d", iconFileName, iconNumber);
- shellLink->Release();
- return result;
- }
- }
-
- IPersistFile *persistFile;
- result = shellLink->QueryInterface(IID_IPersistFile, (LPVOID*)&persistFile);
- if (SUCCEEDED(result)) {
- wchar_t linkFileNameW[MAX_PATH];
- if (MultiByteToWideChar(CP_ACP, 0, linkFileName, -1, linkFileNameW, MAX_PATH) > 0) {
- result = persistFile->Save(linkFileNameW, TRUE);
- } else {
- qCritical("failed to create link: %s", linkFileName);
- }
- persistFile->Release();
- } else {
- qCritical("failed to create IPersistFile instance");
- }
-
- shellLink->Release();
- }
- return result;
-}
-
-
bool MainWindow::modifyExecutablesDialog()
{
bool result = false;
@@ -5144,74 +5065,39 @@ void MainWindow::on_savegameList_customContextMenuRequested(const QPoint &pos)
void MainWindow::linkToolbar()
{
- Executable &exe(getSelectedExecutable());
+ Executable& exe = getSelectedExecutable();
+
exe.setShownOnToolbar(!exe.isShownOnToolbar());
- ui->linkButton->menu()->actions().at(static_cast(ShortcutType::Toolbar))->setIcon(exe.isShownOnToolbar() ? QIcon(":/MO/gui/remove") : QIcon(":/MO/gui/link"));
updatePinnedExecutables();
}
-namespace {
-QString getLinkfile(const QString &dir, const Executable &exec)
+void MainWindow::linkDesktop()
{
- return QDir::fromNativeSeparators(dir) + "/" + exec.title() + ".lnk";
+ env::Shortcut(getSelectedExecutable()).toggle(env::Shortcut::Desktop);
}
-QString getDesktopLinkfile(const Executable &exec)
+void MainWindow::linkMenu()
{
- return getLinkfile(getDesktopDirectory(), exec);
+ env::Shortcut(getSelectedExecutable()).toggle(env::Shortcut::StartMenu);
}
-QString getStartMenuLinkfile(const Executable &exec)
+void MainWindow::on_linkButton_pressed()
{
- return getLinkfile(getStartMenuDirectory(), exec);
-}
-}
+ const Executable& exe = getSelectedExecutable();
-void MainWindow::addWindowsLink(const ShortcutType mapping)
-{
- const Executable &selectedExecutable(getSelectedExecutable());
- QString const linkName = getLinkfile(mapping == ShortcutType::Desktop ? getDesktopDirectory() : getStartMenuDirectory(),
- selectedExecutable);
+ const QIcon addIcon(":/MO/gui/link");
+ const QIcon removeIcon(":/MO/gui/remove");
- if (QFile::exists(linkName)) {
- if (QFile::remove(linkName)) {
- ui->linkButton->menu()->actions().at(static_cast(mapping))->setIcon(QIcon(":/MO/gui/link"));
- } else {
- reportError(tr("failed to remove %1").arg(linkName));
- }
- } else {
- QFileInfo const exeInfo(qApp->applicationFilePath());
- // create link
- QString executable = QDir::toNativeSeparators(selectedExecutable.binaryInfo().absoluteFilePath());
-
- std::wstring targetFile = ToWString(exeInfo.absoluteFilePath());
- std::wstring parameter = ToWString(
- QString("\"moshortcut://%1:%2\"").arg(InstanceManager::instance().currentInstance(),selectedExecutable.title()));
- std::wstring description = ToWString(QString("Run %1 with ModOrganizer").arg(selectedExecutable.title()));
- std::wstring iconFile = ToWString(executable);
- std::wstring currentDirectory = ToWString(QDir::toNativeSeparators(qApp->applicationDirPath()));
-
- if (CreateShortcut(targetFile.c_str()
- , parameter.c_str()
- , QDir::toNativeSeparators(linkName).toUtf8().constData()
- , description.c_str()
- , (selectedExecutable.usesOwnIcon() ? iconFile.c_str() : nullptr), 0
- , currentDirectory.c_str()) == 0) {
- ui->linkButton->menu()->actions().at(static_cast(mapping))->setIcon(QIcon(":/MO/gui/remove"));
- } else {
- reportError(tr("failed to create %1").arg(linkName));
- }
- }
-}
+ env::Shortcut shortcut(exe);
-void MainWindow::linkDesktop()
-{
- addWindowsLink(ShortcutType::Desktop);
-}
+ m_LinkToolbar->setIcon(
+ exe.isShownOnToolbar() ? removeIcon : addIcon);
-void MainWindow::linkMenu()
-{
- addWindowsLink(ShortcutType::StartMenu);
+ m_LinkDesktop->setIcon(
+ shortcut.exists(env::Shortcut::Desktop) ? removeIcon : addIcon);
+
+ m_LinkStartMenu->setIcon(
+ shortcut.exists(env::Shortcut::StartMenu) ? removeIcon : addIcon);
}
void MainWindow::on_actionSettings_triggered()
@@ -6476,21 +6362,6 @@ Executable &MainWindow::getSelectedExecutable()
return m_OrganizerCore.executablesList()->get(name);
}
-void MainWindow::on_linkButton_pressed()
-{
- const Executable &selectedExecutable(getSelectedExecutable());
-
- const QIcon addIcon(":/MO/gui/link");
- const QIcon removeIcon(":/MO/gui/remove");
-
- const QFileInfo linkDesktopFile(getDesktopLinkfile(selectedExecutable));
- const QFileInfo linkMenuFile(getStartMenuLinkfile(selectedExecutable));
-
- ui->linkButton->menu()->actions().at(static_cast(ShortcutType::Toolbar))->setIcon(selectedExecutable.isShownOnToolbar() ? removeIcon : addIcon);
- ui->linkButton->menu()->actions().at(static_cast(ShortcutType::Desktop))->setIcon(linkDesktopFile.exists() ? removeIcon : addIcon);
- ui->linkButton->menu()->actions().at(static_cast(ShortcutType::StartMenu))->setIcon(linkMenuFile.exists() ? removeIcon : addIcon);
-}
-
void MainWindow::on_showHiddenBox_toggled(bool checked)
{
m_OrganizerCore.downloadManager()->setShowHidden(checked);
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 00f15a2b..eee269cf 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -403,18 +403,14 @@ private:
MOBase::DelayedFileWriter m_ArchiveListWriter;
+ QAction* m_LinkToolbar;
+ QAction* m_LinkDesktop;
+ QAction* m_LinkStartMenu;
+
// icon set by the stylesheet, used to remember its original appearance
// when painting the count
QIcon m_originalNotificationIcon;
- enum class ShortcutType {
- Toolbar,
- Desktop,
- StartMenu
- };
-
- void addWindowsLink(ShortcutType const);
-
Executable const &getSelectedExecutable() const;
Executable &getSelectedExecutable();
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 072cee2d..2bf1dba6 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -20,6 +20,8 @@ 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
@@ -307,10 +309,324 @@ struct COMReleaser
}
};
+
template
using COMPtr = std::unique_ptr;
+class ShellLinkException {};
+
+// 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()) {
+ critical() << "path cannot be empty";
+ throw ShellLinkException();
+ }
+
+ 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;
+
+ QDebug critical()
+ {
+ return qCritical().noquote().nospace() << "system shortcut: ";
+ }
+
+ void throwOnFail(HRESULT r, const QString& s)
+ {
+ if (FAILED(r)) {
+ critical() << s << ", " << formatSystemMessageQ(r);
+ throw ShellLinkException();
+ }
+ }
+
+ 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) {
+ critical() << "creating IShellLink worked, but pointer is null";
+ throw ShellLinkException();
+ }
+
+ 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) {
+ critical() << "querying IPersistFile worked, but pointer is null";
+ throw ShellLinkException();
+ }
+
+ 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)
+{
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ if (m_target.isEmpty()) {
+ qCritical() << "system shortcut: target is empty";
+ return false;
+ }
+
+ 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&)
+ {
+ }
+
+ return false;
+}
+
+bool Shortcut::remove(Locations loc)
+{
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ if (!QFile::exists(path)) {
+ qCritical().nospace().noquote()
+ << "system shortcut: can't remove '" << path << "', file not found";
+
+ return false;
+ }
+
+ if (!QFile::remove(path)) {
+ qCritical().nospace().noquote()
+ << "system shortcut: failed to remove '" << path << "'";
+
+ 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:
+ qCritical() << "system shortcut: bad location " << loc;
+ return {};
+ }
+ }
+ catch(std::exception&)
+ {
+ return {};
+ }
+
+ return QDir::toNativeSeparators(dir);
+}
+
+QString Shortcut::shortcutFilename() const
+{
+ if (m_name.isEmpty()) {
+ qCritical() << "system shortcut: name is empty";
+ return {};
+ }
+
+ return m_name + ".lnk";
+}
+
+
+
class WMI
{
public:
@@ -674,7 +990,7 @@ std::optional Environment::getWindowsFirewall() const
if (FAILED(hr) || !rawPolicy) {
qCritical()
<< "CoCreateInstance for NetFwPolicy2 failed, "
- << formatSystemMessage(hr);
+ << formatSystemMessageQ(hr);
return {};
}
@@ -690,7 +1006,7 @@ std::optional Environment::getWindowsFirewall() const
{
qCritical()
<< "get_FirewallEnabled failed, "
- << formatSystemMessage(hr);
+ << formatSystemMessageQ(hr);
return {};
}
diff --git a/src/shared/util.h b/src/shared/util.h
index 4df1d13c..a40fa201 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -29,6 +29,8 @@ along with Mod Organizer. If not, see .
#include
+class Executable;
+
namespace MOShared {
/// Test if a file (or directory) by the specified name exists
@@ -52,6 +54,100 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
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 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;
+};
+
+
// represents one module
//
class Module
--
cgit v1.3.1
From f7c844fcf7684cbde0a3290b1950c52f88236be3 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sat, 6 Jul 2019 19:23:52 -0400
Subject: put the error message in the ShellLinkException instead added more
debug logging when creating and deleting shortcuts
---
src/shared/util.cpp | 111 ++++++++++++++++++++++++++++++++++++++--------------
src/shared/util.h | 13 ++++++
2 files changed, 95 insertions(+), 29 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 2bf1dba6..17df3b92 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -314,7 +314,22 @@ template
using COMPtr = std::unique_ptr;
-class ShellLinkException {};
+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
@@ -331,8 +346,7 @@ public:
void setPath(const QString& s)
{
if (s.isEmpty()) {
- critical() << "path cannot be empty";
- throw ShellLinkException();
+ throw ShellLinkException("path cannot be empty");
}
const auto r = m_link->SetPath(s.toStdWString().c_str());
@@ -385,16 +399,12 @@ private:
COMPtr m_link;
COMPtr m_file;
- QDebug critical()
- {
- return qCritical().noquote().nospace() << "system shortcut: ";
- }
-
void throwOnFail(HRESULT r, const QString& s)
{
if (FAILED(r)) {
- critical() << s << ", " << formatSystemMessageQ(r);
- throw ShellLinkException();
+ throw ShellLinkException(QString("%1, %2")
+ .arg(s)
+ .arg(formatSystemMessageQ(r)));
}
}
@@ -409,8 +419,7 @@ private:
throwOnFail(r, "failed to create IShellLink instance");
if (!link) {
- critical() << "creating IShellLink worked, but pointer is null";
- throw ShellLinkException();
+ throw ShellLinkException("creating IShellLink worked, pointer is null");
}
return COMPtr(static_cast(link));
@@ -424,8 +433,7 @@ private:
throwOnFail(r, "failed to get IPersistFile interface");
if (!file) {
- critical() << "querying IPersistFile worked, but pointer is null";
- throw ShellLinkException();
+ throw ShellLinkException("querying IPersistFile worked, pointer is null");
}
return COMPtr(static_cast(file));
@@ -515,16 +523,27 @@ bool Shortcut::toggle(Locations loc)
bool Shortcut::add(Locations loc)
{
- const auto path = shortcutPath(loc);
- if (path.isEmpty()) {
+ 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;
}
- if (m_target.isEmpty()) {
- qCritical() << "system shortcut: target is empty";
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
return false;
}
+ debug() << "shorcut file will be saved at '" << path << "'";
+
try
{
ShellLinkWrapper link;
@@ -539,8 +558,9 @@ bool Shortcut::add(Locations loc)
return true;
}
- catch(ShellLinkException&)
+ catch(ShellLinkException& e)
{
+ critical() << e.what() << "\nshortcut file was not saved";
}
return false;
@@ -548,21 +568,26 @@ bool Shortcut::add(Locations loc)
bool Shortcut::remove(Locations loc)
{
+ debug() << "removing shortcut for '" << m_name << "' from " << toString(loc);
+
const auto path = shortcutPath(loc);
if (path.isEmpty()) {
return false;
}
- if (!QFile::exists(path)) {
- qCritical().nospace().noquote()
- << "system shortcut: can't remove '" << path << "', file not found";
+ debug() << "path to shortcut file is '" << path << "'";
+ if (!QFile::exists(path)) {
+ critical() << "can't remove '" << path << "', file not found";
return false;
}
- if (!QFile::remove(path)) {
- qCritical().nospace().noquote()
- << "system shortcut: failed to remove '" << path << "'";
+ if (!MOBase::shellDelete({path})) {
+ const auto e = ::GetLastError();
+
+ critical()
+ << "failed to remove '" << path << "', "
+ << formatSystemMessageQ(e);
return false;
}
@@ -603,13 +628,12 @@ QString Shortcut::shortcutDirectory(Locations loc) const
case None:
default:
- qCritical() << "system shortcut: bad location " << loc;
- return {};
+ critical() << "bad location " << loc;
+ break;
}
}
catch(std::exception&)
{
- return {};
}
return QDir::toNativeSeparators(dir);
@@ -618,13 +642,42 @@ QString Shortcut::shortcutDirectory(Locations loc) const
QString Shortcut::shortcutFilename() const
{
if (m_name.isEmpty()) {
- qCritical() << "system shortcut: name is empty";
+ 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
diff --git a/src/shared/util.h b/src/shared/util.h
index a40fa201..c4a2ed7d 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -133,6 +133,14 @@ private:
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
//
@@ -148,6 +156,11 @@ private:
};
+// returns a string representation of the given location
+//
+QString toString(Shortcut::Locations loc);
+
+
// represents one module
//
class Module
--
cgit v1.3.1