S-JIS[2013-01-10]

SetConsoleCtrlHandler関数

SetConsoleCtrlHandler()は、Windows API(C言語)でコンソールのシグナルハンドラーを設定する関数。


概要

WindowsのAPIでは、コンソールアプリのシグナルを捕捉して処理する関数を登録するのにSetConsoleCtrlHandler()を使う。

シグナルというのは、以下の様なもの。


シグナルを捕捉して表示する例。

※この例だとCtrl+CもCtrl+Breakもメッセージを出すだけで無視するので、アプリが終わらなくなる。
 コンソールを閉じるか、taskkillで終了させる。(→taskkillの使用例

#include <stdio.h>
#include <windows.h>
// シグナル発生時に呼ばれる関数
BOOL WINAPI controlHandler(DWORD type)
{
	printf("event-handle: %d\n", type);
	return TRUE;
}

シグナル発生時に呼ばれる関数は、TRUEを返すと「自分が処理した」ことを表す。
ハンドラー関数は複数登録されている可能性があり、FALSEを返すと次のハンドラーが呼ばれ、TRUEを返すと呼ばれない。
Ctrl+CやCtrl+Breakでは、TRUEを返すとシグナルが無視され、アプリケーションは処理を続行する。
コンソールイベントだとコンソールは閉じてしまうので、TRUEを返してもアプリケーションは終了してしまう。
ログオフやシャットダウンは、値を返すまで待ちが入る(強制終了するかどうか確認するダイアログが出る)らしい。

シグナルハンドラーが処理されている間に別のシグナルが発生しても、ハンドラー処理中にハンドラーが呼ばれることは無い。
ハンドラー処理が終わるまでシグナルは待たされ、終わったら次のシグナル処理としてハンドラーが再度呼ばれる。
(Ctrl+CとCtrl+Breakを10回押すと、順番に10回ハンドラーが呼ばれた)
ただしシャットダウン系の優先度の方が高そう。
(Ctrl+Cを10回押してからコンソールを閉じると、最初のハンドラー処理が終わったらコンソールクローズのイベントでハンドラーが呼ばれた)

int main()
{
	printf("main[%d] start\n", GetCurrentProcessId());

	if (!SetConsoleCtrlHandler(controlHandler, TRUE)) {
		printError("SetConsoleCtrlHandler");
		return -1;
	}

	while(1); //無限ループ

	printf("main[%d] end\n", GetCurrentProcessId());

	return 0;
}

今回の例では主処理は無限ループしている。
シグナルによってアプリケーションが終了する場合、主処理が何をしているかは関係なく終了するので、
この主処理の「main end」のメッセージが表示されることは無い。


CreateProcessとの関係

Ctrl+CとCtrl+Breakのデフォルトの動作は、どちらも同じ(アプリケーションを終了させる)である。
しかし、CreateProcess()で子プロセスを作る際には挙動に違いが出てくる。

creationFlagsにCREATE_NEW_PROCESS_GROUPが無い場合

Ctrl+CもCtrl+Breakも、親プロセスと子プロセスの両方に送られる。

creationFlagsにCREATE_NEW_PROCESS_GROUPが有る場合

Ctrl+Cは親プロセスだけに送られて、子プロセスには送られなくなる。
(つまり、子プロセス側で特別なことをしなくても、Ctrl+Cが無視できるようになる(Ctrl+Cが無効化される))
Ctrl+Breakは、親プロセスと子プロセスの両方に送られる。

参考: MSDNのCreateProcessページの最後尾のCREATE_NEW_PROCESS_GROUPフラグの説明


子プロセスをCtrl+CやCtrl+Breakで止めると、親プロセスには子プロセスが正常終了したように見え(WaitForSingleObject()がWAIT_OBJECT_0を返す)、終了コードは0xc000013aになる。


UNIX(POSIX)では、fork()によって子プロセスを生成してからexec()によって別プログラムを実行するので、その間にシグナル捕捉の設定を行うことが出来るが、
CreateProcessはそこが一体化しているので、子プロセス用のシグナルハンドリングを親プロセス側で行うことは出来ない。


Javaの挙動

Java(Windows版)では、アプリケーション実行中にCtrl+Breakを押すと、スレッドダンプが表示される。
つまり、java.exeはCtrl+Breakのハンドリングを行っているということだろう。

ちなみに、「-Xrs」を付けてjava.exeを実行すると、Ctrl+Breakはデフォルトの動作(つまりアプリケーションの終了)になる。

> java -Xrs Hoge

C言語目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま