windows控制台程序如何优雅退出

时间:2022-12-18 11:16:15
通过控制台窗口关闭按钮退出时,很多线程都没法正常退出,而且已构造的对象,都不会析构,即使用SetConsoleCtrlHandler去捕捉也一样。不知道有什么好的方法,我能想到的就是以监听键盘输入的方式来退出。

19 个解决方案

#1


怎么样才算是优雅的退出呢?

#2


资源被正确回收,所有线程返回正确的值,个人有洁癖 windows控制台程序如何优雅退出

#3


引用 楼主 wuxupu 的回复:
通过控制台窗口关闭按钮退出时,很多线程都没法正常退出,而且已构造的对象,都不会析构,即使用SetConsoleCtrlHandler去捕捉也一样。不知道有什么好的方法,我能想到的就是以监听键盘输入的方式来退出。


你这相当于强行终止,比如你正运行个多线程界面程序,然后把主进程在资源管理器给kill了,你还指望什么。
控制台程序运行完,自己就会退出。

#4


引用 3 楼 buyong 的回复:
Quote: 引用 楼主 wuxupu 的回复:

通过控制台窗口关闭按钮退出时,很多线程都没法正常退出,而且已构造的对象,都不会析构,即使用SetConsoleCtrlHandler去捕捉也一样。不知道有什么好的方法,我能想到的就是以监听键盘输入的方式来退出。


你这相当于强行终止,比如你正运行个多线程界面程序,然后把主进程在资源管理器给kill了,你还指望什么。
控制台程序运行完,自己就会退出。

是这样的,服务端程序一般不会自动退出。我希望能有正常退出的方式来辅助我检测内存泄漏。 windows控制台程序如何优雅退出

#5


SetConsoleCtrlHandler应该是可以的,你要在自己的HandlerRoutine中处理CTRL_CLOSE_EVENT信号。

#6


引用 5 楼 DelphiGuy 的回复:
SetConsoleCtrlHandler应该是可以的,你要在自己的HandlerRoutine中处理CTRL_CLOSE_EVENT信号。

你试试就知道了,能捕捉到,但是返回值为2
#include "stdafx.h"
#include <Windows.h>

/*volatile*/ bool g_bExit = false;

BOOL CALLBACK CosonleHandler(DWORD ev)
{
BOOL bRet = FALSE;
switch (ev)
{
case CTRL_CLOSE_EVENT:
g_bExit = true;
bRet = TRUE;
break;
default:
break;
}
return bRet;
}

int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(CosonleHandler, TRUE);

while(!g_bExit);

printf("exit\n");
system("pause");
return 0;
}

#7


看看这个帖子:
控制台多线程安全退出
SetConsoleCtrlHandler可以捕获CTRL+C的事件,但好像无法捕获点击右上角X的事件。
我在8楼帖的代码,HOOK点击右上角X的鼠标消息,然后进行判断处理,只判断了点击右上角X的鼠标消息,没有捕获双击左上角关闭的鼠标消息。

#8


还可以从任务管理中终止呢。以前做过一个类似的,需要在内核中的进程列表中将其移除,这样用户就没有办法关闭这个程序。 

#9


引用 4 楼 wuxupu 的回复:
Quote: 引用 3 楼 buyong 的回复:

Quote: 引用 楼主 wuxupu 的回复:

通过控制台窗口关闭按钮退出时,很多线程都没法正常退出,而且已构造的对象,都不会析构,即使用SetConsoleCtrlHandler去捕捉也一样。不知道有什么好的方法,我能想到的就是以监听键盘输入的方式来退出。


你这相当于强行终止,比如你正运行个多线程界面程序,然后把主进程在资源管理器给kill了,你还指望什么。
控制台程序运行完,自己就会退出。

是这样的,服务端程序一般不会自动退出。我希望能有正常退出的方式来辅助我检测内存泄漏。 windows控制台程序如何优雅退出


如果真有这种需求,那改程序,增加个状态量(比如一个信号量,或者一个特定message(windows),程序再设计个观察者模式,主进程能有个机制接收用户输入一个特殊命令(或者捕获用户关闭的消息),然后把这个事件通知到每个线程,然后wait所有子线程退出后,自己再退出。

#10


引用 7 楼 BeanJoy 的回复:
看看这个帖子:
控制台多线程安全退出
SetConsoleCtrlHandler可以捕获CTRL+C的事件,但好像无法捕获点击右上角X的事件。
我在8楼帖的代码,HOOK点击右上角X的鼠标消息,然后进行判断处理,只判断了点击右上角X的鼠标消息,没有捕获双击左上角关闭的鼠标消息。

这个方法可行

#11


不要企图优雅的结束(因为这是不可能办到的)
而要在烂的不能再烂的摊子上也能重整河山!

尽管如此:
console屏幕处理例子程序。终端窗口屏幕处理相关API使用例子。来自MSVC20\SAMPLES\win32\console\ 
http://download.csdn.net/detail/zhao4zhong1/3461309

#12


引用 7 楼 BeanJoy 的回复:
看看这个帖子:
控制台多线程安全退出
SetConsoleCtrlHandler可以捕获CTRL+C的事件,但好像无法捕获点击右上角X的事件。
我在8楼帖的代码,HOOK点击右上角X的鼠标消息,然后进行判断处理,只判断了点击右上角X的鼠标消息,没有捕获双击左上角关闭的鼠标消息。


CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:


#include <stdio.h>
#include <windows.h>
 
/*volatile*/ bool g_bExit = false;
 
BOOL CALLBACK CosonleHandler(DWORD ev)
{
    BOOL bRet = FALSE;
    switch (ev)
    {
    case CTRL_CLOSE_EVENT:
        printf("exiting ...\n");
        g_bExit = true;
        bRet = TRUE;
        break;
    default:
        break;
    }
    return bRet;
}
 
int main()
{
    SetConsoleCtrlHandler(CosonleHandler, TRUE);
    while(!g_bExit);
    printf("exit\n");
    system("pause");

    return 0;
}

#13


引用 12 楼 DelphiGuy 的回复:
CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:
...

你仔细看那个帖子的所有讨论,有讨论CTRL_CLOSE_EVENT,但实际效果并不理想。
把你代码稍加改动:

int main()
{
    SetConsoleCtrlHandler(CosonleHandler, TRUE);
    while(!g_bExit);
int i = 0;
while(TRUE)
{
printf("%d\n", i++);
}
    printf("exit\n");
    system("pause");

    return 0;
}

可以看到printf只执行了可能一秒时间程序就退出了,并非MSDN上说的5秒。而且就算是5秒,也不能保证5秒之内一定能把所有处理完(PS:为什么MSDN上说是5秒,而实际是1秒,有知道的网友请@我告知

#14


引用 12 楼 DelphiGuy 的回复:
CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:
...

13楼还没写完,就不小心按到回车了。
运行你12楼的代码,先打开任务管理器,定位到这个程序,然后单击右上角的X。此时,我们期望的是再按任意键程序退出,但在任务管理器中看到的实际情况却是单击了右上角X后1秒左右进程就消失了。

#15


崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处。

#16


引用 14 楼 BeanJoy 的回复:
Quote: 引用 12 楼 DelphiGuy 的回复:

CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:
...

13楼还没写完,就不小心按到回车了。
运行你12楼的代码,先打开任务管理器,定位到这个程序,然后单击右上角的X。此时,我们期望的是再按任意键程序退出,但在任务管理器中看到的实际情况却是单击了右上角X后1秒左右进程就消失了。


我在12楼所说的是对7楼“好像无法捕获点击右上角X的事件”而言的,很明确CTRL_CLOSE_EVENT是可以响应关闭按钮点击事件的。
之所以改了一下你的代码,在case CTRL_CLOSE_EVENT:部分加了句printf("exiting ...\n");意思就是你在这加一句调用程序的退出处理过程就行了,至于CTRL_CLOSE_EVENT发出之后程序1秒结束还是5秒结束,应该不是大的问题,现代处理器上1秒可以执行数十亿条指令,你的程序起码也可以获得几十毫秒的时间片,除非要把大量数据写到文件,否则时间是足够的,线程不能及时终止应该是线程结构的问题,估计是在循环中有比较耗时的处理。

#17


引用 16 楼 DelphiGuy 的回复:
之所以改了一下你的代码,在case CTRL_CLOSE_EVENT:部分加了句printf("exiting ...\n");意思就是你在这加一句调用程序的退出处理过程就行了,至于CTRL_CLOSE_EVENT发出之后程序1秒结束还是5秒结束,应该不是大的问题,现代处理器上1秒可以执行数十亿条指令,你的程序起码也可以获得几十毫秒的时间片,除非要把大量数据写到文件,否则时间是足够的,线程不能及时终止应该是线程结构的问题,估计是在循环中有比较耗时的处理。


这是对楼主说的。

#18


用了7楼的鼠标钩子,方法可行。不过有点小问题,也有时候捕捉不到,大概有10%的几率直接退出,没有捕获到关闭消息。
static HHOOK g_hHook;
static bool g_bExit = false;
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
PMSLLHOOKSTRUCT pMouseInfo = (PMSLLHOOKSTRUCT)lParam;
if(nCode>=0 && wParam == WM_LBUTTONDOWN)
{
if(::SendMessage(::GetConsoleWindow(), WM_NCHITTEST, 0, pMouseInfo->pt.x + (pMouseInfo->pt.y<<16)) == HTCLOSE)
{
PostThreadMessage(GetCurrentThreadId(), UM_CLOSE, 0 , 0);
return TRUE;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

int main(int argc, char* argv[])
{
///////////
    g_hHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, 0);
HANDLE hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenTask, &sock, 0, NULL);
MSG msg;
BOOL bRet;
while( bRet=::GetMessage(&msg, NULL, 0, 0) )
{
if (bRet == -1)
{
printf("GetMessage fail: errVal=%d]n", ::GetLastError());
system("pause");
break;
}
if(   msg.message == UM_CLOSE 
   && ::MessageBox(::GetConsoleWindow(), "Confirm exit yes or not", "CenterServer", MB_YESNO) == IDYES )
{
g_bExit = true;
break;
}
}

::WaitForSingleObject(hListenThread, INFINITE);
::shutdown(sock, SD_BOTH);
::closesocket(sock);
//CThread::Delete();
::WSACleanup();
UnhookWindowsHookEx(g_hHook);
return 0;
}

#19


这似乎是个疑难杂症啊,现在也在纠结这个问题,难道要让用户输入个退出命令,而不是叉掉?

#1


怎么样才算是优雅的退出呢?

#2


资源被正确回收,所有线程返回正确的值,个人有洁癖 windows控制台程序如何优雅退出

#3


引用 楼主 wuxupu 的回复:
通过控制台窗口关闭按钮退出时,很多线程都没法正常退出,而且已构造的对象,都不会析构,即使用SetConsoleCtrlHandler去捕捉也一样。不知道有什么好的方法,我能想到的就是以监听键盘输入的方式来退出。


你这相当于强行终止,比如你正运行个多线程界面程序,然后把主进程在资源管理器给kill了,你还指望什么。
控制台程序运行完,自己就会退出。

#4


引用 3 楼 buyong 的回复:
Quote: 引用 楼主 wuxupu 的回复:

通过控制台窗口关闭按钮退出时,很多线程都没法正常退出,而且已构造的对象,都不会析构,即使用SetConsoleCtrlHandler去捕捉也一样。不知道有什么好的方法,我能想到的就是以监听键盘输入的方式来退出。


你这相当于强行终止,比如你正运行个多线程界面程序,然后把主进程在资源管理器给kill了,你还指望什么。
控制台程序运行完,自己就会退出。

是这样的,服务端程序一般不会自动退出。我希望能有正常退出的方式来辅助我检测内存泄漏。 windows控制台程序如何优雅退出

#5


SetConsoleCtrlHandler应该是可以的,你要在自己的HandlerRoutine中处理CTRL_CLOSE_EVENT信号。

#6


引用 5 楼 DelphiGuy 的回复:
SetConsoleCtrlHandler应该是可以的,你要在自己的HandlerRoutine中处理CTRL_CLOSE_EVENT信号。

你试试就知道了,能捕捉到,但是返回值为2
#include "stdafx.h"
#include <Windows.h>

/*volatile*/ bool g_bExit = false;

BOOL CALLBACK CosonleHandler(DWORD ev)
{
BOOL bRet = FALSE;
switch (ev)
{
case CTRL_CLOSE_EVENT:
g_bExit = true;
bRet = TRUE;
break;
default:
break;
}
return bRet;
}

int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(CosonleHandler, TRUE);

while(!g_bExit);

printf("exit\n");
system("pause");
return 0;
}

#7


看看这个帖子:
控制台多线程安全退出
SetConsoleCtrlHandler可以捕获CTRL+C的事件,但好像无法捕获点击右上角X的事件。
我在8楼帖的代码,HOOK点击右上角X的鼠标消息,然后进行判断处理,只判断了点击右上角X的鼠标消息,没有捕获双击左上角关闭的鼠标消息。

#8


还可以从任务管理中终止呢。以前做过一个类似的,需要在内核中的进程列表中将其移除,这样用户就没有办法关闭这个程序。 

#9


引用 4 楼 wuxupu 的回复:
Quote: 引用 3 楼 buyong 的回复:

Quote: 引用 楼主 wuxupu 的回复:

通过控制台窗口关闭按钮退出时,很多线程都没法正常退出,而且已构造的对象,都不会析构,即使用SetConsoleCtrlHandler去捕捉也一样。不知道有什么好的方法,我能想到的就是以监听键盘输入的方式来退出。


你这相当于强行终止,比如你正运行个多线程界面程序,然后把主进程在资源管理器给kill了,你还指望什么。
控制台程序运行完,自己就会退出。

是这样的,服务端程序一般不会自动退出。我希望能有正常退出的方式来辅助我检测内存泄漏。 windows控制台程序如何优雅退出


如果真有这种需求,那改程序,增加个状态量(比如一个信号量,或者一个特定message(windows),程序再设计个观察者模式,主进程能有个机制接收用户输入一个特殊命令(或者捕获用户关闭的消息),然后把这个事件通知到每个线程,然后wait所有子线程退出后,自己再退出。

#10


引用 7 楼 BeanJoy 的回复:
看看这个帖子:
控制台多线程安全退出
SetConsoleCtrlHandler可以捕获CTRL+C的事件,但好像无法捕获点击右上角X的事件。
我在8楼帖的代码,HOOK点击右上角X的鼠标消息,然后进行判断处理,只判断了点击右上角X的鼠标消息,没有捕获双击左上角关闭的鼠标消息。

这个方法可行

#11


不要企图优雅的结束(因为这是不可能办到的)
而要在烂的不能再烂的摊子上也能重整河山!

尽管如此:
console屏幕处理例子程序。终端窗口屏幕处理相关API使用例子。来自MSVC20\SAMPLES\win32\console\ 
http://download.csdn.net/detail/zhao4zhong1/3461309

#12


引用 7 楼 BeanJoy 的回复:
看看这个帖子:
控制台多线程安全退出
SetConsoleCtrlHandler可以捕获CTRL+C的事件,但好像无法捕获点击右上角X的事件。
我在8楼帖的代码,HOOK点击右上角X的鼠标消息,然后进行判断处理,只判断了点击右上角X的鼠标消息,没有捕获双击左上角关闭的鼠标消息。


CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:


#include <stdio.h>
#include <windows.h>
 
/*volatile*/ bool g_bExit = false;
 
BOOL CALLBACK CosonleHandler(DWORD ev)
{
    BOOL bRet = FALSE;
    switch (ev)
    {
    case CTRL_CLOSE_EVENT:
        printf("exiting ...\n");
        g_bExit = true;
        bRet = TRUE;
        break;
    default:
        break;
    }
    return bRet;
}
 
int main()
{
    SetConsoleCtrlHandler(CosonleHandler, TRUE);
    while(!g_bExit);
    printf("exit\n");
    system("pause");

    return 0;
}

#13


引用 12 楼 DelphiGuy 的回复:
CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:
...

你仔细看那个帖子的所有讨论,有讨论CTRL_CLOSE_EVENT,但实际效果并不理想。
把你代码稍加改动:

int main()
{
    SetConsoleCtrlHandler(CosonleHandler, TRUE);
    while(!g_bExit);
int i = 0;
while(TRUE)
{
printf("%d\n", i++);
}
    printf("exit\n");
    system("pause");

    return 0;
}

可以看到printf只执行了可能一秒时间程序就退出了,并非MSDN上说的5秒。而且就算是5秒,也不能保证5秒之内一定能把所有处理完(PS:为什么MSDN上说是5秒,而实际是1秒,有知道的网友请@我告知

#14


引用 12 楼 DelphiGuy 的回复:
CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:
...

13楼还没写完,就不小心按到回车了。
运行你12楼的代码,先打开任务管理器,定位到这个程序,然后单击右上角的X。此时,我们期望的是再按任意键程序退出,但在任务管理器中看到的实际情况却是单击了右上角X后1秒左右进程就消失了。

#15


崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处。

#16


引用 14 楼 BeanJoy 的回复:
Quote: 引用 12 楼 DelphiGuy 的回复:

CTRL_CLOSE_EVENT就是提供给用户程序处理关闭事件的,不管是system菜单的“close”(ALT+F4)还是右上角的关闭按钮,都会触发CTRL_CLOSE_EVENT。楼主的代码稍加修改:
...

13楼还没写完,就不小心按到回车了。
运行你12楼的代码,先打开任务管理器,定位到这个程序,然后单击右上角的X。此时,我们期望的是再按任意键程序退出,但在任务管理器中看到的实际情况却是单击了右上角X后1秒左右进程就消失了。


我在12楼所说的是对7楼“好像无法捕获点击右上角X的事件”而言的,很明确CTRL_CLOSE_EVENT是可以响应关闭按钮点击事件的。
之所以改了一下你的代码,在case CTRL_CLOSE_EVENT:部分加了句printf("exiting ...\n");意思就是你在这加一句调用程序的退出处理过程就行了,至于CTRL_CLOSE_EVENT发出之后程序1秒结束还是5秒结束,应该不是大的问题,现代处理器上1秒可以执行数十亿条指令,你的程序起码也可以获得几十毫秒的时间片,除非要把大量数据写到文件,否则时间是足够的,线程不能及时终止应该是线程结构的问题,估计是在循环中有比较耗时的处理。

#17


引用 16 楼 DelphiGuy 的回复:
之所以改了一下你的代码,在case CTRL_CLOSE_EVENT:部分加了句printf("exiting ...\n");意思就是你在这加一句调用程序的退出处理过程就行了,至于CTRL_CLOSE_EVENT发出之后程序1秒结束还是5秒结束,应该不是大的问题,现代处理器上1秒可以执行数十亿条指令,你的程序起码也可以获得几十毫秒的时间片,除非要把大量数据写到文件,否则时间是足够的,线程不能及时终止应该是线程结构的问题,估计是在循环中有比较耗时的处理。


这是对楼主说的。

#18


用了7楼的鼠标钩子,方法可行。不过有点小问题,也有时候捕捉不到,大概有10%的几率直接退出,没有捕获到关闭消息。
static HHOOK g_hHook;
static bool g_bExit = false;
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
PMSLLHOOKSTRUCT pMouseInfo = (PMSLLHOOKSTRUCT)lParam;
if(nCode>=0 && wParam == WM_LBUTTONDOWN)
{
if(::SendMessage(::GetConsoleWindow(), WM_NCHITTEST, 0, pMouseInfo->pt.x + (pMouseInfo->pt.y<<16)) == HTCLOSE)
{
PostThreadMessage(GetCurrentThreadId(), UM_CLOSE, 0 , 0);
return TRUE;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

int main(int argc, char* argv[])
{
///////////
    g_hHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, 0);
HANDLE hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenTask, &sock, 0, NULL);
MSG msg;
BOOL bRet;
while( bRet=::GetMessage(&msg, NULL, 0, 0) )
{
if (bRet == -1)
{
printf("GetMessage fail: errVal=%d]n", ::GetLastError());
system("pause");
break;
}
if(   msg.message == UM_CLOSE 
   && ::MessageBox(::GetConsoleWindow(), "Confirm exit yes or not", "CenterServer", MB_YESNO) == IDYES )
{
g_bExit = true;
break;
}
}

::WaitForSingleObject(hListenThread, INFINITE);
::shutdown(sock, SD_BOTH);
::closesocket(sock);
//CThread::Delete();
::WSACleanup();
UnhookWindowsHookEx(g_hHook);
return 0;
}

#19


这似乎是个疑难杂症啊,现在也在纠结这个问题,难道要让用户输入个退出命令,而不是叉掉?

#20