I want to start cmd.exe from a Qt app with a specific PATH set. I insert \"Path\" in QProcessEnvironment and set that environment to QProcess. Then I startDetached \"cmd\". On t
As David answered startDetached
doesn't use the environment. So I went and take the original code and adjusted it a bit so it works (for me at least).
WProcess.h:
#ifndef WPROCESS_H
#define WPROCESS_H
#include <QProcessEnvironment>
class WProcess
{
public:
WProcess();
void setProcessEnvironment(const QProcessEnvironment &value) {environment = value;}
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDir);
private:
QProcessEnvironment environment;
};
#endif // WPROCESS_H
WProcess.cpp:
#include "WProcess.h"
#include <Windows.h>
#include <WinBase.h>
static QString w_create_commandline(const QString &program, const QStringList &arguments)
{
QString args;
if (!program.isEmpty()) {
QString programName = program;
if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
// add the prgram as the first arg ... it works better
args = programName + QLatin1Char(' ');
}
for (int i=0; i<arguments.size(); ++i) {
QString tmp = arguments.at(i);
// Quotes are escaped and their preceding backslashes are doubled.
tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
// The argument must not end with a \ since this would be interpreted
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
int i = tmp.length();
while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
--i;
tmp.insert(i, QLatin1Char('"'));
tmp.prepend(QLatin1Char('"'));
}
args += QLatin1Char(' ') + tmp;
}
return args;
}
static QByteArray w_create_environment(const QProcessEnvironment &environment)
{
QByteArray envlist;
if (!environment.isEmpty())
{
static const wchar_t equal = L'=';
static const wchar_t nul = L'\0';
int pos = 0;
QStringList keys = environment.keys();
foreach(QString key, keys)
{
QString value = environment.value(key);
uint tmpSize = sizeof(wchar_t) * (key.length() + value.length() + 2);
// ignore empty strings
if (tmpSize != sizeof(wchar_t) * 2)
{
envlist.resize(envlist.size() + tmpSize);
tmpSize = key.length() * sizeof(wchar_t);
memcpy(envlist.data() + pos, key.utf16(), tmpSize);
pos += tmpSize;
memcpy(envlist.data() + pos, &equal, sizeof(wchar_t));
pos += sizeof(wchar_t);
tmpSize = value.length() * sizeof(wchar_t);
memcpy(envlist.data() + pos, value.utf16(), tmpSize);
pos += tmpSize;
memcpy(envlist.data() + pos, &nul, sizeof(wchar_t));
pos += sizeof(wchar_t);
}
}
// add the 2 terminating 0 (actually 4, just to be on the safe side)
envlist.resize( envlist.size()+4 );
envlist[pos++] = 0;
envlist[pos++] = 0;
envlist[pos++] = 0;
envlist[pos++] = 0;
}
return envlist;
}
WProcess::WProcess()
{
}
bool WProcess::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir)
{
QByteArray envlist;
if (!environment.isEmpty())
{
envlist = w_create_environment(environment);
}
QString args = w_create_commandline(program, arguments);
bool success = false;
PROCESS_INFORMATION pinfo;
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
success = CreateProcess(0, (wchar_t*)args.utf16(),
0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE,
envlist.isEmpty() ? 0 : envlist.data(),
workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
&startupInfo, &pinfo);
if (success)
{
CloseHandle(pinfo.hThread);
CloseHandle(pinfo.hProcess);
//if (pid) *pid = pinfo.dwProcessId;
}
return success;
}
Usage, open command prompt in C:\Qt\Qt-creator and set path to "mypath".
WProcess process;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("Path", "mypath");
process.setProcessEnvironment(env);
process.startDetached("cmd", QStringList(), "C:\\Qt\\Qt-creator");
The startDetached
method is a static method. So all the state that you applied to the process
object is just ignored, because the method cannot see it. If you start the process with start()
instead, the new process will pick up your environment.
process.start("cmd", args);
Of course, you want to the new process to be detached so that the parent can terminate without forcing the new process also to terminate. From what I can tell, the QProcess
class does not offer you a way to achieve that easily. You could modify the environment of the parent process so that the new process inherits those modifications, but that does not sound at all desirable.
This question presents a possible workaround: Detaching a started process.