Поделиться через


Краткое руководство: Уведомления Push в Windows App SDK

В этом кратком руководстве вы создадите классическое приложение Windows, которое отправляет и получает push-уведомления с помощью Windows App SDK.

Prerequisites

Требования к упаковке

Push-уведомления в Windows App SDK поддерживают как упакованные, так и распакованные классические приложения. Однако для фоновой доставки и активации COM требуется идентификатор пакета — наиболее распространенный продуктовый сценарий. В следующей таблице приведены сведения, необходимые для модели упаковки:

Модель упаковки Требуется активатор COM Требуется сопоставление PFN Поддерживается без упаковки
MSIX в виде пакета (WinUI 3, WPF/WinForms в виде пакета) Да — в Package.appxmanifest Да, через электронную почту с сопоставлением PFN Нет
Упаковано с внешним хранилищем Да — в Package.appxmanifest Да — через электронную почту сопоставления PFN Нет
Действительно без упаковки (без удостоверения пакета) Нет (пропустить шаг 3) Нет Да — ограниченная функциональность

Important

Если ваше приложение упаковано (MSIX или упаковано с внешним расположением), необходимо сопоставить Имя семейства пакета (PFN) с Azure AppId до того, как заработают push-уведомления. Запросы сопоставления отправляются по электронной почте Win_App_SDK_Push@microsoft.com и обрабатываются еженедельно. Запланируйте время на подготовку перед запуском.

Дополнительные сведения см. в статье Step 4. Сопоставление имени семейства пакетов приложения с Azure AppId.

Пример приложения

В этом кратком руководстве описывается добавление поддержки push-уведомлений в приложение на Windows App SDK 1.7. См. аналогичный код этого краткого руководства в примерах приложений, найденных в GitHub. Обязательно ознакомьтесь с веткой с предпочитаемой версией Windows App SDK, чтобы найти примеры, которые наилучшим образом соответствуют вашему проекту.

Вы также можете найти примеры для каждой версии Windows App SDK, выбрав ветку версии в репозитории с примерами.

Справочник по API

Справочную документацию по API для push-уведомлений см. в разделе пространство имен Майкрософт.Windows.PushNotifications.

Настройка идентификации приложения в Azure Active Directory (AAD)

Push-уведомления в Windows App SDK используют идентификаторы из Azure Active Directory (AAD). Azure учетные данные требуются при запросе URI канала WNS и при запросе маркеров доступа для отправки push-уведомлений. Примечание: мы НЕ поддерживаем использование push-уведомлений Windows App SDK с Microsoft Partner Center.

Шаг 1. Создание регистрации приложения AAD

Войдите в учетную запись Azure и создайте ресурс AAD App Registration. Выберите Новая регистрация.

Шаг 2. Укажите имя и выберите параметр многоарендности

  1. Введите имя приложения

  2. Для push-уведомлений требуется параметр с несколькими клиентами, поэтому выберите это.

    1. Дополнительные сведения об арендаторах см. в разделе Кто может войти в приложение?.
  3. Нажмите кнопку Зарегистрировать.

  4. Запишите идентификатор Application (клиент) ID, так как это ваш Azure AppId, который вы будете использовать во время регистрации активации и запроса маркера доступа.

  5. Запишите идентификатор Directory (арендатор) ID, так как это ваш идентификатор Azure TenantId, который вы будете использовать при запросе токена доступа.

    Important

    Регистрация приложения AAD для арендатора Запишите идентификатор приложения (клиента) и идентификатор каталога (арендатора).

  6. Запишите идентификатор Object ID, так как это ваш Azure ObjectId, который вы будете использовать при запросе канала. Обратите внимание, что это НЕ идентификатор объекта, указанный на странице Essentials . Вместо этого, чтобы найти правильный идентификатор объекта , щелкните название приложения в поле управляемого приложения в локальном каталоге на странице Essentials.

    Снимок экрана, показывающий параметр управляемого приложения в локальном каталоге на странице Essentials

    Снимок экрана: поле

    Note

    Технический идентификатор службы (service principal) необходим для получения Object ID. Если Object ID не связан с вашим приложением, выполните шаги, описанные в одной из следующих статей, чтобы создать его на портале Azure или с помощью командной строки:

    Используйте портал для создания приложения Azure AD и служебного принципала, которые могут получить доступ к ресурсам

    Использовать Azure PowerShell для создания субъекта-службы с сертификатом

Шаг 3. Создание секрета для регистрации приложения

Ваш секрет будет использоваться вместе с идентификатором приложения Azure (ClientId) при запросе токена доступа для отправки push-уведомлений.

секрет приложения AAD

Перейдите к сертификатам и секретам и выберите новый секрет клиента.

Important

Убедитесь, что вы скопируете секрет после создания и сохраните его в безопасном расположении, например Azure Key Vault. Он будет доступен только один раз сразу после создания.

Шаг 4: Сопоставьте имя семейства пакетов вашего приложения с его Azure AppId

Если ваше приложение упаковано (в том числе упаковано с использованием внешнего источника), вы можете использовать эту схему для сопоставления имени семейства пакетов приложения (PFN) и его Azure AppId.

Если приложение является упакованным приложением Win32, создайте запрос на сопоставление имен семейства пакетов (PFN), отправив сообщение электронной почты Win_App_SDK_Push@microsoft.com с строкой темы "Windows App SDK запрос на сопоставление push-уведомлений" и текстом "PFN: [ваш PFN], AppId: [your APPId], ObjectId: [your ObjectId]. Запросы на сопоставление выполняются еженедельно. Вы получите уведомление после завершения запроса на сопоставление.

После получения Azure AppId, ObjectId и секрета можно добавить эти учетные данные в приведенный ниже пример кода.

Настройка приложения для получения push-уведомлений

Шаг 1. Добавление Windows App SDK и необходимых пакетов NuGet

Затем щелкните решение правой кнопкой мыши в Обозреватель решений и выберите Управление пакетами NuGet.

В диспетчер пакетов добавьте следующие пакеты:

  • Майкрософт. WindowsAppSDK (минимальная версия 1.1.0)
  • Майкрософт. Windows. SDK. BuildTools (минимальная версия 10.0.22000.194)
  • Майкрософт. Windows. CppWinRT, (минимальная версия 2.0.210930.14)
  • Майкрософт. Windows. ImplementationLibrary, (минимальная версия 1.0.210930.1)

Если это первый раз, когда вы используете Windows App SDK в своем проекте и он упакован с внешним расположением или не упакован, вам нужно инициализировать Windows App SDK, добавив в файл проекта следующее свойство:

<!-- your .vcxproj or .proj file -->
<PropertyGroup Label="Globals">
    <!-- Other properties -->
    <WindowsPackageType>None</WindowsPackageType>
</PropertyGroup>

или используйте API загрузчика. Дополнительные сведения см. в разделе Использование среды выполнения Windows App SDK для приложений, упакованных с внешним расположением или без упаковки.

Note

Если пакет SDK не инициализирован, приложение вызовет System.Runtime.InteropServices.COMException (0x80040154): Class not registered (0x80040154 (REGDB_E_CLASSNOTREG)) и не будет выполняться.

Шаг 2. Добавление пространств имен

Затем добавьте пространство имен для push-уведомлений Windows App SDK Майкрософт.Windows.PushNotifications.

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

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

Если вы получите сообщение "Не удается найти Майкрософт.Windows.PushNotifications", это, скорее всего, означает, что файлы заголовков не были созданы. Чтобы устранить проблему, убедитесь, что установлены пакеты выше, закомментируйте инструкции include и using, вызывающие ошибку, и перестроите приложение для создания файлов заголовков. После успешной сборки раскомментируйте инструкции include и using и перестроите проект. Это должно устранить ошибку.

Шаг 3. Добавление активатора COM в манифест приложения

Important

Если ваше приложение неупакованное (т. е. отсутствует идентификатор пакета во время выполнения), перейдите к шагу 4: регистрация push-сообщений и реагирование на них при запуске приложения.

Если приложение упаковано (включая пакет с внешним расположением): откройте 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). Вот как Windows App SDK подключает активацию COM вашего приложения к его идентификатору Azure — когда служба WNS активирует ваше приложение для доставки фонового push-уведомления, используется этот GUID для поиска и запуска правильного COM-сервера. Используйте то же Azure значение AppId, которое вы указали на шаге 1 выше.

Note

Пример завершенного класса C++ для этого примера можно найти после шага 5. Шаги 4 и 5 предоставляют пошаговые инструкции по добавлению каждого элемента в последнем примере.

Шаг 4. Регистрация и реагирование на push-уведомления при запуске приложения

Обновите метод приложения main() , чтобы добавить следующее:

  1. Зарегистрируйте приложение для получения push-уведомлений, вызвав PushNotificationManager::Default().Register().
  2. Проверьте источник запроса активации, вызвав AppInstance::GetCurrent().GetActivatedEventArgs(). Если активация была вызвана push-уведомлением, ответьте, учитывая данные уведомления.

Important

Необходимо вызвать PushNotificationManager::D efault(). Зарегистрируйте перед вызовом 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()

Затем добавьте проверку на поддержку API PushNotification с помощью PushNotificationManager.IsSupported(). В противном случае рекомендуется использовать опрос или собственную реализацию пользовательского сокета.

Теперь, когда поддержка push-уведомлений подтверждена, добавьте поведение на основе PushNotificationReceivedEventArgs.

Шаг 5. Запрос URI канала WNS и регистрация его с помощью сервера WNS

URI канала WNS — это конечные точки HTTP для отправки push-уведомлений. Каждый клиент должен запросить URI канала и зарегистрировать его на сервере WNS для получения push-уведомлений.

Note

Срок действия URI канала WNS истекает через 30 дней. Запрос нового URI канала для каждого запуска приложения , а не кэширования предыдущего. Если новый URI отличается от того, что хранит серверная часть, отправьте обновленный URI в облачную службу, чтобы поддерживать актуальность своих записей. Не предполагайте, что универсальный код ресурса (URI) будет оставаться стабильным между сеансами: рассматривайте его как изменяемое значение в контексте сеанса, чтобы избежать тихих сбоев доставки, вызванных истекшим сроком действия или устаревшими URI канала.

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

Если вы используете код из руководства, добавьте здесь идентификатор объекта Azure.

// To obtain an AAD RemoteIdentifier for your app,
// follow the instructions on https://dori-uw-1.kuma-moon.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 минут. Создайте обработчик событий для ожидания завершения вызова. После завершения вызова, если он был успешным, зарегистрируйте URI на сервере WNS.

Пример кода

#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://dori-uw-1.kuma-moon.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 отладчика.

Консоль кода учебника будет выглядеть следующим образом:

рабочий образец консоли

Для отправки push-уведомления в приложение потребуется маркер.

Отправка push-уведомления в приложение

На этом этапе все конфигурации завершено, и сервер WNS может отправлять push-уведомления клиентским приложениям. В следующих шагах см. дополнительные сведения в заголовках запросов и ответов сервера push-уведомлений.

Шаг 1: Запрос токена доступа

Чтобы отправить push-уведомление, сервер WNS сначала должен запросить маркер доступа. Отправьте HTTP-запрос POST с помощью идентификатора клиента Azure, Azure AppId и секрета. Сведения о получении идентификатора клиента (TenantId) Azure и идентификатора приложения (AppId) Azure см. в разделе Получение значений идентификатора клиента и приложения для входа в систему.

Пример 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, содержащий маркер доступа, полученный на предыдущем шаге, и содержимое push-уведомления, которое вы хотите отправить. Содержимое push-уведомления будет доставлено в приложение.

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. Отправка уведомления об облачном приложении

Если вы заинтересованы только в отправке необработанных уведомлений, игнорируйте этот шаг. Чтобы отправить уведомление из облака, также известное всплывающее уведомление, сначала выполните Quickstart: уведомления приложений в Windows App SDK. Уведомления приложений могут отправляться (отправляться из облака) или отправляться локально. Отправка уведомления об использовании облачного приложения аналогична отправке необработанного уведомления в шаге 2, за исключением заголовка X-WNS-Type , типа контента, а содержимое содержит полезные данные XML-уведомлений приложения. См. схему уведомлений XML и для получения дополнительных сведений о том, как создать полезную нагрузку XML.

Создайте HTTP-запрос POST, содержащий маркер доступа и содержимое уведомления о облачном приложении, которое вы хотите отправить. Содержимое push-уведомления будет доставлено в приложение.

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