From ab83912c7900805987f7efe38cee2c60baf5f315 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 5 Apr 2016 11:26:20 +0200 Subject: 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 Reviewed-by: Joerg Bornemann --- src/corelib/io/qprocess_win.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'src/corelib/io/qprocess_win.cpp') diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index e7cd9d9a17..7e9cffe129 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -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(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(program.utf16()); + if (!args.isEmpty()) + shellExecuteExInfo.lpParameters = reinterpret_cast(args.utf16()); + if (!workingDir.isEmpty()) + shellExecuteExInfo.lpDirectory = reinterpret_cast(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; -- cgit v1.2.3