问题描述:通过一个进程(CUI/GUI)向另一个CUI进程发送CTRL-C,使后者结束执行。
基本要求:1 CUI子进程无法更改,如PING。
2 父进程可以是GUI,也可以是CUI进程。
3 保证子进程有机会在结束前保存其数据。一般CUI应该对用户点击关闭按钮和CTRL-C这2种关闭方式有对应的处理,以保证其数据可以被保存。这第三条的要求其实就是说实现的方式不要比CTRL-C和点击关闭按钮来得更暴力就可以。
目前花了近30个小时,查看各类文档,试验各种技术,代码组合,仅一种方式可以实现:
TCHAR cmdline[] = _T("ping 127.0.0.1 -t");
BOOL b = CreateProcess(NULL,cmdline,NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi);
if (!b)
{
printf("CreateProcess failed!\n");
return -1;
}
SetConsoleCtrlHandler(CtrlHandler,TRUE);
这样子可以做到CTRL-C的时候结束PING子进程。但是离我要求的发送CTRL-C还有距离。
下面的是另外的实现,这种情况很恶心,拜其所赐,30多小时毫无进展。
TCHAR cmdline[] = _T("ping 127.0.0.1 -t");
BOOL b = CreateProcess(NULL,cmdline,NULL,NULL,FALSE,CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi);
if (!b)
{
printf("CreateProcess failed!\n");
return -1;
}
SetConsoleCtrlHandler(CtrlHandler,TRUE);
//中间略去一些无关紧要的代码
GenerateConsoleCtrlEvent(CTRL_C_EVENT,pi.dwProcessId);
无论怎么发送CTRL-C或者是你手动CTRL-C,PING都不会理会(这就是其恶心之处,在同一个CONSOLE下,PING子程序用着通用的ctrl handler居然不处理CTRL-C)
上面就是大概的描述,请问有知道此问题如何解决的吗?
最后附上我的测试代码方便大家试验(用了预编译头,你可能需要更改下头文件的引用)
#include "stdafx.h"
#include <Windows.h>
#include <process.h>
PROCESS_INFORMATION pi;
BOOL WINAPI CtrlHandler(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("Pid=%d",_getpid());
return TRUE;
case CTRL_BREAK_EVENT:
printf("server Break Event\n");
printf("%s",GenerateConsoleCtrlEvent(CTRL_C_EVENT,pi.dwProcessId)?"true\n":"false\n");
return TRUE;
default:
return FALSE;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb = sizeof(si);
TCHAR cmdline[] = _T("ping 127.0.0.1 -t");
BOOL b = CreateProcess(NULL,cmdline,NULL,NULL,FALSE,CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi);
if (!b)
{
printf("CreateProcess failed!\n");
return -1;
}
SetConsoleCtrlHandler(CtrlHandler,TRUE);
int i = 0;
while(1)
{
scanf("%d",&i);
if (i==1)
{
ZeroMemory(&pi,sizeof(pi));
b = CreateProcess(NULL,cmdline,NULL,NULL,FALSE,NULL/*CREATE_NEW_PROCESS_GROUP*/,NULL,NULL,&si,&pi);
}
if (i>1)
{
//GenerateConsoleCtrlEvent(CTRL_C_EVENT,pi.dwProcessId);
printf("%s",GenerateConsoleCtrlEvent(CTRL_C_EVENT,i)?"true\n":"false\n");
}
if(WaitForSingleObject(pi.hProcess,200)==WAIT_OBJECT_0)
printf("Ping exited!");
// break;
}
return 0;
}
11 个解决方案
#1
纯属路过 ··· 不懂
#2
一个CUI程序我目前知晓的有3中方式获得其CONSOLE窗口:
1、系统为其分配,父窗口没CONSOLE和CreateProcess指定CREATE_NEW_CONSOLE参数这2种情况吧。
2、继承父进程的CONSOLE,在不指定CREATE_NEW_CONSOLE的情况下,会继承父进程的CONSOLE(似乎跟CreateProcess中的继承相关参数没关系,我上面指定继承为FALSE它还是用父进程的CONSOLE,难道我这里猜错了?)
3、显示指定DETACH_PROCESS,然后自己AllocConsole或者AttachConsole。
其中第3种情况,需要子进程主动配合,所以用不了。 对于第一种情况,父窗口没CONSOLE的话,一般是GUI程序,系统为子CUI程序创建新的CONSOLE,这时虽有父子关系,但是CONSOLE上却没关系,有个概念是process group,MSDN里仅仅点到为止,GOOGLE了很多网页全部是UNIX/LINUX下的,WINDOWS下的process group一篇都没见过,不过有篇似乎脱离特定系统进行了讲解,我看了这些,虽然不同系统,不过概念上还是十分相近。套用其中一句说每个process都属于一个process group,这里的情况是gid未知...我也不知道如何去获取,底层接触不够。
再说CREATE_NEW_CONSOLE这种情况,如果父进程没CONSOLE,跟上面讨论差不多,若父进程是CUI程序的话,这时桌面会出现2个CONSOLE,它们显然是不同的,CONSOLE窗口间没什么关系。
插入一点GenerateConsoleCtrlEvent的说明,这个函数带有2个参数,其一知道EVENT类型,只有CTRL_C_EVENT跟CTRL_BREAK_EVENT,另一个是上面提到的process group的标识符。函数的功能是向gid标识的进程组中所有的进程发送event,但是只有跟调用GenerateConsoleCtrlEvent的进程共用同一个CONSOLE的进程才可以收到消息。
再说CreateProcess时使用CREATE_NEW_PROCESS_GROUP的情况,指定这个参数可以产生一个process group,其gid跟产生出的子进程pid相同。这样在继承父窗口的情形下,满足了使用GenerateConsoleCtrlEvent的2个条件:1.已知一个gid;2.目标进程(子进程)与调用进程(父进程)共用CONSOLE。这就是我上面的程序代码里表达的情形,但是无法得到我想要的,这是个问题。
上面写了我在处理时所考虑的,有什么地方想得不对的吗?请指出,谢谢!
PS:windows下的process group真的是什么资料都没有的么?MSDN里居然有篇processor group,当时由于看错还激动了个好几十秒。
1、系统为其分配,父窗口没CONSOLE和CreateProcess指定CREATE_NEW_CONSOLE参数这2种情况吧。
2、继承父进程的CONSOLE,在不指定CREATE_NEW_CONSOLE的情况下,会继承父进程的CONSOLE(似乎跟CreateProcess中的继承相关参数没关系,我上面指定继承为FALSE它还是用父进程的CONSOLE,难道我这里猜错了?)
3、显示指定DETACH_PROCESS,然后自己AllocConsole或者AttachConsole。
其中第3种情况,需要子进程主动配合,所以用不了。 对于第一种情况,父窗口没CONSOLE的话,一般是GUI程序,系统为子CUI程序创建新的CONSOLE,这时虽有父子关系,但是CONSOLE上却没关系,有个概念是process group,MSDN里仅仅点到为止,GOOGLE了很多网页全部是UNIX/LINUX下的,WINDOWS下的process group一篇都没见过,不过有篇似乎脱离特定系统进行了讲解,我看了这些,虽然不同系统,不过概念上还是十分相近。套用其中一句说每个process都属于一个process group,这里的情况是gid未知...我也不知道如何去获取,底层接触不够。
再说CREATE_NEW_CONSOLE这种情况,如果父进程没CONSOLE,跟上面讨论差不多,若父进程是CUI程序的话,这时桌面会出现2个CONSOLE,它们显然是不同的,CONSOLE窗口间没什么关系。
插入一点GenerateConsoleCtrlEvent的说明,这个函数带有2个参数,其一知道EVENT类型,只有CTRL_C_EVENT跟CTRL_BREAK_EVENT,另一个是上面提到的process group的标识符。函数的功能是向gid标识的进程组中所有的进程发送event,但是只有跟调用GenerateConsoleCtrlEvent的进程共用同一个CONSOLE的进程才可以收到消息。
再说CreateProcess时使用CREATE_NEW_PROCESS_GROUP的情况,指定这个参数可以产生一个process group,其gid跟产生出的子进程pid相同。这样在继承父窗口的情形下,满足了使用GenerateConsoleCtrlEvent的2个条件:1.已知一个gid;2.目标进程(子进程)与调用进程(父进程)共用CONSOLE。这就是我上面的程序代码里表达的情形,但是无法得到我想要的,这是个问题。
上面写了我在处理时所考虑的,有什么地方想得不对的吗?请指出,谢谢!
PS:windows下的process group真的是什么资料都没有的么?MSDN里居然有篇processor group,当时由于看错还激动了个好几十秒。
#3
BOOL WINAPI CtrlHandler(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("Pid=%d",_getpid());
return TRUE;
case CTRL_BREAK_EVENT:
printf("server Break Event\n");
printf("%s",GenerateConsoleCtrlEvent(CTRL_C_EVENT,pi.dwProcessId)?"true\n":"false\n");
return TRUE;
default:
return FALSE;
}
}
加上如下事件进行测试
case CTRL_C_EVENT:
message = "A CTRL_C_EVENT was raised by the user.";
break;
case CTRL_BREAK_EVENT:
message = "A CTRL_BREAK_EVENT was raised by the user.";
break;
case CTRL_CLOSE_EVENT:
message = "A CTRL_CLOSE_EVENT was raised by the user.";
break;
case CTRL_LOGOFF_EVENT:
message = "A CTRL_LOGOFF_EVENT was raised by the user.";
break;
case CTRL_SHUTDOWN_EVENT:
message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
break;
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("Pid=%d",_getpid());
return TRUE;
case CTRL_BREAK_EVENT:
printf("server Break Event\n");
printf("%s",GenerateConsoleCtrlEvent(CTRL_C_EVENT,pi.dwProcessId)?"true\n":"false\n");
return TRUE;
default:
return FALSE;
}
}
加上如下事件进行测试
case CTRL_C_EVENT:
message = "A CTRL_C_EVENT was raised by the user.";
break;
case CTRL_BREAK_EVENT:
message = "A CTRL_BREAK_EVENT was raised by the user.";
break;
case CTRL_CLOSE_EVENT:
message = "A CTRL_CLOSE_EVENT was raised by the user.";
break;
case CTRL_LOGOFF_EVENT:
message = "A CTRL_LOGOFF_EVENT was raised by the user.";
break;
case CTRL_SHUTDOWN_EVENT:
message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
break;
#4
感谢你的回复!
对于CLOSE,LOGOFF,SHUTDOWN由于无法使用GenerateConsoleCtrlEvent发送,所以没有做处理,而是转交给默认handler(好象是这个名字ExitProcess)处理。
我代码中pirntf的一些信息,一方面也是出于判断CtrlHandler是否被调用而写的。
#5
这个CtrlHandler是给我自己的程序用的,目的是在于忽略CTRL-C事件,不会导致关闭子进程是连自己也关闭了。
子进程创建之前没有对CONSOLE做什么事,相信此时CONSOLE还是默认的event handler(ExitProcess),不清楚子进程是否会有可能继承这个事件处理list。不过至少MSDN里明确说明,CONSOLE对CTRL-C的态度(忽略或是处理)是会被继承的(通过SetConsoleCtrlHandler(NULL,TRUE))。
子进程创建之前没有对CONSOLE做什么事,相信此时CONSOLE还是默认的event handler(ExitProcess),不清楚子进程是否会有可能继承这个事件处理list。不过至少MSDN里明确说明,CONSOLE对CTRL-C的态度(忽略或是处理)是会被继承的(通过SetConsoleCtrlHandler(NULL,TRUE))。
#6
拜膜 3楼
#7
有人么?
我再一次把问题简化吧。。。
基本情况:
1.使用process group + GenerateConsoleCtrlEvent实现对子进程发生ctrl-c事件。
2.父进程和子进程共用一个console。
3.子进程代表了一个process group的leader。
4.父进程跟子进程的event handler都被设置成功了,而是都对CTRL-C做了处理:打印一行信息。
5.共用的console的INPUT buff mode设置了ENABLE_PROCESSED_INPUT。
此时,按下CTRL-C
父进程捕获到了CTRL-C,然后打印了一行文本,子进程确没有任何反应,为什么呢?
我再一次把问题简化吧。。。
基本情况:
1.使用process group + GenerateConsoleCtrlEvent实现对子进程发生ctrl-c事件。
2.父进程和子进程共用一个console。
3.子进程代表了一个process group的leader。
4.父进程跟子进程的event handler都被设置成功了,而是都对CTRL-C做了处理:打印一行信息。
5.共用的console的INPUT buff mode设置了ENABLE_PROCESSED_INPUT。
此时,按下CTRL-C
父进程捕获到了CTRL-C,然后打印了一行文本,子进程确没有任何反应,为什么呢?
#8
向楼主学习!
#9
关注,楼主现在这个问题解决了吗?
#10
还没有解决,模拟不出来。不过直接重定向stdin,stdout,手动CTRL-C是可以的,但是如果只能手动的话那就没什么意义了。
或许只能试着在中间添加一个代理来解决,听说VS也是如此实现的,不过时间关系还没有实践。
或许只能试着在中间添加一个代理来解决,听说VS也是如此实现的,不过时间关系还没有实践。
#11
前些日子想写个GUI版的CMD,CreateProcess了一个cmd.exe,用管道重定向,但是一直没办法实现ctrl+c跟ctrl+break,后来一们外国大哥说用CreateRemoteThread把GenerateConsoleCtrlEvent注入到cmd.exe可以实现,但是只有ctrl+break有用,ctrl+c没反应,求解
#1
纯属路过 ··· 不懂
#2
一个CUI程序我目前知晓的有3中方式获得其CONSOLE窗口:
1、系统为其分配,父窗口没CONSOLE和CreateProcess指定CREATE_NEW_CONSOLE参数这2种情况吧。
2、继承父进程的CONSOLE,在不指定CREATE_NEW_CONSOLE的情况下,会继承父进程的CONSOLE(似乎跟CreateProcess中的继承相关参数没关系,我上面指定继承为FALSE它还是用父进程的CONSOLE,难道我这里猜错了?)
3、显示指定DETACH_PROCESS,然后自己AllocConsole或者AttachConsole。
其中第3种情况,需要子进程主动配合,所以用不了。 对于第一种情况,父窗口没CONSOLE的话,一般是GUI程序,系统为子CUI程序创建新的CONSOLE,这时虽有父子关系,但是CONSOLE上却没关系,有个概念是process group,MSDN里仅仅点到为止,GOOGLE了很多网页全部是UNIX/LINUX下的,WINDOWS下的process group一篇都没见过,不过有篇似乎脱离特定系统进行了讲解,我看了这些,虽然不同系统,不过概念上还是十分相近。套用其中一句说每个process都属于一个process group,这里的情况是gid未知...我也不知道如何去获取,底层接触不够。
再说CREATE_NEW_CONSOLE这种情况,如果父进程没CONSOLE,跟上面讨论差不多,若父进程是CUI程序的话,这时桌面会出现2个CONSOLE,它们显然是不同的,CONSOLE窗口间没什么关系。
插入一点GenerateConsoleCtrlEvent的说明,这个函数带有2个参数,其一知道EVENT类型,只有CTRL_C_EVENT跟CTRL_BREAK_EVENT,另一个是上面提到的process group的标识符。函数的功能是向gid标识的进程组中所有的进程发送event,但是只有跟调用GenerateConsoleCtrlEvent的进程共用同一个CONSOLE的进程才可以收到消息。
再说CreateProcess时使用CREATE_NEW_PROCESS_GROUP的情况,指定这个参数可以产生一个process group,其gid跟产生出的子进程pid相同。这样在继承父窗口的情形下,满足了使用GenerateConsoleCtrlEvent的2个条件:1.已知一个gid;2.目标进程(子进程)与调用进程(父进程)共用CONSOLE。这就是我上面的程序代码里表达的情形,但是无法得到我想要的,这是个问题。
上面写了我在处理时所考虑的,有什么地方想得不对的吗?请指出,谢谢!
PS:windows下的process group真的是什么资料都没有的么?MSDN里居然有篇processor group,当时由于看错还激动了个好几十秒。
1、系统为其分配,父窗口没CONSOLE和CreateProcess指定CREATE_NEW_CONSOLE参数这2种情况吧。
2、继承父进程的CONSOLE,在不指定CREATE_NEW_CONSOLE的情况下,会继承父进程的CONSOLE(似乎跟CreateProcess中的继承相关参数没关系,我上面指定继承为FALSE它还是用父进程的CONSOLE,难道我这里猜错了?)
3、显示指定DETACH_PROCESS,然后自己AllocConsole或者AttachConsole。
其中第3种情况,需要子进程主动配合,所以用不了。 对于第一种情况,父窗口没CONSOLE的话,一般是GUI程序,系统为子CUI程序创建新的CONSOLE,这时虽有父子关系,但是CONSOLE上却没关系,有个概念是process group,MSDN里仅仅点到为止,GOOGLE了很多网页全部是UNIX/LINUX下的,WINDOWS下的process group一篇都没见过,不过有篇似乎脱离特定系统进行了讲解,我看了这些,虽然不同系统,不过概念上还是十分相近。套用其中一句说每个process都属于一个process group,这里的情况是gid未知...我也不知道如何去获取,底层接触不够。
再说CREATE_NEW_CONSOLE这种情况,如果父进程没CONSOLE,跟上面讨论差不多,若父进程是CUI程序的话,这时桌面会出现2个CONSOLE,它们显然是不同的,CONSOLE窗口间没什么关系。
插入一点GenerateConsoleCtrlEvent的说明,这个函数带有2个参数,其一知道EVENT类型,只有CTRL_C_EVENT跟CTRL_BREAK_EVENT,另一个是上面提到的process group的标识符。函数的功能是向gid标识的进程组中所有的进程发送event,但是只有跟调用GenerateConsoleCtrlEvent的进程共用同一个CONSOLE的进程才可以收到消息。
再说CreateProcess时使用CREATE_NEW_PROCESS_GROUP的情况,指定这个参数可以产生一个process group,其gid跟产生出的子进程pid相同。这样在继承父窗口的情形下,满足了使用GenerateConsoleCtrlEvent的2个条件:1.已知一个gid;2.目标进程(子进程)与调用进程(父进程)共用CONSOLE。这就是我上面的程序代码里表达的情形,但是无法得到我想要的,这是个问题。
上面写了我在处理时所考虑的,有什么地方想得不对的吗?请指出,谢谢!
PS:windows下的process group真的是什么资料都没有的么?MSDN里居然有篇processor group,当时由于看错还激动了个好几十秒。
#3
BOOL WINAPI CtrlHandler(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("Pid=%d",_getpid());
return TRUE;
case CTRL_BREAK_EVENT:
printf("server Break Event\n");
printf("%s",GenerateConsoleCtrlEvent(CTRL_C_EVENT,pi.dwProcessId)?"true\n":"false\n");
return TRUE;
default:
return FALSE;
}
}
加上如下事件进行测试
case CTRL_C_EVENT:
message = "A CTRL_C_EVENT was raised by the user.";
break;
case CTRL_BREAK_EVENT:
message = "A CTRL_BREAK_EVENT was raised by the user.";
break;
case CTRL_CLOSE_EVENT:
message = "A CTRL_CLOSE_EVENT was raised by the user.";
break;
case CTRL_LOGOFF_EVENT:
message = "A CTRL_LOGOFF_EVENT was raised by the user.";
break;
case CTRL_SHUTDOWN_EVENT:
message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
break;
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("Pid=%d",_getpid());
return TRUE;
case CTRL_BREAK_EVENT:
printf("server Break Event\n");
printf("%s",GenerateConsoleCtrlEvent(CTRL_C_EVENT,pi.dwProcessId)?"true\n":"false\n");
return TRUE;
default:
return FALSE;
}
}
加上如下事件进行测试
case CTRL_C_EVENT:
message = "A CTRL_C_EVENT was raised by the user.";
break;
case CTRL_BREAK_EVENT:
message = "A CTRL_BREAK_EVENT was raised by the user.";
break;
case CTRL_CLOSE_EVENT:
message = "A CTRL_CLOSE_EVENT was raised by the user.";
break;
case CTRL_LOGOFF_EVENT:
message = "A CTRL_LOGOFF_EVENT was raised by the user.";
break;
case CTRL_SHUTDOWN_EVENT:
message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
break;
#4
感谢你的回复!
对于CLOSE,LOGOFF,SHUTDOWN由于无法使用GenerateConsoleCtrlEvent发送,所以没有做处理,而是转交给默认handler(好象是这个名字ExitProcess)处理。
我代码中pirntf的一些信息,一方面也是出于判断CtrlHandler是否被调用而写的。
#5
这个CtrlHandler是给我自己的程序用的,目的是在于忽略CTRL-C事件,不会导致关闭子进程是连自己也关闭了。
子进程创建之前没有对CONSOLE做什么事,相信此时CONSOLE还是默认的event handler(ExitProcess),不清楚子进程是否会有可能继承这个事件处理list。不过至少MSDN里明确说明,CONSOLE对CTRL-C的态度(忽略或是处理)是会被继承的(通过SetConsoleCtrlHandler(NULL,TRUE))。
子进程创建之前没有对CONSOLE做什么事,相信此时CONSOLE还是默认的event handler(ExitProcess),不清楚子进程是否会有可能继承这个事件处理list。不过至少MSDN里明确说明,CONSOLE对CTRL-C的态度(忽略或是处理)是会被继承的(通过SetConsoleCtrlHandler(NULL,TRUE))。
#6
拜膜 3楼
#7
有人么?
我再一次把问题简化吧。。。
基本情况:
1.使用process group + GenerateConsoleCtrlEvent实现对子进程发生ctrl-c事件。
2.父进程和子进程共用一个console。
3.子进程代表了一个process group的leader。
4.父进程跟子进程的event handler都被设置成功了,而是都对CTRL-C做了处理:打印一行信息。
5.共用的console的INPUT buff mode设置了ENABLE_PROCESSED_INPUT。
此时,按下CTRL-C
父进程捕获到了CTRL-C,然后打印了一行文本,子进程确没有任何反应,为什么呢?
我再一次把问题简化吧。。。
基本情况:
1.使用process group + GenerateConsoleCtrlEvent实现对子进程发生ctrl-c事件。
2.父进程和子进程共用一个console。
3.子进程代表了一个process group的leader。
4.父进程跟子进程的event handler都被设置成功了,而是都对CTRL-C做了处理:打印一行信息。
5.共用的console的INPUT buff mode设置了ENABLE_PROCESSED_INPUT。
此时,按下CTRL-C
父进程捕获到了CTRL-C,然后打印了一行文本,子进程确没有任何反应,为什么呢?
#8
向楼主学习!
#9
关注,楼主现在这个问题解决了吗?
#10
还没有解决,模拟不出来。不过直接重定向stdin,stdout,手动CTRL-C是可以的,但是如果只能手动的话那就没什么意义了。
或许只能试着在中间添加一个代理来解决,听说VS也是如此实现的,不过时间关系还没有实践。
或许只能试着在中间添加一个代理来解决,听说VS也是如此实现的,不过时间关系还没有实践。
#11
前些日子想写个GUI版的CMD,CreateProcess了一个cmd.exe,用管道重定向,但是一直没办法实现ctrl+c跟ctrl+break,后来一们外国大哥说用CreateRemoteThread把GenerateConsoleCtrlEvent注入到cmd.exe可以实现,但是只有ctrl+break有用,ctrl+c没反应,求解