0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2025 WinForms 無操作→自動ログアウトを自作する

Last updated at Posted at 2025-05-03

WinFormsアプリケーション向けに「指定時間操作されなければログアウトする機能」を自作しましたので、実装方法をまとめます。

  • ユーザー操作(マウス・キー)を監視
  • 指定時間操作が無ければ自動的にログアウト
  • IMessageFilter でグローバルに操作を監視
  • ApplicationContext によるアプリ管理

業務アプリにも安心して使えるレベルを目指しました✨

0.イメージ図

image.png

1.前提条件

  • Visual studio 2022 Version 17.13.6
  • .Net 9

なお、すべてのソースコードを公開しています。

2.なぜ IMessageFilter を選んだのか? 🎯

Windowsアプリで「ユーザー操作の監視」を行う手段は複数ありますが、本記事では IMessageFilter を採用しました。その理由を説明します。

(1) Win32 API を使った DLL ベースのグローバルフックは危険

グローバルにマウスやキーボードの操作をフックするには、SetWindowsHookEx を使って DLL を別プロセスに挿入する方式があります。
ですが、これは以下の理由から本プロジェクトでは採用しませんでした:

  • 管理者権限が必要な場合がある(UAC との相性が悪い)
  • アンチウイルスソフトに引っかかる可能性がある
  • アプリがクラッシュした際に OS 全体に影響するリスクがある
  • コードが煩雑で保守性が下がる

つまり、業務アプリのような 安定性と信頼性が求められるシステム では不適です。

(2) すべてのコントロールにイベントハンドラを設定するのは重い

当初、以下のような実装をしていました:

foreach (Control ctrl in parent.Controls)
{
    ctrl.MouseDown += OnMouseDown;
    ctrl.KeyDown += OnKeyDown;
    ...
}

しかしこの方式では以下の課題があります:

  • フォーム内のコントロールが多いと、イベントフック数が指数的に増える
  • 一部の動的に追加されるコントロールが漏れる可能性がある
  • ループが深くなり、起動時や画面切り替え時のパフォーマンスが落ちる

これは UI が複雑になるほど問題になっていきます。

(3) IMessageFilter のメリット

代わりに採用した IMessageFilter は以下の点で優れています:

  • すべての WinForms メッセージを低コストで横取りできる
  • キーボードやマウスのクリック操作を簡単に検知できる
  • フォームやコントロールの構造に依存せず、一括で操作を監視できる
  • 軽量で安定性も高く、商用利用にも安心

この方法で、「コントロール数が多いと重くなる」「マウス・キーボードの操作を正確に拾えない」といった課題を すべて解消 できます。

3.実装ポイント

A) IMessageFilter の導入

public class UserActivityMessageFilter : IMessageFilter
{
    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            case 0x0100: // WM_KEYDOWN
            case 0x0201: // WM_LBUTTONDOWN
                UserActivityTracker.Update();
                break;
        }
        return false;
    }
}

→ Application.AddMessageFilter(...) により一括フック。マウス移動を除外して負荷軽減。

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    // ユーザーアクティビティを監視するフィルタを追加
    Application.AddMessageFilter(new UserActivityMessageFilter());

    var loginForm = new FormLogin();
    AppContextInstance = new MyAppContext(loginForm);
    Application.Run(AppContextInstance);
}

🔸この中の Application.AddMessageFilter(new UserActivityMessageFilter()) の行が、ユーザー操作の検知の要です。

B) アクティビティ監視の共通クラス

public static class UserActivityTracker
{
    public static DateTime LastActionTime { get; private set; } = DateTime.Now;

    public static void Update() => LastActionTime = DateTime.Now;

    public static bool IsInactive(TimeSpan timeout) =>
        DateTime.Now - LastActionTime > timeout;
}

C) 自動ログアウトの呼び出しをFormの基底クラスで共通化する

public partial class BaseForm : Form

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        // 未操作タイマーを開始
        StartInactivityTimer();
    }

    private void StartInactivityTimer()
    {
        _inactivityTimer.Interval = CheckIntervalMilliseconds;
        _inactivityTimer.Tick += (_, _) => CheckTimeout();
        _inactivityTimer.Start();
    }

    private void CheckTimeout()
    {
        if (UserActivityTracker.IsInactive(Timeout))
        {
            _inactivityTimer.Stop();
            BeginInvoke(() =>
            {
                Program.AppContextInstance!.ShowLoginAndCloseOthers();
            });
        }
    }
}

→ 各フォームで BaseForm を継承するだけで自動ログアウト対応。

D) 自動ログアウト処理

public class MyAppContext : ApplicationContext
{
    // 新しいログインフォームを表示し、他のフォームを全て閉じる
    public void ShowLoginAndCloseOthers()
    {
        // ログインフォームは常にNEWする
        var loginForm = new FormLogin();
        loginForm.Show();

        foreach (Form f in Application.OpenForms.Cast<Form>().ToList())
        {
            // 過去のログインフォームが残っていても、NEWしたインスタンス以外は閉じる
            if (f != loginForm)
                f.Close();
        }
    }
}

4.最終構成 📦

  • UserActivityMessageFilter.cs(ユーザーの操作を監視するフィルタ)
  • UserActivityTracker.cs(ユーザー操作の間隔を取得)
  • BaseForm.cs(すべてのフォームが継承する監視用の継承元)
  • MyAppContext.cs(ログアウト処理)

5.最後に 📝

この構成は、
✅ シンプル
✅ 軽量
✅ 実務アプリでも安定
をすべて両立できることを目指しました。

現場のWinFormsアプリに自動ログアウトを追加したい場面はよくあります。
色々試した結果、IMessageFilterの採用が一番有効でした🌸

0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?