パッケージ化されたデスクトップ アプリとパッケージ化されていないデスクトップ アプリは、ユニバーサル Windows プラットフォーム (UWP) アプリと同様に対話型トースト通知を送信できます。 これには、パッケージ化されたアプリが含まれます (パッケージ化された WinUI 3 デスクトップ アプリの新しいプロジェクトの作成
ただし、パッケージ化されていないデスクトップ アプリには、いくつかの特別な手順があります。 これは、さまざまなアクティブ化スキームと、実行時にパッケージ ID が不足しているためです。
Von Bedeutung
UWP アプリを作成する場合は、UWP のドキュメントを参照してください。 その他のデスクトップ言語については、デスクトップ C#を参照してください。
手順 1: Windows SDK を有効にする
アプリに対して Windows SDK を有効にしていない場合は、最初に有効にする必要があります。 いくつかの重要な手順があります。
-
runtimeobject.lib
に、 を追加します。 - Windows SDK をターゲットにします。
プロジェクトを右クリックし、[プロパティ]
上部の
リンカー -> 入力で、runtimeobject.lib
に を追加します。
次に、全般で、Windows SDK バージョン がバージョン 10.0 以降に設定されていることを確認します。
手順 2: 互換性ライブラリ コードをコピーする
DesktopNotificationManagerCompat.h ファイルと DesktopNotificationManagerCompat.cpp ファイルを GitHub からプロジェクトにコピーします。 互換性ライブラリは、デスクトップ通知の複雑さの多くを抽象化します。 次の手順では、互換性ライブラリが必要です。
プリコンパイル済みヘッダーを使用している場合は、必ずDesktopNotificationManagerCompat.cpp ファイルの最初の行として #include "stdafx.h"
してください。
手順 3: ヘッダー ファイルと名前空間を含める
互換性ライブラリのヘッダー ファイルと、Windows トースト API の使用に関連するヘッダー ファイルと名前空間を含めます。
#include "DesktopNotificationManagerCompat.h"
#include <NotificationActivationCallback.h>
#include <windows.ui.notifications.h>
using namespace ABI::Windows::Data::Xml::Dom;
using namespace ABI::Windows::UI::Notifications;
using namespace Microsoft::WRL;
手順 4: アクティベーターを実装する
ユーザーがトーストをクリックしたときにアプリが何かを実行できるように、トーストのアクティブ化ハンドラーを実装する必要があります。 これは、トーストをアクション センターで保持するために必要です (トーストは、アプリが閉じられた数日後にクリックされる可能性があるため)。 このクラスは、プロジェクト内の任意の場所に配置できます。
次に示すように、UUID を含む
// The UUID CLSID must be unique to your app. Create a new GUID if copying this code.
class DECLSPEC_UUID("replaced-with-your-guid-C173E6ADF0C3") NotificationActivator WrlSealed WrlFinal
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, INotificationActivationCallback>
{
public:
virtual HRESULT STDMETHODCALLTYPE Activate(
_In_ LPCWSTR appUserModelId,
_In_ LPCWSTR invokedArgs,
_In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data,
ULONG dataCount) override
{
// TODO: Handle activation
}
};
// Flag class as COM creatable
CoCreatableClass(NotificationActivator);
手順 5: 通知プラットフォームに登録する
その後、通知プラットフォームに登録する必要があります。 アプリがパッケージ化されているかパッケージ化されていないかに応じて、さまざまな手順があります。 両方をサポートしている場合は、両方の手順セットを実行する必要があります (ただし、ライブラリがコードを処理するため、コードをフォークする必要はありません)。
梱包済み
アプリがパッケージ化されている場合 (パッケージ化された WinUI 3 デスクトップ アプリの新しいプロジェクトを作成する
- xmlns:com の宣言
- xmlns:desktop の宣言
- IgnorableNamespaces 属性で、com と デスクトップ が無視されます。
- 手順 4 の GUID を使用して、COM アクティベーターの の com:Extension を行います。 必ず、起動がトーストから行われたことを確認するために
Arguments="-ToastActivated"
を含めます - desktop:Extension を用いて、windows.toastNotificationActivation におけるトースト アクティベーターの CLSID(手順 4 の GUID)を宣言。
Package.appxmanifest
<Package
...
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
IgnorableNamespaces="... com desktop">
...
<Applications>
<Application>
...
<Extensions>
<!--Register COM CLSID LocalServer32 registry key-->
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="YourProject\YourProject.exe" Arguments="-ToastActivated" DisplayName="Toast activator">
<com:Class Id="replaced-with-your-guid-C173E6ADF0C3" DisplayName="Toast activator"/>
</com:ExeServer>
</com:ComServer>
</com:Extension>
<!--Specify which CLSID to activate when toast clicked-->
<desktop:Extension Category="windows.toastNotificationActivation">
<desktop:ToastNotificationActivation ToastActivatorCLSID="replaced-with-your-guid-C173E6ADF0C3" />
</desktop:Extension>
</Extensions>
</Application>
</Applications>
</Package>
未梱包
アプリがパッケージ化されていない場合 (「パッケージ化されていない WinUI 3 デスクトップ アプリ用の新しいプロジェクトを作成する」を参照)、または両方をサポートしている場合は、スタート画面でアプリのショートカットでアプリケーション ユーザー モデル ID (AUMID) とトースト アクティベーター CLSID (手順 4 の GUID) を宣言する必要があります。
アプリを識別する一意の AUMID を選択します。 これは通常、[CompanyName] の形式です。[AppName]。 ただし、すべてのアプリで一意であることを確認する必要があります (末尾に数字を自由に追加してください)。
手順 5.1: WiX インストーラー
インストーラーに WiX を使用している場合は、Product.wxs ファイルを編集して、次に示すように、2 つのショートカット プロパティをスタート メニューのショートカットに追加します。 手順 4 の GUID が、次に示すように {}
で囲まれていることを確認します。
Product.wxs
<Shortcut Id="ApplicationStartMenuShortcut" Name="Wix Sample" Description="Wix Sample" Target="[INSTALLFOLDER]WixSample.exe" WorkingDirectory="INSTALLFOLDER">
<!--AUMID-->
<ShortcutProperty Key="System.AppUserModel.ID" Value="YourCompany.YourApp"/>
<!--COM CLSID-->
<ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{replaced-with-your-guid-C173E6ADF0C3}"/>
</Shortcut>
Von Bedeutung
通知を実際に使用するには、通常どおりにデバッグする前にインストーラーを使用してアプリをインストールする必要があります。これにより、AUMID と CLSID を含むスタート ショートカットが表示されます。 スタート ショートカットが表示されたら、Visual Studio から F5 キーを使用してデバッグできます。
手順 5.2: AUMID と COM サーバーを登録する
その後、インストーラーに関係なく、(通知 API を呼び出す前に) アプリのスタートアップ コードで、RegisterAumidAndComServer メソッドを呼び出し、手順 4 の通知アクティベーター クラスと上記で使用した AUMID を指定します。
// Register AUMID and COM server (for a packaged app, this is a no-operation)
hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(L"YourCompany.YourApp", __uuidof(NotificationActivator));
アプリがパッケージ化されたデプロイとパッケージ化されていないデプロイの両方をサポートしている場合は、関係なくこのメソッドを自由に呼び出すことができます。 パッケージ化 (つまり、実行時にパッケージ ID を使用) を実行している場合、このメソッドはすぐに返されます。 コードをフォークする必要はありません。
このメソッドを使用すると、常に AUMID を提供することなく、compat API を呼び出して通知を送信および管理できます。 また、COM サーバーの LocalServer32 レジストリ キーが挿入されます。
手順 6: COM アクティベーターを登録する
パッケージアプリとパッケージ化されていないアプリの両方で、トーストのアクティブ化を処理できるように、通知アクティベーターの種類を登録する必要があります。
アプリのスタートアップ コードで、次の RegisterActivator メソッドを呼び出します。 トーストのアクティブ化を受け取るには、これを呼び出す必要があります。
// Register activator type
hr = DesktopNotificationManagerCompat::RegisterActivator();
手順 7: 通知を送信する
通知の送信は UWP アプリと同じですが、DesktopNotificationManagerCompat
従来の Windows 8.1 トースト通知テンプレートでは、手順 4 で作成した COM 通知アクティベーターはアクティブ化されないため、次に示すように、ToastGeneric バインドを使用してください。
Von Bedeutung
Http イメージは、マニフェストにインターネット機能があるパッケージ アプリでのみサポートされます。 パッケージ化されていないアプリは http イメージをサポートしていません。イメージをローカル アプリ データにダウンロードし、ローカルで参照する必要があります。
// Construct XML
ComPtr<IXmlDocument> doc;
hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(
L"<toast><visual><binding template='ToastGeneric'><text>Hello world</text></binding></visual></toast>",
&doc);
if (SUCCEEDED(hr))
{
// See full code sample to learn how to inject dynamic text, buttons, and more
// Create the notifier
// Desktop apps must use the compat method to create the notifier.
ComPtr<IToastNotifier> notifier;
hr = DesktopNotificationManagerCompat::CreateToastNotifier(¬ifier);
if (SUCCEEDED(hr))
{
// Create the notification itself (using helper method from compat library)
ComPtr<IToastNotification> toast;
hr = DesktopNotificationManagerCompat::CreateToastNotification(doc.Get(), &toast);
if (SUCCEEDED(hr))
{
// And show it!
hr = notifier->Show(toast.Get());
}
}
}
Von Bedeutung
デスクトップ アプリでは、レガシ トースト テンプレート (ToastText02 など) を使用できません。 COM CLSID が指定されている場合、レガシ テンプレートのアクティブ化は失敗します。 上記のように、Windows ToastGeneric テンプレートを使用する必要があります。
手順 8: アクティブ化の処理
ユーザーがトーストやトースト内のボタンをクリックしたときに、NotificationActivator クラスの Activate メソッドが呼び出されます。
Activate メソッド内では、トーストで指定した引数を解析し、ユーザーが入力または選択したユーザー入力を取得し、それに応じてアプリをアクティブ化できます。
注
Activate メソッドは、メイン スレッドとは別のスレッドで呼び出されます。
// The GUID must be unique to your app. Create a new GUID if copying this code.
class DECLSPEC_UUID("replaced-with-your-guid-C173E6ADF0C3") NotificationActivator WrlSealed WrlFinal
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, INotificationActivationCallback>
{
public:
virtual HRESULT STDMETHODCALLTYPE Activate(
_In_ LPCWSTR appUserModelId,
_In_ LPCWSTR invokedArgs,
_In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data,
ULONG dataCount) override
{
std::wstring arguments(invokedArgs);
HRESULT hr = S_OK;
// Background: Quick reply to the conversation
if (arguments.find(L"action=reply") == 0)
{
// Get the response user typed.
// We know this is first and only user input since our toasts only have one input
LPCWSTR response = data[0].Value;
hr = DesktopToastsApp::SendResponse(response);
}
else
{
// The remaining scenarios are foreground activations,
// so we first make sure we have a window open and in foreground
hr = DesktopToastsApp::GetInstance()->OpenWindowIfNeeded();
if (SUCCEEDED(hr))
{
// Open the image
if (arguments.find(L"action=viewImage") == 0)
{
hr = DesktopToastsApp::GetInstance()->OpenImage();
}
// Open the app itself
// User might have clicked on app title in Action Center which launches with empty args
else
{
// Nothing to do, already launched
}
}
}
if (FAILED(hr))
{
// Log failed HRESULT
}
return S_OK;
}
~NotificationActivator()
{
// If we don't have window open
if (!DesktopToastsApp::GetInstance()->HasWindow())
{
// Exit (this is for background activation scenarios)
exit(0);
}
}
};
// Flag class as COM creatable
CoCreatableClass(NotificationActivator);
アプリが閉じている間に起動を適切にサポートするためには、WinMain 関数でトーストからの起動かどうかを判断する必要があります。 トーストから起動すると、"-ToastActivated" の起動引数が表示されます。 これが表示されたら、通常の起動アクティブ化コードの実行を停止し、必要に応じて、NotificationActivator で起動ウィンドウを処理できるようにする必要があります。
// Main function
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR cmdLineArgs, _In_ int)
{
RoInitializeWrapper winRtInitializer(RO_INIT_MULTITHREADED);
HRESULT hr = winRtInitializer;
if (SUCCEEDED(hr))
{
// Register AUMID and COM server (for a packaged app, this is a no-operation)
hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(L"WindowsNotifications.DesktopToastsCpp", __uuidof(NotificationActivator));
if (SUCCEEDED(hr))
{
// Register activator type
hr = DesktopNotificationManagerCompat::RegisterActivator();
if (SUCCEEDED(hr))
{
DesktopToastsApp app;
app.SetHInstance(hInstance);
std::wstring cmdLineArgsStr(cmdLineArgs);
// If launched from toast
if (cmdLineArgsStr.find(TOAST_ACTIVATED_LAUNCH_ARG) != std::string::npos)
{
// Let our NotificationActivator handle activation
}
else
{
// Otherwise launch like normal
app.Initialize(hInstance);
}
app.RunMessageLoop();
}
}
}
return SUCCEEDED(hr);
}
イベントのアクティブ化シーケンス
アクティブ化シーケンスは次のとおりです。...
アプリが既に実行されている場合:
- を NotificationActivator でアクティブにし、 を呼び出す
アプリが実行されていない場合:
- EXE が起動された状態のアプリでは、コマンドライン引数 "-ToastActivated" が取得されます。
- を NotificationActivator でアクティブにし、 を呼び出す
フォアグラウンドとバックグラウンドのアクティブ化
デスクトップ アプリの場合、フォアグラウンドとバックグラウンドのアクティブ化は同じように処理されます。COM アクティベーターが呼び出されます。 ウィンドウを表示するか、単にいくつかの作業を実行して終了するかを決定するのは、アプリのコードにかかってください。 そのため、トースト コンテンツでバックグラウンド の
手順 9: 通知を削除して管理する
通知の削除と管理は、UWP アプリと同じです。 ただし、デスクトップ アプリに AUMID を提供する必要がないように、compat ライブラリを使用して DesktopNotificationHistoryCompat
std::unique_ptr<DesktopNotificationHistoryCompat> history;
auto hr = DesktopNotificationManagerCompat::get_History(&history);
if (SUCCEEDED(hr))
{
// Remove a specific toast
hr = history->Remove(L"Message2");
// Clear all toasts
hr = history->Clear();
}
手順 10: デプロイとデバッグ
パッケージ アプリをデプロイしてデバッグするには、「パッケージ化されたデスクトップ アプリを実行、デバッグ、テストする」
デスクトップ アプリをデプロイしてデバッグするには、通常どおりにデバッグする前にインストーラーを使用してアプリをインストールする必要があります。これにより、AUMID と CLSID のスタート ショートカットが表示されます。 スタート ショートカットが表示されたら、Visual Studio から F5 キーを使用してデバッグできます。
通知がデスクトップアプリに表示されない場合(例外が発生していない場合)、スタートショートカットが存在しないか(インストーラーを使用してアプリをインストール)、コードで使用したAUMIDがスタートショートカットのAUMIDと一致していない可能性があります。
通知が表示されてもアクション センターに保持されない場合 (ポップアップが閉じた後に消えます)、COM アクティベーターが正しく実装されていないことを意味します。
パッケージ化されたデスクトップ アプリとパッケージ化されていないデスクトップ アプリの両方をインストールした場合、トーストのアクティブ化を処理するときに、パッケージ化されていないアプリがパッケージ化されていないアプリよりも優先されることに注意してください。 つまり、パッケージ化されていないアプリからの通知をクリックすると、パッケージ化されたアプリが起動します。 パッケージ化されたアプリをアンインストールすると、アクティブ化がパッケージ化されていないアプリに戻ります。
HRESULT 0x800401f0 CoInitialize has not been called.
を受け取った場合は、API を呼び出す前に、必ずアプリで CoInitialize(nullptr)
を呼び出してください。
Compat API の呼び出し中に HRESULT 0x8000000e A method was called at an unexpected time.
を受け取った場合は、必要な Register メソッドの呼び出しに失敗した可能性があります (または、パッケージ化されたアプリの場合は、パッケージ化されたコンテキストで現在アプリを実行していません)。
多数の unresolved external symbol
コンパイル エラーが発生した場合は、手順 1 で runtimeobject.lib
に を追加するのを忘れた可能性があります (または、リリース構成ではなくデバッグ構成にのみ追加しました)。
以前のバージョンの Windows の処理
Windows 8.1 以前をサポートしている場合は、DesktopNotificationManagerCompat API を呼び出すか、ToastGeneric トーストを送信する前に、実行時に Windows で実行しているかどうかを確認する必要があります。
Windows 8 ではトースト通知が導入されましたが、ToastText01 など、レガシ トースト テンプレートを使用しました。 トーストは永続化されていない短いポップアップのみであるため、ToastNotification クラスのメモリ内 Activated イベントによってアクティブ化が処理されました。 Windows 10 では、対話型の ToastGeneric トースト
オペレーティングシステム (OS) | ToastGeneric | COM アクティベーター | レガシー トースト テンプレート |
---|---|---|---|
Windows 10 以降 | サポートされています | サポートされています | サポートされています (ただし、COM サーバーはアクティブ化されません) |
Windows 8.1 / 8 | なし | なし | サポートされています |
Windows 7 以前 | なし | なし | なし |
Windows 10 以降で実行されているかどうかを確認するには、<VersionHelpers.h>
ヘッダーを含め、IsWindows10OrGreater メソッドを確認します。
true
が返される場合は、このドキュメントで説明されているすべてのメソッドを引き続き呼び出します。
#include <VersionHelpers.h>
if (IsWindows10OrGreater())
{
// Running on Windows 10 or later, continue with sending toasts!
}
既知の問題
修正済み: トーストをクリックしてもアプリがフォーカスされない: ビルド 15063 以前では、COM サーバーをアクティブ化したときに、フォアグラウンド権限がアプリケーションに転送されませんでした。 そのため、フォアグラウンドに移動しようとすると、アプリが単にフラッシュされます。 この問題の回避策はありませんでした。 ビルド 16299 以降でこれを修正しました。
リソース
- GitHubで完全なコードサンプルを参照してください
- デスクトップ アプリからトースト通知を
- トーストコンテンツ ドキュメンテーション
Windows developer