クイックスタート: Windows App SDK のプッシュ通知

このクイックスタートでは、Windows アプリ SDK を使用してプッシュ通知を送受信するデスクトップ Windows アプリケーションを作成します。

前提条件

サンプル アプリ

このクイックスタートでは、アプリにプッシュ通知のサポートを追加する手順について説明します。 このクイックスタートのコード例は、GitHubにあるサンプル アプリのコンテキストで参照してください。

API リファレンス

プッシュ通知の API リファレンス ドキュメントについては、「Microsoft.Windows.PushNotifications 名前空間」を参照してください。

Azure Active Directory (AAD) でアプリの ID を構成する

Windows アプリ SDK のプッシュ通知では、Azure Active Directory (AAD) の ID が使用されます。 Azure 資格情報は、WNS チャネル URI を要求するとき、およびプッシュ通知を送信するためにアクセス トークンを要求するときに必要です。 : Microsoft パートナー センターでの Windows アプリ SDK プッシュ通知の使用はサポートされていません

手順 1: AAD アプリ登録を作成する

Azure アカウントにログインし、新しい AAD アプリ登録リソースを作成します。 [新規登録] を選択します。

手順 2: 名前を指定し、マルチテナント オプションを選ぶ

  1. アプリケーションの名前を指定します。

  2. プッシュ通知にはマルチテナント オプションが必須なので、選びます。

    1. テナントの詳細については、「アプリケーションにサインインできるユーザー」を参照してください。
  3. [登録] を選択します

  4. アプリケーション (クライアント) ID は、アクティブ化の登録とアクセス トークンの要求時に使用する Azure AppId であるため、メモしておきます。

  5. ディレクトリ (テナント) ID は、アクセス トークンを要求するときに使用する Azure TenantId であるため、メモしておきます。

    重要

    AAD App Registration Tenantアプリケーション (クライアント) IDディレクトリ (テナント) ID をメモします。

  6. オブジェクト ID は、チャネル要求を要求するときに使用する Azure ObjectId であるため、メモしておきます。 これは、[要点] ページに一覧表示されているオブジェクト ID ではないことに注意してください。 代わりに、正しいオブジェクト ID を見つけるには、[要点] ページの [ローカル ディレクトリ内のマネージド アプリケーション] フィールドでアプリ名をクリックします。

    Screenshot showing the Managed application in local directory option on the Essentials page

    Screenshot showing the Object ID field

    Note

    オブジェクト ID を取得するにはサービス プリンシパルが必要であり、アプリに関連付けられているものがない場合は、次のいずれかの記事の手順に従って、Azure portal またはコマンド ラインを使用して作成します。

    リソースにアクセスできる Azure AD アプリケーションとサービス プリンシパルをポータルで作成する

    Azure PowerShell を使用して資格情報でのサービス プリンシパルを作成する

手順 3: アプリ登録のシークレットを作成する

シークレットは、プッシュ通知を送信するためのアクセス トークンを要求するときに、Azure AppId/ClientId と共に使用されます。

AAD App Secret

認定資格証 & シークレット に移動し、新しいクライアント シークレットを選択します。

重要

作成したシークレットは必ずコピーして、Azure Key Vault などの安全な場所に格納してください。 確認できるのは、作成直後の一度だけです。

手順 4: アプリのパッケージ ファミリ名を Azure AppId にマップする

重要

Windows プッシュ通知サービス (WNS) が Azure Portal と統合されました。 新しい登録エクスペリエンスはプレビューで利用できます。 パッケージ アプリ (外部の場所でパッケージ化されたアプリを含む) の場合は、このフローを使用して、アプリのパッケージ ファミリ名 (PFN) とその Azure AppId をマップできます。

アプリがパッケージ化された Win32 アプリの場合は、件名が "Windows アプリ SDK プッシュ通知 要求" で本文が "Azure サブスクリプション: [your Azure Subscription ID]" という内容で Win_App_SDK_Push@microsoft.com にメールを送信して、新しい Azure Portal プレビュー エクスペリエンスへのアクセスを要求します。 リクエストは週単位で完了します。 マッピング要求が完了すると、通知されます。

プッシュ通知を受け取るようにアプリを構成する

ステップ 1: 名前空間の宣言を追加する

Windows アプリ SDK プッシュ通知 Microsoft.Windows.PushNotifications の名前空間を追加します。

#include <winrt/Microsoft.Windows.PushNotifications.h>

using namespace winrt::Microsoft::Windows::PushNotifications;

手順 2: COM アクティベーターをアプリ マニフェストに追加する

重要

アプリがパッケージ化されていない場合 (つまり、実行時にパッケージ ID がない場合) は、「手順 3: アプリの起動時にプッシュ通知に登録して応答する」に進んでください。

アプリがパッケージ化されている場合 (外部の場所でパッケージ化されている場合を含む): Package.appxmanifest を開きます。 <Application> 要素内に以下を追加します。 IdExecutableDisplayName の値をアプリ固有の値に置き換えます。

<!--Packaged apps only-->
<!--package.appxmanifest-->

<Package
  ...
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
  ...
  <Applications>
    <Application>
      ...
      <Extensions>

        <!--Register COM activator-->    
        <com:Extension Category="windows.comServer">
          <com:ComServer>
              <com:ExeServer Executable="SampleApp\SampleApp.exe" DisplayName="SampleApp" Arguments="----WindowsAppRuntimePushServer:">
                <com:Class Id="[Your app's Azure AppId]" DisplayName="Windows App SDK Push" />
            </com:ExeServer>
          </com:ComServer>
        </com:Extension>
    
      </Extensions>
    </Application>
  </Applications>
 </Package>    

手順 3: アプリの起動時にプッシュ通知を登録して応答する

アプリの main() メソッドを更新して、以下を追加します。

  1. PushNotificationManager::D efault() を呼び出して、プッシュ通知を受信するようにアプリを登録します。Register()です。
  2. AppInstance::GetCurrent().GetActivatedEventArgs() を呼び出して、アクティブ化要求のソースを確認します。 アクティブ化がプッシュ通知からトリガーされた場合は、通知のペイロードに基づいて応答します。

重要

AppInstance.GetCurrent.GetActivatedEventArgs を呼び出す前に、AppNotificationManager::Default().Register を呼び出す必要があります。

次のサンプルは、GitHub にあるサンプル パッケージ アプリからの抜粋です。

// cpp-console.cpp
#include "pch.h"
#include <iostream>
#include <winrt/Microsoft.Windows.PushNotifications.h>
#include <winrt/Microsoft.Windows.AppLifecycle.h>
#include <winrt/Windows.Foundation.h>
#include <wil/result.h>
#include <wil/cppwinrt.h>


using namespace winrt;
using namespace Windows::Foundation;

using namespace winrt::Microsoft::Windows::PushNotifications;
using namespace winrt::Microsoft::Windows::AppLifecycle;

winrt::guid remoteId{ "7edfab6c-25ae-4678-b406-d1848f97919a" }; // Replace this with your own Azure ObjectId



void SubscribeForegroundEventHandler()
{
    winrt::event_token token{ PushNotificationManager::Default().PushReceived([](auto const&, PushNotificationReceivedEventArgs const& args)
    {
        auto payload{ args.Payload() };

        std::string payloadString(payload.begin(), payload.end());
        std::cout << "\nPush notification content received in the FOREGROUND: " << payloadString << std::endl;
    }) };
}

int main()
{
    // Setup an event handler, so we can receive notifications in the foreground while the app is running.
    SubscribeForegroundEventHandler();

    PushNotificationManager::Default().Register();

    auto args{ AppInstance::GetCurrent().GetActivatedEventArgs() };
    switch (args.Kind())
    {
        // When it is launched normally (by the users, or from the debugger), the sample requests a WNS Channel URI and
        // displays it, then waits for notifications. This user can take a copy of the WNS Channel URI and use it to send
        // notifications to the sample
        case ExtendedActivationKind::Launch:
        {
            // Checks to see if push notifications are supported. Certain self-contained apps may not support push notifications by design
            if (PushNotificationManager::IsSupported())
            {
                // Request a WNS Channel URI which can be passed off to an external app to send notifications to.
                // The WNS Channel URI uniquely identifies this app for this user and device.
                PushNotificationChannel channel{ RequestChannel() };
                if (!channel)
                {
                    std::cout << "\nThere was an error obtaining the WNS Channel URI" << std::endl;
    
                    if (remoteId == winrt::guid { "00000000-0000-0000-0000-000000000000" })
                    {
                        std::cout << "\nThe ObjectID has not been set. Refer to the readme file accompanying this sample\nfor the instructions on how to obtain and setup an ObjectID" << std::endl;
                    }
                }
    
                std::cout << "\nPress 'Enter' at any time to exit App." << std::endl;
                std::cin.ignore();
            }
            else
            {
                // App implements its own custom socket here to receive messages from the cloud since Push APIs are unsupported.
            }
        }
        break;

        // When it is activated from a push notification, the sample only displays the notification.
        // It doesn’t register for foreground activation of perform any other actions
        // because background activation is meant to let app perform only small tasks in order to preserve battery life.
        case ExtendedActivationKind::Push:
        {
            PushNotificationReceivedEventArgs pushArgs{ args.Data().as<PushNotificationReceivedEventArgs>() };

            // Call GetDeferral to ensure that code runs in low power
            auto deferral{ pushArgs.GetDeferral() };

            auto payload{ pushArgs.Payload() } ;

            // Do stuff to process the raw notification payload
            std::string payloadString(payload.begin(), payload.end());
            std::cout << "\nPush notification content received in the BACKGROUND: " << payloadString.c_str() << std::endl;
            std::cout << "\nPress 'Enter' to exit the App." << std::endl;

            // Call Complete on the deferral when finished processing the payload.
            // This removes the override that kept the app running even when the system was in a low power mode.
            deferral.Complete();
            std::cin.ignore();
        }
        break;

        default:
            std::cout << "\nUnexpected activation type" << std::endl;
            std::cout << "\nPress 'Enter' to exit the App." << std::endl;
            std::cin.ignore();
            break;
    }

    // We do not call PushNotificationManager::UnregisterActivator
    // because then we wouldn't be able to receive background activations, once the app has closed.
    // Call UnregisterActivator once you don't want to receive push notifications anymore.
}

手順 4: WNS チャネル URI を要求し、WNS サーバーに登録する

WNS チャネル URI は、プッシュ通知を送信するための HTTP エンドポイントです。 プッシュ通知を受信するには、各クライアントがチャネル URI を要求し、それを WNS サーバーに登録する必要があります。

Note

WNS チャネル URI は 30 日後に期限切れになります。

auto channelOperation{ PushNotificationManager::Default().CreateChannelAsync(winrt::guid("[Your app's Azure ObjectID]")) };

PushNotificationManager はチャネル URI の作成を試み、15 分以内に自動的に再試行します。 イベント ハンドラーを作成して、呼び出しが完了するのを待ちます。 呼び出しが完了したら、呼び出しが成功した場合は、URI を WNS サーバーに登録します。

// cpp-console.cpp

winrt::Windows::Foundation::IAsyncOperation<PushNotificationChannel> RequestChannelAsync()
{
    // To obtain an AAD RemoteIdentifier for your app,
    // follow the instructions on https://learn.microsoft.com/azure/active-directory/develop/quickstart-register-app
    auto channelOperation = PushNotificationManager::Default().CreateChannelAsync(remoteId);

    // Setup the inprogress event handler
    channelOperation.Progress(
        [](auto&& sender, auto&& args)
        {
            if (args.status == PushNotificationChannelStatus::InProgress)
            {
                // This is basically a noop since it isn't really an error state
                std::cout << "Channel request is in progress." << std::endl << std::endl;
            }
            else if (args.status == PushNotificationChannelStatus::InProgressRetry)
            {
                LOG_HR_MSG(
                    args.extendedError,
                    "The channel request is in back-off retry mode because of a retryable error! Expect delays in acquiring it. RetryCount = %d",
                    args.retryCount);
            }
        });

    auto result = co_await channelOperation;

    if (result.Status() == PushNotificationChannelStatus::CompletedSuccess)
    {
        auto channelUri = result.Channel().Uri();

        std::cout << "channelUri: " << winrt::to_string(channelUri.ToString()) << std::endl << std::endl;

        auto channelExpiry = result.Channel().ExpirationTime();

        // Caller's responsibility to keep the channel alive
        co_return result.Channel();
    }
    else if (result.Status() == PushNotificationChannelStatus::CompletedFailure)
    {
        LOG_HR_MSG(result.ExtendedError(), "We hit a critical non-retryable error with channel request!");
        co_return nullptr;
    }
    else
    {
        LOG_HR_MSG(result.ExtendedError(), "Some other failure occurred.");
        co_return nullptr;
    }

};

PushNotificationChannel RequestChannel()
{
    auto task = RequestChannelAsync();
    if (task.wait_for(std::chrono::seconds(300)) != AsyncStatus::Completed)
    {
        task.Cancel();
        return nullptr;
    }

    auto result = task.GetResults();
    return result;
}

手順 5: アプリをビルドしてインストールする

Visual Studio を使って、アプリをビルドしてインストールします。 ソリューション エクスプローラーでソリューション ファイルを右クリックし、[配置] を選択します。 Visual Studio によってアプリがビルドされ、マシンにインストールされます。 アプリを実行するには、スタート メニューまたは Visual Studio デバッガーから起動します。

アプリにプッシュ通知を送信する

この時点で、すべての構成が完了し、WNS サーバーはクライアント アプリにプッシュ通知を送信できます。 次の手順では、プッシュ通知サーバーの要求ヘッダーと応答ヘッダーを参照してください。

手順 1: アクセス トークンを要求する

プッシュ通知を送信するには、まず WNS サーバーがアクセス トークンを要求する必要があります。 Azure TenantId、Azure AppId、シークレットを含む HTTP POST 要求を送信します。 Azure TenantId と Azure AppId の取得については、「サインイン用のテナント ID とアプリ ID の値を取得する」を参照してください。

HTTP のサンプル要求:

POST /{tenantID}/oauth2/v2.0/token Http/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 160

grant_type=client_credentials&client_id=<Azure_App_Registration_AppId_Here>&client_secret=<Azure_App_Registration_Secret_Here>&scope=https://wns.windows.com/.default/

C# のサンプル要求:

//Sample C# Access token request
var client = new RestClient("https://login.microsoftonline.com/{tenantID}/oauth2/v2.0");
var request = new RestRequest("/token", Method.Post);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", "[Your app's Azure AppId]");
request.AddParameter("client_secret", "[Your app's secret]");
request.AddParameter("scope", "https://wns.windows.com/.default");
RestResponse response = await client.ExecutePostAsync(request);
Console.WriteLine(response.Content);

要求が成功すると、access_token フィールドにトークンを含む応答を受け取ります。

{
    "token_type":"Bearer",
    "expires_in":"86399",
    "ext_expires_in":"86399",
    "expires_on":"1653771789",
    "not_before":"1653685089",
    "access_token":"[your access token]"
}

ステップ 2. 直接通知を送信する

前の手順で取得したアクセス トークンと、送信するプッシュ通知の内容を含む HTTP POST 要求を作成します。 プッシュ通知の内容がアプリに配信されます。

POST /?token=[The token query string parameter from your channel URL. E.g. AwYAAABa5cJ3...] HTTP/1.1
Host: dm3p.notify.windows.com
Content-Type: application/octet-stream
X-WNS-Type: wns/raw
Authorization: Bearer [your access token]
Content-Length: 46

{ Sync: "Hello from the Contoso App Service" }
var client = new RestClient("[Your channel URL. E.g. https://wns2-by3p.notify.windows.com/?token=AwYAAABa5cJ3...]");
var request = new RestRequest();
request.Method = Method.Post; 
request.AddHeader("Content-Type", "application/octet-stream");
request.AddHeader("X-WNS-Type", "wns/raw");
request.AddHeader("Authorization", "Bearer [your access token]");
request.AddBody("Notification body");
RestResponse response = await client.ExecutePostAsync(request);");

ステップ 3: クラウドソースのアプリ通知を送信する

直接通知の送信のみに関心がある場合は、この手順を無視してください。 クラウドソースのアプリ通知 (プッシュ トースト通知とも呼ばれます) を送信するには、まず Windows アプリ SDK の「クイック スタート: アプリ通知」に従います。 アプリの通知は、プッシュ (クラウドから送信) またはローカルで送信できます。 クラウドソースのアプリ通知の送信は、手順 2 の直接通知の送信と似ていますが、X-WNS-Type ヘッダーが toastContent-Typetext/xml で、コンテンツにアプリ通知 XML ペイロードが含まれている点が異なります。 XMLペイロードの構築方法の詳細は、通知XMLスキーマを参照してください。

アクセス トークンと、送信するクラウド ソースのアプリ通知の内容を含む HTTP POST 要求を作成します。 プッシュ通知の内容がアプリに配信されます。

POST /?token=AwYAAAB%2fQAhYEiAESPobjHzQcwGCTjHu%2f%2fP3CCNDcyfyvgbK5xD3kztniW%2bjba1b3aSSun58SA326GMxuzZooJYwtpgzL9AusPDES2alyQ8CHvW94cO5VuxxLDVzrSzdO1ZVgm%2bNSB9BAzOASvHqkMHQhsDy HTTP/1.1
Host: dm3p.notify.windows.com
Content-Type: text/xml
X-WNS-Type: wns/toast
Authorization: Bearer [your access token]
Content-Length: 180

<toast><visual><binding template="ToastGeneric"><text>Example cloud toast notification</text><text>This is an example cloud notification using XML</text></binding></visual></toast>
var client = new RestClient("https://dm3p.notify.windows.com/?token=AwYAAAB%2fQAhYEiAESPobjHzQcwGCTjHu%2f%2fP3CCNDcyfyvgbK5xD3kztniW%2bjba1b3aSSun58SA326GMxuzZooJYwtpgzL9AusPDES2alyQ8CHvW94cO5VuxxLDVzrSzdO1ZVgm%2bNSB9BAzOASvHqkMHQhsDy");
client.Timeout = -1;

var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "text/xml");
request.AddHeader("X-WNS-Type", "wns/toast");
request.AddHeader("Authorization", "Bearer <AccessToken>");
request.AddParameter("text/xml", "<toast><visual><binding template=\"ToastGeneric\"><text>Example cloud toast notification</text><text>This is an example cloud notification using XML</text></binding></visual></toast>",  ParameterType.RequestBody);
Console.WriteLine(response.Content);

リソース