LoginSignup
5
7

More than 5 years have passed since last update.

PowerShellとC#でグローバルホットキーを登録する

Last updated at Posted at 2016-09-19

C#|グローバルホットキーを登録するに感銘を受けたので、一部アレンジして、PowerShellから使用できるようにしました。

概要

ホットキーを登録し、登録したキーを押下するとPowerShellコマンドが実行できるようにします。

実装

クラス定義(C#)

hotkeyClass.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

//クラス(ホットキー登録・解除用フォーム)
public class HotKeyForm : Form
{
    [DllImport("user32.dll")]
    extern static int RegisterHotKey(IntPtr HWnd, int ID, int MOD_KEY, Keys KEY);

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

    const int WM_HOTKEY = 0x0312;
    public readonly int modKey;
    public readonly Keys key;
    public readonly int id;
    public readonly ThreadStart proc;

    //ホットキー登録
    public HotKeyForm(int modKey, Keys key, ThreadStart proc)
    {
        for (int i = 0x0000; i <= 0xbfff; i++)
        {
            if (RegisterHotKey(this.Handle, i, modKey, key) != 0)
            {
                this.modKey = modKey;
                this.key = key;
                this.id = i;
                this.proc = proc;
                break;
            }
        }
    }

    //ウィンドウメッセージ監視
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        //ホットキーが押された時の処理
        if (m.Msg == WM_HOTKEY)
        {
            if ((int)m.WParam == this.id)
            {
                this.proc();
            }
        }
    }

    //ホットキー解除
    protected override void Dispose(bool disposing)
    {
        UnregisterHotKey(this.Handle, this.id);
        base.Dispose(disposing);
    }
}

//クラス(ホットキー制御用フォーム)
public class HotKeyController : Form
{
    public List<HotKeyForm> HotKeyFormList;

    //ホットキー登録・解除用フォームを表示すると、ウィンドウメッセージの監視が開始される
    private void HotKeyController_Load(object sender,EventArgs e)
    {
        foreach (HotKeyForm hotKeyForm in this.HotKeyFormList)
        {
            hotKeyForm.WindowState = System.Windows.Forms.FormWindowState.Minimized;
            hotKeyForm.Show();
            hotKeyForm.Hide();
        }
    }

    private void HotKeyController_FormClosing(object sender,EventArgs e)
    {
        foreach (HotKeyForm hotKeyForm in this.HotKeyFormList)
        {
            if (!hotKeyForm.IsDisposed)
            {
                hotKeyForm.Close();
            }
        }
    }

    public HotKeyController()
    {
        this.HotKeyFormList = new List<HotKeyForm>();

        this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
        this.ShowInTaskbar = false;

        this.Load += HotKeyController_Load;
        this.FormClosing += HotKeyController_FormClosing;
    }
}

モジュール定義(PowerShell)

hotkeyModule.ps1
$here = Split-Path $MyInvocation.MyCommand.Path -Parent

New-Module -ArgumentList $here {
  param($here)

  # --------------------
  # 非公開定義
  # --------------------
  # ホットキー登録・監視用クラス(C#)
  Add-Type `
    -ReferencedAssemblies 'System.Windows.Forms' `
    -Path (Join-Path $here 'hotkeyClass.cs')

  # 修飾キー
  $MODKEY_LIST = @{
    ALT   = 0x0001;
    CTRL  = 0x0002;
    SHIFT = 0x0004;
    _     = 0x0000;
  }

  # ハッシュテーブルを検索
  filter Select-HashTable{
    param(
      [Alias('with')][hashtable]$HashTable,
      $DefaultKey = '_'
    )

    if($HashTable.Contains($_)){
      return $HashTable[$_]
    } else {
      return $HashTable[$DefaultKey]
    }
  }
  Set-Alias match Select-HashTable

  # キー文字列を修飾キーとキーコードへ変換
  function ConvertTo-HotKeyArguments($KeyString){
    # キー文字列をリスト化
    # 例)Ctrl + A -> CTRL,A
    $KeyList = $KeyString.ToUpper().Split('+') | foreach{$_.Trim()}

    # 修飾キー(全て取得)
    $modkey = ($KeyList | match -with $MODKEY_LIST) -join ' -bor ' | iex

    # キーコード(初めの1つだけ取得)
    $key = ($KeyList | where{-not ($MODKEY_LIST.Contains($_))} | foreach{[Windows.Forms.Keys]::"$_"})[0]

    return $modkey,$key
  }
  Set-Alias hkargs ConvertTo-HotKeyArguments

  # --------------------
  # 公開定義
  # --------------------
  # ホットキーを登録し、ホットキーが押下された時に指定のコマンドを実行
  function Watch-HotKey($HotKeyList,$CloseKey){
    $ctl = New-Object HotKeyController

    # 各種ホットキーの登録
    $HotKeyList | foreach{
      $modKey,$key = hkargs $_.KeyString
      $proc        = $_.Procedure
      $ctl.HotKeyFormList.Add((New-Object HotKeyForm($modKey,$key,$proc)))
    }

    # 終了用ホットキーの登録
    $modKey,$key = hkargs $CloseKey
    $proc        = {$ctl.Close()}
    $ctl.HotKeyFormList.Add((New-Object HotKeyForm($modKey,$key,$proc)))    

    # ホットキー監視(終了用ホットキーが押下されるまで持続)
    [void]$ctl.ShowDialog()
  }

  Export-ModuleMember -Function Watch-HotKey
}

使用方法

上記のhotkeyModule.ps1を実行しモジュールを登録した後、Watch-Hotkey関数を呼び出してホットキーの登録及び監視を行います。

hotkeyModuleTest.ps1
# モジュール登録
$here = Split-Path $MyInvocation.MyCommand.Path -Parent
& (Join-Path $here 'hotkeyModule.ps1') | Out-Null

# ホットキーの登録及び監視
Watch-HotKey @(
  @{KeyString='Ctrl+Shift+L';Procedure={Write-Host '処理1'}},
  @{KeyString='Ctrl+Shift+M';Procedure={Write-Host '処理2'}}
) -CloseKey 'Ctrl+W'

まとめ

少しコマンドを実行する程度なら、AutoHotkeyなどのフリーソフトを使うより、PowerShellで組んでしまった方が、(Windows7以降だと)別途インストールが不要な分手軽で良いのかもしれません。

活用例としては、各種アプリケーションの起動、アクティブなプロセスに応じたコマンド実行(Excelマクロ実行など)、マウスカーソルがある座標の色情報取得・・・といった辺りが思いつきます。工夫次第では他にも色々使えそうです。
類似としては、WndProcを使うという点ではクリップボード監視ツールも同じような方法で実装できそうです。

余裕があればチャレンジしたいと思います。

5
7
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
5
7