Compartilhar via


Gravar um plug-in personalizado para o Portal de Dispositivos do Windows

Saiba como escrever um aplicativo UWP que usa o Portal de Dispositivos do Windows (WDP) para hospedar uma página da Web e fornecer informações de diagnóstico.

A partir do Windows 10 Creators Update (versão 1703, build 15063), você pode usar o Portal do Dispositivo para hospedar as interfaces de diagnóstico do aplicativo. Este artigo aborda as três partes necessárias para criar um DevicePortalProvider para seu aplicativo: as alterações no manifesto do pacote de aplicativos , a configuração da conexão do aplicativo com o serviço Device Portale lidar com uma solicitação de entrada.

Criar um novo projeto de aplicativo UWP

No Microsoft Visual Studio, crie um novo projeto de aplicativo UWP. Acesse Arquivo > Novo > Projeto e selecione Aplicativo em Branco (Windows Universal) para C#, e clique em Avançar. Na caixa de diálogo Configuração do seu novo projeto. Nomeie o projeto como "DevicePortalProvider" e clique em Criar. Esse será o aplicativo que contém o serviço de aplicativo. Talvez seja necessário atualizar o Visual Studio ou instalar o SDK do Windows mais recente.

Adicione a extensão devicePortalProvider ao manifesto do pacote do aplicativo

Você precisará adicionar algum código ao arquivo package.appxmanifest para tornar seu aplicativo funcional como um plug-in do Portal do Dispositivo. Primeiro, adicione as seguintes definições de namespace na parte superior do arquivo. Adicione-os também ao IgnorableNamespaces atributo.

<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">
    ...

Para declarar que seu aplicativo é um Provedor de Portal de Dispositivos, você precisa criar um serviço de aplicativo e uma nova extensão do Provedor do Portal de Dispositivos que o use. Adicione as extensões windows.appService e windows.devicePortalProvider no elemento Extensionsem Application. Verifique se os AppServiceName atributos correspondem em cada extensão. Isso indica ao serviço Portal do Dispositivo que esse serviço de aplicativo pode ser iniciado para lidar com solicitações no namespace do manipulador.

...   
<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>
...

O HandlerRoute atributo faz referência ao namespace REST reivindicado pelo seu aplicativo. Todas as solicitações HTTP nesse namespace (seguidas implicitamente por um curinga) recebidas pelo serviço do Portal do Dispositivo serão enviadas ao seu aplicativo para serem tratadas. Nesse caso, qualquer solicitação <ip_address>/MyNamespace/api/* HTTP autenticada com êxito será enviada ao seu aplicativo. Os conflitos entre as rotas do manipulador são resolvidos através de uma verificação em que "a rota mais longa vence": a rota que corresponder a mais solicitações é selecionada, o que significa que uma solicitação para "/MyNamespace/api/foo" corresponderá a um provedor com "/MyNamespace/api" em vez de um com "/MyNamespace".

Duas novas funcionalidades são necessárias para essa funcionalidade. eles também devem ser adicionados ao seu arquivo package.appxmanifest.

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

Observação

A funcionalidade "devicePortalProvider" é restrita ("rescap"), o que significa que você deve obter aprovação prévia da Microsoft Store antes que seu aplicativo possa ser publicado lá. No entanto, isso não impede que você teste seu aplicativo localmente através do carregamento lateral. Para obter mais informações sobre funcionalidades restritas, consulte declarações de funcionalidade do aplicativo.

Configure a sua tarefa em segundo plano e o componente WinRT

Para configurar a conexão com o Portal do Dispositivo, seu aplicativo deve estabelecer uma conexão de serviço de aplicativo com o serviço do Portal do Dispositivo e a instância do Portal do Dispositivo que está em execução dentro do seu aplicativo. Para fazer isso, adicione um novo Componente WinRT ao seu aplicativo com uma classe que implementa 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;
        //...
    }

Verifique se seu nome corresponde ao namespace e ao nome da classe configurados pelo AppService EntryPoint ("MySampleProvider.SampleProvider"). Quando você fizer sua primeira solicitação ao provedor do Portal do Dispositivo, o Portal do Dispositivo armazenará a solicitação, iniciará a tarefa em segundo plano do aplicativo, chamará seu método Executar e passará um IBackgroundTaskInstance. Em seguida, seu aplicativo o usa para configurar uma instância 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;
}

Há dois eventos que o aplicativo deve tratar para concluir o ciclo de manuseio de solicitações: Fechado, que ocorre sempre que o serviço do Device Portal é desligado, e RequestReceived, que exibe as solicitações HTTP de entrada e fornece a funcionalidade principal do provedor do Device Portal.

Tratar o evento RequestReceived

O evento RequestReceived será gerado uma vez para cada solicitação HTTP feita na rota do manipulador especificada do plug-in. O loop de tratamento de solicitação para provedores do Portal de Dispositivos é semelhante ao do NodeJS Express: os objetos de solicitação e resposta são fornecidos junto com o evento e o manipulador responde preenchendo o objeto de resposta. Em provedores do Portal de Dispositivos, o evento RequestReceived e seus manipuladores usam objetos Windows.Web.Http.HttpRequestMessage e 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;            
    }
    //...
}

Neste manipulador de solicitação de exemplo, primeiro extraímos os objetos de solicitação e resposta do parâmetro args e, em seguida, criamos uma cadeia de caracteres com a URL de solicitação e alguma formatação HTML adicional. Isso é adicionado ao objeto Response como uma instância de HttpStringContent. Outras classes IHttpContent , como as de "String" e "Buffer", também são permitidas.

Em seguida, a resposta é definida como uma resposta HTTP e recebe um código de status 200 (OK). Deve ser renderizado conforme esperado no navegador que fez a chamada original. Observe que, quando o manipulador de eventos RequestReceived retorna, a mensagem de resposta é retornada automaticamente ao agente do usuário: nenhum método "send" adicional é necessário.

mensagem de resposta do portal do dispositivo

Fornecendo conteúdo estático

O conteúdo estático pode ser servido diretamente de uma pasta em seu pacote, facilitando muito a adição de uma interface do usuário ao seu provedor. A maneira mais fácil de fornecer conteúdo estático é criar uma pasta de conteúdo em seu projeto que possa ser mapeada para uma URL.

pasta de conteúdo estático do portal do dispositivo

Em seguida, adicione um manipulador de rotas ao manipulador de eventos RequestReceived que detecta rotas de conteúdo estático e mapeia uma solicitação adequadamente.

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");
    }
}

Verifique se todos os arquivos dentro da pasta de conteúdo estão marcados como "Conteúdo" e definidos como "Copiar se for mais recente" ou "Copiar sempre" no menu Propriedades do Visual Studio. Isso garante que os arquivos estarão dentro do pacote do AppX quando você implantá-lo.

configurar o arquivo de conteúdo estático copiando

Usando recursos e APIs existentes do Portal de Dispositivos

O conteúdo estático servido por um provedor do Portal do Dispositivo é servido na mesma porta que o serviço principal do Portal de Dispositivos. Isso significa que você pode referenciar o JS e o CSS existentes incluídos no Device Portal com tags simples <link> e <script> em seu HTML. Em geral, sugerimos o uso de rest.js, que encapsula todas as APIs REST principais do Portal do Dispositivo em um objeto webbRest conveniente e o arquivo common.css , que permitirá que você estilize seu conteúdo para se ajustar ao restante da interface do usuário do Portal do Dispositivo. Você pode ver um exemplo disso na página index.html incluída no exemplo. Ele usa rest.js para recuperar o nome do dispositivo e os processos em execução do Portal do Dispositivo.

saída do plug-in do portal do dispositivo

É importante ressaltar que o uso dos métodos HttpPost/DeleteExpect200 no webbRest realizará automaticamente o tratamento de CSRF para você, o que permite que sua página web chame APIs REST que alteram o estado.

Observação

O conteúdo estático incluído no Portal do Dispositivo não vem com uma garantia contra alterações interruptivas. Embora as APIs não sejam esperadas para serem alteradas com frequência, elas podem, especialmente nos arquivos common.js e controls.js , que seu provedor não deve usar.

Depuração da conexão do Portal de Dispositivos

Para depurar sua tarefa em segundo plano, você deve alterar a maneira como o Visual Studio executa seu código. Siga as etapas abaixo para depurar uma conexão de serviço de aplicativo para inspecionar como seu provedor está lidando com as solicitações HTTP:

  1. No menu Depurar: selecione Propriedades do DevicePortalProvider.
  2. Na guia Depuração, na seção Iniciar ação, selecione "Não iniciar, mas depurar meu código assim que ele começar".
    colocar o plug-in no modo de depuração
  3. Defina um ponto de interrupção na função de manipulador RequestReceived. ponto de interrupção no manipulador de recebimento de solicitações

Observação

Verifique se a arquitetura de build corresponde exatamente à arquitetura do destino. Se você estiver usando um computador de 64 bits, deverá implantar usando um build AMD64. 4. Pressione F5 para implantar seu aplicativo 5. Desative o Portal do Dispositivo e, em seguida, ative-o novamente para que ele encontre seu aplicativo (necessário apenas quando você alterar o manifesto do aplicativo – o restante do tempo você pode simplesmente implantar novamente e ignorar esta etapa). 6. No navegador, acesse o namespace do provedor para que o ponto de interrupção seja atingido.