diff options
Diffstat (limited to 'src/shared')
| -rw-r--r-- | src/shared/util.cpp | 373 | ||||
| -rw-r--r-- | src/shared/util.h | 109 |
2 files changed, 480 insertions, 2 deletions
diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 072cee2d..17df3b92 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -20,6 +20,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "util.h"
#include "windows_error.h"
#include "error_report.h"
+#include "executableslist.h"
+#include "instancemanager.h"
#include <utility.h>
#include <sstream>
@@ -307,10 +309,377 @@ struct COMReleaser }
};
+
template <class T>
using COMPtr = std::unique_ptr<T, COMReleaser>;
+class ShellLinkException
+{
+public:
+ ShellLinkException(QString s)
+ : m_what(std::move(s))
+ {
+ }
+
+ const QString& what() const
+ {
+ return m_what;
+ }
+
+private:
+ QString m_what;
+};
+
+// just a wrapper around IShellLink operations that throws ShellLinkException
+// on errors
+//
+class ShellLinkWrapper
+{
+public:
+ ShellLinkWrapper()
+ {
+ m_link = createShellLink();
+ m_file = createPersistFile();
+ }
+
+ void setPath(const QString& s)
+ {
+ if (s.isEmpty()) {
+ throw ShellLinkException("path cannot be empty");
+ }
+
+ const auto r = m_link->SetPath(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set target path '%1'").arg(s));
+ }
+
+ void setArguments(const QString& s)
+ {
+ const auto r = m_link->SetArguments(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set arguments '%1'").arg(s));
+ }
+
+ void setDescription(const QString& s)
+ {
+ if (s.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetDescription(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set description '%1'").arg(s));
+ }
+
+ void setIcon(const QString& file, int i)
+ {
+ if (file.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetIconLocation(file.toStdWString().c_str(), i);
+ throwOnFail(r, QString("failed to set icon '%1' @ %2").arg(file).arg(i));
+ }
+
+ void setWorkingDirectory(const QString& s)
+ {
+ if (s.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetWorkingDirectory(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set working directory '%1'").arg(s));
+ }
+
+ void save(const QString& path)
+ {
+ const auto r = m_file->Save(path.toStdWString().c_str(), TRUE);
+ throwOnFail(r, QString("failed to save link '%1'").arg(path));
+ }
+
+private:
+ COMPtr<IShellLink> m_link;
+ COMPtr<IPersistFile> m_file;
+
+ void throwOnFail(HRESULT r, const QString& s)
+ {
+ if (FAILED(r)) {
+ throw ShellLinkException(QString("%1, %2")
+ .arg(s)
+ .arg(formatSystemMessageQ(r)));
+ }
+ }
+
+ COMPtr<IShellLink> createShellLink()
+ {
+ void* link = nullptr;
+
+ const auto r = CoCreateInstance(
+ CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IShellLink, &link);
+
+ throwOnFail(r, "failed to create IShellLink instance");
+
+ if (!link) {
+ throw ShellLinkException("creating IShellLink worked, pointer is null");
+ }
+
+ return COMPtr<IShellLink>(static_cast<IShellLink*>(link));
+ }
+
+ COMPtr<IPersistFile> createPersistFile()
+ {
+ void* file = nullptr;
+
+ const auto r = m_link->QueryInterface(IID_IPersistFile, &file);
+ throwOnFail(r, "failed to get IPersistFile interface");
+
+ if (!file) {
+ throw ShellLinkException("querying IPersistFile worked, pointer is null");
+ }
+
+ return COMPtr<IPersistFile>(static_cast<IPersistFile*>(file));
+ }
+};
+
+
+Shortcut::Shortcut()
+ : m_iconIndex(0)
+{
+}
+
+Shortcut::Shortcut(const Executable& exe)
+ : Shortcut()
+{
+ m_name = exe.title();
+ m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
+
+ m_arguments = QString("\"moshortcut://%1:%2\"")
+ .arg(InstanceManager::instance().currentInstance())
+ .arg(exe.title());
+
+ m_description = QString("Run %1 with ModOrganizer").arg(exe.title());
+
+ if (exe.usesOwnIcon()) {
+ m_icon = exe.binaryInfo().absoluteFilePath();
+ }
+
+ m_workingDirectory = qApp->applicationDirPath();
+}
+
+Shortcut& Shortcut::name(const QString& s)
+{
+ m_name = s;
+ return *this;
+}
+
+Shortcut& Shortcut::target(const QString& s)
+{
+ m_target = s;
+ return *this;
+}
+
+Shortcut& Shortcut::arguments(const QString& s)
+{
+ m_arguments = s;
+ return *this;
+}
+
+Shortcut& Shortcut::description(const QString& s)
+{
+ m_description = s;
+ return *this;
+}
+
+Shortcut& Shortcut::icon(const QString& s, int index)
+{
+ m_icon = s;
+ m_iconIndex = index;
+ return *this;
+}
+
+Shortcut& Shortcut::workingDirectory(const QString& s)
+{
+ m_workingDirectory = s;
+ return *this;
+}
+
+bool Shortcut::exists(Locations loc) const
+{
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ return QFileInfo(path).exists();
+}
+
+bool Shortcut::toggle(Locations loc)
+{
+ if (exists(loc)) {
+ return remove(loc);
+ } else {
+ return add(loc);
+ }
+}
+
+bool Shortcut::add(Locations loc)
+{
+ debug()
+ << "adding shortcut to " << toString(loc) << ":\n"
+ << " . name: '" << m_name << "'\n"
+ << " . target: '" << m_target << "'\n"
+ << " . arguments: '" << m_arguments << "'\n"
+ << " . description: '" << m_description << "'\n"
+ << " . icon: '" << m_icon << "' @ " << m_iconIndex << "\n"
+ << " . working directory: '" << m_workingDirectory << "'";
+
+ if (m_target.isEmpty()) {
+ critical() << "target is empty";
+ return false;
+ }
+
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ debug() << "shorcut file will be saved at '" << path << "'";
+
+ try
+ {
+ ShellLinkWrapper link;
+
+ link.setPath(m_target);
+ link.setArguments(m_arguments);
+ link.setDescription(m_description);
+ link.setIcon(m_icon, m_iconIndex);
+ link.setWorkingDirectory(m_workingDirectory);
+
+ link.save(path);
+
+ return true;
+ }
+ catch(ShellLinkException& e)
+ {
+ critical() << e.what() << "\nshortcut file was not saved";
+ }
+
+ return false;
+}
+
+bool Shortcut::remove(Locations loc)
+{
+ debug() << "removing shortcut for '" << m_name << "' from " << toString(loc);
+
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ debug() << "path to shortcut file is '" << path << "'";
+
+ if (!QFile::exists(path)) {
+ critical() << "can't remove '" << path << "', file not found";
+ return false;
+ }
+
+ if (!MOBase::shellDelete({path})) {
+ const auto e = ::GetLastError();
+
+ critical()
+ << "failed to remove '" << path << "', "
+ << formatSystemMessageQ(e);
+
+ return false;
+ }
+
+ return true;
+}
+
+QString Shortcut::shortcutPath(Locations loc) const
+{
+ const auto dir = shortcutDirectory(loc);
+ if (dir.isEmpty()) {
+ return {};
+ }
+
+ const auto file = shortcutFilename();
+ if (file.isEmpty()) {
+ return {};
+ }
+
+ return dir + QDir::separator() + file;
+}
+
+QString Shortcut::shortcutDirectory(Locations loc) const
+{
+ QString dir;
+
+ try
+ {
+ switch (loc)
+ {
+ case Desktop:
+ dir = MOBase::getDesktopDirectory();
+ break;
+
+ case StartMenu:
+ dir = MOBase::getStartMenuDirectory();
+ break;
+
+ case None:
+ default:
+ critical() << "bad location " << loc;
+ break;
+ }
+ }
+ catch(std::exception&)
+ {
+ }
+
+ return QDir::toNativeSeparators(dir);
+}
+
+QString Shortcut::shortcutFilename() const
+{
+ if (m_name.isEmpty()) {
+ critical() << "name is empty";
+ return {};
+ }
+
+ return m_name + ".lnk";
+}
+
+QDebug Shortcut::debug() const
+{
+ return qDebug().noquote().nospace() << "system shortcut: ";
+}
+
+QDebug Shortcut::critical() const
+{
+ return qCritical().noquote().nospace() << "system shortcut: ";
+}
+
+
+QString toString(Shortcut::Locations loc)
+{
+ switch (loc)
+ {
+ case Shortcut::None:
+ return "none";
+
+ case Shortcut::Desktop:
+ return "desktop";
+
+ case Shortcut::StartMenu:
+ return "start menu";
+
+ default:
+ return QString("? (%1)").arg(static_cast<int>(loc));
+ }
+}
+
+
+
class WMI
{
public:
@@ -674,7 +1043,7 @@ std::optional<SecurityProduct> Environment::getWindowsFirewall() const if (FAILED(hr) || !rawPolicy) {
qCritical()
<< "CoCreateInstance for NetFwPolicy2 failed, "
- << formatSystemMessage(hr);
+ << formatSystemMessageQ(hr);
return {};
}
@@ -690,7 +1059,7 @@ std::optional<SecurityProduct> 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..c4a2ed7d 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -29,6 +29,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <versioninfo.h>
+class Executable;
+
namespace MOShared {
/// Test if a file (or directory) by the specified name exists
@@ -52,6 +54,113 @@ 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 a qCritical() logger with a prefix already logged
+ //
+ QDebug critical() const;
+
+ // returns a qDebug() logger with a prefix already logged
+ //
+ QDebug debug() const;
+
+
+ // returns the path where the shortcut file should be saved
+ //
+ QString shortcutPath(Locations loc) const;
+
+ // returns the directory where the shortcut file should be saved
+ //
+ QString shortcutDirectory(Locations loc) const;
+
+ // returns the filename of the shortcut file that should be used when saving
+ //
+ QString shortcutFilename() const;
+};
+
+
+// returns a string representation of the given location
+//
+QString toString(Shortcut::Locations loc);
+
+
// represents one module
//
class Module
|
