Move "Windows Explorer integration" from Installer to UI.

This commit is contained in:
Nodir Temirkhodjaev 2021-05-02 11:11:37 +03:00
parent a70a4d195f
commit e5b9388bdc
9 changed files with 233 additions and 34 deletions

View File

@ -49,7 +49,6 @@ Name: pt; MessagesFile: "compiler:Languages\Portuguese.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; Flags: unchecked
Name: "explorer"; Description: "File Explorer integration"; Flags: unchecked
Name: "portable"; Description: "Portable"; Flags: unchecked
[Files]
@ -57,16 +56,6 @@ Source: "build\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createa
Source: "{#APP_EXE_NAME}.example.ini"; DestDir: "{app}"
Source: "README.portable"; DestDir: "{app}"; Tasks: portable
[Registry]
; Explorer's Context Menu
#define REG_SHELL "SystemFileAssociations\.exe\Shell"
#define REG_SHELL_MENU REG_SHELL + "\Fort Firewall"
Root: HKCR; Subkey: "{#REG_SHELL}"; Flags: uninsdeletekeyifempty; Tasks: explorer
Root: HKCR; Subkey: "{#REG_SHELL_MENU}"; Flags: deletekey uninsdeletekey
Root: HKCR; Subkey: "{#REG_SHELL_MENU}"; ValueType: string; ValueName: "icon"; ValueData: "{#APP_EXE}"; Tasks: explorer
Root: HKCR; Subkey: "{#REG_SHELL_MENU}"; ValueType: string; ValueName: "MUIVerb"; ValueData: "Fort Firewall ..."; Tasks: explorer
Root: HKCR; Subkey: "{#REG_SHELL_MENU}\command"; ValueType: string; ValueData: """{#APP_EXE}"" -w -c prog add ""%1"""; Tasks: explorer
[Icons]
; Start menu shortcut
Name: "{group}\{#APP_NAME}"; Filename: "{#APP_EXE}"; WorkingDir: "{app}"; Parameters: "--lang {code:LanguageName}"

View File

@ -129,6 +129,7 @@ SOURCES += \
util/net/netutil.cpp \
util/osutil.cpp \
util/processinfo.cpp \
util/regkey.cpp \
util/serviceworker.cpp \
util/startuputil.cpp \
util/stringutil.cpp \
@ -265,6 +266,7 @@ HEADERS += \
util/net/netutil.h \
util/osutil.h \
util/processinfo.h \
util/regkey.h \
util/serviceworker.h \
util/startuputil.h \
util/stringutil.h \

View File

@ -26,7 +26,7 @@
#include "../optionscontroller.h"
OptionsPage::OptionsPage(OptionsController *ctrl, QWidget *parent) :
BasePage(ctrl, parent), m_iniEdited(false), m_currentStartMode(0)
BasePage(ctrl, parent), m_iniEdited(false), m_currentStartMode(0), m_explorerIntegrated(false)
{
setupUi();
}
@ -50,6 +50,7 @@ void OptionsPage::onEditResetted()
void OptionsPage::onSaved()
{
saveStartMode();
saveExplorerIntegration();
if (iniEdited()) {
saveIni();
@ -72,6 +73,14 @@ void OptionsPage::saveStartMode()
}
}
void OptionsPage::saveExplorerIntegration()
{
if (m_explorerIntegrated != m_cbExplorerMenu->isChecked()) {
m_explorerIntegrated = m_cbExplorerMenu->isChecked();
StartupUtil::integrateExplorer(m_explorerIntegrated);
}
}
void OptionsPage::saveIni()
{
settings()->setHotKeyEnabled(m_cbHotKeys->isChecked());
@ -110,6 +119,8 @@ void OptionsPage::onRetranslateUi()
m_cbStopTraffic->setText(tr("Stop Traffic"));
m_cbStopInetTraffic->setText(tr("Stop Internet Traffic"));
m_cbAllowAllNew->setText(tr("Auto-Allow New Programs"));
m_cbExplorerMenu->setText(tr("Windows Explorer integration"));
m_cbHotKeys->setText(tr("Hot Keys"));
m_cbPassword->setText(tr("Password:"));
@ -290,6 +301,11 @@ void OptionsPage::setupTrafficBox()
void OptionsPage::setupGlobalBox()
{
m_explorerIntegrated = StartupUtil::isExplorerIntegrated();
m_cbExplorerMenu = ControlUtil::createCheckBox(
m_explorerIntegrated, [&](int) { ctrl()->setOthersEdited(true); });
m_cbExplorerMenu->setEnabled(OsUtil::isUserAdmin());
m_cbHotKeys = ControlUtil::createCheckBox(
settings()->hotKeyEnabled(), [&](bool) { setIniEdited(true); });
@ -300,6 +316,7 @@ void OptionsPage::setupGlobalBox()
auto langLayout = setupLangLayout();
auto layout = new QVBoxLayout();
layout->addWidget(m_cbExplorerMenu);
layout->addWidget(m_cbHotKeys);
layout->addLayout(passwordLayout);
layout->addWidget(m_btPasswordLock, 0, Qt::AlignCenter);

View File

@ -21,6 +21,7 @@ protected slots:
private:
void saveStartMode();
void saveExplorerIntegration();
void saveIni();
void retranslateComboStartMode();
@ -47,6 +48,7 @@ private:
private:
uint m_iniEdited : 1;
uint m_currentStartMode : 4;
uint m_explorerIntegrated : 1;
QGroupBox *m_gbStartup = nullptr;
QGroupBox *m_gbTraffic = nullptr;
@ -62,6 +64,7 @@ private:
QCheckBox *m_cbStopTraffic = nullptr;
QCheckBox *m_cbStopInetTraffic = nullptr;
QCheckBox *m_cbAllowAllNew = nullptr;
QCheckBox *m_cbExplorerMenu = nullptr;
QCheckBox *m_cbHotKeys = nullptr;
QCheckBox *m_cbPassword = nullptr;
QLineEdit *m_editPassword = nullptr;

View File

@ -30,10 +30,11 @@ static void setupAppStyle()
int main(int argc, char *argv[])
{
// Uninstall: Unregister booted provider, startup entries and exit
// Uninstall
if (argc > 1 && !strcmp(argv[1], "-u")) {
StartupUtil::setStartupMode(StartupUtil::StartupDisabled);
DriverCommon::provUnregister();
StartupUtil::setStartupMode(StartupUtil::StartupDisabled); // Remove auto-run and service
StartupUtil::integrateExplorer(false); // Remove Windows Explorer integration
DriverCommon::provUnregister(); // Unregister booted provider
return 0;
}

103
src/ui/util/regkey.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "regkey.h"
#define WIN32_LEAN_AND_MEAN
#include <qt_windows.h>
#define PREDEFINED_BASE_HANDLE quint32(HKEY_CLASSES_ROOT)
RegKey::RegKey(quint32 parentHandle, const QString &subKey, quint32 flags)
{
LPCWSTR subKeyStr = (LPCWSTR) subKey.utf16();
const REGSAM samDesired = ((flags & ReadOnly) != 0 ? KEY_READ : KEY_ALL_ACCESS)
| ((flags & Native64Key) != 0 ? KEY_WOW64_64KEY : 0)
| ((flags & Native32Key) != 0 ? KEY_WOW64_32KEY : 0);
if ((flags & Create) != 0) {
DWORD disposition;
RegCreateKeyEx((HKEY) parentHandle, subKeyStr, 0, nullptr, 0, samDesired, nullptr,
(PHKEY) &m_handle, &disposition);
} else {
RegOpenKeyEx((HKEY) parentHandle, subKeyStr, 0, samDesired, (PHKEY) &m_handle);
}
}
RegKey::RegKey(Root root, const QString &subKey, quint32 flags) :
RegKey(PREDEFINED_BASE_HANDLE + root, subKey, flags)
{
}
RegKey::RegKey(const RegKey &parent, const QString &subKey, quint32 flags) :
RegKey(parent.handle(), subKey, flags)
{
}
RegKey::~RegKey()
{
if (!isNull() && handle() < PREDEFINED_BASE_HANDLE) {
RegCloseKey((HKEY) handle());
}
}
bool RegKey::removeKey(const QString &subKey)
{
return !RegDeleteKey((HKEY) handle(), (LPCWSTR) subKey.utf16());
}
bool RegKey::clearTree(const QString &subKey)
{
return !RegDeleteTree((HKEY) handle(), subKey.isEmpty() ? nullptr : (LPCWSTR) subKey.utf16());
}
bool RegKey::removeRecursively(const QString &subKey)
{
return clearTree(subKey) && removeKey(subKey);
}
bool RegKey::removeValue(const QString &name)
{
return !RegDeleteValue((HKEY) handle(), (LPCWSTR) name.utf16());
}
bool RegKey::setValue(const QString &name, const QVariant &value)
{
QString text;
union {
qint64 i64;
qint32 i32;
} num;
const unsigned char *data = nullptr;
DWORD size = 0, type = REG_SZ;
switch (value.userType()) {
case QMetaType::UnknownType:
case QMetaType::Void:
case QMetaType::Nullptr:
break;
case QMetaType::Bool:
case QMetaType::Int:
case QMetaType::UInt:
num.i32 = value.toInt();
data = (const unsigned char *) &num.i32;
size = sizeof(qint32);
type = REG_DWORD;
break;
case QMetaType::LongLong:
case QMetaType::ULongLong:
num.i64 = value.toLongLong();
data = (const unsigned char *) &num.i64;
size = sizeof(qint64);
type = REG_QWORD;
break;
default:
text = value.toString();
data = (const unsigned char *) text.utf16();
size = sizeof(wchar_t) * (text.size() + 1); /* + terminating null character */
}
return !RegSetValueEx((HKEY) handle(), (LPCWSTR) name.utf16(), 0, type, data, size);
}
bool RegKey::contains(const QString &name) const
{
return !RegQueryValueEx((HKEY) handle(), (LPCWSTR) name.utf16(), 0, nullptr, nullptr, nullptr);
}

52
src/ui/util/regkey.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef REGKEY_H
#define REGKEY_H
#include <QObject>
#include <QVariant>
#include "../util/classhelpers.h"
class RegKey
{
public:
enum Root : qint8 { HKCR = 0, HKCU, HKLM, HKU };
enum OpenFlag : qint8 {
ReadOnly = 0x01,
ReadWrite = 0x02,
Create = 0x04,
Native64Key = 0x10,
Native32Key = 0x20,
DefaultReadOnly = (ReadOnly | Native64Key),
DefaultReadWrite = (ReadWrite | Native64Key),
DefaultCreate = (ReadWrite | Create | Native64Key)
};
protected:
explicit RegKey(quint32 parentHandle, const QString &subKey, quint32 flags);
public:
explicit RegKey(Root root, const QString &subKey = QString(), quint32 flags = DefaultReadOnly);
explicit RegKey(const RegKey &parent, const QString &subKey = QString(),
quint32 flags = DefaultReadOnly);
~RegKey();
CLASS_DELETE_COPY_MOVE(RegKey)
bool isNull() const { return m_handle == 0; }
bool removeKey(const QString &subKey);
bool clearTree(const QString &subKey = QString());
bool removeRecursively(const QString &subKey);
bool removeValue(const QString &name);
bool setValue(const QString &name, const QVariant &value);
bool setDefaultValue(const QVariant &value) { return setValue(QString(), value); }
bool contains(const QString &name) const;
protected:
quint32 handle() const { return m_handle; }
private:
quint32 m_handle = 0;
};
#endif // REGKEY_H

View File

@ -10,13 +10,18 @@
#include <fort_version.h>
#include "fileutil.h"
#include "regkey.h"
namespace {
const char *const regCurUserRun =
R"(HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run)";
const char *const regAllUsersRun =
R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run)";
constexpr RegKey::Root regCurUserRoot = RegKey::HKCU;
const char *const regCurUserRun = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Run)";
constexpr RegKey::Root regAllUsersRoot = RegKey::HKLM;
const char *const regAllUsersRun = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Run)";
constexpr RegKey::Root regShellRoot = RegKey::HKLM;
const char *const regShellMenu = R"(SOFTWARE\Classes\SystemFileAssociations\.exe\Shell)";
QString startupShortcutPath()
{
@ -26,56 +31,56 @@ QString startupShortcutPath()
QString wrappedAppFilePath()
{
const auto filePath = QCoreApplication::applicationFilePath().replace('/', '\\');
const auto filePath = FileUtil::toNativeSeparators(QCoreApplication::applicationFilePath());
return QString("\"%1\"").arg(filePath);
}
bool isAutorunForUser(const char *key)
bool isAutorunForUser(RegKey::Root root, const char *key)
{
const QSettings reg(key, QSettings::Registry64Format);
return !reg.value(APP_NAME).isNull();
const RegKey reg(root, key, RegKey::DefaultReadOnly);
return reg.contains(APP_NAME);
}
bool isAutorunForCurrentUser()
{
return isAutorunForUser(regCurUserRun);
return isAutorunForUser(regCurUserRoot, regCurUserRun);
}
bool isAutorunForAllUsers()
{
return isAutorunForUser(regAllUsersRun);
return isAutorunForUser(regAllUsersRoot, regAllUsersRun);
}
void setAutorunForUser(const char *key, const QString &command)
void setAutorunForUser(RegKey::Root root, const char *key, const QString &command)
{
QSettings reg(key, QSettings::Registry64Format);
RegKey reg(root, key, RegKey::DefaultReadWrite);
reg.setValue(APP_NAME, command);
}
void setAutorunForCurrentUser(const QString &command)
{
setAutorunForUser(regCurUserRun, command);
setAutorunForUser(regCurUserRoot, regCurUserRun, command);
}
void setAutorunForAllUsers(const QString &command)
{
setAutorunForUser(regAllUsersRun, command);
setAutorunForUser(regAllUsersRoot, regAllUsersRun, command);
}
void removeAutorunForUser(const char *key)
void removeAutorunForUser(RegKey::Root root, const char *key)
{
QSettings reg(key, QSettings::Registry64Format);
reg.remove(APP_NAME);
RegKey reg(root, key, RegKey::DefaultReadWrite);
reg.removeValue(APP_NAME);
}
void removeAutorunForCurrentUser()
{
removeAutorunForUser(regCurUserRun);
removeAutorunForUser(regCurUserRoot, regCurUserRun);
}
void removeAutorunForAllUsers()
{
removeAutorunForUser(regAllUsersRun);
removeAutorunForUser(regAllUsersRoot, regAllUsersRun);
}
bool installService(
@ -194,3 +199,27 @@ bool StartupUtil::isServiceMode(int mode)
{
return mode == StartupAllUsers || mode == StartupAllUsersBackground;
}
bool StartupUtil::isExplorerIntegrated()
{
const RegKey regShell(regShellRoot, regShellMenu, RegKey::DefaultReadOnly);
const RegKey reg(regShell, APP_NAME, RegKey::DefaultReadOnly);
return !reg.isNull();
}
void StartupUtil::integrateExplorer(bool integrate)
{
RegKey regShell(regShellRoot, regShellMenu, RegKey::DefaultReadWrite);
if (integrate) {
const QString wrappedPath = wrappedAppFilePath();
RegKey reg(regShell, APP_NAME, RegKey::DefaultCreate);
reg.setValue("icon", wrappedPath);
reg.setValue("MUIVerb", APP_NAME + QLatin1String(" ..."));
RegKey regCommand(reg, "command", RegKey::DefaultCreate);
regCommand.setDefaultValue(wrappedPath + " -w -c prog add \"%1\"");
} else {
regShell.removeRecursively(APP_NAME);
}
}

View File

@ -23,6 +23,9 @@ public:
static void setStartupMode(int mode, const QString &defaultLanguage = QString());
static bool isServiceMode(int mode);
static bool isExplorerIntegrated();
static void integrateExplorer(bool integrate);
};
#endif // STARTUPUTIL_H