add a way to modify CreateProcess parameters

[ChangeLog][QtCore][QProcess] Added method
setCreateProcessArgumentsModifier to QProcess on Windows to enable
users to intercept and modify CreateProcess parameters.

With such a modifier, calling code can decide whether to inherit
handles, modify the STARTUPINFO struct, and pass its own
combination of process flags to CreateProcess.

Task-number: QTBUG-390
Task-number: QTBUG-6917
Task-number: QTBUG-9350
Task-number: QTBUG-24619
Change-Id: I14757dbbacfebb1c89f52402d36fba0ba9c45f3a
Reviewed-by: Björn Breitmeyer <bjoern.breitmeyer@kdab.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Joerg Bornemann 2015-06-05 16:57:18 +02:00
parent 4ae0b655b2
commit 068baa9bb6
6 changed files with 196 additions and 8 deletions

View File

@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QCoreApplication>
#include <QProcess>
#include <qt_windows.h>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
//! [0]
QProcess process;
process.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args)
{
args->flags |= CREATE_NEW_CONSOLE;
args->startupInfo->dwFlags &= ~STARTF_USESTDHANDLES;
args->startupInfo->dwFlags |= STARTF_USEFILLATTRIBUTE;
args->startupInfo->dwFillAttribute = BACKGROUND_BLUE | FOREGROUND_RED
| FOREGROUND_INTENSITY;
});
process.start("C:\\Windows\\System32\\cmd.exe", QStringList() << "/k" << "title" << "The Child Process");
//! [0]
return app.exec();
}

View File

@ -743,6 +743,47 @@ void QProcessPrivate::Channel::clear()
\sa exitStatus()
*/
/*!
\typedef QProcess::CreateProcessArgumentModifier
\note This typedef is only available on desktop Windows and Windows CE.
On Windows, QProcess uses the Win32 API function \c CreateProcess to
start child processes. While QProcess provides a comfortable way to start
processes without worrying about platform
details, it is in some cases desirable to fine-tune the parameters that are
passed to \c CreateProcess. This is done by defining a
\c CreateProcessArgumentModifier function and passing it to
\c setCreateProcessArgumentsModifier.
A \c CreateProcessArgumentModifier function takes one parameter: a pointer
to a \c CreateProcessArguments struct. The members of this struct will be
passed to \c CreateProcess after the \c CreateProcessArgumentModifier
function is called.
The following example demonstrates how to pass custom flags to
\c CreateProcess.
When starting a console process B from a console process A, QProcess will
reuse the console window of process A for process B by default. In this
example, a new console window with a custom color scheme is created for the
child process B instead.
\snippet qprocess/qprocess-createprocessargumentsmodifier.cpp 0
\sa QProcess::CreateProcessArguments
\sa setCreateProcessArgumentsModifier()
*/
/*!
\class QProcess::CreateProcessArguments
\note This struct is only available on the Windows platform.
This struct is a representation of all parameters of the Windows API
function \c CreateProcess. It is used as parameter for
\c CreateProcessArgumentModifier functions.
\sa QProcess::CreateProcessArgumentModifier
*/
/*!
\fn void QProcess::error(QProcess::ProcessError error)
\obsolete
@ -1563,6 +1604,39 @@ void QProcess::setNativeArguments(const QString &arguments)
d->nativeArguments = arguments;
}
/*!
\since 5.7
Returns a previously set \c CreateProcess modifier function.
\note This function is available only on the Windows platform.
\sa setCreateProcessArgumentsModifier()
\sa QProcess::CreateProcessArgumentModifier
*/
QProcess::CreateProcessArgumentModifier QProcess::createProcessArgumentsModifier() const
{
Q_D(const QProcess);
return d->modifyCreateProcessArgs;
}
/*!
\since 5.7
Sets the \a modifier for the \c CreateProcess Win32 API call.
Pass \c QProcess::CreateProcessArgumentModifier() to remove a previously set one.
\note This function is available only on the Windows platform and requires
C++11.
\sa QProcess::CreateProcessArgumentModifier
*/
void QProcess::setCreateProcessArgumentsModifier(CreateProcessArgumentModifier modifier)
{
Q_D(QProcess);
d->modifyCreateProcessArgs = modifier;
}
#endif
/*!

View File

@ -38,6 +38,8 @@
#include <QtCore/qstringlist.h>
#include <QtCore/qshareddata.h>
#include <functional>
QT_BEGIN_NAMESPACE
@ -48,6 +50,8 @@ typedef qint64 Q_PID;
#else
QT_END_NAMESPACE
typedef struct _PROCESS_INFORMATION *Q_PID;
typedef struct _SECURITY_ATTRIBUTES Q_SECURITY_ATTRIBUTES;
typedef struct _STARTUPINFOW Q_STARTUPINFO;
QT_BEGIN_NAMESPACE
#endif
@ -180,7 +184,23 @@ public:
#if defined(Q_OS_WIN)
QString nativeArguments() const;
void setNativeArguments(const QString &arguments);
#endif
struct CreateProcessArguments
{
const wchar_t *applicationName;
wchar_t *arguments;
Q_SECURITY_ATTRIBUTES *processAttributes;
Q_SECURITY_ATTRIBUTES *threadAttributes;
bool inheritHandles;
unsigned long flags;
void *environment;
const wchar_t *currentDirectory;
Q_STARTUPINFO *startupInfo;
Q_PID processInformation;
};
typedef std::function<void(CreateProcessArguments *)> CreateProcessArgumentModifier;
CreateProcessArgumentModifier createProcessArgumentsModifier() const;
void setCreateProcessArgumentsModifier(CreateProcessArgumentModifier modifier);
#endif // Q_OS_WIN
QString workingDirectory() const;
void setWorkingDirectory(const QString &dir);

View File

@ -329,6 +329,7 @@ public:
QStringList arguments;
#if defined(Q_OS_WIN)
QString nativeArguments;
QProcess::CreateProcessArgumentModifier modifyCreateProcessArgs;
#endif
QProcessEnvironment environment;

View File

@ -497,11 +497,21 @@ void QProcessPrivate::startProcess()
0, 0, 0,
stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
};
success = CreateProcess(0, (wchar_t*)args.utf16(),
0, 0, TRUE, dwCreationFlags,
environment.isEmpty() ? 0 : envlist.data(),
workingDirectory.isEmpty() ? 0 : (wchar_t*)QDir::toNativeSeparators(workingDirectory).utf16(),
&startupInfo, pid);
QProcess::CreateProcessArguments cpargs = {
0, (wchar_t*)args.utf16(),
0, 0, TRUE, dwCreationFlags,
environment.isEmpty() ? 0 : envlist.data(),
workingDirectory.isEmpty() ? 0 : (wchar_t*)QDir::toNativeSeparators(workingDirectory).utf16(),
&startupInfo, pid
};
if (modifyCreateProcessArgs)
modifyCreateProcessArgs(&cpargs);
success = CreateProcess(cpargs.applicationName, cpargs.arguments, cpargs.processAttributes,
cpargs.threadAttributes, cpargs.inheritHandles, cpargs.flags,
cpargs.environment, cpargs.currentDirectory, cpargs.startupInfo,
cpargs.processInformation);
QString errorString;
if (!success) {
// Capture the error string before we do CloseHandle below

View File

@ -138,7 +138,8 @@ private slots:
void spaceArgsTest();
#if defined(Q_OS_WIN)
void nativeArguments();
#endif
void createProcessArgumentsModifier();
#endif // Q_OS_WIN
void exitCodeTest();
void systemEnvironment();
void lockupsInStartDetached();
@ -1512,7 +1513,26 @@ void tst_QProcess::nativeArguments()
QCOMPARE(actual, expected);
}
#endif
void tst_QProcess::createProcessArgumentsModifier()
{
int calls = 0;
const QString reversedCommand = "lamroNssecorPtset/lamroNssecorPtset";
QProcess process;
process.setCreateProcessArgumentsModifier([&calls] (QProcess::CreateProcessArguments *args)
{
calls++;
std::reverse(args->arguments, args->arguments + wcslen(args->arguments) - 1);
});
process.start(reversedCommand);
QVERIFY2(process.waitForStarted(), qUtf8Printable(process.errorString()));
QVERIFY(process.waitForFinished());
QCOMPARE(calls, 1);
process.setCreateProcessArgumentsModifier(QProcess::CreateProcessArgumentModifier());
QVERIFY(!process.waitForStarted());
QCOMPARE(calls, 1);
}
#endif // Q_OS_WIN
void tst_QProcess::exitCodeTest()
{