なお、言語はC#で、トースト内のインタラクティブ機能については別途。
1. 概要
まず基本情報とサンプル(C++とC#)は以下のとおり。
- Respond to toast activations
- Quickstart: Handling toast activations from Win32 apps in Windows 10
- WindowsNotifications/desktop-toasts
- トーストがタイムアウトなどで消えた後もアクションセンターに残るので、後から見てアクティベートできる。
- トーストを出したアプリが終了した後でも、トーストからアプリを直接起動して処理を続けることができる。
コード的には、実はトーストの出し方は基本的にWindows 8と変わりません。これらの機能はトーストの設定によってではなく、アプリでCOMのINotificationActivationCallbackインターフェイスを実装したクラスを使って行います。というより、UWP用に用意された機能を(ラップした関数は提供されないので)剥き出しで使っちゃいなよという感じ。
INotificationActivationCallbackのメンバーはActivateメソッドだけで、トーストがアクティベートされるとこのメソッドが実行されます。これを利用するレベルとしては、
- 下準備として、INotificationActivationCallbackを実装したCOMクラスのCLSIDをアプリのショートカットに含める。
- そのクラスの型をCOMに登録すると、トーストがアクションセンターに残るようになる。
- そのクラスのCLSIDとアプリの実行ファイルのパスをレジストリに登録してCOMサーバーで起動できるようにすると、トーストからアプリを起動できるようになる。
なお、元々のToastNotificationクラスのイベントも発生するので、両方をうまく組み合わせる必要があります。もしくはActivatedの場合だけ捉えるのであれば、INotificationActivationCallbackのActivateは常に実行されるので、こちらだけ処理する手もあります。
2. コード
先に、INotificationActivationCallbackを実装したCOMクラスのCLSIDについては、ショートカットのプロパティにSystem.AppUserModel.ToastActivatorCLSIDが追加されています(Windows 10 SDKのpropkey.hにある)。読み書きは型がGuidになる以外はAppUserModelIDの場合と同様にできるので省略。
具体的なCOMクラスとしては以下のようなものです(INotificationActivationCallbackの宣言などは省略)。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
[Guid("f5b13fa4-8472-4f82-8a47-515b879006ba"), ComVisible(true), ClassInterface(ClassInterfaceType.None)] | |
[ComSourceInterfaces(typeof(INotificationActivationCallback))] | |
public class NotificationActivator : NotificationActivatorBase | |
{ } | |
[Guid("1EC6017A-77C7-44DB-AF97-22452FA26652"), ComVisible(true), ClassInterface(ClassInterfaceType.None)] | |
[ComSourceInterfaces(typeof(INotificationActivationCallback))] | |
public class NotificationActivatorBase : INotificationActivationCallback | |
{ | |
public void Activate(string appUserModelId, string invokedArgs, NOTIFICATION_USER_INPUT_DATA[] data, uint count) | |
{ | |
_action?.Invoke(invokedArgs, data?.Take((int)count).ToDictionary(x => x.Key, x => x.Value)); | |
} | |
private static int? _cookie; | |
private static Action<string, Dictionary<string, string>> _action; | |
public static void RegisterComType(Type activatorType, Action<string, Dictionary<string, string>> action) | |
{ | |
_cookie = new RegistrationServices().RegisterTypeForComClients( | |
activatorType, | |
RegistrationClassContext.LocalServer, | |
RegistrationConnectionType.MultipleUse); | |
_action = action; | |
} | |
public static void UnregisterComType() | |
{ | |
if (!_cookie.HasValue) | |
return; | |
new RegistrationServices().UnregisterTypeForComClients(_cookie.Value); | |
_cookie = null; | |
_action = null; | |
} | |
} |
アプリの起動時にNotificationActivatorの型を引数としてRegisterComTypeメソッドを実行し、COMに登録します。終了時にはUnregisterComTypeメソッドで登録を解除します。トーストがアクティベートされるとActivateメソッドが実行され、RegisterComTypeの引数で与えられていたActionが実行されます。Activateの引数のinvokedArgsとdataはインタラクティブなトーストから返ってくる情報ですが、必要がなければ無視していいです。
次に、アプリをCOMサーバーで起動できるようにレジストリに登録するヘルパークラス。これは普通にレジストリを読み書きするだけです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Microsoft.Win32; | |
public class NotificationHelper | |
{ | |
public static void RegisterComServer(Type activatorType, string executablePath, string arguments = null) | |
{ | |
var combinedPath = $@"""{executablePath}"" {arguments}"; | |
var keyName = $@"SOFTWARE\Classes\CLSID\{{{activatorType.GUID}}}\LocalServer32"; | |
using (var key = Registry.CurrentUser.OpenSubKey(keyName)) | |
{ | |
if (string.Equals(key?.GetValue(null) as string, combinedPath, StringComparison.OrdinalIgnoreCase)) | |
return; | |
} | |
using (var key = Registry.CurrentUser.CreateSubKey(keyName)) | |
{ | |
key.SetValue(null, combinedPath); | |
} | |
} | |
public static void UnregisterComServer(Type activatorType) | |
{ | |
var keyName = $@"SOFTWARE\Classes\CLSID\{{{activatorType.GUID}}}"; | |
using (var key = Registry.CurrentUser.OpenSubKey(keyName)) | |
{ | |
if (key == null) | |
return; | |
} | |
Registry.CurrentUser.DeleteSubKeyTree(keyName); | |
} | |
} |
INotificationActivationCallbackの動作は、アプリが実行中に(アクションセンター内に限らず)トーストがアクティベートされると、そのままActivateが実行されます。アプリが終了後にアクティベートされると、レジストリの情報に従ってアプリが起動された後にActivateが実行されます。その際、アプリのコマンドライン引数に自動的に"-Embedding"が追加されるので、その有無でトーストから起動されたか否か判別できます。
細かい実装とサンプル(WPF)はレポジトリを見てください。ToastNotificationのイベントとINotificationActivationCallbackのActivateメソッドのタイミングが分かるようになっています。
3. まとめ
以上のように、単に通知を出すのとは違って、アクションセンターへの対応はアプリのライフサイクルに関わってくるので少し大事になります。またアクティベート時の状態をどうするかによって動作の練り直しが必要になるかもしれません。
ちなみに、Windows 10で実行する場合は、WinRTのアセンブリはWindows.winmdではなくWindows.Foundation.UniversalApiContract.winmdの方が適当かなと思って参照しようとしたら、このパスが以前と変わってました。この、どのアセンブリを使えばいいのか分からん問題が、個人的にWinRTの大きな難点なんですけどね。
0 コメント :
コメントを投稿