もくじ
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