多重起動を防ぐためにプロセス間通信を実装する
ここまでで、プロトコルアクティベーションで起動されたときに指定のURLを表示できるようになりましたが、依然としてアクティベーションされるたびに新しいプロセスが立ち上がってしまいます。
多重起動に対処するために、プロセス間通信の仕組みを導入します。プロセス間通信のサーバーとクライアントを同じWPFアプリが兼ねるようにして、クライアント側からURLを投げ、サーバー側ではそれを受け取って表示するようにします。この仕組みができたら、2番目に起動されたプロセスはクライアントとなってサーバーに処理を渡し、自分は終了してしまうことで、多重起動を防げます。
プロセス間通信の仕組みは、名前付きパイプを使ったWCF(Windows Communication Foundation)にしました。コードの全体は「IpcService.cs」ファイルを見てください。その概要は次のようになっています。
// System.ServiceModel アセンブリへの参照追加が必要 // サービスの定義 [ServiceContract] public interface INavigationService { [OperationContract] void Navigate(string url); } // サーバー側 public class IpcServer : INavigationService { // サービスの実装(WCFを通じてクライアント側から呼び出される) public void Navigate(string url) { // ……省略…… // URL の Web ページを表示 if (!string.IsNullOrWhiteSpace(url)) mainWindow.Navigate(url); } // サーバーを起動するメソッド public static void StartService() { // ……省略…… } } // クライアント側 public class IpcClient { // サーバーにリクエストを投げるメソッド(成功したらtrueを返す) public static bool RequestNavigation(string url) { // ……省略…… // サービスを呼び出し navigationService.Navigate(url); return true; // ……省略…… } }
アプリが起動されたら、まずIpcClient(クライアント側)のRequestNavigationメソッドを呼び出してみます。呼び出しが成功したら(=サーバーと交信できたら)、すでに他のプロセスが動いていたということですから、自分は終了します。呼び出しに失敗したときは、まだ他のプロセスが動いていないということですから、IpcServer(サーバー側)のStartServiceメソッドを呼び出して自分がサーバーになります。
多重起動を防止する
上記のプロセス間通信の仕組みを使って、多重起動を防止します。起動時にサーバーにリクエストを投げてみて、成功したら自分は終了する、失敗したときは自分がサーバーになるというコードをMain関数に組み込みます(次のコード、Appクラス内)。
[STAThread] public static void Main(string[] args) { // プロトコルアクティベーションで起動されたときの、表示すべきURL string protocolActivationUrl = GetProtocolActivationUrl(); // サーバーにリクエストを投げてみる if (IpcClient.RequestNavigation(protocolActivationUrl)) { // サーバーとの通信に成功した == 多重起動である // 多重起動の場合は、アプリを終了する return; } App app = new App(); app.Startup += OnStartup; app.InitializeComponent(); app.Run(); return; // 以下はローカル関数のみ string GetProtocolActivationUrl() { // ……省略…… } void OnStartup(object sender, StartupEventArgs e) { // プロセス間通信のサーバーを起動する IpcServer.StartService(); // MainWindowを表示する MainWindow mainWindow = new MainWindow(); mainWindow.Show(); if (protocolActivationUrl != null) // プロトコルアクティベーションのときは、そのURLを表示する mainWindow.Navigate(protocolActivationUrl); } }
これで、アプリが実行中に「タイムライン」からプロトコルアクティベーションを受けても、実行中のウインドウにそのWebページが表示されるようになります。
なお、上のコードでは、IpcClient.RequestNavigationメソッド呼び出しとIpcServer.StartServiceメソッド呼び出しの間に別のサーバーが起動してしまうという、本当に運の悪いことが起きる可能性がゼロではありません。その場合は複数のプロセスが起動してしまうことになりますが、今回は気にしないことにしました。そこも気になる方は、MutexなりSemaphoreなりを使ったプロセス間の排他処理を加えてください(ロックの取得に失敗したら、サーバーが動いているはずなので、リクエストを投げて自身を終了させる)。
まとめ
Widnows 10 1803の新機能「タイムライン」をWPFアプリで利用するために、デスクトップブリッジを使いました。インストール時に独自の処理を必要としないアプリなら、ものすごく簡単に「UWPアプリ化」できることがお分かりいただけたかと思います。なお、「タイムライン」に登録する方法やその表示をカスタマイズする方法については前回で詳しく解説しましたので、そちらをご覧ください。
UWPアプリに興味が湧いてきた人へ
Windowsデスクトップ用アプリの開発はWindows Formsなら経験があるけど、ちょっとUWPアプリ開発も気になってきたという方へ。ぴったりの電子書籍を書きました!
『UWP アプリ開発 101 第2版: Windows Forms 開発者のための C# / XAML による UWP アプリ開発入門』 (BluewaterSoft 2017/7/1)
・Kindle版とUWPアプリ版があります。
・どちらも無料で最初の方を読めるので、とりあえず試し読みをしてみてください。
また、Microsoft公式のチュートリアルやたくさんのサンプルコードなどもあります。UWPアプリの開発環境を整えてUWPアプリのプロジェクトを作れるようになったら、これらのドキュメントやサンプルも参考にしてみてください。