diff --git a/src/libs/utils/launcherpackets.cpp b/src/libs/utils/launcherpackets.cpp index 8b201ecf4c4..7409576bf5e 100644 --- a/src/libs/utils/launcherpackets.cpp +++ b/src/libs/utils/launcherpackets.cpp @@ -60,7 +60,7 @@ void StartProcessPacket::doSerialize(QDataStream &stream) const { stream << command << arguments << workingDir << env << int(processMode) << writeData << int(processChannelMode) << standardInputFile << belowNormalPriority - << nativeArguments << lowPriority << unixTerminalDisabled; + << nativeArguments << lowPriority << unixTerminalDisabled << useCtrlCStub; } void StartProcessPacket::doDeserialize(QDataStream &stream) @@ -68,7 +68,7 @@ void StartProcessPacket::doDeserialize(QDataStream &stream) int cm, pm; stream >> command >> arguments >> workingDir >> env >> pm >> writeData >> cm >> standardInputFile >> belowNormalPriority >> nativeArguments >> lowPriority - >> unixTerminalDisabled; + >> unixTerminalDisabled >> useCtrlCStub; processChannelMode = QProcess::ProcessChannelMode(cm); processMode = Utils::ProcessMode(pm); } diff --git a/src/libs/utils/launcherpackets.h b/src/libs/utils/launcherpackets.h index 9d05782a4e7..06326378df1 100644 --- a/src/libs/utils/launcherpackets.h +++ b/src/libs/utils/launcherpackets.h @@ -119,6 +119,7 @@ public: QString nativeArguments; bool lowPriority = false; bool unixTerminalDisabled = false; + bool useCtrlCStub = false; private: void doSerialize(QDataStream &stream) const override; diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp index 050d3d1fb02..4ececa40178 100644 --- a/src/libs/utils/launchersocket.cpp +++ b/src/libs/utils/launchersocket.cpp @@ -402,6 +402,7 @@ void CallerHandle::start(const QString &program, const QStringList &arguments) p->nativeArguments = m_setup->m_nativeArguments; p->lowPriority = m_setup->m_lowPriority; p->unixTerminalDisabled = m_setup->m_unixTerminalDisabled; + p->useCtrlCStub = m_setup->m_useCtrlCStub; m_startPacket.reset(p); if (LauncherInterface::isReady()) doStart(); diff --git a/src/libs/utils/processinterface.h b/src/libs/utils/processinterface.h index 0f8fef684f8..8d6a3046075 100644 --- a/src/libs/utils/processinterface.h +++ b/src/libs/utils/processinterface.h @@ -60,7 +60,7 @@ public: bool m_haveEnv = false; bool m_lowPriority = false; bool m_unixTerminalDisabled = false; - bool m_useCtrlCStub = false; // debug only + bool m_useCtrlCStub = false; // release only bool m_belowNormalPriority = false; // internal, dependent on other fields and specific code path }; diff --git a/src/libs/utils/processreaper.cpp b/src/libs/utils/processreaper.cpp index 025d0cdd201..30985d947f2 100644 --- a/src/libs/utils/processreaper.cpp +++ b/src/libs/utils/processreaper.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "processreaper.h" +#include "processutils.h" #include "qtcassert.h" #include @@ -116,8 +117,7 @@ signals: private: void terminate() { - // TODO: do a custom terminate here for ctrlCStub - m_reaperSetup.m_process->terminate(); + ProcessHelper::terminateProcess(m_reaperSetup.m_process); QTimer::singleShot(m_reaperSetup.m_timeoutMs, this, &Reaper::handleTerminateTimeout); } diff --git a/src/libs/utils/processutils.cpp b/src/libs/utils/processutils.cpp index 1f57fc15d1f..3c1f982e4b0 100644 --- a/src/libs/utils/processutils.cpp +++ b/src/libs/utils/processutils.cpp @@ -26,6 +26,9 @@ #include "processutils.h" #ifdef Q_OS_WIN +#ifdef QTCREATOR_PCH_H +#define CALLBACK WINAPI +#endif #include #else #include @@ -82,6 +85,79 @@ void ProcessStartHandler::setNativeArguments(const QString &arguments) #endif // Q_OS_WIN } +#ifdef Q_OS_WIN +static BOOL sendMessage(UINT message, HWND hwnd, LPARAM lParam) +{ + DWORD dwProcessID; + GetWindowThreadProcessId(hwnd, &dwProcessID); + if ((DWORD)lParam == dwProcessID) { + SendNotifyMessage(hwnd, message, 0, 0); + return FALSE; + } + return TRUE; +} + +BOOL CALLBACK sendShutDownMessageToAllWindowsOfProcess_enumWnd(HWND hwnd, LPARAM lParam) +{ + static UINT uiShutDownMessage = RegisterWindowMessage(L"qtcctrlcstub_shutdown"); + return sendMessage(uiShutDownMessage, hwnd, lParam); +} + +BOOL CALLBACK sendInterruptMessageToAllWindowsOfProcess_enumWnd(HWND hwnd, LPARAM lParam) +{ + static UINT uiInterruptMessage = RegisterWindowMessage(L"qtcctrlcstub_interrupt"); + return sendMessage(uiInterruptMessage, hwnd, lParam); +} +#endif + +void ProcessHelper::setUseCtrlCStub(bool enabled) +{ + // Do not use the stub in debug mode. Activating the stub will shut down + // Qt Creator otherwise, because they share the same Windows console. + // See QTCREATORBUG-11995 for details. +#ifdef QT_DEBUG + Q_UNUSED(enabled) +#else + m_useCtrlCStub = enabled; +#endif +} + +void ProcessHelper::terminateProcess() +{ +#ifdef Q_OS_WIN + if (m_useCtrlCStub) + EnumWindows(sendShutDownMessageToAllWindowsOfProcess_enumWnd, processId()); + else + terminate(); +#else + terminate(); +#endif +} + +void ProcessHelper::terminateProcess(QProcess *process) +{ + ProcessHelper *helper = qobject_cast(process); + if (helper) + helper->terminateProcess(); + else + process->terminate(); +} + +void ProcessHelper::interruptPid(qint64 pid) +{ +#ifdef Q_OS_WIN + EnumWindows(sendInterruptMessageToAllWindowsOfProcess_enumWnd, pid); +#else + Q_UNUSED(pid) +#endif +} + +void ProcessHelper::interruptProcess(QProcess *process) +{ + ProcessHelper *helper = qobject_cast(process); + if (helper && helper->m_useCtrlCStub) + ProcessHelper::interruptPid(process->processId()); +} void ProcessHelper::setupChildProcess_impl() { diff --git a/src/libs/utils/processutils.h b/src/libs/utils/processutils.h index 90405704e1f..a9ae590ed26 100644 --- a/src/libs/utils/processutils.h +++ b/src/libs/utils/processutils.h @@ -26,6 +26,7 @@ #pragma once #include "processenums.h" +#include "processreaper.h" #include #include @@ -52,6 +53,8 @@ private: class ProcessHelper : public QProcess { + Q_OBJECT + public: ProcessHelper(QObject *parent) : QProcess(parent), m_processStartHandler(this) { @@ -70,11 +73,19 @@ public: void setLowPriority() { m_lowPriority = true; } void setUnixTerminalDisabled() { m_unixTerminalDisabled = true; } + void setUseCtrlCStub(bool enabled); // release only + + static void terminateProcess(QProcess *process); + static void interruptProcess(QProcess *process); + static void interruptPid(qint64 pid); private: + void terminateProcess(); void setupChildProcess_impl(); + bool m_lowPriority = false; bool m_unixTerminalDisabled = false; + bool m_useCtrlCStub = false; // release only ProcessStartHandler m_processStartHandler; }; diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index 3f914d37311..e2f51bde0da 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -59,13 +59,6 @@ #include #include -#ifdef Q_OS_WIN -#ifdef QTCREATOR_PCH_H -#define CALLBACK WINAPI -#endif -#include -#endif - using namespace Utils::Internal; namespace Utils { @@ -412,9 +405,9 @@ public: QByteArray readAllStandardError() override { return m_process->readAllStandardError(); } void interrupt() override - { QTC_CHECK(false); } // TODO: provide default impl + { ProcessHelper::interruptProcess(m_process); } void terminate() override - { m_process->terminate(); } + { ProcessHelper::terminateProcess(m_process); } void kill() override { m_process->kill(); } void close() override @@ -462,6 +455,7 @@ private: m_process->setLowPriority(); if (m_setup->m_unixTerminalDisabled) m_process->setUnixTerminalDisabled(); + m_process->setUseCtrlCStub(m_setup->m_useCtrlCStub); m_process->start(program, arguments, handler->openMode()); handler->handleProcessStart(); } @@ -510,7 +504,10 @@ public: QByteArray readAllStandardError() override { return m_handle->readAllStandardError(); } void interrupt() override - { QTC_CHECK(false); } // TODO: send it to process launcher and use there a default impl of QProcessImpl + { + if (m_setup->m_useCtrlCStub) // bypass launcher and interrupt directly + ProcessHelper::interruptPid(processId()); + } void terminate() override { cancel(); } // TODO: what are differences among terminate, kill and close? void kill() override { cancel(); } // TODO: see above void close() override { cancel(); } // TODO: see above @@ -863,10 +860,10 @@ void QtcProcess::setUseCtrlCStub(bool enabled) // Do not use the stub in debug mode. Activating the stub will shut down // Qt Creator otherwise, because they share the same Windows console. // See QTCREATORBUG-11995 for details. -#ifndef QT_DEBUG - d->m_setup.m_useCtrlCStub = enabled; -#else +#ifdef QT_DEBUG Q_UNUSED(enabled) +#else + d->m_setup.m_useCtrlCStub = enabled; #endif } @@ -900,49 +897,14 @@ void QtcProcess::start() d->m_process->start(); } -#ifdef Q_OS_WIN -static BOOL sendMessage(UINT message, HWND hwnd, LPARAM lParam) -{ - DWORD dwProcessID; - GetWindowThreadProcessId(hwnd, &dwProcessID); - if ((DWORD)lParam == dwProcessID) { - SendNotifyMessage(hwnd, message, 0, 0); - return FALSE; - } - return TRUE; -} - -BOOL CALLBACK sendShutDownMessageToAllWindowsOfProcess_enumWnd(HWND hwnd, LPARAM lParam) -{ - static UINT uiShutDownMessage = RegisterWindowMessage(L"qtcctrlcstub_shutdown"); - return sendMessage(uiShutDownMessage, hwnd, lParam); -} - -BOOL CALLBACK sendInterruptMessageToAllWindowsOfProcess_enumWnd(HWND hwnd, LPARAM lParam) -{ - static UINT uiInterruptMessage = RegisterWindowMessage(L"qtcctrlcstub_interrupt"); - return sendMessage(uiInterruptMessage, hwnd, lParam); -} -#endif - void QtcProcess::terminate() { -#ifdef Q_OS_WIN - if (d->m_setup.m_useCtrlCStub) - EnumWindows(sendShutDownMessageToAllWindowsOfProcess_enumWnd, processId()); - else -#endif if (d->m_process) d->m_process->terminate(); } void QtcProcess::interrupt() { -#ifdef Q_OS_WIN - if (d->m_setup.m_useCtrlCStub) - EnumWindows(sendInterruptMessageToAllWindowsOfProcess_enumWnd, processId()); - else -#endif if (d->m_process) d->m_process->interrupt(); } diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index 0bf9bf2c524..93759904ae1 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -111,7 +111,7 @@ public: void setWriteData(const QByteArray &writeData); - void setUseCtrlCStub(bool enabled); // debug only + void setUseCtrlCStub(bool enabled); // release only void setLowPriority(); void setDisableUnixTerminal(); void setRunAsRoot(bool on); diff --git a/src/tools/processlauncher/launchersockethandler.cpp b/src/tools/processlauncher/launchersockethandler.cpp index 054a6e3966f..ccf7fa09509 100644 --- a/src/tools/processlauncher/launchersockethandler.cpp +++ b/src/tools/processlauncher/launchersockethandler.cpp @@ -216,6 +216,7 @@ void LauncherSocketHandler::handleStartPacket() process->setLowPriority(); if (packet.unixTerminalDisabled) process->setUnixTerminalDisabled(); + process->setUseCtrlCStub(packet.useCtrlCStub); process->start(packet.command, packet.arguments, handler->openMode()); handler->handleProcessStart(); }