Windows/QProcess::startDetached(): Fall back to ShellExecuteEx() for UAC prompt.

When running a process that requires elevated privileges (such as
regedt32 or an installer), the Win32 API CreateProcess fails
with error ERROR_ELEVATION_REQUIRED.
Fall back to ShellExecuteEx() using the verb "runas" in that case,
bringing up the UAC prompt.

Task-number: QTBUG-7645
Change-Id: Iee82a86a30f78c5a49246d2c0d4566306f3afc71
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
This commit is contained in:
Friedemann Kleint 2016-04-05 11:26:20 +02:00 committed by Friedemann Kleint
parent 759b3f49c5
commit ab83912c79

View File

@ -42,6 +42,7 @@
#include <qfileinfo.h>
#include <qregexp.h>
#include <qwineventnotifier.h>
#include <private/qsystemlibrary_p.h>
#include <private/qthread_p.h>
#include <qdebug.h>
@ -808,8 +809,45 @@ bool QProcessPrivate::waitForWrite(int msecs)
return false;
}
// Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails
// with ERROR_ELEVATION_REQUIRED.
static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
const QString &workingDir, qint64 *pid)
{
typedef BOOL (WINAPI *ShellExecuteExType)(SHELLEXECUTEINFOW *);
static const ShellExecuteExType shellExecuteEx = // XP ServicePack 1 onwards.
reinterpret_cast<ShellExecuteExType>(QSystemLibrary::resolve(QLatin1String("shell32"),
"ShellExecuteExW"));
if (!shellExecuteEx)
return false;
const QString args = qt_create_commandline(QString(), arguments); // needs arguments only
SHELLEXECUTEINFOW shellExecuteExInfo;
memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW));
shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI;
shellExecuteExInfo.lpVerb = L"runas";
const QString program = QDir::toNativeSeparators(programIn);
shellExecuteExInfo.lpFile = reinterpret_cast<LPCWSTR>(program.utf16());
if (!args.isEmpty())
shellExecuteExInfo.lpParameters = reinterpret_cast<LPCWSTR>(args.utf16());
if (!workingDir.isEmpty())
shellExecuteExInfo.lpDirectory = reinterpret_cast<LPCWSTR>(workingDir.utf16());
shellExecuteExInfo.nShow = SW_SHOWNORMAL;
if (!shellExecuteEx(&shellExecuteExInfo))
return false;
if (pid)
*pid = qint64(GetProcessId(shellExecuteExInfo.hProcess));
CloseHandle(shellExecuteExInfo.hProcess);
return true;
}
bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
{
static const DWORD errorElevationRequired = 740;
QString args = qt_create_commandline(program, arguments);
bool success = false;
PROCESS_INFORMATION pinfo;
@ -829,6 +867,8 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
CloseHandle(pinfo.hProcess);
if (pid)
*pid = pinfo.dwProcessId;
} else if (GetLastError() == errorElevationRequired) {
success = startDetachedUacPrompt(program, arguments, workingDir, pid);
}
return success;