【发布时间】:2016-09-21 12:19:53
【问题描述】:
我试图在单击按钮时向另一个进程的控制台发送 ctrl+C 信号,方法是将当前进程附加到其控制台,发送 ctrl+C 信号,然后从控制台释放当前进程.这第一次工作正常,但第二次没有做任何事情。
void abort(){
AttachConsole(processInfo.dwProcessId); //processInfo is of type PROCESS_INFORMATION
SetConsoleCtrlHandler(0, true);
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
FreeConsole();
}
此外,即使在控制台中手动单击并按 ctrl+C 第一次也有效,但第二次无效。手动关闭控制台始终有效。
process.exe 是一个子进程,其创建方式与this post 完全相同。
重现问题的完整代码(在 Windows 10 上使用 Qt 4.8 和 vs2010 编译器进行 gui/线程):
main.cpp:
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
dialog.h:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include "ui_dialog.h"
#include "worker.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0):
QDialog(parent),
ui(new Ui::Dialog),
w(new worker()),
t(new QThread(this))
{
ui->setupUi(this);
connect(ui->startButton, SIGNAL(clicked(bool)), this, SLOT(startProcess()));
connect(ui->abortButton, SIGNAL(clicked(bool)), this, SLOT(abortProcess()));
connect(w, SIGNAL(finished()), t, SLOT(quit()));
w->setup(t);
w->moveToThread(t);
}
~Dialog(){delete ui;}
private slots:
void startProcess(){
t->start();
}
void abortProcess(){
w->abort();
t->quit();
}
private:
Ui::Dialog *ui;
worker *w;
QThread *t;
};
#endif // DIALOG_H
worker.h:
#ifndef WORKER_H
#define WORKER_H
#include <QDebug>
#include <QObject>
#include <QThread>
#include <Windows.h>
class worker : public QObject
{
Q_OBJECT
public:
explicit worker(QObject *parent = 0):QObject(parent){}
void setup(QThread *t){
connect(t, SIGNAL(started()), this, SLOT(startProcess()));
}
void abort(){
if(!AttachConsole(p.dwProcessId)) qDebug() << "AttachConsole" << GetLastError();
if(!SetConsoleCtrlHandler(0, true)) qDebug() << "SetConsoleCtrlHandler" << GetLastError();
if(!GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) qDebug() << "GenerateConsoleCtrlEvent" << GetLastError();
if(!FreeConsole()) qDebug() << "FreeConsole" << GetLastError();
}
private slots:
void startProcess(){
ZeroMemory(&p, sizeof(p));
STARTUPINFOA s;
SECURITY_ATTRIBUTES sec;
HANDLE read = NULL;
HANDLE write = NULL;
ZeroMemory(&sec, sizeof(sec));
sec.nLength = sizeof(SECURITY_ATTRIBUTES);
sec.bInheritHandle = true;
sec.lpSecurityDescriptor = NULL;
if(!CreatePipe(&read, &write, &sec, 0)) qDebug() << "CreatePipe error" << GetLastError();
if(!SetHandleInformation(read, HANDLE_FLAG_INHERIT, 0)) qDebug() << "SetHandleInformation error" << GetLastError();
ZeroMemory(&s, sizeof(s));
s.cb = sizeof(s);
s.hStdOutput = write;
s.hStdError = write;
s.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
s.dwFlags |= STARTF_USESTDHANDLES;
if(!CreateProcessA("helloworld.exe", "helloworld", NULL, NULL, true, CREATE_NO_WINDOW, NULL, NULL, &s, &p)) qDebug() << "CreateProcessA error " << GetLastError() << "\n";
CloseHandle(write);
char buff[65];
DWORD bytesRead;
while(true){
if(!ReadFile(read, buff, 64, &bytesRead, NULL)){
qDebug() << "ReadFile error";
break;
}
if(bytesRead > 0){
buff[64] = '\0';
qDebug() << QByteArray(buff);
}
}
CloseHandle(p.hProcess);
CloseHandle(p.hThread);
emit finished();
}
private:
PROCESS_INFORMATION p;
signals:
void finished();
};
#endif // WORKER_H
UI 只包含 2 个按钮,startButton 和 abortButton。 helloworld.exe 只是一个打印“Hello world!”的程序。然后等待输入。单击 startButton 时,将创建进程,并将输出正确重定向到我的程序。单击 abortButton 时,进程正确终止。 startProcess 第二次也可以完美运行,但是现在单击 abortButton 不起作用。调用了abort()函数,没有打印错误信息,但是helloworld进程终止失败,QThread t没有退出。
【问题讨论】:
-
我没有看到任何错误检查。你怎么能确定这些函数调用中的任何一个确实成功了?
-
@DavidHeffernan 我检查了函数返回的值,它们都不为零,这意味着它们成功了。
-
AttachConsole的文档确实建议您只能在调用FreeConsole后附加到 其他 控制台。但这个建议可能是无意的;通常这些限制都会明确说明。 -
"Works" 和 "doesn't work" 太模糊了,无法做出任何解释。但是,从它的声音来看,似乎这个其他过程是问题所在(正如手动测试所证明的那样)。无论如何,您需要正确说明预期的行为以及观察到的行为。
-
@IInspectable 好吧,我不确定你在期待什么,但预期的行为是调用 abort() 会将 ctrl+c 发送到进程控制台并关闭它,观察到的行为是这在我第一次运行该过程时正确发生,但之后没有任何反应。
标签: c++ windows winapi signals