Debugger/Console stub: Pass on main thread id on Windows.

Introduce private class to utils/ConsoloProcess as not to expose
<windows.h> from its header.

Task-number: QTCREATORBUG-1020
This commit is contained in:
Friedemann Kleint
2010-09-02 13:39:19 +02:00
parent a340cd118e
commit 6650275e76
5 changed files with 265 additions and 176 deletions

View File

@@ -66,9 +66,9 @@ QString ConsoleProcess::msgCannotCreateTempDir(const QString & dir, const QStrin
return tr("Cannot create temporary directory '%1': %2").arg(dir, why); return tr("Cannot create temporary directory '%1': %2").arg(dir, why);
} }
QString ConsoleProcess::msgUnexpectedOutput() QString ConsoleProcess::msgUnexpectedOutput(const QByteArray &what)
{ {
return tr("Unexpected output from helper program."); return tr("Unexpected output from helper program (%1).").arg(QString::fromAscii(what));
} }
QString ConsoleProcess::msgCannotChangeToWorkDir(const QString & dir, const QString &why) QString ConsoleProcess::msgCannotChangeToWorkDir(const QString & dir, const QString &why)

View File

@@ -36,22 +36,14 @@
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QProcess> #include <QtCore/QProcess>
#include <QtCore/QScopedPointer>
#include <QtNetwork/QLocalServer>
#ifdef Q_OS_WIN
#include <windows.h>
QT_BEGIN_NAMESPACE
class QWinEventNotifier;
QT_END_NAMESPACE
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QSettings; class QSettings;
class QTemporaryFile;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Utils { namespace Utils {
struct ConsoleProcessPrivate;
class QTCREATOR_UTILS_EXPORT ConsoleProcess : public QObject, public AbstractProcess class QTCREATOR_UTILS_EXPORT ConsoleProcess : public QObject, public AbstractProcess
{ {
@@ -65,16 +57,21 @@ public:
bool start(const QString &program, const QStringList &args); bool start(const QString &program, const QStringList &args);
void stop(); void stop();
void setMode(Mode m) { m_mode = m; } void setMode(Mode m);
Mode mode() const { return m_mode; } Mode mode() const;
bool isRunning() const; // This reflects the state of the console+stub bool isRunning() const; // This reflects the state of the console+stub
qint64 applicationPID() const { return m_appPid; } qint64 applicationPID() const;
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
qint64 applicationMainThreadID() const;
#endif
int exitCode() const;
QProcess::ExitStatus exitStatus() const;
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
void setSettings(QSettings *settings) { m_settings = settings; } void setSettings(QSettings *settings);
static QString defaultTerminalEmulator(); static QString defaultTerminalEmulator();
static QString terminalEmulator(const QSettings *settings); static QString terminalEmulator(const QSettings *settings);
static void setTerminalEmulator(QSettings *settings, const QString &term); static void setTerminalEmulator(QSettings *settings, const QString &term);
@@ -104,7 +101,7 @@ private:
static QString msgPromptToClose(); static QString msgPromptToClose();
static QString msgCannotCreateTempFile(const QString &why); static QString msgCannotCreateTempFile(const QString &why);
static QString msgCannotCreateTempDir(const QString & dir, const QString &why); static QString msgCannotCreateTempDir(const QString & dir, const QString &why);
static QString msgUnexpectedOutput(); static QString msgUnexpectedOutput(const QByteArray &what);
static QString msgCannotChangeToWorkDir(const QString & dir, const QString &why); static QString msgCannotChangeToWorkDir(const QString & dir, const QString &why);
static QString msgCannotExecute(const QString & p, const QString &why); static QString msgCannotExecute(const QString & p, const QString &why);
@@ -115,25 +112,7 @@ private:
void cleanupInferior(); void cleanupInferior();
#endif #endif
Mode m_mode; QScopedPointer<ConsoleProcessPrivate> d;
qint64 m_appPid;
int m_appCode;
QString m_executable;
QProcess::ExitStatus m_appStatus;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket;
QTemporaryFile *m_tempFile;
#ifdef Q_OS_WIN
PROCESS_INFORMATION *m_pid;
HANDLE m_hInferior;
QWinEventNotifier *inferiorFinishedNotifier;
QWinEventNotifier *processFinishedNotifier;
#else
QProcess m_process;
QByteArray m_stubServerDir;
QSettings *m_settings;
#endif
}; };
} //namespace Utils } //namespace Utils

View File

@@ -35,6 +35,7 @@
#include <QtCore/QTemporaryFile> #include <QtCore/QTemporaryFile>
#include <QtNetwork/QLocalSocket> #include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@@ -42,19 +43,39 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
using namespace Utils; namespace Utils {
struct ConsoleProcessPrivate {
ConsoleProcessPrivate();
ConsoleProcess::ConsoleProcess(QObject *parent) : ConsoleProcess::Mode m_mode;
QObject(parent), qint64 m_appPid;
m_mode(Run), qint64 m_appMainThreadId;
int m_appCode;
QString m_executable;
QProcess::ExitStatus m_appStatus;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket;
QTemporaryFile *m_tempFile;
QProcess m_process;
QByteArray m_stubServerDir;
QSettings *m_settings;
};
ConsoleProcessPrivate::ConsoleProcessPrivate() :
m_mode(ConsoleProcess::Run),
m_appPid(0), m_appPid(0),
m_stubSocket(0), m_stubSocket(0),
m_settings(0) m_settings(0)
{ {
connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable())); }
ConsoleProcess::ConsoleProcess(QObject *parent) :
QObject(parent), d(new ConsoleProcessPrivate)
{
connect(&d->m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
m_process.setProcessChannelMode(QProcess::ForwardedChannels); d->m_process.setProcessChannelMode(QProcess::ForwardedChannels);
connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), connect(&d->m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
SLOT(stubExited())); SLOT(stubExited()));
} }
@@ -63,6 +84,36 @@ ConsoleProcess::~ConsoleProcess()
stop(); stop();
} }
void ConsoleProcess::setMode(Mode m)
{
d->m_mode = m;
}
ConsoleProcess::Mode ConsoleProcess::mode() const
{
return d->m_mode;
}
qint64 ConsoleProcess::applicationPID() const
{
return d->m_appPid;
}
int ConsoleProcess::exitCode() const
{
return d->m_appCode;
} // This will be the signal number if exitStatus == CrashExit
QProcess::ExitStatus ConsoleProcess::exitStatus() const
{
return d->m_appStatus;
}
void ConsoleProcess::setSettings(QSettings *settings)
{
d->m_settings = settings;
}
bool ConsoleProcess::start(const QString &program, const QStringList &args) bool ConsoleProcess::start(const QString &program, const QStringList &args)
{ {
if (isRunning()) if (isRunning())
@@ -75,45 +126,45 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
} }
if (!environment().isEmpty()) { if (!environment().isEmpty()) {
m_tempFile = new QTemporaryFile(); d->m_tempFile = new QTemporaryFile();
if (!m_tempFile->open()) { if (!d->m_tempFile->open()) {
stubServerShutdown(); stubServerShutdown();
emit processMessage(msgCannotCreateTempFile(m_tempFile->errorString()), true); emit processMessage(msgCannotCreateTempFile(d->m_tempFile->errorString()), true);
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
return false; return false;
} }
foreach (const QString &var, environment()) { foreach (const QString &var, environment()) {
m_tempFile->write(var.toLocal8Bit()); d->m_tempFile->write(var.toLocal8Bit());
m_tempFile->write("", 1); d->m_tempFile->write("", 1);
} }
m_tempFile->flush(); d->m_tempFile->flush();
} }
QStringList xtermArgs = terminalEmulator(m_settings).split(QLatin1Char(' ')); // FIXME: quoting QStringList xtermArgs = terminalEmulator(d->m_settings).split(QLatin1Char(' ')); // FIXME: quoting
xtermArgs xtermArgs
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
<< (QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/qtcreator_process_stub")) << (QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/qtcreator_process_stub"))
#else #else
<< (QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_process_stub")) << (QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_process_stub"))
#endif #endif
<< modeOption(m_mode) << modeOption(d->m_mode)
<< m_stubServer.fullServerName() << d->m_stubServer.fullServerName()
<< msgPromptToClose() << msgPromptToClose()
<< workingDirectory() << workingDirectory()
<< (m_tempFile ? m_tempFile->fileName() : QString()) << (d->m_tempFile ? d->m_tempFile->fileName() : QString())
<< program << args; << program << args;
QString xterm = xtermArgs.takeFirst(); QString xterm = xtermArgs.takeFirst();
m_process.start(xterm, xtermArgs); d->m_process.start(xterm, xtermArgs);
if (!m_process.waitForStarted()) { if (!d->m_process.waitForStarted()) {
stubServerShutdown(); stubServerShutdown();
emit processMessage(tr("Cannot start the terminal emulator '%1'.").arg(xterm), true); emit processMessage(tr("Cannot start the terminal emulator '%1'.").arg(xterm), true);
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
return false; return false;
} }
m_executable = program; d->m_executable = program;
emit wrapperStarted(); emit wrapperStarted();
return true; return true;
} }
@@ -123,16 +174,16 @@ void ConsoleProcess::stop()
if (!isRunning()) if (!isRunning())
return; return;
stubServerShutdown(); stubServerShutdown();
m_appPid = 0; d->m_appPid = 0;
m_process.terminate(); d->m_process.terminate();
if (!m_process.waitForFinished(1000)) if (!d->m_process.waitForFinished(1000))
m_process.kill(); d->m_process.kill();
m_process.waitForFinished(); d->m_process.waitForFinished();
} }
bool ConsoleProcess::isRunning() const bool ConsoleProcess::isRunning() const
{ {
return m_process.state() != QProcess::NotRunning; return d->m_process.state() != QProcess::NotRunning;
} }
QString ConsoleProcess::stubServerListen() QString ConsoleProcess::stubServerListen()
@@ -148,34 +199,34 @@ QString ConsoleProcess::stubServerListen()
stubFifoDir = QFile::encodeName(tf.fileName()); stubFifoDir = QFile::encodeName(tf.fileName());
} }
// By now the temp file was deleted again // By now the temp file was deleted again
m_stubServerDir = QFile::encodeName(stubFifoDir); d->m_stubServerDir = QFile::encodeName(stubFifoDir);
if (!::mkdir(m_stubServerDir.constData(), 0700)) if (!::mkdir(d->m_stubServerDir.constData(), 0700))
break; break;
if (errno != EEXIST) if (errno != EEXIST)
return msgCannotCreateTempDir(stubFifoDir, QString::fromLocal8Bit(strerror(errno))); return msgCannotCreateTempDir(stubFifoDir, QString::fromLocal8Bit(strerror(errno)));
} }
const QString stubServer = stubFifoDir + "/stub-socket"; const QString stubServer = stubFifoDir + "/stub-socket";
if (!m_stubServer.listen(stubServer)) { if (!d->m_stubServer.listen(stubServer)) {
::rmdir(m_stubServerDir.constData()); ::rmdir(d->m_stubServerDir.constData());
return tr("Cannot create socket '%1': %2").arg(stubServer, m_stubServer.errorString()); return tr("Cannot create socket '%1': %2").arg(stubServer, d->m_stubServer.errorString());
} }
return QString(); return QString();
} }
void ConsoleProcess::stubServerShutdown() void ConsoleProcess::stubServerShutdown()
{ {
delete m_stubSocket; delete d->m_stubSocket;
m_stubSocket = 0; d->m_stubSocket = 0;
if (m_stubServer.isListening()) { if (d->m_stubServer.isListening()) {
m_stubServer.close(); d->m_stubServer.close();
::rmdir(m_stubServerDir.constData()); ::rmdir(d->m_stubServerDir.constData());
} }
} }
void ConsoleProcess::stubConnectionAvailable() void ConsoleProcess::stubConnectionAvailable()
{ {
m_stubSocket = m_stubServer.nextPendingConnection(); d->m_stubSocket = d->m_stubServer.nextPendingConnection();
connect(m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput())); connect(d->m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
} }
static QString errorMsg(int code) static QString errorMsg(int code)
@@ -185,33 +236,33 @@ static QString errorMsg(int code)
void ConsoleProcess::readStubOutput() void ConsoleProcess::readStubOutput()
{ {
while (m_stubSocket->canReadLine()) { while (d->m_stubSocket->canReadLine()) {
QByteArray out = m_stubSocket->readLine(); QByteArray out = d->m_stubSocket->readLine();
out.chop(1); // \n out.chop(1); // \n
if (out.startsWith("err:chdir ")) { if (out.startsWith("err:chdir ")) {
emit processMessage(msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out.mid(10).toInt())), true); emit processMessage(msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out.mid(10).toInt())), true);
} else if (out.startsWith("err:exec ")) { } else if (out.startsWith("err:exec ")) {
emit processMessage(msgCannotExecute(m_executable, errorMsg(out.mid(9).toInt())), true); emit processMessage(msgCannotExecute(d->m_executable, errorMsg(out.mid(9).toInt())), true);
} else if (out.startsWith("pid ")) { } else if (out.startsWith("pid ")) {
// Will not need it any more // Will not need it any more
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
m_appPid = out.mid(4).toInt(); d->m_appPid = out.mid(4).toInt();
emit processStarted(); emit processStarted();
} else if (out.startsWith("exit ")) { } else if (out.startsWith("exit ")) {
m_appStatus = QProcess::NormalExit; d->m_appStatus = QProcess::NormalExit;
m_appCode = out.mid(5).toInt(); d->m_appCode = out.mid(5).toInt();
m_appPid = 0; d->m_appPid = 0;
emit processStopped(); emit processStopped();
} else if (out.startsWith("crash ")) { } else if (out.startsWith("crash ")) {
m_appStatus = QProcess::CrashExit; d->m_appStatus = QProcess::CrashExit;
m_appCode = out.mid(6).toInt(); d->m_appCode = out.mid(6).toInt();
m_appPid = 0; d->m_appPid = 0;
emit processStopped(); emit processStopped();
} else { } else {
emit processMessage(msgUnexpectedOutput(), true); emit processMessage(msgUnexpectedOutput(out), true);
m_process.terminate(); d->m_process.terminate();
break; break;
} }
} }
@@ -220,15 +271,15 @@ void ConsoleProcess::readStubOutput()
void ConsoleProcess::stubExited() void ConsoleProcess::stubExited()
{ {
// The stub exit might get noticed before we read the error status. // The stub exit might get noticed before we read the error status.
if (m_stubSocket && m_stubSocket->state() == QLocalSocket::ConnectedState) if (d->m_stubSocket && d->m_stubSocket->state() == QLocalSocket::ConnectedState)
m_stubSocket->waitForDisconnected(); d->m_stubSocket->waitForDisconnected();
stubServerShutdown(); stubServerShutdown();
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
if (m_appPid) { if (d->m_appPid) {
m_appStatus = QProcess::CrashExit; d->m_appStatus = QProcess::CrashExit;
m_appCode = -1; d->m_appCode = -1;
m_appPid = 0; d->m_appPid = 0;
emit processStopped(); // Maybe it actually did not, but keep state consistent emit processStopped(); // Maybe it actually did not, but keep state consistent
} }
emit wrapperStopped(); emit wrapperStopped();
@@ -257,3 +308,4 @@ void ConsoleProcess::setTerminalEmulator(QSettings *settings, const QString &ter
{ {
return settings->setValue(QLatin1String("General/TerminalEmulator"), term); return settings->setValue(QLatin1String("General/TerminalEmulator"), term);
} }
} // namespace Utils

View File

@@ -30,6 +30,8 @@
#include "consoleprocess.h" #include "consoleprocess.h"
#include "winutils.h" #include "winutils.h"
#include <windows.h>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QTemporaryFile> #include <QtCore/QTemporaryFile>
@@ -37,15 +39,32 @@
#include <QtCore/private/qwineventnotifier_p.h> #include <QtCore/private/qwineventnotifier_p.h>
#include <QtNetwork/QLocalSocket> #include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <stdlib.h> #include <stdlib.h>
using namespace Utils; namespace Utils {
struct ConsoleProcessPrivate {
ConsoleProcessPrivate();
ConsoleProcess::ConsoleProcess(QObject *parent) : ConsoleProcess::Mode m_mode;
QObject(parent), qint64 m_appPid;
m_mode(Run), qint64 m_appMainThreadId;
m_appPid(0), int m_appCode;
QString m_executable;
QProcess::ExitStatus m_appStatus;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket;
QTemporaryFile *m_tempFile;
PROCESS_INFORMATION *m_pid;
HANDLE m_hInferior;
QWinEventNotifier *inferiorFinishedNotifier;
QWinEventNotifier *processFinishedNotifier;
};
ConsoleProcessPrivate::ConsoleProcessPrivate() :
m_mode(ConsoleProcess::Run),
m_appPid(0),m_appMainThreadId(0),
m_stubSocket(0), m_stubSocket(0),
m_tempFile(0), m_tempFile(0),
m_pid(0), m_pid(0),
@@ -53,7 +72,12 @@ ConsoleProcess::ConsoleProcess(QObject *parent) :
inferiorFinishedNotifier(0), inferiorFinishedNotifier(0),
processFinishedNotifier(0) processFinishedNotifier(0)
{ {
connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable())); }
ConsoleProcess::ConsoleProcess(QObject *parent) :
QObject(parent), d(new ConsoleProcessPrivate)
{
connect(&d->m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
} }
ConsoleProcess::~ConsoleProcess() ConsoleProcess::~ConsoleProcess()
@@ -61,6 +85,36 @@ ConsoleProcess::~ConsoleProcess()
stop(); stop();
} }
void ConsoleProcess::setMode(Mode m)
{
d->m_mode = m;
}
ConsoleProcess::Mode ConsoleProcess::mode() const
{
return d->m_mode;
}
qint64 ConsoleProcess::applicationPID() const
{
return d->m_appPid;
}
qint64 ConsoleProcess::applicationMainThreadID() const
{
return d->m_appMainThreadId;
}
int ConsoleProcess::exitCode() const
{
return d->m_appCode;
} // This will be the signal number if exitStatus == CrashExit
QProcess::ExitStatus ConsoleProcess::exitStatus() const
{
return d->m_appStatus;
}
bool ConsoleProcess::start(const QString &program, const QStringList &args) bool ConsoleProcess::start(const QString &program, const QStringList &args)
{ {
if (isRunning()) if (isRunning())
@@ -73,15 +127,15 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
} }
if (!environment().isEmpty()) { if (!environment().isEmpty()) {
m_tempFile = new QTemporaryFile(); d->m_tempFile = new QTemporaryFile();
if (!m_tempFile->open()) { if (!d->m_tempFile->open()) {
stubServerShutdown(); stubServerShutdown();
emit processMessage(msgCannotCreateTempFile(m_tempFile->errorString()), true); emit processMessage(msgCannotCreateTempFile(d->m_tempFile->errorString()), true);
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
return false; return false;
} }
QTextStream out(m_tempFile); QTextStream out(d->m_tempFile);
out.setCodec("UTF-16LE"); out.setCodec("UTF-16LE");
out.setGenerateByteOrderMark(false); out.setGenerateByteOrderMark(false);
foreach (const QString &var, fixWinEnvironment(environment())) foreach (const QString &var, fixWinEnvironment(environment()))
@@ -93,18 +147,18 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
ZeroMemory(&si, sizeof(si)); ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si); si.cb = sizeof(si);
m_pid = new PROCESS_INFORMATION; d->m_pid = new PROCESS_INFORMATION;
ZeroMemory(m_pid, sizeof(PROCESS_INFORMATION)); ZeroMemory(d->m_pid, sizeof(PROCESS_INFORMATION));
QString workDir = QDir::toNativeSeparators(workingDirectory()); QString workDir = QDir::toNativeSeparators(workingDirectory());
if (!workDir.isEmpty() && !workDir.endsWith('\\')) if (!workDir.isEmpty() && !workDir.endsWith('\\'))
workDir.append('\\'); workDir.append('\\');
QStringList stubArgs; QStringList stubArgs;
stubArgs << modeOption(m_mode) stubArgs << modeOption(d->m_mode)
<< m_stubServer.fullServerName() << d->m_stubServer.fullServerName()
<< workDir << workDir
<< (m_tempFile ? m_tempFile->fileName() : 0) << (d->m_tempFile ? d->m_tempFile->fileName() : 0)
<< createWinCommandline(program, args) << createWinCommandline(program, args)
<< msgPromptToClose(); << msgPromptToClose();
@@ -114,95 +168,97 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(), bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(),
0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0, FALSE, CREATE_NEW_CONSOLE,
0, 0, 0, 0,
&si, m_pid); &si, d->m_pid);
if (!success) { if (!success) {
delete m_pid; delete d->m_pid;
m_pid = 0; d->m_pid = 0;
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
stubServerShutdown(); stubServerShutdown();
emit processMessage(tr("The process '%1' could not be started: %2").arg(cmdLine, winErrorMessage(GetLastError())), true); emit processMessage(tr("The process '%1' could not be started: %2").arg(cmdLine, winErrorMessage(GetLastError())), true);
return false; return false;
} }
processFinishedNotifier = new QWinEventNotifier(m_pid->hProcess, this); d->processFinishedNotifier = new QWinEventNotifier(d->m_pid->hProcess, this);
connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), SLOT(stubExited())); connect(d->processFinishedNotifier, SIGNAL(activated(HANDLE)), SLOT(stubExited()));
emit wrapperStarted(); emit wrapperStarted();
return true; return true;
} }
void ConsoleProcess::stop() void ConsoleProcess::stop()
{ {
if (m_hInferior != NULL) { if (d->m_hInferior != NULL) {
TerminateProcess(m_hInferior, (unsigned)-1); TerminateProcess(d->m_hInferior, (unsigned)-1);
cleanupInferior(); cleanupInferior();
} }
if (m_pid) { if (d->m_pid) {
TerminateProcess(m_pid->hProcess, (unsigned)-1); TerminateProcess(d->m_pid->hProcess, (unsigned)-1);
WaitForSingleObject(m_pid->hProcess, INFINITE); WaitForSingleObject(d->m_pid->hProcess, INFINITE);
cleanupStub(); cleanupStub();
} }
} }
bool ConsoleProcess::isRunning() const bool ConsoleProcess::isRunning() const
{ {
return m_pid != 0; return d->m_pid != 0;
} }
QString ConsoleProcess::stubServerListen() QString ConsoleProcess::stubServerListen()
{ {
if (m_stubServer.listen(QString::fromLatin1("creator-%1-%2") if (d->m_stubServer.listen(QString::fromLatin1("creator-%1-%2")
.arg(QCoreApplication::applicationPid()) .arg(QCoreApplication::applicationPid())
.arg(rand()))) .arg(rand())))
return QString(); return QString();
return m_stubServer.errorString(); return d->m_stubServer.errorString();
} }
void ConsoleProcess::stubServerShutdown() void ConsoleProcess::stubServerShutdown()
{ {
delete m_stubSocket; delete d->m_stubSocket;
m_stubSocket = 0; d->m_stubSocket = 0;
if (m_stubServer.isListening()) if (d->m_stubServer.isListening())
m_stubServer.close(); d->m_stubServer.close();
} }
void ConsoleProcess::stubConnectionAvailable() void ConsoleProcess::stubConnectionAvailable()
{ {
m_stubSocket = m_stubServer.nextPendingConnection(); d->m_stubSocket = d->m_stubServer.nextPendingConnection();
connect(m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput())); connect(d->m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
} }
void ConsoleProcess::readStubOutput() void ConsoleProcess::readStubOutput()
{ {
while (m_stubSocket->canReadLine()) { while (d->m_stubSocket->canReadLine()) {
QByteArray out = m_stubSocket->readLine(); QByteArray out = d->m_stubSocket->readLine();
out.chop(2); // \r\n out.chop(2); // \r\n
if (out.startsWith("err:chdir ")) { if (out.startsWith("err:chdir ")) {
emit processMessage(msgCannotChangeToWorkDir(workingDirectory(), winErrorMessage(out.mid(10).toInt())), true); emit processMessage(msgCannotChangeToWorkDir(workingDirectory(), winErrorMessage(out.mid(10).toInt())), true);
} else if (out.startsWith("err:exec ")) { } else if (out.startsWith("err:exec ")) {
emit processMessage(msgCannotExecute(m_executable, winErrorMessage(out.mid(9).toInt())), true); emit processMessage(msgCannotExecute(d->m_executable, winErrorMessage(out.mid(9).toInt())), true);
} else if (out.startsWith("thread ")) { // Windows only
d->m_appMainThreadId = out.mid(7).toLongLong();
} else if (out.startsWith("pid ")) { } else if (out.startsWith("pid ")) {
// Wil not need it any more // Will not need it any more
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
d->m_appPid = out.mid(4).toLongLong();
m_appPid = out.mid(4).toInt(); d->m_hInferior = OpenProcess(
m_hInferior = OpenProcess(
SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE,
FALSE, m_appPid); FALSE, d->m_appPid);
if (m_hInferior == NULL) { if (d->m_hInferior == NULL) {
emit processMessage(tr("Cannot obtain a handle to the inferior: %1") emit processMessage(tr("Cannot obtain a handle to the inferior: %1")
.arg(winErrorMessage(GetLastError())), true); .arg(winErrorMessage(GetLastError())), true);
// Uhm, and now what? // Uhm, and now what?
continue; continue;
} }
inferiorFinishedNotifier = new QWinEventNotifier(m_hInferior, this); d->inferiorFinishedNotifier = new QWinEventNotifier(d->m_hInferior, this);
connect(inferiorFinishedNotifier, SIGNAL(activated(HANDLE)), SLOT(inferiorExited())); connect(d->inferiorFinishedNotifier, SIGNAL(activated(HANDLE)), SLOT(inferiorExited()));
emit processStarted(); emit processStarted();
} else { } else {
emit processMessage(msgUnexpectedOutput(), true); emit processMessage(msgUnexpectedOutput(out), true);
TerminateProcess(m_pid->hProcess, (unsigned)-1); TerminateProcess(d->m_pid->hProcess, (unsigned)-1);
break; break;
} }
} }
@@ -210,52 +266,53 @@ void ConsoleProcess::readStubOutput()
void ConsoleProcess::cleanupInferior() void ConsoleProcess::cleanupInferior()
{ {
delete inferiorFinishedNotifier; delete d->inferiorFinishedNotifier;
inferiorFinishedNotifier = 0; d->inferiorFinishedNotifier = 0;
CloseHandle(m_hInferior); CloseHandle(d->m_hInferior);
m_hInferior = NULL; d->m_hInferior = NULL;
m_appPid = 0; d->m_appPid = 0;
} }
void ConsoleProcess::inferiorExited() void ConsoleProcess::inferiorExited()
{ {
DWORD chldStatus; DWORD chldStatus;
if (!GetExitCodeProcess(m_hInferior, &chldStatus)) if (!GetExitCodeProcess(d->m_hInferior, &chldStatus))
emit processMessage(tr("Cannot obtain exit status from inferior: %1") emit processMessage(tr("Cannot obtain exit status from inferior: %1")
.arg(winErrorMessage(GetLastError())), true); .arg(winErrorMessage(GetLastError())), true);
cleanupInferior(); cleanupInferior();
m_appStatus = QProcess::NormalExit; d->m_appStatus = QProcess::NormalExit;
m_appCode = chldStatus; d->m_appCode = chldStatus;
emit processStopped(); emit processStopped();
} }
void ConsoleProcess::cleanupStub() void ConsoleProcess::cleanupStub()
{ {
stubServerShutdown(); stubServerShutdown();
delete processFinishedNotifier; delete d->processFinishedNotifier;
processFinishedNotifier = 0; d->processFinishedNotifier = 0;
CloseHandle(m_pid->hThread); CloseHandle(d->m_pid->hThread);
CloseHandle(m_pid->hProcess); CloseHandle(d->m_pid->hProcess);
delete m_pid; delete d->m_pid;
m_pid = 0; d->m_pid = 0;
delete m_tempFile; delete d->m_tempFile;
m_tempFile = 0; d->m_tempFile = 0;
} }
void ConsoleProcess::stubExited() void ConsoleProcess::stubExited()
{ {
// The stub exit might get noticed before we read the pid for the kill. // The stub exit might get noticed before we read the pid for the kill.
if (m_stubSocket && m_stubSocket->state() == QLocalSocket::ConnectedState) if (d->m_stubSocket && d->m_stubSocket->state() == QLocalSocket::ConnectedState)
m_stubSocket->waitForDisconnected(); d->m_stubSocket->waitForDisconnected();
cleanupStub(); cleanupStub();
if (m_hInferior != NULL) { if (d->m_hInferior != NULL) {
TerminateProcess(m_hInferior, (unsigned)-1); TerminateProcess(d->m_hInferior, (unsigned)-1);
cleanupInferior(); cleanupInferior();
m_appStatus = QProcess::CrashExit; d->m_appStatus = QProcess::CrashExit;
m_appCode = -1; d->m_appCode = -1;
emit processStopped(); emit processStopped();
} }
emit wrapperStopped(); emit wrapperStopped();
} }
} // namespace Utils

View File

@@ -208,6 +208,7 @@ int main()
SetConsoleCtrlHandler(ctrlHandler, TRUE); SetConsoleCtrlHandler(ctrlHandler, TRUE);
sendMsg("thread %d\n", pi.dwThreadId);
sendMsg("pid %d\n", pi.dwProcessId); sendMsg("pid %d\n", pi.dwProcessId);
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)