この記事は公開から3年以上経過しています。
前回の記事に引き続き、コンソールアプリケーションでCTRL_LOGOFF_EVENT
とCTRL_SHUTDOWN_EVENT
タイミングで処理を行う方法。
本方法ではFormを別スレッドのワーカースレッド上で起動していることから、イベントハンドラもワーカースレッド上で実行される(マルチスレッドである)ことに注意が必要です。両者を同じスレッドで処理したい場合には、参考ウェブサイトに説明のある独自のSynchronizationContext
を用意するなどの対策を行ってください。
問題
.NETのコンソールアプリケーションは、SetConsoleCtrlHandler()
のCTRL_LOGOFF_EVENT
とCTRL_SHUTDOWN_EVENT
で、ログオフ/シャットダウン/リブート時にイベントハンドリングを行うことができない。
対応
コンソールアプリケーションで非表示フォームを用いて、ログオフ/シャットダウン/リブート時のイベントハンドリングを行う。
サンプルソースコード
WinFormsを利用するため、参照設定でSystem.Windows.Forms
を追加する必要があります。
C#
using System;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleAppTest210213
{
internal class Program
{
private static void Main(string[] args)
{
var evw = new EventWatcher((o, e) =>
{
// FormClosed(終了時)に行う処理
System.IO.File.AppendAllText("closed_log.txt", $"{DateTime.Now}\r\n");
});
// ダミーループ
while (true)
Console.ReadKey();
}
}
// 終了イベント監視クラス
internal sealed class EventWatcher
{
public EventWatcher(FormClosedEventHandler handler)
{
// 別スレッドで非表示フォームを生成
var t = new Thread((p) =>
{
using (var form = new EventWatcherForm())
{
form.FormClosed += handler;
form.ShowDialog();
}
});
t.SetApartmentState(ApartmentState.STA);
t.Name = "EventWatcherThread";
t.Start();
}
// 非表示フォームクラス
private sealed class EventWatcherForm : Form
{
// CreateParams
protected override CreateParams CreateParams
{
get
{
// 非表示フォーム用スタイル設定
var cp = base.CreateParams;
cp.Style = unchecked((int)(
WindowStyles.WS_POPUP
| WindowStyles.WS_MAXIMIZEBOX
| WindowStyles.WS_SYSMENU
| WindowStyles.WS_VISIBLE));
cp.ExStyle = unchecked((int)(
ExWindowStyles.WS_EX_TOOLWINDOW));
cp.Width = 0;
cp.Height = 0;
return cp;
}
}
// ウィンドウスタイル
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
private enum WindowStyles : long
{
WS_MAXIMIZEBOX = 0x00010000L,
WS_SYSMENU = 0x00080000L,
WS_VISIBLE = 0x10000000L,
WS_POPUP = 0x80000000L,
}
// 拡張ウィンドウスタイル
// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
private enum ExWindowStyles : long
{
WS_EX_TOOLWINDOW = 0x00000080L,
}
}
}
}
VB.NET
Imports System.Threading
Imports System.Windows.Forms
Module Module1
Sub Main()
Dim evw = New EventWatcher(
Sub(o, e)
' FormClosed(終了時)に行う処理
System.IO.File.AppendAllText("closed_log.txt", $"{DateTime.Now}{vbCrLf}")
End Sub)
' ダミーループ
While True
Console.ReadKey()
End While
End Sub
' 終了イベント監視クラス
Private NotInheritable Class EventWatcher
Public Sub New(handler As FormClosedEventHandler)
' 別スレッドで非表示フォームを生成
Dim t = New Thread(
Sub(p)
Using form As Form = New EventWatcherForm()
AddHandler form.FormClosed, handler
form.ShowDialog()
End Using
End Sub)
t.SetApartmentState(ApartmentState.STA)
t.Name = "EventWatcherThread"
t.Start()
End Sub
' 非表示フォームクラス
Private NotInheritable Class EventWatcherForm
Inherits Form
' CreateParams
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
' 非表示フォーム用スタイル設定
Dim cp = MyBase.CreateParams
Dim style = WindowStyles.WS_POPUP Or WindowStyles.WS_MAXIMIZEBOX Or WindowStyles.WS_SYSMENU Or WindowStyles.WS_VISIBLE
cp.Style = WindowStyles.WS_POPUP _
Or WindowStyles.WS_MAXIMIZEBOX _
Or WindowStyles.WS_SYSMENU _
Or WindowStyles.WS_VISIBLE
cp.ExStyle = ExWindowStyles.WS_EX_TOOLWINDOW
cp.Width = 0
cp.Height = 0
Return cp
End Get
End Property
' ウィンドウスタイル
' https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
Private Enum WindowStyles
WS_MAXIMIZEBOX = &H10000
WS_SYSMENU = &H80000
WS_VISIBLE = &H10000000
WS_POPUP = &H80000000
End Enum
' 拡張ウィンドウスタイル
' https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
Private Enum ExWindowStyles
WS_EX_TOOLWINDOW = &H80
End Enum
End Class
End Class
End Module
参考ウェブサイトなど
- Microsoft Docs
並列コンピューティング SynchronizationContext こそすべて
以上です。