次の方法で共有


WRL C++ デスクトップ アプリからローカル トースト通知を送信する

パッケージ化されたデスクトップ アプリとパッケージ化されていないデスクトップ アプリは、ユニバーサル Windows プラットフォーム (UWP) アプリと同様に対話型トースト通知を送信できます。 これには、パッケージ化されたアプリが含まれます (パッケージ化された WinUI 3 デスクトップ アプリの新しいプロジェクトの作成参照)。外部の場所を持つパッケージ化されたアプリ (外部の場所パッケージ化によるパッケージ ID の付与に関するページを参照してください)。およびパッケージ化されていないアプリ (パッケージ化されていない WinUI 3 デスクトップ アプリの新しいプロジェクトの作成を参照してください)。

ただし、パッケージ化されていないデスクトップ アプリには、いくつかの特別な手順があります。 これは、さまざまなアクティブ化スキームと、実行時にパッケージ ID が不足しているためです。

Von Bedeutung

UWP アプリを作成する場合は、UWP のドキュメントを参照してください。 その他のデスクトップ言語については、デスクトップ C#を参照してください。

手順 1: Windows SDK を有効にする

アプリに対して Windows SDK を有効にしていない場合は、最初に有効にする必要があります。 いくつかの重要な手順があります。

  1. runtimeobject.libに、 を追加します。
  2. 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 を含む INotificationActivationCallback インターフェイスを実装し、CoCreatableClass 呼び出して、クラスに COM 作成可能としてフラグを設定します。 UUID の場合は、多数のオンライン GUID ジェネレーターのいずれかを使用して一意の GUID を作成します。 この GUID CLSID (クラス識別子) は、アクション センターが COM でアクティブ化するクラスを認識する方法です。

// 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 デスクトップ アプリの新しいプロジェクトを作成する を参照) または外部の場所でパッケージ化されている場合 (外部の場所でパッケージ化してパッケージ ID を付与する 参照)、または両方をサポートする場合は、Package.appxmanifest 追加します。

  1. xmlns:com の宣言
  2. xmlns:desktop の宣言
  3. IgnorableNamespaces 属性で、comデスクトップ が無視されます。
  4. 手順 4 の GUID を使用して、COM アクティベーターの の com:Extension を行います。 必ず、起動がトーストから行われたことを確認するために Arguments="-ToastActivated" を含めます
  5. 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 を使用して、ToastNotifierを作成する点が異なります。 互換性ライブラリは、パッケージ化されたアプリとパッケージ化されていないアプリの違いを自動的に処理するため、コードをフォークする必要はありません。 パッケージ化されていないアプリの場合、compat ライブラリは、RegisterAumidAndComServer を呼び出したときに指定した AUMID をキャッシュするため、AUMID を提供するタイミングについて心配する必要はありません。

従来の 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(&notifier);
    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);
}

イベントのアクティブ化シーケンス

アクティブ化シーケンスは次のとおりです。...

アプリが既に実行されている場合:

  1. NotificationActivator でアクティブにし、 を呼び出す

アプリが実行されていない場合:

  1. EXE が起動された状態のアプリでは、コマンドライン引数 "-ToastActivated" が取得されます。
  2. NotificationActivator でアクティブにし、 を呼び出す

フォアグラウンドとバックグラウンドのアクティブ化

デスクトップ アプリの場合、フォアグラウンドとバックグラウンドのアクティブ化は同じように処理されます。COM アクティベーターが呼び出されます。 ウィンドウを表示するか、単にいくつかの作業を実行して終了するかを決定するのは、アプリのコードにかかってください。 そのため、トースト コンテンツでバックグラウンド の activationType 指定しても、動作は変更されません。

手順 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 トーストが導入され、通知が数日間保持されるアクションセンターも導入されました。 アクション センターの導入では、作成した数日後にトーストをアクティブ化できるように、COM アクティベーターの導入が必要でした。

オペレーティングシステム (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 以降でこれを修正しました。

リソース