このクイック スタートでは、Windows アプリ SDK を使用してプッシュ通知を送受信するデスクトップ Windows アプリケーションを作成します。
Prerequisites
- Windows アプリの開発を開始します
Windows アプリ SDK を使用する新しいプロジェクトを作成する または既存のプロジェクトで Windows アプリ SDK を使用する - Windows アプリ SDKプッシュ通知を使用するには、Azure アカウントが必要です。
- プッシュ通知の概要を読む
パッケージ化の要件
Windows アプリ SDKのプッシュ通知では、パッケージ化されたデスクトップ アプリとパッケージ化されていないデスクトップ アプリの両方がサポートされます。 ただし、最も一般的な運用シナリオ であるバックグラウンド配信と COM のアクティブ化には、パッケージ ID が必要です 。 次の表は、パッケージ モデルに基づいて必要な内容をまとめたものです。
| パッケージ モデル | COM アクティベーターが必要 | PFN マッピングが必要 | パッケージ化されていないサポート |
|---|---|---|---|
| MSIX packaged (WinUI 3、パッケージ化されたWPF/WinForms) | はい — |
はい — PFN マッピング 電子メール経由 | いいえ |
| 外部の場所を含むパッケージ化 | はい — |
はい — PFN マッピング 電子メール経由 | いいえ |
| 真にパッケージ化されていない (パッケージ ID なし) | いいえ (手順 3 をスキップ) | いいえ | はい — 制限付き機能 |
Important
アプリがパッケージ化されている場合 (MSIX または外部の場所でパッケージ化されている場合)、プッシュ通知が機能する前に、アプリの Package Family Name (PFN) を Azure AppId にマップする必要があります。 マッピング要求は電子メールで Win_App_SDK_Push@microsoft.com に送信され、 毎週処理されます。 起動前に、このリード タイムを計画します。
詳細については、「手順 4: アプリのパッケージ ファミリ名を Azure AppId にマップする」を参照してください。
サンプル アプリ
このクイック スタートでは、Windows アプリ SDK 1.7 でアプリにプッシュ通知のサポートを追加する手順について説明します。 GitHub にあるサンプル アプリで、このクイック スタートと同様のコードを参照してください。 プロジェクトに最適なサンプルを確認するために、お好みのバージョンのWindows アプリ SDKを使用したブランチを確認してください。
サンプル リポジトリでバージョン ブランチを選択>
API リファレンス
プッシュ通知の API リファレンス ドキュメントについては、Microsoft.Windows を参照してください。PushNotifications 名前空間。
Azure Active Directory (AAD) でアプリの ID を構成する
Windows アプリ SDKのプッシュ通知では、Azure Active Directory (AAD) からの ID が使用されます。 Azure資格情報は、WNS チャネル URI を要求するとき、およびプッシュ通知を送信するためにアクセス トークンを要求するときに必要です。 注意: Windows アプリ SDK プッシュ通知のMicrosoft Partner Centerでの使用はサポートされていません。
手順 1: AAD アプリの登録を作成する
Azure アカウントにログインし、新しい AAD アプリの登録 リソースを作成します。 [新規登録] を選択します。
手順 2: 名前を指定し、マルチテナント オプションを選択する
アプリケーションの名前を指定します。
プッシュ通知にはマルチテナント オプションが必要なので、これを選択します。
- テナントの詳細については、「 アプリにサインインできるユーザー」を参照してください。
[登録] を選択します
アクティブ化の登録とアクセス トークンの要求時に使用する Azure AppId であるため、Application (クライアント) ID を控えておいてください。
Directory (テナント) ID を書き留めます。これは、アクセス トークンを要求するときに使用する Azure TenantId であるためです。
Important
アプリケーション (クライアント) ID とディレクトリ (テナント) ID を書き留めます。Object ID を書き留めます。これは、チャネル要求を要求するときに使用する Azure ObjectId であるためです。 これは、[ 要点 ] ページに一覧表示されているオブジェクト ID ではないことに注意してください。 代わりに、正しいオブジェクト ID を見つけるには、[要点] ページの [ローカル ディレクトリのマネージド アプリケーション] フィールドでアプリ名をクリックします。
Note
オブジェクト ID を取得するには、サービス プリンシパルが必要です。アプリに関連付けられていない場合は、次のいずれかの記事の手順に従って、Azure ポータルで作成するか、コマンド ラインを使用します。
手順 3: アプリ登録用のシークレットを作成する
プッシュ通知を送信するアクセス トークンを要求するときに、シークレットが Azure AppId/ClientId と共に使用されます。
認証と秘密の質問 に移動し、 新しいクライアントの秘密の質問を選択します。
Important
作成したらシークレットをコピーし、Azure Key Vaultなどの安全な場所に保存してください。 作成直後に表示できるのは 1 回だけです。
手順 4: アプリのパッケージ ファミリ名を Azure AppId にマップする
アプリがパッケージ化されている場合 (外部の場所でパッケージ化されている場合を含む)、このフローを使用して、アプリのパッケージ ファミリ名 (PFN) とそのAzure AppId をマップできます。
アプリがパッケージ化された Win32 アプリの場合は、件名に "Windows アプリ SDK プッシュ通知マッピング要求" と本文 "PFN: [your PFN]"、AppId: [your APPId]、ObjectId: [your ObjectId]を含む Win_App_SDK_Push@microsoft.com を電子メールで送信して、パッケージ ファミリ名 (PFN) マッピング要求を作成します。 マッピング要求は週単位で完了します。 マッピング要求が完了すると、通知が表示されます。
Azure AppId、ObjectId、シークレットを取得したら、これらの資格情報を以下のサンプル コードに追加できます。
プッシュ通知を受信するようにアプリを構成する
手順 1: Windows アプリ SDKと必要な NuGet パッケージを追加する
次に、ソリューション エクスプローラーでソリューションを右クリックし、 Manage NuGet パッケージを選択します。
パッケージ マネージャーで、次のパッケージを追加します。
- Microsoft。WindowsAppSDK (最小バージョン 1.1.0)
- Microsoft。Windows。Sdk。BuildTools (最小バージョン 10.0.22000.194)
- Microsoft。Windows。CppWinRT、(最小バージョン 2.0.210930.14)
- Microsoft。Windows。ImplementationLibrary、(最小バージョン 1.0.210930.1)
プロジェクトでWindows アプリ SDKを初めて使用し、外部の場所でパッケージ化またはパッケージ化されていない場合は、次のプロパティをプロジェクト ファイルに追加してWindows アプリ SDKを初期化します。
<!-- your .vcxproj or .proj file -->
<PropertyGroup Label="Globals">
<!-- Other properties -->
<WindowsPackageType>None</WindowsPackageType>
</PropertyGroup>
またはブートストラップ API を使用します。 詳細については、「外部ソースまたは非パッケージアプリ用のWindows アプリ SDKランタイムを使用する」を参照してください。
Note
SDK が初期化されていない場合、アプリは System.Runtime.InteropServices.COMException (0x80040154): Class not registered (0x80040154 (REGDB_E_CLASSNOTREG)) をスローし、実行されません。
手順 2: 名前空間を追加する
次に、Windows アプリ SDK プッシュ通知の名前空間を Microsoft.Windows.PushNotifications に追加します。
#include <winrt/Microsoft.Windows.PushNotifications.h>
using namespace winrt::Microsoft::Windows::PushNotifications;
"「Can't find Microsoft.Windows.PushNotifications」というエラーが表示された場合、ヘッダー ファイルが生成されていないことを意味している可能性があります。" 解決するには、上記のパッケージがインストールされていることを確認し、エラーを引き起こしているインクルード文と using ステートメントをコメントアウトし、アプリケーションをリビルドしてヘッダーファイルを生成してください。 ビルドが成功したら、include ステートメントと using ステートメントのコメントを解除し、プロジェクトをリビルドします。 これにより、エラーが解決されます。
手順 3: COM アクティベーターをアプリのマニフェストに追加する
Important
アプリがパッケージ化されていない (つまり、実行時にパッケージ ID がない) 場合は、「 手順 4: アプリの起動時にプッシュ通知に登録して応答する」に進みます。
アプリがパッケージ化されている場合 (外部の場所でパッケージ化を含む): Package.appxmanifest を開きます。
<Application>要素内に次のコードを追加します。
Id、Executable、およびDisplayNameの値を、アプリに固有の値に置き換えます。
<!--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>
Note
Id の <com:Class> 属性は、Azure AppId (Azure AD アプリ登録のアプリケーション (クライアント) ID) に設定する必要があります。 これは、Windows アプリ SDKアプリの COM アクティブ化をAzure ID に接続する方法です。WNS がアプリをアクティブ化してバックグラウンド プッシュ通知を配信すると、この GUID を使用して正しい COM サーバーを見つけて起動します。 上記の手順 1 で示したものと同じAzure AppId 値を使用します。
Note
この例の完成した C++ クラスの例は、 手順 5. の後にあります。 手順 4 と 5 では、最後の例で各部分を追加するための詳細なガイダンスを提供します。
手順 4: アプリの起動時にプッシュ通知に登録して応答する
アプリの main() メソッドを更新して、次のコードを追加します。
- PushNotificationManager::D efault() を呼び出して、プッシュ通知を受信するようにアプリを登録します。Register().
- AppInstance::GetCurrent() を呼び出して、アクティブ化要求のソースを確認します。GetActivatedEventArgs(). プッシュ通知からアクティブ化がトリガーされた場合は、通知のペイロードに基づいて応答します。
Important
PushNotificationManager::Default().Register を呼び出す必要があります。これは、AppInstance.GetCurrent.GetActivatedEventArgsを呼び出す前に行ってください。
フォアグラウンド イベント ハンドラーの追加
フォアグラウンドでイベントを処理するには、 PushNotificationManager.PushReceived のハンドラーを登録します。
Important
PushNotificationManager.Register() を呼び出す前に、 PushNotificationManager.PushReceived イベント ハンドラーを登録する必要もあります。 それ以外の場合は、次のランタイム例外がスローされます。
System.Runtime.InteropServices.COMException: Element not found. Must register event handlers before calling Register().
PushNotificationManager::IsSupported() チェックを追加する
次に、 PushNotificationManager.IsSupported() で PushNotification API がサポートされているかどうかを確認します。 そうでない場合は、ポーリングまたは独自のカスタム ソケット実装を使用することをお勧めします。
プッシュ通知のサポートが確認されたので、 PushNotificationReceivedEventArgs に基づいて動作を追加します。
手順 5: WNS チャネル URI を要求し、WNS サーバーに登録する
WNS チャネル URI は、プッシュ通知を送信するための HTTP エンドポイントです。 各クライアントはチャネル URI を要求し、それを WNS サーバーに登録してプッシュ通知を受信する必要があります。
Note
WNS チャネル URI の有効期限は 30 日後です。 前のアプリをキャッシュするのではなく、すべてのアプリ起動時に新しいチャネル URI を要求します。 新しい URI がバックエンドに格納されているものと異なる場合は、更新された URI をクラウド サービスに送信して、レコードを最新の状態に保つことができます。 セッション間で URI が安定していると想定しないでください。変更可能なセッション スコープの値として扱うと、期限切れまたは古いチャネル URI によって発生するサイレント配信エラーを回避できます。
auto channelOperation{ PushNotificationManager::Default().CreateChannelAsync(winrt::guid("[Your app's Azure ObjectID]")) };
チュートリアル コードに従っている場合は、ここで Azure オブジェクト ID を追加します。
// To obtain an AAD RemoteIdentifier for your app,
// follow the instructions on https://learn.microsoft.com/azure/active-directory/develop/quickstart-register-app
winrt::guid remoteId{ "00000000-0000-0000-0000-000000000000" }; // Replace this with your own Azure ObjectId
PushNotificationManager はチャネル URI の作成を試み、15 分以内に自動的に再試行します。 呼び出しが完了するまで待機するイベント ハンドラーを作成します。 呼び出しが完了したら、成功した場合は、WNS サーバーに URI を登録します。
コード例
#include <iostream>
#include <winrt/Microsoft.Windows.PushNotifications.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Microsoft.Windows.AppLifecycle.h>
#include <winrt/Windows.ApplicationModel.Background.h>
#include <wil/cppwinrt.h>
#include <wil/result.h>
using namespace winrt::Microsoft::Windows::PushNotifications;
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::Windows::AppLifecycle;
// To obtain an AAD RemoteIdentifier for your app,
// follow the instructions on https://learn.microsoft.com/azure/active-directory/develop/quickstart-register-app
winrt::guid remoteId{ "00000000-0000-0000-0000-000000000000" }; // Replace this with your own Azure ObjectId
winrt::Windows::Foundation::IAsyncOperation<PushNotificationChannel> RequestChannelAsync()
{
auto channelOperation = PushNotificationManager::Default().CreateChannelAsync(remoteId);
// Set up the in-progress 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;
}
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;
}) };
std::cout << "Push notification foreground event handler registered." << std::endl;
}
int main()
{
// Set up an event handler, so we can receive notifications in the foreground while the app is running.
// You must register notification event handlers before calling Register(). Otherwise, the following runtime
// exception will be thrown: System.Runtime.InteropServices.COMException: 'Element not found. Must register
// event handlers before calling Register().'
SubscribeForegroundEventHandler();
// Register the app for push notifications.
PushNotificationManager::Default().Register();
auto args{ AppInstance::GetCurrent().GetActivatedEventArgs() };
switch (args.Kind())
{
case ExtendedActivationKind::Launch:
{
std::cout << "App launched by user or from the debugger." << std::endl;
if (PushNotificationManager::IsSupported())
{
std::cout << "Push notifications are supported on this device." << std::endl;
// 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
{
std::cout << "Push notifications are NOT supported on this device." << std::endl;
std::cout << "App implements its own custom socket here to receive messages from the cloud since Push APIs are unsupported." << std::endl;
std::cin.ignore();
}
}
break;
case ExtendedActivationKind::Push:
{
std::cout << "App activated via push notification." << std::endl;
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;
}
}
手順 6: アプリをビルドしてインストールする
Visual Studioを使用してアプリをビルドしてインストールします。 ソリューション エクスプローラーでソリューション ファイルを右クリックし、Deploy を選択します。 Visual Studioによってアプリがビルドされ、コンピューターにインストールされます。 アプリを実行するには、スタート メニューまたはVisual Studio デバッガーを使用してアプリを起動します。
チュートリアル コードのコンソールは次のようになります。
アプリにプッシュ通知を送信する
この時点で、すべての構成が完了し、WNS サーバーはクライアント アプリにプッシュ通知を送信できます。 次の手順では、 プッシュ通知サーバーの要求ヘッダーと応答ヘッダー を参照して詳細を確認します。
手順 1: アクセス トークンを要求する
プッシュ通知を送信するには、まず WNS サーバーがアクセス トークンを要求する必要があります。 Azure TenantId、Azure AppId、シークレットを使用して HTTP POST 要求を送信します。 Azure TenantId と Azure AppId の取得の詳細については、サインインに関する Get テナントとアプリ 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: クラウドソースのアプリ通知を送信する
未加工の通知のみを送信する場合は、この手順を無視してください。 クラウドソースのアプリ通知 (プッシュ トースト通知とも呼ばれます) を送信するには、最初に toast、 Content-Type が text/xmlされ、コンテンツにアプリ通知 XML ペイロードが含まれている点が異なります。 XML ペイロードを構築する方法の詳細については、 Notifications 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);
Resources
Windows developer