diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c4523ab..96418479 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added
- added hwid display
- added Language Spoof "CustomLCID=1033" [#4024](https://github.com/sandboxie-plus/Sandboxie/pull/4024) (thanks Yeyixiao)
+- added option to always run the sandman UI as admin [#4090](https://github.com/sandboxie-plus/Sandboxie/issues/4090)
### Fixed
- fixed Getting two advanced supporter certificate popups everytime I open Sandbox Settings on any sandbox [#4074](https://github.com/sandboxie-plus/Sandboxie/issues/4074)
diff --git a/SandboxiePlus/SandMan/Forms/SettingsWindow.ui b/SandboxiePlus/SandMan/Forms/SettingsWindow.ui
index 073b00c6..d83142cb 100644
--- a/SandboxiePlus/SandMan/Forms/SettingsWindow.ui
+++ b/SandboxiePlus/SandMan/Forms/SettingsWindow.ui
@@ -2196,7 +2196,41 @@ Unlike the preview channel, it does not include untested, potentially breaking,
Sandboxie.ini Presets
- -
+
-
+
+
+ Only Administrator user accounts can use Pause Forcing Programs command
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Watch Sandboxie.ini for changes
+
+
+
+ -
+
+
+ Only Administrator user accounts can make changes
+
+
+
+ -
Qt::Vertical
@@ -2209,46 +2243,10 @@ Unlike the preview channel, it does not include untested, potentially breaking,
- -
-
-
- Clear password when main window becomes hidden
-
-
-
- -
-
-
- Only Administrator user accounts can use Pause Forcing Programs command
-
-
-
- -
-
-
- Watch Sandboxie.ini for changes
-
-
-
- -
-
-
- Change Password
-
-
-
- -
-
-
- Only Administrator user accounts can make changes
-
-
-
- -
+
-
- 75
true
true
@@ -2258,25 +2256,33 @@ Unlike the preview channel, it does not include untested, potentially breaking,
- -
+
-
+
+
+ Clear password when main window becomes hidden
+
+
+
+ -
Password must be entered in order to make changes
- -
-
-
- Qt::Horizontal
+
-
+
+
+ Change Password
-
-
- 40
- 20
-
+
+
+ -
+
+
+ Always run SandMan UI as Admin
-
+
diff --git a/SandboxiePlus/SandMan/Helpers/WinAdmin.cpp b/SandboxiePlus/SandMan/Helpers/WinAdmin.cpp
index 52535ac9..54ca5a88 100644
--- a/SandboxiePlus/SandMan/Helpers/WinAdmin.cpp
+++ b/SandboxiePlus/SandMan/Helpers/WinAdmin.cpp
@@ -164,4 +164,336 @@ bool AutorunEnable (bool is_enable)
}
return false;
-}
\ No newline at end of file
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// Skip UAC
+
+#define SKIP_UAC_TASK_NAME APP_NAME L"_SkipUac"
+
+#include
+#include
+
+struct MBSTR
+{
+ MBSTR (LPCWSTR asString = nullptr)
+ {
+ ms_bstr = asString ? SysAllocString (asString) : nullptr;
+ }
+
+ ~MBSTR ()
+ {
+ Free ();
+ }
+
+ operator BSTR() const
+ {
+ return ms_bstr;
+ }
+
+ MBSTR& operator=(LPCWSTR asString)
+ {
+ if (asString != ms_bstr)
+ {
+ Free ();
+ ms_bstr = asString ? ::SysAllocString (asString) : NULL;
+ }
+
+ return *this;
+ }
+
+ void Free ()
+ {
+ if (ms_bstr)
+ {
+ SysFreeString (ms_bstr);
+ ms_bstr = nullptr;
+ }
+ }
+protected:
+ BSTR ms_bstr;
+};
+
+bool SkipUacEnable (bool is_enable)
+{
+ bool result = false;
+ bool action_result = false;
+
+ ITaskService* service = nullptr;
+ ITaskFolder* folder = nullptr;
+ ITaskDefinition* task = nullptr;
+ IRegistrationInfo* reginfo = nullptr;
+ IPrincipal* principal = nullptr;
+ ITaskSettings* settings = nullptr;
+ IActionCollection* action_collection = nullptr;
+ IAction* action = nullptr;
+ IExecAction* exec_action = nullptr;
+ IRegisteredTask* registered_task = nullptr;
+
+ wchar_t szPath[MAX_PATH];
+ if (!GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
+ return false;
+ std::wstring::size_type pos = std::wstring(szPath).find_last_of( L"\\/" );
+ std::wstring dir = std::wstring(szPath).substr(0, pos);
+
+ MBSTR root (L"\\");
+ MBSTR name (SKIP_UAC_TASK_NAME);
+ MBSTR author (APP_NAME);
+ MBSTR path (szPath);
+ MBSTR directory (dir.c_str());
+ MBSTR args (L"$(Arg0)");
+ MBSTR timelimit (L"PT0S");
+
+ VARIANT vtEmpty = {VT_EMPTY};
+
+ if (SUCCEEDED (CoInitializeEx (nullptr, COINIT_APARTMENTTHREADED)))
+ {
+ //if (SUCCEEDED (CoInitializeSecurity (nullptr, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, 0, nullptr)))
+ {
+ if (SUCCEEDED (CoCreateInstance (CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&service)))
+ {
+ if (SUCCEEDED (service->Connect (vtEmpty, vtEmpty, vtEmpty, vtEmpty)))
+ {
+ if (SUCCEEDED (service->GetFolder (root, &folder)))
+ {
+ // create task
+ if (is_enable)
+ {
+ if (SUCCEEDED (service->NewTask (0, &task)))
+ {
+ if (SUCCEEDED (task->get_RegistrationInfo (®info)))
+ {
+ reginfo->put_Author (author);
+ reginfo->Release ();
+ }
+
+ if (SUCCEEDED (task->get_Principal (&principal)))
+ {
+ principal->put_RunLevel (TASK_RUNLEVEL_HIGHEST);
+ principal->Release ();
+ }
+
+ if (SUCCEEDED (task->get_Settings (&settings)))
+ {
+ settings->put_AllowHardTerminate (VARIANT_BOOL (FALSE));
+ settings->put_StartWhenAvailable (VARIANT_BOOL (FALSE));
+ settings->put_DisallowStartIfOnBatteries (VARIANT_BOOL (FALSE));
+ settings->put_StopIfGoingOnBatteries (VARIANT_BOOL (FALSE));
+ settings->put_MultipleInstances (TASK_INSTANCES_PARALLEL);
+ settings->put_ExecutionTimeLimit (timelimit);
+
+ settings->Release ();
+ }
+
+ if (SUCCEEDED (task->get_Actions (&action_collection)))
+ {
+ if (SUCCEEDED (action_collection->Create (TASK_ACTION_EXEC, &action)))
+ {
+ if (SUCCEEDED (action->QueryInterface (IID_IExecAction, (LPVOID*)&exec_action)))
+ {
+ if (
+ SUCCEEDED (exec_action->put_Path (path)) &&
+ SUCCEEDED (exec_action->put_WorkingDirectory (directory)) &&
+ SUCCEEDED (exec_action->put_Arguments (args))
+ )
+ {
+ action_result = true;
+ }
+
+ exec_action->Release ();
+ }
+
+ action->Release ();
+ }
+
+ action_collection->Release ();
+ }
+
+ if (action_result)
+ {
+ if (SUCCEEDED (folder->RegisterTaskDefinition (
+ name,
+ task,
+ TASK_CREATE_OR_UPDATE,
+ vtEmpty,
+ vtEmpty,
+ TASK_LOGON_INTERACTIVE_TOKEN,
+ vtEmpty,
+ ®istered_task)
+ ))
+ {
+ {
+ //ConfigSet (L"SkipUacIsEnabled", true);
+ result = true;
+
+ registered_task->Release ();
+ }
+ }
+
+ task->Release ();
+ }
+ }
+ }
+ else
+ {
+ // remove task
+ result = SUCCEEDED (folder->DeleteTask (name, 0));
+
+ //ConfigSet (L"SkipUacIsEnabled", false);
+ }
+
+ folder->Release ();
+ }
+ }
+
+ service->Release ();
+ }
+ }
+
+ CoUninitialize ();
+ }
+
+ return result;
+}
+
+bool SkipUacRun (bool test_only)
+{
+ bool result = false;
+
+ ITaskService* service = nullptr;
+ ITaskFolder* folder = nullptr;
+ IRegisteredTask* registered_task = nullptr;
+
+ ITaskDefinition* task = nullptr;
+ IActionCollection* action_collection = nullptr;
+ IAction* action = nullptr;
+ IExecAction* exec_action = nullptr;
+
+ IRunningTask* running_task = nullptr;
+
+ wchar_t szPath[MAX_PATH];
+ if (!GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
+ return false;
+
+ MBSTR root (L"\\");
+ MBSTR name (SKIP_UAC_TASK_NAME);
+
+ VARIANT vtEmpty = {VT_EMPTY};
+
+ if (SUCCEEDED (CoInitializeEx (nullptr, COINIT_APARTMENTTHREADED)))
+ {
+ //if (SUCCEEDED (CoInitializeSecurity (nullptr, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, 0, nullptr)))
+ {
+ if (SUCCEEDED (CoCreateInstance (CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&service)))
+ {
+ if (SUCCEEDED (service->Connect (vtEmpty, vtEmpty, vtEmpty, vtEmpty)))
+ {
+ if (SUCCEEDED (service->GetFolder (root, &folder)))
+ {
+ if (SUCCEEDED (folder->GetTask (name, ®istered_task)))
+ {
+ if (SUCCEEDED (registered_task->get_Definition (&task)))
+ {
+ if (SUCCEEDED (task->get_Actions (&action_collection)))
+ {
+ if (SUCCEEDED (action_collection->get_Item (1, &action)))
+ {
+ if (SUCCEEDED (action->QueryInterface (IID_IExecAction, (LPVOID*)&exec_action)))
+ {
+ BSTR path = nullptr;
+
+ exec_action->get_Path (&path);
+
+ PathUnquoteSpaces (path);
+
+ // check path is to current module
+ if (_wcsicmp (path, szPath) == 0)
+ {
+ if (test_only)
+ {
+ result = true;
+ }
+ else
+ {
+ std::wstring args;
+
+ // get arguments
+ {
+ INT numargs = 0;
+ LPWSTR* arga = CommandLineToArgvW(GetCommandLine(), &numargs);
+
+ for (INT i = 1; i < numargs; i++) {
+ if (i > 1)
+ args.append(L" ");
+ args.append(arga[i]);
+ }
+
+ LocalFree(arga);
+ }
+
+ variant_t params = args.c_str();
+
+ if (SUCCEEDED(registered_task->RunEx(params, TASK_RUN_NO_FLAGS, 0, nullptr, &running_task)))
+ {
+ UINT8 count = 3; // try count
+
+ do
+ {
+ QThread::msleep(250);
+
+ TASK_STATE state = TASK_STATE_UNKNOWN;
+
+ running_task->Refresh();
+ running_task->get_State(&state);
+
+ if (
+ state == TASK_STATE_RUNNING ||
+ state == TASK_STATE_READY ||
+ state == TASK_STATE_DISABLED
+ )
+ {
+ if (
+ state == TASK_STATE_RUNNING ||
+ state == TASK_STATE_READY
+ )
+ {
+ result = true;
+ }
+
+ break;
+ }
+ } while (count--);
+
+ running_task->Release();
+ }
+ }
+ }
+
+ exec_action->Release ();
+ }
+
+ action->Release ();
+ }
+
+ action_collection->Release ();
+ }
+
+ task->Release ();
+ }
+
+ registered_task->Release ();
+ }
+
+ folder->Release ();
+ }
+ }
+
+ service->Release ();
+ }
+ }
+
+ CoUninitialize ();
+ }
+
+ return result;
+}
diff --git a/SandboxiePlus/SandMan/Helpers/WinAdmin.h b/SandboxiePlus/SandMan/Helpers/WinAdmin.h
index 6c6a8215..211c1f6b 100644
--- a/SandboxiePlus/SandMan/Helpers/WinAdmin.h
+++ b/SandboxiePlus/SandMan/Helpers/WinAdmin.h
@@ -10,3 +10,6 @@ bool IsAdminUser(bool OnlyFull = false);
bool IsAutorunEnabled();
bool AutorunEnable(bool is_enable);
+
+bool SkipUacRun(bool test_only = false);
+bool SkipUacEnable(bool is_enable);
\ No newline at end of file
diff --git a/SandboxiePlus/SandMan/Windows/SettingsWindow.cpp b/SandboxiePlus/SandMan/Windows/SettingsWindow.cpp
index 7559f36e..2f2f41df 100644
--- a/SandboxiePlus/SandMan/Windows/SettingsWindow.cpp
+++ b/SandboxiePlus/SandMan/Windows/SettingsWindow.cpp
@@ -433,6 +433,10 @@ CSettingsWindow::CSettingsWindow(QWidget* parent)
connect(ui.chkWatchConfig, SIGNAL(stateChanged(int)), this, SLOT(OnOptChanged())); // not sbie ini
+ connect(ui.chkSkipUAC, SIGNAL(stateChanged(int)), this, SLOT(OnSkipUAC()));
+ ui.chkSkipUAC->setEnabled(IsElevated());
+ m_SkipUACChanged = false;
+
connect(ui.chkAdminOnly, SIGNAL(stateChanged(int)), this, SLOT(OnProtectionChange()));
connect(ui.chkPassRequired, SIGNAL(stateChanged(int)), this, SLOT(OnProtectionChange()));
connect(ui.btnSetPassword, SIGNAL(clicked(bool)), this, SLOT(OnSetPassword()));
@@ -953,6 +957,7 @@ void CSettingsWindow::LoadSettings()
ui.chkMonitorSize->setChecked(theConf->GetBool("Options/WatchBoxSize", false));
ui.chkWatchConfig->setChecked(theConf->GetBool("Options/WatchIni", true));
+ ui.chkSkipUAC->setChecked(SkipUacRun(true));
ui.chkScanMenu->setChecked(theConf->GetBool("Options/ScanStartMenu", true));
ui.cmbIntegrateMenu->setCurrentIndex(theConf->GetInt("Options/IntegrateStartMenu", 0));
@@ -1657,6 +1662,8 @@ void CSettingsWindow::SaveSettings()
theConf->SetValue("Options/WatchBoxSize", ui.chkMonitorSize->isChecked());
theConf->SetValue("Options/WatchIni", ui.chkWatchConfig->isChecked());
+ if (m_SkipUACChanged)
+ SkipUacEnable(ui.chkSkipUAC->isChecked());
theConf->SetValue("Options/ScanStartMenu", ui.chkScanMenu->isChecked());
int OldIntegrateStartMenu = theConf->GetInt("Options/IntegrateStartMenu", 0);
diff --git a/SandboxiePlus/SandMan/Windows/SettingsWindow.h b/SandboxiePlus/SandMan/Windows/SettingsWindow.h
index ab11cfa6..058970d7 100644
--- a/SandboxiePlus/SandMan/Windows/SettingsWindow.h
+++ b/SandboxiePlus/SandMan/Windows/SettingsWindow.h
@@ -88,6 +88,8 @@ private slots:
void OnOptChanged();
+ void OnSkipUAC() { m_SkipUACChanged = true; OnOptChanged(); }
+
void OnChangeGUI() { m_bRebuildUI = true; OnOptChanged(); }
void OnFeaturesChanged() { m_FeaturesChanged = true; OnGeneralChanged(); }
void OnGeneralChanged() { m_GeneralChanged = true; OnOptChanged(); }
@@ -176,6 +178,7 @@ protected:
bool m_VolumeChanged;
bool m_CompatChanged;
bool m_RunChanged;
+ bool m_SkipUACChanged;
bool m_ProtectionChanged;
bool m_GeneralChanged;
bool m_FeaturesChanged;