From 3c689f78808dc71373336dc71b45ed71c4bf7e25 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <qtc-committer@nokia.com> Date: Tue, 3 Mar 2009 17:11:27 +0100 Subject: [PATCH] Fixes: Move Windows helpers for AbstractProcess in place for use with Windows Debugger. --- src/libs/utils/abstractprocess.h | 10 ++ src/libs/utils/abstractprocess_win.cpp | 117 ++++++++++++++++++ src/libs/utils/consoleprocess.h | 6 - src/libs/utils/consoleprocess_win.cpp | 57 +-------- src/libs/utils/utils.pro | 3 +- src/plugins/debugger/cdb/cdbdebugengine.cpp | 16 ++- src/plugins/projectexplorer/winguiprocess.cpp | 20 +-- 7 files changed, 146 insertions(+), 83 deletions(-) create mode 100644 src/libs/utils/abstractprocess_win.cpp diff --git a/src/libs/utils/abstractprocess.h b/src/libs/utils/abstractprocess.h index 72049c36c02..d3671f55a40 100644 --- a/src/libs/utils/abstractprocess.h +++ b/src/libs/utils/abstractprocess.h @@ -59,6 +59,16 @@ public: //signals: virtual void processError(const QString &error) = 0; +#ifdef Q_WS_WIN + // Add PATH and SystemRoot environment variables in case they are missing + static QStringList fixWinEnvironment(const QStringList &env); + // Quote a Windows command line correctly for the "CreateProcess" API + static QString createWinCommandline(const QString &program, const QStringList &args); + // Create a bytearray suitable to be passed on as environment + // to the "CreateProcess" API (0-terminated UTF 16 strings). + static QByteArray createWinEnvironment(const QStringList &env); +#endif + private: QString m_workingDir; QStringList m_environment; diff --git a/src/libs/utils/abstractprocess_win.cpp b/src/libs/utils/abstractprocess_win.cpp new file mode 100644 index 00000000000..1bb820c93fd --- /dev/null +++ b/src/libs/utils/abstractprocess_win.cpp @@ -0,0 +1,117 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#include "abstractprocess.h" + +#include <windows.h> + +namespace Core { +namespace Utils { + +QStringList AbstractProcess::fixWinEnvironment(const QStringList &env) +{ + QStringList envStrings = env; + // add PATH if necessary (for DLL loading) + if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) { + QByteArray path = qgetenv("PATH"); + if (!path.isEmpty()) + envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path))); + } + // add systemroot if needed + if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) { + QByteArray systemRoot = qgetenv("SystemRoot"); + if (!systemRoot.isEmpty()) + envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot))); + } + return envStrings; +} + +QString AbstractProcess::createWinCommandline(const QString &program, const QStringList &args) +{ + const QChar doubleQuote = QLatin1Char('"'); + const QChar blank = QLatin1Char(' '); + const QChar backSlash = QLatin1Char('\\'); + + QString programName = program; + if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote) && programName.contains(blank)) { + programName.insert(0, doubleQuote); + programName.append(doubleQuote); + } + // add the prgram as the first arrg ... it works better + programName.replace(QLatin1Char('/'), backSlash); + QString cmdLine = programName; + if (args.empty()) + return cmdLine; + + cmdLine += blank; + for (int i = 0; i < args.size(); ++i) { + QString tmp = args.at(i); + // in the case of \" already being in the string the \ must also be escaped + tmp.replace(QLatin1String("\\\""), QLatin1String("\\\\\"")); + // escape a single " because the arguments will be parsed + tmp.replace(QString(doubleQuote), QLatin1String("\\\"")); + if (tmp.isEmpty() || tmp.contains(blank) || tmp.contains('\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\" + QString endQuote(doubleQuote); + int i = tmp.length(); + while (i > 0 && tmp.at(i - 1) == backSlash) { + --i; + endQuote += backSlash; + } + cmdLine += QLatin1String(" \""); + cmdLine += tmp.left(i); + cmdLine += endQuote; + } else { + cmdLine += blank; + cmdLine += tmp; + } + } + return cmdLine; +} + +QByteArray AbstractProcess::createWinEnvironment(const QStringList &env) +{ + QByteArray envlist; + int pos = 0; + foreach (const QString &tmp, env) { + const uint tmpSize = sizeof(TCHAR) * (tmp.length() + 1); + envlist.resize(envlist.size() + tmpSize); + memcpy(envlist.data() + pos, tmp.utf16(), tmpSize); + pos += tmpSize; + } + envlist.resize(envlist.size() + 2); + envlist[pos++] = 0; + envlist[pos++] = 0; + return envlist; +} + +} //namespace Utils +} //namespace Core diff --git a/src/libs/utils/consoleprocess.h b/src/libs/utils/consoleprocess.h index ac8d4b3eb9d..370b256c166 100644 --- a/src/libs/utils/consoleprocess.h +++ b/src/libs/utils/consoleprocess.h @@ -67,12 +67,6 @@ public: int exitCode() const { return m_appCode; } // This will be the signal number if exitStatus == CrashExit QProcess::ExitStatus exitStatus() const { return m_appStatus; } -#ifdef Q_OS_WIN - // These are public for WinGuiProcess. Should be in AbstractProcess, but it has no .cpp so far. - static QString createCommandline(const QString &program, const QStringList &args); - static QStringList fixEnvironment(const QStringList &env); -#endif - signals: void processError(const QString &error); // These reflect the state of the actual client process diff --git a/src/libs/utils/consoleprocess_win.cpp b/src/libs/utils/consoleprocess_win.cpp index aa9f0d24659..ce5d15029d8 100644 --- a/src/libs/utils/consoleprocess_win.cpp +++ b/src/libs/utils/consoleprocess_win.cpp @@ -85,7 +85,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args) QTextStream out(m_tempFile); out.setCodec("UTF-16LE"); out.setGenerateByteOrderMark(false); - foreach (const QString &var, fixEnvironment(environment())) + foreach (const QString &var, fixWinEnvironment(environment())) out << var << QChar(0); out << QChar(0); } @@ -106,10 +106,10 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args) << m_stubServer.fullServerName() << workDir << (m_tempFile ? m_tempFile->fileName() : 0) - << createCommandline(program, args) + << createWinCommandline(program, args) << tr("Press <RETURN> to close this window..."); - QString cmdLine = createCommandline( + QString cmdLine = createWinCommandline( QCoreApplication::applicationDirPath() + "/qtcreator_process_stub.exe", stubArgs); bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(), @@ -262,54 +262,3 @@ void ConsoleProcess::stubExited() emit wrapperStopped(); } -QStringList ConsoleProcess::fixEnvironment(const QStringList &env) -{ - QStringList envStrings = env; - // add PATH if necessary (for DLL loading) - if (envStrings.filter(QRegExp("^PATH=",Qt::CaseInsensitive)).isEmpty()) { - QByteArray path = qgetenv("PATH"); - if (!path.isEmpty()) - envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path))); - } - // add systemroot if needed - if (envStrings.filter(QRegExp("^SystemRoot=",Qt::CaseInsensitive)).isEmpty()) { - QByteArray systemRoot = qgetenv("SystemRoot"); - if (!systemRoot.isEmpty()) - envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot))); - } - return envStrings; -} - -QString ConsoleProcess::createCommandline(const QString &program, const QStringList &args) -{ - QString programName = program; - if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(" ")) - programName = "\"" + programName + "\""; - programName.replace("/", "\\"); - - QString cmdLine; - // add the prgram as the first arrg ... it works better - cmdLine = programName + " "; - for (int i = 0; i < args.size(); ++i) { - QString tmp = args.at(i); - // in the case of \" already being in the string the \ must also be escaped - tmp.replace( "\\\"", "\\\\\"" ); - // escape a single " because the arguments will be parsed - tmp.replace( "\"", "\\\"" ); - if (tmp.isEmpty() || tmp.contains(' ') || tmp.contains('\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\" - QString endQuote("\""); - int i = tmp.length(); - while (i > 0 && tmp.at(i - 1) == '\\') { - --i; - endQuote += "\\"; - } - cmdLine += QString(" \"") + tmp.left(i) + endQuote; - } else { - cmdLine += ' ' + tmp; - } - } - return cmdLine; -} diff --git a/src/libs/utils/utils.pro b/src/libs/utils/utils.pro index 5c33948ae12..7649ab87486 100644 --- a/src/libs/utils/utils.pro +++ b/src/libs/utils/utils.pro @@ -27,7 +27,8 @@ SOURCES += \ synchronousprocess.cpp win32 { - SOURCES += consoleprocess_win.cpp \ + SOURCES += abstractprocess_win.cpp \ + consoleprocess_win.cpp \ winutils.cpp HEADERS += winutils.h } else { diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 4217378cf2e..d204e158c96 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -35,6 +35,7 @@ #include "stackhandler.h" #include <utils/qtcassert.h> +#include <utils/consoleprocess.h> #include <QtCore/QDebug> #include <QtCore/QTimerEvent> @@ -202,12 +203,19 @@ bool CdbDebugEngine::startDebugger() qWarning("CdbDebugEngine: attach to process not yet implemented!"); return false; } else { + QString cmd = Core::Utils::AbstractProcess::createWinCommandline(filename, m_d->m_debuggerManager->m_processArgs); + PCWSTR env = 0; + QByteArray envData; + if (!m_d->m_debuggerManager->m_environment.empty()) { + envData = Core::Utils::AbstractProcess::createWinEnvironment(Core::Utils::AbstractProcess::fixWinEnvironment(m_d->m_debuggerManager->m_environment)); + env = reinterpret_cast<PCWSTR>(envData.data()); + } HRESULT hr = m_d->m_pDebugClient->CreateProcess2Wide(NULL, - const_cast<PWSTR>(filename.utf16()), + const_cast<PWSTR>(cmd.utf16()), &dbgopts, sizeof(dbgopts), - NULL, // TODO: think about the initial directory - NULL); // TODO: think about setting the environment + m_d->m_debuggerManager->m_workingDir.utf16(), + env); if (FAILED(hr)) { //qWarning("CreateProcess2Wide failed"); m_d->m_debuggerManagerAccess->notifyInferiorExited(); @@ -503,7 +511,7 @@ void CdbDebugEngine::reloadRegisters() } void CdbDebugEngine::timerEvent(QTimerEvent* te) -{ +{ if (te->timerId() != m_d->m_watchTimer) return; diff --git a/src/plugins/projectexplorer/winguiprocess.cpp b/src/plugins/projectexplorer/winguiprocess.cpp index 86b90509186..75efc7cb736 100644 --- a/src/plugins/projectexplorer/winguiprocess.cpp +++ b/src/plugins/projectexplorer/winguiprocess.cpp @@ -34,22 +34,6 @@ using namespace ProjectExplorer::Internal; -static QByteArray createEnvironment(const QStringList &env) -{ - QByteArray envlist; - int pos = 0; - foreach (const QString &tmp, env) { - uint tmpSize = sizeof(TCHAR) * (tmp.length() + 1); - envlist.resize(envlist.size() + tmpSize); - memcpy(envlist.data() + pos, tmp.utf16(), tmpSize); - pos += tmpSize; - } - envlist.resize(envlist.size() + 2); - envlist[pos++] = 0; - envlist[pos++] = 0; - return envlist; -} - WinGuiProcess::WinGuiProcess(QObject *parent) : QThread(parent) { @@ -125,11 +109,11 @@ void WinGuiProcess::run() bool dbgInterface = setupDebugInterface(bufferReadyEvent, dataReadyEvent, sharedFile, sharedMem); - QString cmdLine = ConsoleProcess::createCommandline(m_program, m_args); + QString cmdLine = createWinCommandline(m_program, m_args); bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(), 0, 0, TRUE, CREATE_UNICODE_ENVIRONMENT, environment().isEmpty() ? 0 - : createEnvironment(ConsoleProcess::fixEnvironment(environment())).data(), + : createWinEnvironment(fixWinEnvironment(environment())).data(), workingDirectory().isEmpty() ? 0 : (WCHAR*)QDir::convertSeparators(workingDirectory()).utf16(), &si, m_pid); -- GitLab