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


Создание пользовательского плагина для портала устройств Windows

Узнайте, как написать приложение UWP, использующее портал устройств Windows (WDP) для размещения веб-страницы и предоставления диагностических сведений.

Начиная с Windows 10 Creators Update (версия 1703, сборка 15063), вы можете использовать портал устройств для размещения диагностических интерфейсов приложения. В этой статье рассматриваются три элемента, необходимые для создания DevicePortalProvider для вашего приложения — изменения в манифесте пакета приложения , настройка подключения вашего приложения к службе Портала устройств и обработка входящего запроса.

Создание проекта приложения UWP

В Microsoft Visual Studio создайте проект приложения UWP. Перейдите в раздел Файл > Создать > Project и выберите пустое приложение (универсальное приложение Windows) для C#, а затем нажмите кнопку Далее. В диалоговом окне Настройка нового проекта. Присвойте проекту имя DevicePortalProvider, а затем щелкните Создать. Это будет приложение, содержащее службу приложений. Возможно, потребуется обновить Visual Studio или установить последний пакет SDK для Windows.

Добавление расширения devicePortalProvider в манифест пакета приложения

Вам потребуется добавить код в файл package.appxmanifest, чтобы сделать приложение функциональным в качестве подключаемого модуля Device Portal. Сначала добавьте следующие определения пространства имен в верхней части файла. Кроме того, добавьте их в IgnorableNamespaces атрибут.

<Package
    ... 
    xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
    xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
    IgnorableNamespaces="uap mp rescap uap4">
    ...

Чтобы объявить, что приложение является поставщиком портала устройств, необходимо создать службу приложений и новое расширение поставщика портала устройств, которое его использует. Добавьте расширение windows.appService и расширение windows.devicePortalProvider в элементе Extensions в Application. Убедитесь, что атрибуты соответствуют каждому AppServiceName расширению. Это указывает службе портала устройств, что данный служебный процесс приложения может быть запущен для обработки запросов в пространстве имен обработчика.

...   
<Application 
    Id="App" 
    Executable="$targetnametoken$.exe"
    EntryPoint="DevicePortalProvider.App">
    ...
    <Extensions>
        <uap:Extension Category="windows.appService" EntryPoint="MySampleProvider.SampleProvider">
            <uap:AppService Name="com.sampleProvider.wdp" />
        </uap:Extension>
        <uap4:Extension Category="windows.devicePortalProvider">
            <uap4:DevicePortalProvider 
                DisplayName="My Device Portal Provider Sample App" 
                AppServiceName="com.sampleProvider.wdp" 
                HandlerRoute="/MyNamespace/api/" />
        </uap4:Extension>
    </Extensions>
</Application>
...

Атрибут HandlerRoute ссылается на пространство имен REST, которое утверждает приложение. Все HTTP-запросы внутри этого пространства имен (неявно подразумевающие использование подстановочного знака), полученные службой портала устройств, будут отправлены в ваше приложение для обработки. В этом случае любой успешно прошедший проверку подлинности HTTP-запрос <ip_address>/MyNamespace/api/* будет отправлен в приложение. Конфликты между маршрутами обработчика разрешаются с помощью проверки "выбирается маршрут с наибольшим числом совпадений": то есть, запрос на "/MyNamespace/api/foo" будет соответствовать маршруту с "/MyNamespace/api", а не с "/MyNamespace".

Для этой функции требуются две новые возможности. Они также должны быть добавлены в файл package.appxmanifest.

...
<Capabilities>
    ...
    <Capability Name="privateNetworkClientServer" />
    <rescap:Capability Name="devicePortalProvider" />
</Capabilities>
...

Замечание

Функция "devicePortalProvider" ограничена ("rescap"), что означает, что необходимо получить предварительное утверждение в Магазине приложений, прежде чем приложение будет опубликовано там. Однако это не препятствует локальному тестированию приложения через установку в обход магазина приложений. Дополнительные сведения об ограниченных возможностях см. в разделе "Объявления возможностей приложений".

Настройка фоновой задачи и компонента WinRT

Чтобы настроить подключение к порталу устройств, ваше приложение должно установить соединение службы приложений из службы портала устройств с экземпляром портала устройств, запущенным в приложении. Для этого добавьте новый компонент WinRT в приложение с классом, реализующим IBackgroundTask.

using Windows.System.Diagnostics.DevicePortal;
using Windows.ApplicationModel.Background;

namespace MySampleProvider {
    // Implementing a DevicePortalConnection in a background task
    public sealed class SampleProvider : IBackgroundTask {
        BackgroundTaskDeferral taskDeferral;
        DevicePortalConnection devicePortalConnection;
        //...
    }

Убедитесь, что его имя соответствует пространству имен и имени класса, настроенным в AppService EntryPoint ("MySampleProvider.SampleProvider"). При первом запросе к поставщику Портала устройств, Портал устройств сохранит запрос, запустит фоновую задачу приложения, вызовет метод Run и передаст IBackgroundTaskInstance. Затем приложение использует его для настройки экземпляра DevicePortalConnection.

// Implement background task handler with a DevicePortalConnection
public void Run(IBackgroundTaskInstance taskInstance) {
    // Take a deferral to allow the background task to continue executing 
    this.taskDeferral = taskInstance.GetDeferral();
    taskInstance.Canceled += TaskInstance_Canceled;

    // Create a DevicePortal client from an AppServiceConnection 
    var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
    var appServiceConnection = details.AppServiceConnection;
    this.devicePortalConnection = DevicePortalConnection.GetForAppServiceConnection(appServiceConnection);

    // Add Closed, RequestReceived handlers 
    devicePortalConnection.Closed += DevicePortalConnection_Closed;
    devicePortalConnection.RequestReceived += DevicePortalConnection_RequestReceived;
}

Существуют два события, которые должны обрабатываться приложением, чтобы завершить цикл обработки запросов: Closed, когда служба портала устройств завершает работу, и RequestReceived, которое обрабатывает входящие HTTP-запросы и предоставляет основные функциональные возможности поставщика портала устройств.

Обработать событие RequestReceived

Событие RequestReceived будет вызываться один раз для каждого HTTP-запроса, который выполняется на указанном в подключаемом модуле маршруте обработчика. Цикл обработки запросов для поставщиков портала устройств аналогичен тому, что в NodeJS Express: объекты запроса и ответа предоставляются вместе с событием, а обработчик отвечает, заполняя объект ответа. В поставщиках портала устройств событие RequestReceived и его обработчики используют объектах Windows.Web.Http.HttpRequestMessage и Объектах HttpResponseMessage.

// Sample RequestReceived echo handler: respond with an HTML page including the query and some additional process information. 
private void DevicePortalConnection_RequestReceived(DevicePortalConnection sender, DevicePortalConnectionRequestReceivedEventArgs args)
{
    var req = args.RequestMessage;
    var res = args.ResponseMessage;

    if (req.RequestUri.AbsolutePath.EndsWith("/echo"))
    {
        // construct an html response message
        string con = "<h1>" + req.RequestUri.AbsoluteUri + "</h1><br/>";
        var proc = Windows.System.Diagnostics.ProcessDiagnosticInfo.GetForCurrentProcess();
        con += String.Format("This process is consuming {0} bytes (Working Set)<br/>", proc.MemoryUsage.GetReport().WorkingSetSizeInBytes);
        con += String.Format("The process PID is {0}<br/>", proc.ProcessId);
        con += String.Format("The executable filename is {0}", proc.ExecutableFileName);
        res.Content = new Windows.Web.HttpStringContent(con);
        res.Content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("text/html");
        res.StatusCode = Windows.Web.Http.HttpStatusCode.Ok;            
    }
    //...
}

В этом примере обработчика запросов сначала извлекаем объекты запроса и ответа из параметра args , а затем создадим строку с URL-адресом запроса и дополнительным форматированием HTML. Это добавляется в объект Response в качестве экземпляра HttpStringContent . Кроме того, разрешены другие классы IHttpContent , такие как String и Buffer.

Затем ответ задается как HTTP-ответ и получает код состояния 200 (ОК). Он должен отображаться должным образом в браузере, который сделал исходный вызов. Обратите внимание, что при возврате обработчика событий RequestReceived сообщение ответа автоматически возвращается агенту пользователя: дополнительный метод send не требуется.

ответное сообщение портала устройства

Предоставление статического содержимого

Статическое содержимое можно распространять непосредственно из папки в пакете, тем самым упрощая добавление пользовательского интерфейса в провайдер. Самый простой способ обслуживать статическое содержимое — создать папку содержимого в проекте, которая может сопоставляться с URL-адресом.

статическая папка содержимого портала устройства

Затем добавьте обработчик маршрутов в обработчик событий RequestReceived , который обнаруживает статические маршруты содержимого и сопоставляет запрос соответствующим образом.

if (req.RequestUri.LocalPath.ToLower().Contains("/www/")) {
    var filePath = req.RequestUri.AbsolutePath.Replace('/', '\\').ToLower();
    filePath = filePath.Replace("\\backgroundprovider", "")
    try {
        var fileStream = Windows.ApplicationModel.Package.Current.InstalledLocation.OpenStreamForReadAsync(filePath).GetAwaiter().GetResult();
        res.StatusCode = HttpStatusCode.Ok;
        res.Content = new HttpStreamContent(fileStream.AsInputStream());
        res.Content.Headers.ContentType = new HttpMediaTypeHeaderValue("text/html");
    } catch(FileNotFoundException e) {
        string con = String.Format("<h1>{0} - not found</h1>\r\n", filePath);
        con += "Exception: " + e.ToString();
        res.Content = new Windows.Web.Http.HttpStringContent(con);
        res.StatusCode = Windows.Web.Http.HttpStatusCode.NotFound;
        res.Content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("text/html");
    }
}

Убедитесь, что все файлы в папке содержимого помечены как "Содержимое" и установите значение "Копировать, если новее" или "Копировать всегда" в меню свойств Visual Studio. Это гарантирует, что файлы будут находиться в пакете AppX при его развертывании.

настройка копирования статических файлов содержимого

Использование существующих ресурсов портала устройств и API

Статическое содержимое, обслуживаемое поставщиком портала устройств, обслуживается на том же порту, что и основная служба портала устройств. Это означает, что вы можете ссылаться на существующие JS и CSS, включенные в Портале устройств, с простыми тегами <link> и <script> в HTML. Как правило, мы рекомендуем использовать rest.js, который оборачивает все основные интерфейсы REST API портала устройств в удобный объект webbRest, а также файл common.css, который позволит оформить содержимое в соответствии с остальным пользовательским интерфейсом портала устройств. Пример этого можно увидеть на странице index.html , включенной в пример. Он использует rest.js для получения имени устройства и выполнения процессов на портале устройств.

выходные данные подключаемого модуля портала устройств

Важно отметить, что использование методов HttpPost/DeleteExpect200 в webbRest автоматически выполняет обработку CSRF, что позволяет веб-странице вызывать интерфейсы REST API изменения состояния.

Замечание

Статический контент, включенный в портал устройства, не имеет гарантии от несовместимых изменений. Хотя от API-интерфейсов не ожидаются частые изменения, они могут измениться, особенно в файлах common.js и controls.js, которые не должен использовать вашим поставщик.

Отладка подключения портала устройств

Чтобы отладить фоновую задачу, необходимо изменить способ запуска кода Visual Studio. Выполните следующие действия для отладки подключения службы приложений, чтобы проверить, как поставщик обрабатывает HTTP-запросы:

  1. В меню отладки выберите Свойства DevicePortalProvider.
  2. На вкладке "Отладка" в разделе "Пуск" выберите "Не запускать, а отлаживать код при запуске".
    установить плагин в режим отладки
  3. Задайте точку останова в функции обработчика RequestReceived. точка останова в обработчике получения запроса

Замечание

Убедитесь, что архитектура сборки соответствует архитектуре целевого объекта точно. Если вы используете 64-разрядный ПК, нужно использовать сборку AMD64. 4. Нажмите клавишу F5, чтобы развернуть приложение 5. Отключите портал устройств, а затем включите его обратно, чтобы найти приложение (необходимо только при изменении манифеста приложения — в остальное время вы можете просто повторно развернуть и пропустить этот шаг). 6. В браузере перейдите к пространству имен поставщика, и точка останова должна быть достигнута.