Windows, F#でグローバルホットキーを設定する

commits4 min read読了の目安(約4100字

概要

起動している間、グローバルホットキーを有効にするタスクトレイ常駐型のアプリを F# で作る。

実装例

Windows + Alt + C」を入力すると空のメッセージボックスが表示されるサンプル。

open System.Windows.Forms

let createIcon () =
    let toolStripMenuItem = new ToolStripMenuItem(Text = "&終了")
    toolStripMenuItem.Click.Add(fun e -> Application.Exit())

    let contextMenuStrip = new ContextMenuStrip()
    contextMenuStrip.Items.Add toolStripMenuItem |> ignore

    new NotifyIcon(
        Icon = new System.Drawing.Icon @"icon.ico",
        Visible = true,
        Text = "TestApp",
        ContextMenuStrip = contextMenuStrip
    )


open System
open System.Runtime.InteropServices

[<DllImport("user32.dll")>]
extern int RegisterHotKey(IntPtr HWnd, int ID, int MOD_KEY, int KEY)

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


type HotkeyForm () as this =
    inherit Form()

    let icon = createIcon()

    do
        RegisterHotKey(this.Handle, 0x0000, 0x0001 ||| 0x0008, int Keys.C) |> ignore

        Application.ApplicationExit.Add(fun e ->
            UnregisterHotKey(this.Handle, 0x0000) |> ignore
        )

    override this.WndProc m =
        base.WndProc(&m)

        if m.Msg = 0x0312 && int m.WParam = 0x0000 then
            MessageBox.Show("") |> ignore

    interface IDisposable with
        member this.Dispose() =
            icon.Dispose()


do
    use form = new HotkeyForm()
    Application.Run()

解説

System.Windows.Forms

.NET 5 で参照できない場合は下記の記事にて設定方法を紹介しています。

https://zenn.dev/shikatan/articles/8f3d768266935c

タスクトレイにアイコンを表示する

open System.Windows.Forms

let createIcon () =

    // アイコンをホバーした時に表示されるメニューの定義

    // - クリックしたらアプリを終了する「終了」ボタンの作成
    let toolStripMenuItem = new ToolStripMenuItem(Text = "&終了")
    toolStripMenuItem.Click.Add(fun e -> Application.Exit())

    // - メニューに「終了」ボタンを追加
    let contextMenuStrip = new ContextMenuStrip()
    contextMenuStrip.Items.Add toolStripMenuItem |> ignore

    // アイコンの生成
    new NotifyIcon(

        // 表示する画像 (.ico)
        Icon = new System.Drawing.Icon @"icon.ico",

        // 表示/非表示
        Visible = true,

        // ホバー時に表示するテキスト (アプリ名など)
        Text = "TestApp",

        // 上で作成したメニューを追加
        ContextMenuStrip = contextMenuStrip
    )

グローバルホットキーを設定する

// System.Windows.Forms は前の部分で読み込み済み

open System
open System.Runtime.InteropServices


// Windows API

// - ホットキーの登録用
[<DllImport("user32.dll")>]
extern int RegisterHotKey(IntPtr HWnd, int ID, int MOD_KEY, int KEY)

// - ホットキーの解除用
[<DllImport("user32.dll")>]
extern int UnregisterHotKey(IntPtr HWnd, int ID)


type HotkeyForm () as this =
    inherit Form()

    let icon = createIcon()

    do
        // ホットキーを登録する
        RegisterHotKey(this.Handle, 0x0000, 0x0001 ||| 0x0008, int Keys.C) |> ignore

        // アプリ終了時にホットキーを解除する
        Application.ApplicationExit.Add(fun e ->
            UnregisterHotKey(this.Handle, 0x0000) |> ignore
        )

    override this.WndProc m =
        base.WndProc(&m)

	// グローバルホットキーが入力された時の処理

        // && の左辺は固定
        if m.Msg = 0x0312 && int m.WParam = 0x0000 then
            MessageBox.Show("") |> ignore

    interface IDisposable with
        member this.Dispose() =
            icon.Dispose()

ホットキーの登録

RegisterHotKey

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey
第2引数

0x00000xbfff 内の適当な値を渡す。また、以下の部分で同じ値を使う。

  • 解除時 UnregisterHotKey の第2引数
  • WndProc 内での int m.WParam との等価判定
第3引数

修飾キー (Alt, Ctrl, Shift, Windows) の指定。
複数の場合は ||| で区切る。

修飾キー
Alt 0x0001
Ctrl 0x0002
Shift 0x0004
Windows 0x0008
第4引数

キーコードの指定。
int Keys.キー で取得できる。

https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.keys?view=net-5.0

実行する

do
    use form = new HotkeyForm()

    // フォームを表示しない場合、引数なしで実行
    Application.Run()

更新履歴

日付 内容
2021/03/25 Dispose を呼び出す処理を追加。

end