スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

WinFormsでホットキーを使う

アプリケーションが非アクティブのときにもショートカットキーを捕捉して実行したくなったケースがあったので。

ググってたら、SetWindowsHookEx(グローバルフック)を使い方法があるらしい。いいラッパーがあったんだけど、キーが1文字ずつしか取得できない(例えばCtrl+Cを押すと、Ctrlが押されたイベント→Cが押されたイベントと別々に出る)ので、押されたキーをキャッシュする等の実装を行わなきゃいけなくてめんどい。さらにグローバルフックの挙動がキーボードで押されたキー1つ1つに反応して効率が悪い。さらに挙動がキーロガーっぽくてあんまり正攻法じゃないなという感じがしたので、調べてたらホットキーというもっと直球なやり方があった(なんでこれが最初に出てこなかった)

SetWindowsHookExでやる場合はこちらを参考に
http://tomoemon.hateblo.jp/entry/20060706/p1
http://hongliang.seesaa.net/article/7539988.html

上のリンク下のSetWindowsHookExのラッパーをパクって参考に、ホットキーの場合のラッパーを書いてみた。ここらへんを参考に実装:
http://anis774.net/codevault/hotkey.html
http://inner2.hatenablog.com/entry/2013/10/27/215707


///
/// ホットキーが入力されたときに実行されるメソッドを表すイベントハンドラ
///

public delegate void HotKeyEventHandler(object sender, HotKeyEventArgs e);

///
/// HotKeyPushイベントのデータを提供するクラス
///

public class HotKeyEventArgs : KeyEventArgs
{
///
/// ホットキーの名前
///

public string HotKeyName { get; private set; }
///
/// ホットキーのID
///

public int HotKeyId { get; private set; }

public HotKeyEventArgs(HotKeyItem hotKeyItem)
: base(hotKeyItem.keydata)
{
this.HotKeyName = hotKeyItem.name;
this.HotKeyId = hotKeyItem.id;
}
}

///
/// 設定でホットキーの設定を保存するためのクラス
///

[DataContract]
public class HotKeyItem
{
///
/// ホットキーの名前
///

[DataMember]
public string name { get; set; }
///
/// ホットキーのID
///

[IgnoreDataMember]
public int id { get; set; }
///
/// キー(KeyEventArgs.KeyDataの値)
///

[DataMember]
public Keys keydata { get; set; }
///
/// ホットキーが有効かどうか
///

[DataMember]
public bool enable { get; set; }

}

///
/// ホットキーの操作を補足し、任意のメソッドを挿入する
///

public class HotKey : UserControl
{
const int MOD_ALT = 0x0001;
const int MOD_CONTROL = 0x0002;
const int MOD_SHIFT = 0x0004;
const int WM_HOTKEY = 0x0312;

[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);


//イベントリストのキー
public static readonly object EventHotKeyPressed = new object();
///
/// 補足するイベントリストのコレクション
///

public List HotKeyList { get; private set; }
///
/// ホットキーが操作されたときに発生する
///

public event HotKeyEventHandler HotKeyPressed
{
add { base.Events.AddHandler(EventHotKeyPressed, value);}
remove{base.Events.RemoveHandler(EventHotKeyPressed, value);}
}

protected virtual void OnHotKeyPressed(HotKeyEventArgs e)
{
HotKeyEventHandler handler = base.Events[EventHotKeyPressed] as HotKeyEventHandler;
if (handler != null)
handler(this, e);
}

///
/// 新しいインスタンスを作成する
///

public HotKey()
{
this.HotKeyList = new List();
}

///
/// ホットキーを追加する
///

/// ホットキーの設定のインスタンス
public void AddHotKey(HotKeyItem hotKeyItem)
{
//修飾キー
int modflag = 0;
Keys modkeys = hotKeyItem.keydata & Keys.Modifiers;
if ((modkeys & Keys.Alt) == Keys.Alt) modflag = modflag | MOD_ALT;
if ((modkeys & Keys.Control) == Keys.Control) modflag = modflag | MOD_CONTROL;
if ((modkeys & Keys.Shift) == Keys.Shift) modflag = modflag | MOD_SHIFT;
//修飾キー以外のKeyCode
Keys keycode = hotKeyItem.keydata & Keys.KeyCode;
//ホットキーの登録
for(int i=HotKeyList.Count; i<=0xbfff; i++)
{
if(RegisterHotKey(this.Handle, i, modflag, keycode) != 0)
{
hotKeyItem.id = i;
break;
}
}
//コレクションに追加
HotKeyList.Add(hotKeyItem);
}

///
/// ホットキーを削除する
///

/// ホットキーの設定のインスタンス
public void RemoveHotKey(HotKeyItem hotKeyItem)
{
UnregisterHotKey(this.Handle, hotKeyItem.id);
//コレクションから削除
HotKeyList.Remove(hotKeyItem);
//設定のIDを0に戻す
hotKeyItem.id = 0;
}

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

if(m.Msg == WM_HOTKEY)
{
//Windowsメッセージで送られてくるhotkeyid
int hotkeyid = (int)m.WParam;
//該当のホットキーを探す
foreach(var h in HotKeyList)
{
if (h.id == hotkeyid) OnHotKeyPressed(new HotKeyEventArgs(h));
}
}
}

///
/// 使用されているアンマネージリソースを解放し、オプションでマネージリソースも解放する。
///

/// マネージリソースも解放する場合はtrue。
protected override void Dispose(bool disposing)
{
if(!disposed)
{
disposed = true;
while (HotKeyList.Count > 0) RemoveHotKey(HotKeyList[0]);
base.Dispose(disposing);
}
}

///
/// ファイナライザ
///

~HotKey()
{
Dispose(false);
}
private bool disposed = false;
}


HotKeyEventArgsとHotKeyItemの別々のクラスがあるのは、ホットキーの設定をJSONに出力して保存したかったから。EventArgsをそのままシリアル化するのもねえ…って感じしたので。終了時に設定したホットキーの設定を必ず削除するようにしたいので、Disposeメソッドを実装。HotKeyのメインのクラスはなんでもいいけどControl.Handleを取得できると楽なので、デザイナーで扱いやすいようにUserControlでやってみた。

これの使い方は簡単で、

private void Form1_Load(object sender, EventArgs e)
{
//ホットキー
hotKey1.AddHotKey(new HotKeyItem()
{
enable = true,
keydata = Keys.Control | Keys.C,
name = "コピー",
});
}
//ホットキーのイベントハンドラ
private void hotKey1_HotKeyPressed(object sender, HotKeyEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("ホットキー");
sb.AppendLine(" ID:" + e.HotKeyId);
sb.AppendLine(" 名前:" + e.HotKeyName);
sb.AppendLine(" KeyData:" + e.KeyData);
sb.AppendLine(" KeyCode:" + e.KeyCode);
sb.AppendLine(" Shift:" + e.Shift);
sb.AppendLine(" Alt:" + e.Alt);
sb.AppendLine(" Ctrl:" + e.Control);
sb.AppendLine("が押されました");

MessageBox.Show(sb.ToString(), "ホットキー");
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
hotKey1.Dispose();
}


例えばCtrl+Cをホットキーに登録。非アクティブの状態でCtrl+Cを入力すると、
20151018_1.png
このようにダイアログが出力される。このときついでにアクティブ状態にしてやるといいかもしれない。ただし、起動中はどのアプリに対しても通常のCtrl+C=コピーが無効化されるので注意。(ホットキーを使えばコピペできないようにするマシンが作れるんじゃ
スポンサーサイト
プロフィール

こしあん

Author:こしあん
(:3[____]
【TwitterID : koshian2】
【ほしい物リスト】http://goo.gl/bDtvG2

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。