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
parent
4ae0b655b2
commit
068baa9bb6
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -329,6 +329,7 @@ public:
|
|||
QStringList arguments;
|
||||
#if defined(Q_OS_WIN)
|
||||
QString nativeArguments;
|
||||
QProcess::CreateProcessArgumentModifier modifyCreateProcessArgs;
|
||||
#endif
|
||||
QProcessEnvironment environment;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue