Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 1 year has passed since last update.

[WPF] ウインドウ非表示中にキー入力を検知(グローバルホットキー)

Last updated at Posted at 2023-01-18

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

自分のアプリのウインドウが表示されていないときに、キーボードのキー入力を監視したい。

以前の記事で、メインスレッドの空き時間(Idle時間)にキー入力を監視するということをしたが、
ずっと監視し続けるというやり方ではなく、指定のキー入力があったら割り込みでなにかする、的なことをしたい。

やりかた

グローバルホットキーというのを使えばいいらしい。

参考:WPFでホットキーの登録

下記のあたり。

[DllImport("user32.dll")]
extern static int RegisterHotKey(IntPtr HWnd, int ID, MOD_KEY MOD_KEY, Keys KEY);

[DllImport("user32.dll")]
extern static int UnregisterHotKey(IntPtr HWnd, int ID);

const int WM_HOTKEY = 0x0312;

RegisterHotKeyの使い方

IDは、同じhWnd or スレッド内で、一意のIDであればいいっぽい。(かぶるとRegisterHotKeyが0以外返して失敗するみたい)

サンプルコード

WPFのテンプレートを使って作成したプロジェクト。

Windows.Formsを使うので、対象がWindowsでなければならない。
そのうえで、UseWindowsFormsをtrueにする。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
	  <UseWindowsForms>true</UseWindowsForms>
	  <Platforms>x64</Platforms>
  </PropertyGroup>

</Project>

<Window x:Class="ToumeiGadget.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local2="using:WpfLibrary1"
        mc:Ignorable="d"
        AllowsTransparency="True"
        WindowStyle="None"
        Title="MainWindow" Height="200" Width="200">
    <Grid>
    </Grid>
</Window>
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Forms;

namespace ToumeiGadget
{
    public partial class MainWindow : Window
    {
        HotKey hotKey;

        public MainWindow()
        {
            InitializeComponent();

            hotKey = new HotKey(MOD_KEY.ALT | MOD_KEY.CONTROL | MOD_KEY.SHIFT, Keys.A);
            hotKey.HotKeyPush += new EventHandler(hotKey_HotKeyPush);

            // HideするまえにhotKey登録しないと登録できない
            this.Hide();
        }

        void hotKey_HotKeyPush(object sender, EventArgs e)
        {
            Debug.WriteLine("ホットキーが押されました。");
        }
    }

    /// <summary>
    /// グローバルホットキーを登録するクラス。
    /// 使用後は必ずDisposeすること。
    /// </summary>
    public class HotKey : IDisposable
    {
        HotKeyForm form;
        /// <summary>
        /// ホットキーが押されると発生する。
        /// </summary>
        public event EventHandler HotKeyPush;

        /// <summary>
        /// ホットキーを指定して初期化する。
        /// 使用後は必ずDisposeすること。
        /// </summary>
        /// <param name="modKey">修飾キー</param>
        /// <param name="key">キー</param>
        public HotKey(MOD_KEY modKey, Keys key)
        {
            form = new HotKeyForm(modKey, key, raiseHotKeyPush);
        }

        private void raiseHotKeyPush()
        {
            if (HotKeyPush != null)
            {
                HotKeyPush(this, EventArgs.Empty);
            }
        }

        public void Dispose()
        {
            form.Dispose();
        }

        private class HotKeyForm : Form
        {
            [DllImport("user32.dll")]
            extern static int RegisterHotKey(IntPtr HWnd, int ID, MOD_KEY MOD_KEY, Keys KEY);

            [DllImport("user32.dll")]
            extern static int UnregisterHotKey(IntPtr HWnd, int ID);

            const int WM_HOTKEY = 0x0312;
            int id;
            ThreadStart proc;

            public HotKeyForm(MOD_KEY modKey, Keys key, ThreadStart proc)
            {
                this.proc = proc;
                for (int i = 0x0000; i <= 0xbfff; i++)
                {
                    if (RegisterHotKey(this.Handle, i, modKey, key) != 0)
                    {
                        id = i;
                        break;
                    }
                }
            }

            protected override void WndProc(ref Message m)
            {
                base.WndProc(ref m);

                if (m.Msg == WM_HOTKEY)
                {
                    if ((int)m.WParam == id)
                    {
                        proc();
                    }
                }
            }

            protected override void Dispose(bool disposing)
            {
                UnregisterHotKey(this.Handle, id);
                base.Dispose(disposing);
            }
        }
    }

    /// <summary>
    /// HotKeyクラスの初期化時に指定する修飾キー
    /// </summary>
    public enum MOD_KEY : int
    {
        ALT = 0x0001,
        CONTROL = 0x0002,
        SHIFT = 0x0004,
    }
}

上記を実行して、CTRL+ALT+SHIFT+Aを押すと、hotKey_HotKeyPush()が実行されて、でバグMsgが表示される。

※コンソールアプリで試してみたが、馬動かず、hotKey_HotKeyPushが実行されてくれなかった。
ざっと試しただけなので、うまくやればコンソールでも行けるのかもしれない。

別解(WPFのバックグラウンドアプリでやってみる)

App.xaml.csで、StartupUri="MainWindow.xaml"を削除して、ウインドウのない、裏で動くアプリにして試した。

<Application x:Class="ToumeiGadget.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ToumeiGadget">
    <Application.Resources>
         
    </Application.Resources>
</Application>

アプリ起動時に、ホットキー登録をした。

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Forms;

namespace ToumeiGadget
{
    public partial class App : System.Windows.Application
    {
        HotKey hotKey;

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            hotKey = new HotKey(MOD_KEY.ALT | MOD_KEY.CONTROL | MOD_KEY.SHIFT, Keys.A);
            hotKey.HotKeyPush += new EventHandler(hotKey_HotKeyPush);
        }

        void hotKey_HotKeyPush(object sender, EventArgs e)
        {
            Debug.WriteLine("ホットキーが押されました。");
        }
    }
}
// HotKeyクラスは、上のウインドウありサンプルと同じもの。

結果、キーを押すと、hotKey_HotKeyPush()が実行されてくれた。
コンソールではだめだったが、WPFのウインドウ無しアプリではうまく動いてそう。

参考

C#|グローバルホットキーを登録する
サンプルコードはほぼこれ。ありがとうございます。

WPFでホットキーの登録

アプリのウィンドウが非アクティブ状態でも任意のキーの入力を感知、WPFでグローバルホットキーの登録

RegisterHotKey

0

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?