|
.NET TIPS
コンソール・アプリケーションの強制終了時に処理を行うには?(Win32編)[C#、VB]
デジタルアドバンテージ 遠藤 孝信
2009/07/23 |
|
|
「TIPS:コンソール・アプリケーションで[閉じる]ボタンを無効にするには?」では、コンソール・アプリケーションの後処理を実行可能にするため、[×]ボタン([閉じる]ボタン)を無効にすることにより、アプリケーションが強制終了されないようにする方法を示したが、Win32 APIを利用すれば、[×]ボタンがクリックされたときに処理を実行することもできる。
これには、Win32 APIである「SetConsoleCtrlHandler関数」を使用する。これは、コンソール・アプリケーションで[Ctrl]+[C]キーが押されたり、コマンド・プロンプトが閉じられたりする際に呼び出されるハンドラ(=特定のメソッド)を登録するための関数だ。
ハンドラは、SetConsoleCtrlHandler関数の第1引数で指定する。C言語などでは「HandlerRoutine関数」へのポインタを指定することになるが、.NETでは、これをメソッドのデリゲートで代用する。第2引数には、ハンドラを登録するという意味のtrueを指定する。ハンドラが呼び出される際には、アプリケーションが閉じられる要因を示す値が、関数(メソッド)のパラメータに渡される。
SetConsoleCtrlHandler関数を使用したサンプル・プログラム
以下では、「TIPS:コンソール・アプリケーションの強制終了時に処理を行うには?」で示したサンプル・プログラムを、SetConsoleCtrlHandler関数を使用して書き換えたものを示す。
// ctrlhandle.cs
using System;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
// 簡単なログ・クラス
public class MyLogger {
StringBuilder log = new StringBuilder();
// ログの追加
public void Add(string line) {
log.AppendLine(line);
}
// ログの保存(ログ・ファイルの名前は「yyyymmdd.txt」の形式)
public void Save() {
string file = DateTime.Now.ToString("yyyyMMdd") + ".txt";
Encoding enc = Encoding.GetEncoding("Shift_JIS");
// 追加書き込み(文字コードはShift-JISを使用)
using (StreamWriter sw = new StreamWriter(file, true, enc)) {
sw.Write(log.ToString());
}
}
}
// ハンドラ・ルーチンに渡される定数の定義
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
class MyProgram {
// Win32 APIであるSetConsoleCtrlHandler関数の宣言
[DllImport("Kernel32")]
static extern bool
SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
// SetConsoleCtrlHandler関数にメソッド(ハンドラ・ルーチン)を
// 渡すためのデリゲート型
delegate bool HandlerRoutine(CtrlTypes CtrlType);
MyLogger logger = new MyLogger();
HandlerRoutine myHandlerDele;
MyProgram() {
// myHandlerメソッドの登録
myHandlerDele = new HandlerRoutine(myHandler);
SetConsoleCtrlHandler((myHandlerDele, true);
}
void Run() {
while (true) {
string line = DateTime.Now + " 出力";
Console.WriteLine(line);
logger.Add(line); // ログの追加
System.Threading.Thread.Sleep(2000); // 2秒間待つ
}
}
// ハンドラ・ルーチン
bool myHandler(CtrlTypes ctrlType)
{
logger.Add("強制終了:" + ctrlType); // ログの追加
logger.Save(); // ログの保存
return false;
}
static void Main() {
(new MyProgram()).Run();
}
}
// コンパイル方法:csc ctrlhandle.cs
|
|
強制終了時に処理を行うC#のサンプル・プログラム(ctrlhandler.cs) |
|
' ctrlhandler.vb
Imports System
Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices
' 簡単なログ・クラス
Public Class MyLogger
Dim log As New StringBuilder()
' ログの追加
Public Sub Add(ByVal line As String)
log.AppendLine(line)
End Sub
' ログの保存(ログ・ファイルの名前は「yyyymmdd.txtの」形式)
Public Sub Save()
Dim file As String = DateTime.Now.ToString("yyyyMMdd") + ".txt"
Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")
' 追加書き込み(文字コードはShift-JISを使用)
Using sw As New StreamWriter(file, True, enc)
sw.Write(log.ToString())
End Using
End Sub
End Class
' ハンドラ・ルーチンに渡される定数の定義
Public Enum CtrlTypes
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6
End Enum
Class MyProgram
' Win32 APIであるSetConsoleCtrlHandler関数の宣言
<DllImport("Kernel32")> _
Shared Function SetConsoleCtrlHandler( _
ByVal Handler As HandlerRoutine, _
ByVal Add As Boolean) As Boolean
End Function
' SetConsoleCtrlHandler関数にメソッド(ハンドラ・ルーチン)を
'渡すためのデリゲート型
Delegate Function _
HandlerRoutine(ByVal CtrlType As CtrlTypes) As Boolean
Dim logger As New MyLogger()
Dim myHandlerDele As HandlerRoutine
Sub New()
' myHandlerメソッドの登録
myHandlerDele = New HandlerRoutine(AddressOf myHandler)
SetConsoleCtrlHandler(myHandlerDele, True)
End Sub
Sub Run()
While True
Dim line As String = DateTime.Now + " 出力"
Console.WriteLine(line)
logger.Add(line) ' ログの追加
System.Threading.Thread.Sleep(2000) ' 2秒間待つ
End While
End Sub
' ハンドラ・ルーチン
Function myHandler(ByVal ctrlType As CtrlTypes) As Boolean
logger.Add("強制終了:" & ctrlType.ToString()) ' ログの追加
logger.Save() ' ログの保存
Return False
End Function
Shared Sub Main()
Dim p As New MyProgram()
p.Run()
End Sub
End Class
' コンパイル方法:vbc ctrlhandler.vb
|
|
強制終了時に処理を行うVBのサンプル・プログラム(ctrlhandler.vb) |
|
このプログラムでは、ハンドラ(myHandlerメソッド)にパラメータとして渡される値を、列挙型により事前に定義している。各値の詳細については、HandlerRoutine関数の説明を参照してほしい。
上記のプログラムを実行し、プログラムを[Ctrl]+[C]キーで終了した場合には、出力されるログの内容は次のようになる。
2009/07/23 12:22:26 出力
2009/07/23 12:22:28 出力
2009/07/23 12:22:30 出力
強制終了:CTRL_C_EVENT
|
|
[Ctrl]+[C]キーで終了した場合のログ出力例 |
また、プログラムを実行したまま、そのコマンド・プロンプトを閉じると、ログは次のようになる。
2009/07/23 12:22:46 出力
2009/07/23 12:22:48 出力
2009/07/23 12:22:50 出力
強制終了:CTRL_CLOSE_EVENT
|
|
コマンド・プロンプトを閉じた場合のログ出力例 |
各ログの最後の行で出力している変数ctrlTypeの値が異なっているのを確認できる。
|
generated by
|
|
更新履歴 |
【2009/7/24】サンプル・プログラムにおいて、ガベージ・コレクション発生時に正しく動作しないという問題が見つかりました。このため、SetConsoleCtrlHandler関数の引数部分でインスタンス化していたハンドラのデリゲート・オブジェクトを、クラスのフィールドに保持するように修正しました。お詫びして訂正させていただきます。 |
|
Insider.NET 記事ランキング
本日
月間