Change PATH environment variable for cmd.exe with QProcessEnvironment

妖精的绣舞 提交于 2019-12-02 11:12:01
David Heffernan

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.

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");
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!