65.9K
CodeProject 正在变化。 阅读更多。
Home

控制台事件处理

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (32投票s)

2002年5月30日

4分钟阅读

viewsIcon

244964

本文讨论如何处理控制台窗口特定的事件。

引言

每个人都会或多或少地编写控制台应用程序。当人们学习编程时,尤其是在学习基于 DOS 的 C/C++ 编程时,这种编程更为普遍。然而,当一个人迁移到 Windows 编程时,控制台应用程序的开发就退居二线了。但是 Win32 控制台开发占据了重要的地位,尤其是在 Win32 API 包含大量专门用于控制台应用程序开发的 API 时。如果注意到,即使是 VC++ 和最新的开发技术,如 C#,也支持控制台项目的开发。控制台应用程序是测试 Windows 应用程序核心功能的良好选择,而无需使用 GUI 带来的不必要的开销。

但是,人们总是对如何知道何时发生某些与系统相关的事件感到无助,例如当用户注销、系统正在关机,或者处理 control+break 或 control+C 键盘事件等。对于基于 Windows 的应用程序,了解何时发生此类事件不是问题,因为它们被分配了一个消息队列进行轮询,并且假设已为相关事件编程,则可以很容易地处理它。但对于控制台应用程序来说情况并非如此,控制台应用程序没有消息队列的概念。

本文旨在讨论如何在任何控制台应用程序中处理各种基于控制台的事件。阅读完本文后,您就会发现这项看似无助的任务是多么微不足道:)

设置控制台陷阱

处理控制台应用程序事件的第一步是设置一个事件陷阱,从技术上讲,这被称为安装事件处理程序。为此,我们使用 SetConsoleCtrlHandler Win32 API,其原型如下所示

BOOL SetConsoleCtrlHandler(
    PHANDLER_ROUTINE HandlerRoutine, // handler function
    BOOL Add // add or remove handler
    );

HandlerRoutine 参数是指向一个函数的指针,该函数具有以下原型

BOOL WINAPI HandlerRoutine(
    DWORD dwCtrlType   //  control signal type
);

HandlerRoutine 只需要一个 DWORD 参数,用于说明发生了哪个控制台事件。该参数可以取以下值

  • CTRL_C_EVENT - 当用户按下 CTRL+C,或者由 GenerateConsoleCtrlEvent API 发送时发生。
  • CTRL_BREAK_EVENT - 当用户按下 CTRL+BREAK,或者由 GenerateConsoleCtrlEvent API 发送时发生。
  • CTRL_CLOSE_EVENT - 当尝试关闭控制台时发生,当系统向与给定控制台关联的所有进程发送关闭信号时发生。
  • CTRL_LOGOFF_EVENT - 当用户注销时发生。但是,无法确定哪个用户正在注销。
  • CTRL_SHUTDOWN_EVENT - 当系统正在关机时发生,通常发送给服务。

在收到事件后,HandlerRoutine 可以选择执行一些处理,或忽略该事件。如果例程选择不处理该事件,则它应该返回 FALSE,然后系统将继续处理下一个已安装的处理程序。但是,如果例程确实处理了事件,则它应该在完成所有必要的处理后返回 TRUECTRL_CLOSE_EVENTCTRL_LOGOFF_EVENTCTRL_SHUTDOWN_EVENT 通常用于执行应用程序所需的任何清理,然后调用 ExitProcess API。因此,系统与这三个事件关联了一些超时,CTRL_CLOSE_EVENT 为 5 秒,其他两个为 20 秒。如果进程未在超时期限内响应,Windows 将继续向用户显示“结束任务”对话框。如果用户继续结束任务,则应用程序将没有任何机会执行清理。因此,所需的任何清理都应在超时期限内完成。下面是处理程序例程的示例

BOOL WINAPI ConsoleHandler(DWORD CEvent)
{
    char mesg[128];

    switch(CEvent)
    {
    case CTRL_C_EVENT:
        MessageBox(NULL,
            "CTRL+C received!","CEvent",MB_OK);
        break;
    case CTRL_BREAK_EVENT:
        MessageBox(NULL,
            "CTRL+BREAK received!","CEvent",MB_OK);
        break;
    case CTRL_CLOSE_EVENT:
        MessageBox(NULL,
            "Program being closed!","CEvent",MB_OK);
        break;
    case CTRL_LOGOFF_EVENT:
        MessageBox(NULL,
            "User is logging off!","CEvent",MB_OK);
        break;
    case CTRL_SHUTDOWN_EVENT:
        MessageBox(NULL,
            "User is logging off!","CEvent",MB_OK);
        break;

    }
    return TRUE;
}

现在我们已经看到了处理程序例程如何工作,让我们看看如何安装处理程序。为此,正如本文前面提到的,我们使用 SetConsoleCtrlHandler API,如下所示

if (SetConsoleCtrlHandler(
    (PHANDLER_ROUTINE)ConsoleHandler,TRUE)==FALSE)
{
    // unable to install handler... 
    // display message to the user
    printf("Unable to install handler!\n");
    return -1;
}

第一个参数是一个类型为 PHANDLER_ROUTINE 的函数指针,其原型已在前面讨论过。第二个参数,如果设置为 TRUE,则尝试安装处理程序,如果设置为 FALSE,则尝试卸载。如果任一尝试成功,则返回值是 TRUE。否则返回 FALSE

所以,这就是处理控制台应用程序事件的全部内容。安装处理程序后,您的应用程序将接收事件,并且当即将终止执行时,可能会卸载处理程序。很容易,对吧 :) ?

© . All rights reserved.