Loading [MathJax]/jax/output/HTML-CSS/jax.js

highlight.pack.js

2012年5月4日金曜日

C# WPFでHotKeyと多重起動を禁止して起動済みプロセスに処理を移管するライブラリ

WPFでHotKeyを登録したいという機能要件は結構あるのでしょうか?私は、Hotkeyでアプリケーションにサクッとアクセスできることに結構魅力を感じる人です。 また、Evernoteなどのように既に起動しているアプリケーションインスタンスがあったらそちらに処理を移管したい、といったこともあるかと思います。 個人的にはこの2つの機能を実際にアプリケーションに実装しようとすると色々と面倒というイメージがありました。

例えばHotKeyの登録なんかだとにNine Workdsさんのブログに「WPFでHotKeyを設定する」というそのまんまのエントリがあったりするわけですが、 .NETなのにHotKeyの登録解除を忘れずに行わなければならないとかそういう制約があって扱いづらいなぁと思っていたわけですね。.NETなら自動的に処理してくれよという話です。

というわけでこの二大機能をWPF上で楽に実装するためにクラスライブラリを作ってみました。 サンプルプロジェクトとセットにしたファイルを上げておくので使いたい人は自由に使ってあげて下さい。

StoneDot.BasicLibrary.zip

と、ここで終わってしまってもいいのですがそれだとあんまりなので簡単に使い方を説明しておきたいと思います。

と言ってもやるべきことはそう多くありません。

このぐらいの事をすればOKです。では順番に説明を。

クラスライブラリへの参照の追加

ココらへんはライブラリを使いまくっている人には当たり前なところではありますがこのブログでは対応する記事がないので一応説明を・・・
大丈夫な方は一気に次の項目まで読み飛ばしちゃって下さい。

.NETではライブラリを使用する際にはライブラリへの参照を追加しなければなりません。たとえ同じソリューションにライブラリがあった場合にもこの作業は必須です。 ここではその方法の一例を紹介したいと思います。

ソリューション エクスプローラーでライブラリを使いたいプロジェクトの参照設定を右クリックすると以下の様なコンテキストメニューが出てくるので、「参照の追加」をクリックしてください。

参照の追加ダイアログが表示されます。ここでプロジェクトのタブを開くと同じソリューションの中にあるプロジェクトを参照できるのでStoneDot.BasicLibraryを選択した状態でOKをクリックすれば参照が追加されます。
正しく参照が追加されていれば以下のようになるはずです。
詳細を知りたい方はMSDNを参照して下さい。

AssemblyInfo.csへGUIDのアセンブリ情報の追加

私もこのライブラリを作っている最中に気づいたのですが、どうやらWPFはデフォルトでアセンブリ属性にGUIDを埋め込まないようです。 というわけで、私が作ったライブラリを使うには手作業でGUIDをアセンブリに埋め込んであげる必要があります。

まずはGUIDを生成しましょう。上部メニューの「ツール」から「GUID の作成」を開きましょう。

すると、適当に生成されたGUIDが下の画像のように表示されます。 ここで5番目のGUIDの形式を選択し、コピーしちゃいましょう。
そして、ソリューション エクスプローラーのPropertiesのなかにあるAssemblyInfo.csを開いて以下の図のように行を追加します。 まずは先程コピーしたGUIDを貼り付けてその先頭に「assembly:」と追加するのが楽なのでお勧めです。 GUIDが2E2CF84F-6D22-4570-93DA-0B4E64BB7B32でしたら、追加する行は、
[assembly: Guid("2E2CF84F-6D22-4570-93DA-0B4E64BB7B32")]
といった感じです。

ソースコードをゴリゴリと書く

ここまで来れば下準備は完了です。あとはソースコードをゴリゴリと書いていくだけなので簡単ですね。 今回は付属しておいたサンプルの解説をしておきます。

まずはApp.xamlを若干編集しなくては行けません。というのも、そのまんまですとアプリケーション起動時に勝手にWindowを表示しちゃうので、 別のプロセスに処理を移管する際にちらっとウィンドウが表示されることになってしまうからです。 そのためにはApp.xamlに記載されているStartupUriを消去して以下のような感じにしなければいけません。

<Application x:Class="SampleApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
view raw App.xaml hosted with ❤ by GitHub
これが終わったらApp.xaml.csを編集していけばOKです。サンプルではこんな感じになっています。
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using StoneDot.BasicLibrary;
namespace SampleApplication
{
/// <summary>
/// App.xaml の相互作用ロジック
/// </summary>
public partial class App : Application
{
private MainWindow _window;
public HotKeyUtilities _hotKey { get; private set; }
/// <summary>
/// Override a OnStartup method to exit instance without window.
/// </summary>
/// <param name="e"></param>
protected override void OnStartup(StartupEventArgs e)
{
if (ApplicationUtilities.IsFirstInstance())
{
// Start to process command from other application instance.
ApplicationUtilities.ProcessCommand += new EventHandler<ProcessEventArgs>(ProcessCommand);
// Open a window.
_window = new MainWindow();
_window.Show();
// Register a hotkey with a system.
_hotKey = new HotKeyUtilities(_window);
var ret = _hotKey.RegisterHotKey(ModifierKeys.Windows, Key.Q, ActivateWindowSecurely);
if (!ret) MessageBox.Show("Fail to register a hotkey.");
}
else
{
// Send a command to a first application instance.
ApplicationUtilities.DelegeteProcessingCommandToFirstInstance("activate");
// Shutdown this instance of application.
Shutdown();
}
}
/// <summary>
/// The ProcessCommand method is called when a command is delegated.
/// </summary>
private void ProcessCommand(object sender, ProcessEventArgs e)
{
switch (e.Command)
{
case "activate":
if (_window != null) ActivateWindowSecurely();
break;
}
}
/// <summary>
/// Activate window via dispatcher,
/// </summary>
private void ActivateWindowSecurely()
{
Dispatcher.BeginInvoke(new Action(() => _window.Activate()), null);
}
}
}
view raw App.xaml.cs hosted with ❤ by GitHub
こんな感じでアプリケーションコードはかなりすっきりとした形で書けます。

やっていることとしてはデフォルトのOnStartupをオーバーライドして独自の処理を行うようにしています。 まずは、起動しているアプリケーションが一番最初に起動したものなのかをチェックして処理を分けています。 一番最初のインスタンスだったら送られてくるコマンドを処理するためのイベントハンドラを登録して、 Windowを開き、その後HotKeyの登録を行なっています。 一番最初に起動したわけではないようだったらコマンドを一番最初に起動したアプリケーションに送りつけて自分自身は終了します。
今回のアプリケーションでは、Acitivateというコマンドが他のアプリケーションインスタンスから送られてきたらウィンドウをアクティベートしています。 よって既にアプリケーションを起動している状態でアプリケーションを起動しようとすると、既に起動していたアプリケーションが前に出てくることになるわけです。
HotKeyが押された場合も同様にウィンドウをアクティベートする処理を行なっているので、Windowsキー+Qキーでウィンドウが前に出てきます。

こんな感じで便利に使えるかと思いますので、機会があれば使ってもらえればと思います。
バグ等があったら報告してもらえれば出来る範囲で対応できればと思います。

最後にこのライブラリを書くにあたって参考にしたページを列挙します。 これらのページの内容はライブラリを書くにあたって非常に重宝させて頂きました。ありがとうございました。

0 件のコメント:

コメントを投稿