コントロール ハンドラー関数の登録

基本的なコントロール ハンドラーの例

これは、コントロール ハンドラーのインストールに使用される SetConsoleCtrlHandler 関数の例です。

Ctrl+C 信号を受信すると、コントロール ハンドラーは TRUE を返し、信号を処理したことを示します。 これを行うと、その他のコントロール ハンドラーが呼び出されなくなります。

CTRL_CLOSE_EVENT 信号を受信すると、コントロール ハンドラーは TRUE を返し、プロセスは終了します。

CTRL_BREAK_EVENT 信号、CTRL_LOGOFF_EVENT 信号、またはCTRL_SHUTDOWN_EVENT 信号を受信すると、コントロール ハンドラーは FALSE を返します。 これを行うと、信号が次のコントロール ハンドラー関数に渡されます。 その他のコントロール ハンドラーが登録されていない場合、または登録されたハンドラーのいずれも TRUE を返さない場合は、デフォルトのハンドラーが使用され、結果としてプロセスが終了します。

Note

AttachConsoleAllocConsole、または FreeConsole を呼び出すと、クライアント プロセスでコントロール ハンドラーのテーブルが初期状態にリセットされます。 アタッチされたコンソール セッションが変更されたときに、ハンドラーを再登録する必要があります。

// CtrlHandler.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <windows.h>
#include <stdio.h>

BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
    switch (fdwCtrlType)
    {
        // Handle the CTRL-C signal.
    case CTRL_C_EVENT:
        printf("Ctrl-C event\n\n");
        Beep(750, 300);
        return TRUE;

        // CTRL-CLOSE: confirm that the user wants to exit.
    case CTRL_CLOSE_EVENT:
        Beep(600, 200);
        printf("Ctrl-Close event\n\n");
        return TRUE;

        // Pass other signals to the next handler.
    case CTRL_BREAK_EVENT:
        Beep(900, 200);
        printf("Ctrl-Break event\n\n");
        return FALSE;

    case CTRL_LOGOFF_EVENT:
        Beep(1000, 200);
        printf("Ctrl-Logoff event\n\n");
        return FALSE;

    case CTRL_SHUTDOWN_EVENT:
        Beep(750, 500);
        printf("Ctrl-Shutdown event\n\n");
        return FALSE;

    default:
        return FALSE;
    }
}

int main(void)
{
    if (SetConsoleCtrlHandler(CtrlHandler, TRUE))
    {
        printf("\nThe Control Handler is installed.\n");
        printf("\n -- Now try pressing Ctrl+C or Ctrl+Break, or");
        printf("\n    try logging off or closing the console...\n");
        printf("\n(...waiting in a loop for events...)\n\n");

        while (1) {}
    }
    else
    {
        printf("\nERROR: Could not set control handler");
        return 1;
    }
    return 0;
}

非表示ウィンドウでリッスンする例

注釈ごとに、gdi32.dll または user32.dll ライブラリが読み込まれると、SetConsoleCtrlHandler は、CTRL_LOGOFF_EVENT イベントおよび CTRL_SHUTDOWN_EVENT イベントに対して呼び出されません。 指定した回避策は、ウィンドウがまだ存在しない場合は、dwExStyle パラメーターを 0 に設定して CreateWindowEx メソッドを呼び出し、WM_QUERYENDSESSION ウィンドウ メッセージと WM_ENDSESSION ウィンドウ メッセージをリッスンして、非表示ウィンドウを作成することです。 ウィンドウが既に存在する場合は、既存のウィンドウ プロシージャに 2 つのメッセージを追加します。

ウィンドウとそのメッセージング ループの設定の詳細については、「ウィンドウの作成」を参照してください。

// CtrlHandler.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <Windows.h>
#include <stdio.h>

BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
    switch (fdwCtrlType)
    {
        // Handle the CTRL-C signal.
    case CTRL_C_EVENT:
        printf("Ctrl-C event\n\n");
        Beep(750, 300);
        return TRUE;

        // CTRL-CLOSE: confirm that the user wants to exit.
    case CTRL_CLOSE_EVENT:
        Beep(600, 200);
        printf("Ctrl-Close event\n\n");
        return TRUE;

        // Pass other signals to the next handler.
    case CTRL_BREAK_EVENT:
        Beep(900, 200);
        printf("Ctrl-Break event\n\n");
        return FALSE;

    case CTRL_LOGOFF_EVENT:
        Beep(1000, 200);
        printf("Ctrl-Logoff event\n\n");
        return FALSE;

    case CTRL_SHUTDOWN_EVENT:
        Beep(750, 500);
        printf("Ctrl-Shutdown event\n\n");
        return FALSE;

    default:
        return FALSE;
    }
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_QUERYENDSESSION:
    {
        // Check `lParam` for which system shutdown function and handle events.
        // See https://learn.microsoft.com/windows/win32/shutdown/wm-queryendsession
        return TRUE; // Respect user's intent and allow shutdown.
    }
    case WM_ENDSESSION:
    {
        // Check `lParam` for which system shutdown function and handle events.
        // See https://learn.microsoft.com/windows/win32/shutdown/wm-endsession
        return 0; // We have handled this message.
    }
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

int main(void)
{
    WNDCLASS sampleClass{ 0 };
    sampleClass.lpszClassName = TEXT("CtrlHandlerSampleClass");
    sampleClass.lpfnWndProc = WindowProc;

    if (!RegisterClass(&sampleClass))
    {
        printf("\nERROR: Could not register window class");
        return 2;
    }

    HWND hwnd = CreateWindowEx(
        0,
        sampleClass.lpszClassName,
        TEXT("Console Control Handler Sample"),
        0,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        nullptr,
        nullptr,
        nullptr,
        nullptr
    );

    if (!hwnd)
    {
        printf("\nERROR: Could not create window");
        return 3;
    }

    ShowWindow(hwnd, SW_HIDE);

    if (SetConsoleCtrlHandler(CtrlHandler, TRUE))
    {
        printf("\nThe Control Handler is installed.\n");
        printf("\n -- Now try pressing Ctrl+C or Ctrl+Break, or");
        printf("\n    try logging off or closing the console...\n");
        printf("\n(...waiting in a loop for events...)\n\n");

        // Pump message loop for the window we created.
        MSG msg{};
        while (GetMessage(&msg, nullptr, 0, 0) > 0)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return 0;
    }
    else
    {
        printf("\nERROR: Could not set control handler");
        return 1;
    }
}