Windows デバイス ポータル (WDP) を使用して Web ページをホストし、診断情報を提供する UWP アプリを作成する方法について説明します。
Windows 10 Creators Update (バージョン 1703、ビルド 15063) 以降では、Device Portal を使用してアプリの診断インターフェイスをホストできます。 この記事では、アプリの DevicePortalProvider を作成するために必要な 3 つの要素 (アプリケーション パッケージ マニフェスト 変更、Device Portal サービスへのアプリの接続の設定、受信要求の処理) について説明します。
新しい UWP アプリ プロジェクトを作成する
Microsoft Visual Studio で、新しい UWP アプリ プロジェクトを作成します。 [
devicePortalProvider 拡張機能をアプリケーション パッケージ マニフェストに追加する
アプリを Device Portal プラグインとして機能させるには、package.appxmanifest ファイルにいくつかのコードを追加する必要があります。 まず、ファイルの先頭に次の名前空間定義を追加します。
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">
...
アプリがデバイス ポータル プロバイダーであることを宣言するには、アプリ サービスと、それを使用する新しい Device Portal プロバイダー拡張機能を作成する必要があります。 windows.appService 拡張機能と windows.devicePortalProvider 拡張機能の両方を、Extensions
の Application
要素に追加します。 各拡張機能で AppServiceName
属性が一致していることを確認します。 これは、このアプリ サービスを起動してハンドラー名前空間の要求を処理できることを Device Portal サービスに示します。
...
<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 名前空間を参照します。 Device Portal サービスによって受信されたその名前空間 (暗黙的にワイルドカードが続く) に対する HTTP 要求はすべて、処理対象のアプリに送信されます。 この場合、<ip_address>/MyNamespace/api/*
に対して正常に認証された HTTP 要求がアプリに送信されます。 ハンドラー ルート間の競合は、「最も具体的な一致が優先される」という原則で解決されます。つまり、"/MyNamespace/api/foo" へのリクエストは、"/MyNamespace" よりも "/MyNamespace/api" を持つプロバイダーと一致します。
この機能には、2 つの新機能が必要です。 これらは、package.appxmanifest ファイルにも追加する必要があります。
...
<Capabilities>
...
<Capability Name="privateNetworkClientServer" />
<rescap:Capability Name="devicePortalProvider" />
</Capabilities>
...
注
機能 "devicePortalProvider" は制限されています ("rescap")。 これは、アプリを公開する前に、ストアから事前の承認を得る必要があることを意味します。 ただし、これにより、サイドローディングによってアプリをローカルでテストできることが妨げられるわけではありません。 制限付き機能の詳細については、「アプリ機能宣言」を参照してください。
バックグラウンド タスクと WinRT コンポーネントを設定する
Device Portal 接続を設定するには、アプリで実行されている Device Portal のインスタンスを使用して、Device Portal サービスからアプリ サービス接続をフックする必要があります。 これを行うには、IBackgroundTaskを実装するクラスを使用して、新しい WinRT コンポーネント
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") によって設定された名前空間とクラス名と一致していることを確認します。 Device Portal プロバイダーに対して最初の要求を行うと、Device Portal によって要求が一時的に保存されるとともに、アプリのバックグラウンド タスクが起動され、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;
}
要求処理ループを完了するためにアプリで処理する必要があるイベントが2つあります。1つは、Device Portal サービスがシャットダウンするときに発生する 終了です。もう1つは、HTTP受信要求を表示し、Device Portal プロバイダーの主な機能を提供する RequestReceivedです。
RequestReceived イベントを処理する
RequestReceived イベントは、プラグインの指定されたハンドラー ルートで行われる HTTP 要求ごとに 1 回発生します。 Device Portal プロバイダーの要求処理ループは、NodeJS Express の場合と似ています。要求オブジェクトと応答オブジェクトはイベントと共に提供され、ハンドラーは応答オブジェクトを入力して応答します。 デバイス ポータル プロバイダーでは、
// 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 書式設定を含む文字列を作成します。 これは、HttpStringContent インスタンス
その後、応答は HTTP 応答として設定され、200 (OK) 状態コードが指定されます。 元の呼び出しを行ったブラウザーで期待どおりにレンダリングされます。 RequestReceived イベント ハンドラーが返されると、応答メッセージは自動的にユーザー エージェントに返されることに注意してください。追加の "送信" メソッドは必要ありません。
静的コンテンツの提供
静的コンテンツはパッケージ内のフォルダーから直接提供できるため、プロバイダーに UI を簡単に追加できます。 静的コンテンツを提供する最も簡単な方法は、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 パッケージ内に確実に配置されます。
既存の Device Portal リソースと API の使用
Device Portal プロバイダーによって提供される静的コンテンツは、コア Device Portal サービスと同じポートで提供されます。 つまり、HTML 内の単純な <link>
タグと <script>
タグを使用して、Device Portal に含まれている既存の JS と CSS を参照できます。 一般に、すべての主要な Device Portal REST API を便利な webbRest オブジェクトにラップする rest.jsと common.css ファイルを使用することをお勧めします。これにより、デバイス ポータルの残りの UI に合わせてコンテンツのスタイルを設定できます。 この例は、サンプルに含まれている index.html ページで確認できます。
rest.js を使用して、Device Portal からデバイス名と実行中のプロセスを取得します。
デバイス ポータル プラグインの出力 を
重要なことに、webbRest で HttpPost/DeleteExpect200 メソッドを使用すると、CSRF 処理 が自動的に行われます。これにより、Web ページで状態変更 REST API を呼び出すことができます。
注
Device Portal に含まれる静的コンテンツには、重大な変更に対する保証はありません。 API は頻繁に変更されるとは思われませんが、特にプロバイダーが使用すべきでない common.js ファイルや controls.js ファイルでは変更される可能性があります。
Device Portal 接続のデバッグ
バックグラウンド タスクをデバッグするには、Visual Studio でコードを実行する方法を変更する必要があります。 アプリ サービス接続をデバッグして、プロバイダーが HTTP 要求をどのように処理しているかを調べるには、次の手順に従います。
- [デバッグ] メニューの [DevicePortalProvider のプロパティ] を選択します。
- [デバッグ] タブの [開始アクション] セクションで、[起動しないが、起動時にコードをデバッグする] を選択します。
- RequestReceived ハンドラー関数にブレークポイントを設定します。
注
ビルド アーキテクチャがターゲットのアーキテクチャと正確に一致していることを確認します。 64 ビット PC を使用している場合は、AMD64 ビルドを使用してデプロイする必要があります。 4. F5 キーを押してアプリ 5 をデプロイします。Device Portal をオフにしてから、アプリを見つけられるようにもう一度オンにします (アプリ マニフェストを変更する場合にのみ必要です。残りの時間は、この手順を再デプロイしてスキップできます)。 6. ブラウザでプロバイダーの名前空間にアクセスすると、ブレークポイントがヒットされます。