QProcess::startDetached: support custom process environment

Starting a detached process with a custom process environment can now be
achieved by:

    QProcess p;
    p.setProgram("foo");
    p.setProcessEnvironment(myEnv);
    p.startDetached();

[ChangeLog][QtCore][QProcess] Added the ability to set a custom process
environment for detached processes.

Task-number: QTBUG-2284
Change-Id: I49406dffb64fa2aed41ea05cb271bd42eeabb729
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Joerg Bornemann 2016-06-01 09:41:15 +02:00
parent 28666d167a
commit 6ba8708a2f
4 changed files with 42 additions and 11 deletions

View File

@ -937,6 +937,14 @@ bool QProcessPrivate::startDetached(qint64 *pid)
argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData()); argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData());
argv[arguments.size() + 1] = 0; argv[arguments.size() + 1] = 0;
// Duplicate the environment.
int envc = 0;
char **envp = nullptr;
if (environment.d.constData()) {
QProcessEnvironmentPrivate::MutexLocker locker(environment.d);
envp = _q_dupEnvironment(environment.d.constData()->hash, &envc);
}
QByteArray tmp; QByteArray tmp;
if (!program.contains(QLatin1Char('/'))) { if (!program.contains(QLatin1Char('/'))) {
const QString &exeFilePath = QStandardPaths::findExecutable(program); const QString &exeFilePath = QStandardPaths::findExecutable(program);
@ -946,7 +954,11 @@ bool QProcessPrivate::startDetached(qint64 *pid)
if (tmp.isEmpty()) if (tmp.isEmpty())
tmp = QFile::encodeName(program); tmp = QFile::encodeName(program);
argv[0] = tmp.data(); argv[0] = tmp.data();
qt_safe_execv(argv[0], argv);
if (envp)
qt_safe_execve(argv[0], argv, envp);
else
qt_safe_execv(argv[0], argv);
struct sigaction noaction; struct sigaction noaction;
memset(&noaction, 0, sizeof(noaction)); memset(&noaction, 0, sizeof(noaction));

View File

@ -867,6 +867,13 @@ bool QProcessPrivate::startDetached(qint64 *pid)
bool success = false; bool success = false;
PROCESS_INFORMATION pinfo; PROCESS_INFORMATION pinfo;
void *envPtr = nullptr;
QByteArray envlist;
if (environment.d.constData()) {
envlist = qt_create_environment(environment.d.constData()->hash);
envPtr = envlist.data();
}
DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW); DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
@ -875,7 +882,7 @@ bool QProcessPrivate::startDetached(qint64 *pid)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}; };
success = CreateProcess(0, (wchar_t*)args.utf16(), success = CreateProcess(0, (wchar_t*)args.utf16(),
0, 0, FALSE, dwCreationFlags, 0, 0, 0, FALSE, dwCreationFlags, envPtr,
workingDirectory.isEmpty() ? 0 : (wchar_t*)workingDirectory.utf16(), workingDirectory.isEmpty() ? 0 : (wchar_t*)workingDirectory.utf16(),
&startupInfo, &pinfo); &startupInfo, &pinfo);
@ -885,6 +892,8 @@ bool QProcessPrivate::startDetached(qint64 *pid)
if (pid) if (pid)
*pid = pinfo.dwProcessId; *pid = pinfo.dwProcessId;
} else if (GetLastError() == errorElevationRequired) { } else if (GetLastError() == errorElevationRequired) {
if (envPtr)
qWarning("QProcess: custom environment will be ignored for detached elevated process.");
success = startDetachedUacPrompt(program, arguments, workingDirectory, pid); success = startDetachedUacPrompt(program, arguments, workingDirectory, pid);
} }

View File

@ -65,6 +65,8 @@ int main(int argc, char **argv)
f.write(QByteArray::number(quint64(GetCurrentProcessId()))); f.write(QByteArray::number(quint64(GetCurrentProcessId())));
#endif #endif
f.putChar('\n'); f.putChar('\n');
f.write(qgetenv("tst_QProcess"));
f.putChar('\n');
f.close(); f.close();

View File

@ -126,7 +126,7 @@ private slots:
void systemEnvironment(); void systemEnvironment();
void lockupsInStartDetached(); void lockupsInStartDetached();
void waitForReadyReadForNonexistantProcess(); void waitForReadyReadForNonexistantProcess();
void detachedWorkingDirectoryAndPid(); void detachedProcessParameters();
void startFinishStartFinish(); void startFinishStartFinish();
void invalidProgramString_data(); void invalidProgramString_data();
void invalidProgramString(); void invalidProgramString();
@ -2030,7 +2030,7 @@ void tst_QProcess::fileWriterProcess()
} while (stopWatch.elapsed() < 3000); } while (stopWatch.elapsed() < 3000);
} }
void tst_QProcess::detachedWorkingDirectoryAndPid() void tst_QProcess::detachedProcessParameters()
{ {
qint64 pid; qint64 pid;
@ -2042,9 +2042,17 @@ void tst_QProcess::detachedWorkingDirectoryAndPid()
QVERIFY(QFile::exists(workingDir)); QVERIFY(QFile::exists(workingDir));
QStringList args; QVERIFY(qgetenv("tst_QProcess").isEmpty());
args << infoFile.fileName(); QByteArray envVarValue("foobarbaz");
QVERIFY(QProcess::startDetached(QDir::currentPath() + QLatin1String("/testDetached/testDetached"), args, workingDir, &pid)); QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
environment.insert(QStringLiteral("tst_QProcess"), QString::fromUtf8(envVarValue));
QProcess process;
process.setProgram(QDir::currentPath() + QLatin1String("/testDetached/testDetached"));
process.setArguments(QStringList(infoFile.fileName()));
process.setWorkingDirectory(workingDir);
process.setProcessEnvironment(environment);
QVERIFY(process.startDetached(&pid));
QFileInfo fi(infoFile); QFileInfo fi(infoFile);
fi.setCaching(false); fi.setCaching(false);
@ -2055,10 +2063,9 @@ void tst_QProcess::detachedWorkingDirectoryAndPid()
} }
QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text)); QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text));
QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()); QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()).trimmed();
actualWorkingDir.chop(1); // strip off newline QByteArray processIdString = infoFile.readLine().trimmed();
QByteArray processIdString = infoFile.readLine(); QByteArray actualEnvVarValue = infoFile.readLine().trimmed();
processIdString.chop(1);
infoFile.close(); infoFile.close();
infoFile.remove(); infoFile.remove();
@ -2068,6 +2075,7 @@ void tst_QProcess::detachedWorkingDirectoryAndPid()
QCOMPARE(actualWorkingDir, workingDir); QCOMPARE(actualWorkingDir, workingDir);
QCOMPARE(actualPid, pid); QCOMPARE(actualPid, pid);
QCOMPARE(actualEnvVarValue, envVarValue);
} }
void tst_QProcess::switchReadChannels() void tst_QProcess::switchReadChannels()