コンソールウィンドウの割り込み制御


ウィンドウプログラムから標準出力をコンソールウィンドウに出力するには以下のコードで可能です
//標準出力を指定し、標準出力関数を使ってみる
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <iostream>
#include <io.h>
#include <fcntl.h>
using namespace std;

int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
    int hConsole;

    ::AllocConsole();     //コンソール割り当て

    hConsole = ::_open_osfhandle((intptr_t)::GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
    *stdout = *::_tfdopen(hConsole, TEXT("w"));
    ::setvbuf(stdout, NULL, _IONBF, 0);

    printf("printfで出力\n");
    cout<<"coutで出力"<<endl;

    ::MessageBox(NULL, TEXT("ストッパーです"), TEXT("AllocConsole"), MB_OK);
    ::FreeConsole();      //コンソールを解放します
    ::_close(hConsole);   //ハンドルを閉じます

    return 0;
}
しかし、このままでは以下の条件でプログラムが終了してしまいます
・(コンソールウィンドウがアクティブな状態で)Ctrl+C、またはCtrl+Breakキーを押す
・コンソールウィンドウの×ボタンを押す
一見コンソールウィンドウだけが閉じるかのような条件ですが
メインプログラム自体も終了してしまいます

そこで今回は、コンソール側からは終了させない処理を追加してみたいと思います

コンソールウィンドウは、Ctrl+Cや×ボタンが押されたとき、その信号を処理するHandlerRoutine関数を
持っています
デフォルトでは、Ctrl+Cなどを受け取るとExitProcess関数を呼び出すというだけの処理になっているので
プロセスが強制終了してしまいます
そこで、割り込みの信号を制御するHandlerRoutine関数を追加します
ハンドラ関数の定義は以下のような形になります
BOOL WINAPI HandlerRoutine(DWORD type)
HandlerRoutineの名前は任意です
Ctrl+Cなど、割り込みが入るごとにこの関数が処理されることになります
どの割り込みが入ったのかは、引数のtypeに定数が格納されています
定数 割り込みの種類
CTRL_C_EVENT Ctrl+Cが押された
CTRL_BREAK_EVENT Ctrl+Breakが押された
CTRL_CLOSE_EVENT コンソールを閉じた(×ボタンなど)
CTRL_LOGOFF_EVENT ログオフした
CTRL_SHUTDOWN_EVENT シャットダウンした

そして、HandlerRoutine関数の戻り値にFALSEを指定すると強制終了するので、
終了したくない場合はTRUEを返します
例えばCtrl+Cを無効にしたい場合は、ハンドラ関数を
//コンソールのCtrl+Cを無効にする
BOOL WINAPI HandlerRoutine(DWORD type)
{
    switch(type)
    {
    case CTRL_C_EVENT:   //Ctrl+Cを受け取った
        return TRUE;     //終了しないのでTRUEを返す
    }

    return FALSE;        //それ以外の時は強制終了
}
のように処理します
定数の5パターン分用意すると以下のようになります
//コンソールのCtrl+C,Ctrl+Break,閉じるを無効にする
//ログオフとシャットダウンが発生したときは終了するようにする
BOOL WINAPI HandlerRoutine(DWORD type)
{
    switch(type)
    {
    case CTRL_C_EVENT:      //Ctrl+Cを受け取った
    case CTRL_BREAK_EVENT:  //Ctrl+Breakを受け取った
    case CTRL_CLOSE_EVENT:  //コンソールを閉じた
        return TRUE;        //無効にするのでTRUEを返す
    case CTRL_LOGOFF_EVENT:
    case CTRL_SHUTDOWN_EVENT:
        return FALSE;
    }

    return FALSE;           //それ以外の時は強制終了
}
これで、コンソールウィンドウの割り込みを制御できます
あとはSetConsoleCtrlHandler関数を使い、自分の指定したハンドラ関数を追加します
BOOL SetConsoleCtrlHandler(
  PHANDLER_ROUTINE HandlerRoutine,  // ハンドラ関数のアドレス
  BOOL Add                          // ハンドラを追加するか、削除するか
);
第1引数はハンドラ関数のアドレスを入れます
第2引数はハンドラを追加するのでTRUEを指定します(削除する場合はFALSE)
以下はHandlerRoutine関数を追加したコードです
//標準出力を指定し、標準出力関数を使ってみる
//割り込み制御追加
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <iostream>
#include <io.h>
#include <fcntl.h>
using namespace std;

//割り込み制御
BOOL WINAPI HandlerRoutine(DWORD type)
{
    switch(type)
    {
    case CTRL_C_EVENT:      //Ctrl+Cを受け取った
    case CTRL_BREAK_EVENT:  //Ctrl+Breakを受け取った
    case CTRL_CLOSE_EVENT:  //コンソールを閉じた
        return TRUE;        //無効にするのでTRUEを返す
    case CTRL_LOGOFF_EVENT:
    case CTRL_SHUTDOWN_EVENT:
        return FALSE;
    }
    return FALSE;           //それ以外の時は強制終了
}

//メイン関数
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
    int hConsole;

    ::AllocConsole();     //コンソール割り当て

    hConsole = ::_open_osfhandle((intptr_t)::GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
    *stdout = *::_tfdopen(hConsole, TEXT("w"));
    ::setvbuf(stdout, NULL, _IONBF, 0);
    ::SetConsoleCtrlHandler(HandlerRoutine, TRUE); //←ここを追加!

    printf("printfで出力\n");
    cout<<"coutで出力"<<endl;

    ::MessageBox(NULL,TEXT("ストッパーです"),TEXT("AllocConsole"),MB_OK);
    ::FreeConsole();      //コンソールを解放します
    ::_close(hConsole);

    return 0;
}
これでコンソールのCtrl+CとCtrl+Breakを無効化できます
ただし、コンソールを閉じたとき(×ボタンを押したときなど)は以下のようになります
×ボタンを押した

キャンセルを押すと終了しません(処理を続行します)
すぐに終了を押すと終了してしまうのですが、コンソール画面から不用意に終了させない
という効果はあると思います

次は、標準エラー出力についてです

続き


TOPプログラミング>コンソールウィンドウの割り込み制御