This commit is contained in:
Tim Keosababian 2019-01-09 16:27:02 -08:00 коммит произвёл GitHub
Родитель 7b998cf109
Коммит aeb452583a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
60 изменённых файлов: 10787 добавлений и 0 удалений

6
App.config Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
</configuration>

117
DevicePortalTool.csproj Normal file
Просмотреть файл

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A1A457E2-2559-4400-9C37-9ABC3D8D5D35}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>DevicePortalTool</RootNamespace>
<AssemblyName>DevicePortalTool</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject>DevicePortalTool.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Source\AppOperation.cs" />
<Compile Include="Source\AppxManifest.cs" />
<Compile Include="Source\PackageHelper.cs" />
<Compile Include="Source\ParameterHelper.cs" />
<Compile Include="WindowsDevicePortalWrapper\AppDeployment.cs" />
<Compile Include="WindowsDevicePortalWrapper\ApplicationManager.cs" />
<Compile Include="WindowsDevicePortalWrapper\CertificateHandling.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\AppCrashDumpCollection.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\AppDeployment.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\AppFileExplorer.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\DeviceManager.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\Dns-Sd.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\DumpCollection.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\Etw.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\Networking.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\OsInformation.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\PerformanceData.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\Power.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\RemoteControl.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\TaskManager.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\WiFiManagement.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\WindowsErrorReporting.cs" />
<Compile Include="WindowsDevicePortalWrapper\Core\WindowsPerformanceRecorder.cs" />
<Compile Include="WindowsDevicePortalWrapper\DefaultDevicePortalConnection.cs" />
<Compile Include="WindowsDevicePortalWrapper\DeviceInfo.cs" />
<Compile Include="WindowsDevicePortalWrapper\DevicePortal.cs" />
<Compile Include="WindowsDevicePortalWrapper\Events\ApplicationInstallStatus.cs" />
<Compile Include="WindowsDevicePortalWrapper\Events\ConnectionStatus.cs" />
<Compile Include="WindowsDevicePortalWrapper\Events\WebSocketMessageReceivedEventArgs.cs" />
<Compile Include="WindowsDevicePortalWrapper\Exceptions\DevicePortalException.cs" />
<Compile Include="WindowsDevicePortalWrapper\HoloLens\HolographicOs.cs" />
<Compile Include="WindowsDevicePortalWrapper\HoloLens\HolographicPerception.cs" />
<Compile Include="WindowsDevicePortalWrapper\HoloLens\HolographicThermal.cs" />
<Compile Include="WindowsDevicePortalWrapper\HoloLens\MixedRealityCapture.cs" />
<Compile Include="WindowsDevicePortalWrapper\HoloLens\PerceptionSimulationPlayback.cs" />
<Compile Include="WindowsDevicePortalWrapper\HoloLens\PerceptionSimulationRecording.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpMultipartFileContent.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\HttpHeadersHelper.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\RequestHelpers.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\ResponseHelpers.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\RestDelete.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\RestGet.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\RestPost.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\RestPut.cs" />
<Compile Include="WindowsDevicePortalWrapper\HttpRest\WebSocket.cs" />
<Compile Include="WindowsDevicePortalWrapper\Interfaces\IDevicePortalConnection.cs" />
<Compile Include="WindowsDevicePortalWrapper\RestDelete.cs" />
<Compile Include="WindowsDevicePortalWrapper\RestGet.cs" />
<Compile Include="WindowsDevicePortalWrapper\RestPost.cs" />
<Compile Include="WindowsDevicePortalWrapper\RestPut.cs" />
<Compile Include="WindowsDevicePortalWrapper\UnvalidatedCert.cs" />
<Compile Include="WindowsDevicePortalWrapper\Utilities.cs" />
<Compile Include="WindowsDevicePortalWrapper\WebSocket.cs" />
<Compile Include="Source\Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

25
DevicePortalTool.sln Normal file
Просмотреть файл

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.168
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevicePortalTool", "DevicePortalTool.csproj", "{A1A457E2-2559-4400-9C37-9ABC3D8D5D35}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1A457E2-2559-4400-9C37-9ABC3D8D5D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1A457E2-2559-4400-9C37-9ABC3D8D5D35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1A457E2-2559-4400-9C37-9ABC3D8D5D35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1A457E2-2559-4400-9C37-9ABC3D8D5D35}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F79B122-3A33-472D-907F-74ABFAF87B99}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DevicePortalTool")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Unity Technologies")]
[assembly: AssemblyProduct("DevicePortalTool")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a1a457e2-2559-4400-9c37-9abc3d8d5d35")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

4
README.md Normal file
Просмотреть файл

@ -0,0 +1,4 @@
# DevicePortalTool
A command line program to execute Windows Device Portal REST APIs on a remote Windows 10 device, intended for use within an automated toolchain.
This program was developed using the [WindowsDevicePortalWrapper project](https://github.com/Microsoft/WindowsDevicePortalWrapper) provided by Microsoft.

734
Source/AppOperation.cs Normal file
Просмотреть файл

@ -0,0 +1,734 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Tools.WindowsDevicePortal;
using static Microsoft.Tools.WindowsDevicePortal.DevicePortal;
namespace DevicePortalTool
{
class AppOperation
{
public enum Operation
{
None,
ListInstalledApps,
Install,
Run,
Uninstall,
}
public static readonly string AvailableOperationsText =
"Supported App operations are the following:\n"
+ " list\n"
+ " install\n"
+ " run\n"
+ " uninstall\n"
;
public static readonly string AppOperationUsageText =
"Execute an Application operation on the remote device:\n"
+ " /op:<operation> [<operation arguments>]\n"
+ " Executes the app operation with operation-specific parameters\n"
+ "\n"
+ " /op:<operation> /?\n"
+ " Shows usage for specified operation\n"
+ "\n"
+ AvailableOperationsText
;
public static readonly string ListOpUsageText =
"Lists the apps currently installed on the device\n"
;
public static readonly string InstallOpUsageText =
"Installs the specified app package to the device and optionally runs the specified app\n"
+ " /appx:<path to Appx file> [/cert:<path to certificate file>] [/launch]\n"
+ " Installs the given AppX package and optionally launches the app on the remote device\n"
;
public static readonly string RunOpUsageText =
"Runs the specified app on the device\n"
+ " /package:<full package name> /aumid:<app's user model ID>\n"
+ " Launches the app installed specified by package and aumid on the remote device\n"
;
public static readonly string UninstallOpUsageText =
"Uninstall the specified app from the device\n"
+ " /package:<app package name>\n"
+ " Uninstall the app matching the specified full package name from the device\n"
;
public static readonly string ListAppsOpUsageTExt =
"Outputs a list of installed app packages on the device to stdout\n"
;
public static readonly string ParameterAppx = "appx";
public static readonly string ParameterCert = "cert";
public static readonly string ParameterLaunch = "launch";
public static readonly string parameterPackage = "package";
public static readonly string ParameterAumid = "aumid";
public static Operation OperationStringToEnum(string operationName)
{
if (String.IsNullOrWhiteSpace(operationName))
{
return Operation.None;
}
if (operationName.Equals("list", StringComparison.OrdinalIgnoreCase))
{
return Operation.ListInstalledApps;
}
if (operationName.Equals("install", StringComparison.OrdinalIgnoreCase))
{
return Operation.Install;
}
if (operationName.Equals("run", StringComparison.OrdinalIgnoreCase))
{
return Operation.Run;
}
if (operationName.Equals("uninstall", StringComparison.OrdinalIgnoreCase))
{
return Operation.Uninstall;
}
return Operation.None;
}
public static string OperationSpecificUsageText(Operation op)
{
switch (op)
{
case Operation.Install:
return InstallOpUsageText;
case Operation.Run:
return RunOpUsageText;
case Operation.Uninstall:
return UninstallOpUsageText;
default: return String.Empty;
}
}
public AppOperation(DevicePortal portal)
{
if (portal == null)
throw new System.ArgumentNullException("Must specify a valid DevicePortal object");
_portal = portal;
_runningOperation = new SemaphoreSlim(1, 1);
}
public void ExecuteOperation(Operation op, ParameterHelper parameters)
{
ExecuteOperationInternal(op, parameters);
}
public static bool TryExecuteApplicationOperation(DevicePortal portal, ParameterHelper parameters)
{
try
{
var appOperation = new AppOperation(portal);
appOperation.ExecuteOperation(AppOperation.OperationStringToEnum(parameters.GetParameterValue(ParameterHelper.Operation)), parameters);
}
catch (Exception)
{
return false;
}
return true;
}
// Class fields
private DevicePortal _portal;
private SemaphoreSlim _runningOperation;
// During "install" operation contains the Identity data extracted from appx Manifest
private AppPackageIdentity _installedAppId;
private bool _verbose;
private bool _helpFlag;
private ApplicationInstallStatusEventArgs _lastInstallStatus;
// Internal methods
private void ExecuteOperationInternal(Operation op, ParameterHelper parameters)
{
if (!_runningOperation.Wait(0))
{
throw new SemaphoreFullException("Existing operation still running");
}
_verbose = parameters.HasFlag(ParameterHelper.VerboseFlag);
_helpFlag = parameters.HasFlag(ParameterHelper.HelpFlag);
try
{
if (_helpFlag)
{
OutputOperationSpecificUsageText(op);
return;
}
switch (op)
{
case Operation.ListInstalledApps:
ExecuteListInstalledAppsOperation(parameters);
break;
case Operation.Install:
ExecuteInstallOperation(parameters);
// Optionally run app on the device if "/launch" switch used
if (parameters.HasFlag(ParameterLaunch))
{
ExecuteRunOperation(parameters);
}
break;
case Operation.Run:
ExecuteRunOperation(parameters);
break;
case Operation.Uninstall:
ExecuteUninstallyOperation(parameters);
break;
default:
OutputDefaultUsageText();
break;
}
}
catch (Exception ex)
{
var errorMessage = new StringBuilder();
if (_verbose)
{
errorMessage.Append(ex.ToString());
errorMessage.Append("\n\n");
}
else
{
string message = ex.Message;
if (String.IsNullOrWhiteSpace(message))
{
message = "No exception message";
}
errorMessage.Append("App operation '" + op.ToString() + "' failed: " + message + "\n\n");
}
var wdpEx = ex as DevicePortalException;
if (wdpEx != null)
{
errorMessage.Append("DevicePortal exception details: \n");
errorMessage.Append("RequestURI: " + wdpEx.RequestUri + "\n");
errorMessage.Append("Reason: " + wdpEx.Reason + "\n");
errorMessage.Append("HTTP Status: " + wdpEx.StatusCode.ToString() + "\n");
errorMessage.Append("\n");
}
Console.Out.WriteLine(errorMessage.ToString());
throw;
}
finally
{
_runningOperation.Release();
}
}
private void OutputOperationSpecificUsageText(Operation op)
{
switch (op)
{
case Operation.ListInstalledApps:
Console.Out.WriteLine(ListAppsOpUsageTExt);
break;
case Operation.Install:
Console.Out.WriteLine(InstallOpUsageText);
break;
case Operation.Run:
Console.Out.WriteLine(RunOpUsageText);
break;
case Operation.Uninstall:
Console.Out.WriteLine(UninstallOpUsageText);
break;
default:
OutputDefaultUsageText();
break;
}
}
private void OutputDefaultUsageText()
{
string errorMessage = "";
if (!_helpFlag)
{
errorMessage = "Invalid App operation\n";
}
Console.Out.WriteLine(errorMessage);
Console.Out.WriteLine(AppOperationUsageText);
}
private void ExecuteListInstalledAppsOperation(ParameterHelper parameters)
{
Task<AppPackages> packagesTask = _portal.GetInstalledAppPackagesAsync();
packagesTask.Wait();
var packages = packagesTask.Result;
Console.Out.WriteLine(packages.ToString());
}
private void ExecuteInstallOperation(ParameterHelper parameters)
{
// Parse app and dependency filenames from the parameters
string appxFile = parameters.GetParameterValue(ParameterAppx);
string certificate = parameters.GetParameterValue(ParameterCert);
_portal.AppInstallStatus += OnAppInstallStatus;
_lastInstallStatus = null;
try
{
if (!String.IsNullOrWhiteSpace(appxFile))
{
ExecuteInstallAppx(parameters, appxFile, certificate);
}
else
{
throw new System.ArgumentNullException("Must specify an appx file to install");
}
}
finally
{
_portal.AppInstallStatus -= OnAppInstallStatus;
_lastInstallStatus = null;
}
}
private void ExecuteInstallAppx(ParameterHelper parameters, string appxFile, string certificate)
{
if (_verbose)
{
Console.Out.WriteLine("Starting Appx installation...");
}
var file = new FileInfo(Path.GetFullPath(appxFile));
if (!file.Exists)
{
throw new System.IO.FileNotFoundException("Specified appx file '" + appxFile + "' wasn't found");
}
if (!String.IsNullOrWhiteSpace(certificate))
{
var certFile = new FileInfo(certificate);
if (!certFile.Exists)
{
throw new System.IO.FileNotFoundException("Specified certificate file '" + certFile + "' wasn't found");
}
certificate = certFile.FullName;
}
else
{
// Must pass in null instead of empty string if certificate is omitted
certificate = null;
}
// Parse the AppxManfest contained in the Appx file to extract the package name, dependencies, AppID, etc.
AppxManifest appxData;
try
{
appxData = AppxManifest.Get(appxFile);
}
catch (Exception ex)
{
Console.Out.WriteLine("Failed to parse Appx manifest: " + ex.Message);
throw;
}
if (!appxData.IsValid)
{
throw new System.ArgumentException("Specified Appx '" + appxFile + "' contains an invalid AppxManifest");
}
// Construct an "identity" object from the AppxManifest data which can be referenced later to launch the installed app
var appIdentity = new AppPackageIdentity(appxData);
// Query for app packages already installed on the device and uninstall them if necessary
// NOTE: Check for any uninstall any package matching this appx PackageName and Publisher to
// ensure a clean install of the new build
List<PackageInfo> matchingPackages;
if (TryRetrieveInstalledPackages(appIdentity, out matchingPackages) && matchingPackages.Count() > 0)
{
if (_verbose)
{
Console.Out.WriteLine("Uninstalling previous app..");
}
foreach (var package in matchingPackages)
{
try
{
if (_verbose)
{
Console.Out.WriteLine("Uninstalling package: " + package.FullName);
}
Task uninstallTask = _portal.UninstallApplicationAsync(package.FullName);
}
catch (AggregateException ex)
{
// NOTE: We really shouldn't continue with installation if we failed to remove a previous version of the app.
// If a version of the app remains on the device, the Install API will NOT replace it but still reports "success",
// meaning the user could be running old code and not know it. A hard fail is the only way to ensure this doesn't happen.
Console.Out.WriteLine("Uninstall of package '" + package.FullName + "' failed: " + ex.InnerException.Message);
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
}
if (_verbose)
{
Console.Out.WriteLine("Finished uninstalling previous app packages");
}
}
Task installTask = _portal.InstallApplicationAsync(null, file.FullName, appxData.Dependencies, certificate, 500, 1, false);
try
{
installTask.Wait();
Console.Out.WriteLine("Installation completed successfully");
}
catch (AggregateException ex)
{
Console.Out.WriteLine("Installation of Appx failed!");
HandleInstallOperationException(ex);
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
// Save AppIdentity to field after successful installation
_installedAppId = appIdentity;
// If the app Identity is "complete" we have the FullPackageName and AUMID parameters, so we'll
// add them to our parameter set to later launch the app; no need to query package info from the device
if (_installedAppId.CompleteIdentity)
{
parameters.AddOrUpdateParameter(parameterPackage, _installedAppId.PackageFullName);
parameters.AddOrUpdateParameter(ParameterAumid, _installedAppId.LaunchId);
}
}
private void HandleInstallOperationException(AggregateException ex)
{
if (ex == null) return;
// If available, log the last status update before the exception
if (_lastInstallStatus != null)
{
string errorMessage;
errorMessage = String.Format("Installation failed in phase {0} - last status: {1}", _lastInstallStatus.Phase, _lastInstallStatus.Message);
if (_verbose)
{
Console.Out.WriteLine(errorMessage);
}
}
// If multiple exception were encountered we want to log each of them
// The "main" exception will be handled by the top-level ExecuteOperation method
if (ex.InnerExceptions.Count > 1)
{
var sb = new StringBuilder();
sb.Append("Multiple exception were thrown during operation:\n");
foreach (var item in ex.InnerExceptions)
{
sb.Append(" " + item.Message + "\n");
}
if (_verbose)
{
Console.Out.WriteLine(sb.ToString());
}
}
}
private void ExecuteRunOperation(ParameterHelper parameters)
{
string packageName = parameters.GetParameterValue(parameterPackage);
string launchId = parameters.GetParameterValue(ParameterAumid);
// These parameters are required unless we just performed and install operation
if (String.IsNullOrWhiteSpace(packageName) && _installedAppId == null)
{
throw new System.ArgumentException("Must provide full name of app package to launch");
}
if (String.IsNullOrWhiteSpace(launchId) && _installedAppId == null)
{
throw new System.ArgumentException("Must provide the AUMID of the app to launch from the specified package");
}
// Just installed an app but unable to retrieve package's full name and/or family name
// So we'll try to query the values from the remote device
if (_installedAppId != null && !_installedAppId.CompleteIdentity)
{
if (!TryRetrievePackageNameAndLaunchIdFromDevice(_installedAppId, 4, out packageName, out launchId))
{
throw new System.Exception("Failed to retrieve necessary app package info from the remote device; cannot launch app");
}
}
else if (_installedAppId != null)
{
// If we just installed the app, must wait until it's fully installed/registered with the OS, otherwise launch operation will fail
WaitUnilAppIsFullyInstalled(packageName);
}
Task launchTask = _portal.LaunchApplicationAsync(launchId, packageName);
try
{
launchTask.Wait();
Console.WriteLine("Application launched");
}
catch (AggregateException ex)
{
Console.Out.WriteLine("App launch failed!");
HandleInstallOperationException(ex);
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
}
private void ExecuteUninstallyOperation(ParameterHelper parameters)
{
string package = parameters.GetParameterValue(parameterPackage);
if (String.IsNullOrWhiteSpace(package))
{
throw new System.ArgumentException("Must provide full name of app package to uninstall");
}
Task installTask = _portal.UninstallApplicationAsync(package);
try
{
installTask.Wait();
Console.Out.WriteLine("Uninstall completed successfully");
}
catch (AggregateException ex)
{
Console.Out.WriteLine("Uninstall of app failed!");
HandleInstallOperationException(ex);
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
}
private void OnAppInstallStatus(object sender, ApplicationInstallStatusEventArgs args)
{
if (_verbose)
{
Console.Out.WriteLine(args.Message);
}
_lastInstallStatus = args;
}
private bool TryRetrievePackageNameAndLaunchIdFromDevice(AppPackageIdentity packageId, int numAttempts, out string packageFullName, out string launchId)
{
// Already have the info locally and don't need to query from device
if (packageId.CompleteIdentity)
{
packageFullName = packageId.PackageFullName;
launchId = packageId.LaunchId;
return true;
}
if (_verbose)
{
Console.Out.WriteLine("Attempting to query PackageFullName and AUMID from remote device");
}
packageFullName = String.Empty;
launchId = String.Empty;
bool successful = false;
while (numAttempts > 0 && !successful)
{
numAttempts--;
try
{
Task<AppPackages> packagesTask = _portal.GetInstalledAppPackagesAsync();
packagesTask.Wait();
// This basic query should provide the matching package in most cases
// -PackageName must exactly match
// -Publisher must exactly match
// -AUMID (called "AppId" in PackageInfo class) must contain our AppId value (registered app entry point)
// -Version string must exactly match
var matchingPackages =
(from package in packagesTask.Result.Packages
where
package.Name == packageId.PackageName &&
package.Publisher == package.Publisher &&
package.AppId.Contains(package.AppId) && // AppId from PackageInfo is actually full AUMID
package.Version.ToString() == packageId.Version
select package).ToList();
PackageInfo matchingPackage = null;
if (matchingPackages.Count() == 0)
{
if (_verbose)
{
Console.Out.Write("Failed to find '" + packageId.PackageName + "' package installed on the device...");
Console.Out.WriteLine((numAttempts > 0) ? "trying again" : "giving up");
}
}
else if (matchingPackages.Count() > 1)
{
// It's technically possible for the above query to return multiple packages, in which case we need to
// disambiguate using the optional package identifiers (CPU architecture and ResourceId).
// Since PackageInfo doesn't provide this fields directly, need to split PackageFullName
// into component its component parts =>
// [0] PackageName
// [1] Version
// [2] CPU architecture
// [3] ResourceId (if present)
// [4] Publisher name hash
foreach (var package in matchingPackages)
{
var nameParts = package.FullName.Split(new char[] { '_' }, 5);
if (nameParts.Length < 5) continue;
// ResoruceId will be an empty string if not present, which should match our manifest data
if (nameParts[2] == packageId.CpuArchitecture && nameParts[3] == packageId.ResourceId)
{
matchingPackage = package;
break;
}
}
}
else matchingPackage = matchingPackages.First();
if (matchingPackage != null)
{
packageFullName = matchingPackage.FullName;
launchId = matchingPackage.AppId;
successful = true;
}
// Query attempt may failed because it a few seconds before newly installed apps are reported
// So if we have more attempts then wait a bit before trying again
if (numAttempts > 0 && !successful)
{
Thread.Sleep(2000);
}
}
catch (Exception ex)
{
if (_verbose)
{
Console.Out.WriteLine("Failed to acquire list of installed apps from device: " + ex.Message);
}
}
}
if (_verbose)
{
if (!successful)
{
Console.Out.WriteLine("Failed to retrieve FullPackageName and AUMID from remote device");
}
else Console.Out.WriteLine("Successfully retrieved FullPackageName and AUMID from remote device");
}
return successful;
}
private bool TryRetrieveInstalledPackages(AppPackageIdentity packageId, out List<PackageInfo> matchingPackages)
{
if (_verbose)
{
Console.Out.WriteLine("Attempting to query installed packages matching app");
}
matchingPackages = null;
bool successful = false;
try
{
Task<AppPackages> packagesTask = _portal.GetInstalledAppPackagesAsync();
packagesTask.Wait();
// We want to find all packages that *loosely* match our appx in case something like
// architecture or configuration changed
matchingPackages =
(from package in packagesTask.Result.Packages
where
package.Name == packageId.PackageName &&
package.Publisher == package.Publisher
select package).ToList();
successful = true;
}
catch (Exception ex)
{
if (_verbose)
{
Console.Out.WriteLine("Failed to acquire list of installed apps from device: " + ex.Message);
}
}
if (_verbose)
{
if (!successful)
{
Console.Out.WriteLine("Failed to retrieve installed packages from the device");
}
else Console.Out.WriteLine("Successfully retrieved installed packages matching app from the device");
}
return successful;
}
private void WaitUnilAppIsFullyInstalled(string fullPackageName)
{
PackageInfo appPackage = null;
int numAttempts = 5;
// DevicePortal API has an annoying quirk in which the "install" call will return before the app is fully ready
// on the remote device. It takes a few extra seconds before Windows can launch it, and attempting to launch the app before
// it's ready results in an error. So, wait until we can successfully query the package from the device using the "list"
// operation, once we see it in the returned results we know the app is ready and can be launched.
do
{
Thread.Sleep(3000);
try
{
Task<AppPackages> packagesTask = _portal.GetInstalledAppPackagesAsync();
packagesTask.Wait();
appPackage = packagesTask.Result.Packages.FirstOrDefault(package => (package.FullName == fullPackageName));
}
catch {; }
numAttempts--;
} while (appPackage == null && numAttempts > 0);
}
}
}

203
Source/AppxManifest.cs Normal file
Просмотреть файл

@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Xml.Linq;
namespace DevicePortalTool
{
public class Dependency
{
public string Name { get; private set; }
public Version MinVersion { get; private set; }
public Dependency(string name, string minVersion)
{
Name = name;
MinVersion = new Version(minVersion);
}
}
public class AppxManifest
{
private static Dictionary<string, AppxManifest> ManifestCache = new Dictionary<string, AppxManifest>();
public string AppxPath { get; private set; }
public Guid PhoneProductId { get; private set; }
public string PackageName { get; private set; }
public string Publisher { get; private set; }
public string Version { get; private set; }
public string AppId { get; private set; }
public List<string> Dependencies { get; private set; }
public string CpuArchitecture { get; private set; }
public string ResourceId { get; private set; }
public bool IsFramework { get; private set; }
public bool IsDependency { get; private set; }
public bool IsValid { get; private set; }
public static AppxManifest Get(string appxPath)
{
if (!ManifestCache.ContainsKey(appxPath))
{
return new AppxManifest(appxPath); // Constructor will add it to manifest cache midway through construction.
}
return ManifestCache[appxPath];
}
public static AppxManifest Get(string appxPath, string extractedPath)
{
if (!ManifestCache.ContainsKey(appxPath))
{
return new AppxManifest(appxPath, extractedPath); // Constructor will add it to manifest cache midway through construction.
}
return ManifestCache[appxPath];
}
private AppxManifest(string appxPath)
{
AppxPath = appxPath;
var extension = Path.GetExtension(appxPath);
if (string.Equals(extension, ".xml", StringComparison.OrdinalIgnoreCase))
{
Parse(XDocument.Load(appxPath));
}
else
{
using (var archive = System.IO.Compression.ZipFile.OpenRead(appxPath))
{
var manifestStream = archive.GetEntry("AppxManifest.xml").Open();
Parse(XDocument.Load(manifestStream));
}
}
}
private AppxManifest(string appxPath, string extractedPath)
{
AppxPath = appxPath;
using (var stream = new StreamReader(Path.Combine(extractedPath, "AppXManifest.xml")))
{
Parse(XDocument.Load(stream));
}
}
private void Parse(XDocument manifest)
{
ManifestCache.Add(AppxPath, this);
string namezspace = "http://schemas.microsoft.com/appx/manifest/foundation/windows10";
try
{
var phoneIdentity = manifest.Root.Element(XName.Get("PhoneIdentity", "http://schemas.microsoft.com/appx/2014/phone/manifest"));
if (phoneIdentity != null)
{
PhoneProductId = Guid.Parse(phoneIdentity.Attribute("PhoneProductId").Value);
}
var identity = manifest.Root.Element(XName.Get("Identity", namezspace));
if (identity == null)
{
throw new ArgumentNullException("identity");
}
PackageName = identity.Attribute("Name")?.Value;
if (PackageName == null)
{
throw new ArgumentNullException("packageName");
}
Publisher = identity.Attribute("Publisher")?.Value;
if (Publisher == null)
{
throw new ArgumentNullException("publisher");
}
Version = identity.Attribute("Version")?.Value;
if (Version == null)
{
throw new ArgumentNullException("version");
}
var applications = manifest.Root.Element(XName.Get("Applications", namezspace));
if (applications != null)
{
var applicationElement = applications.Element(XName.Get("Application", namezspace));
if (applicationElement != null)
{
AppId = applicationElement.Attribute(XName.Get("Id"))?.Value;
if (AppId == null)
{
throw new ArgumentNullException("appid");
}
}
}
// Optional identity attributes
CpuArchitecture = identity.Attribute("ProcessorArchitecture")?.Value ?? "neutral";
ResourceId = identity.Attribute("ResourceId")?.Value ?? "";
var properties = manifest.Root.Element(XName.Get("Properties", namezspace));
var frameworkAttribute = properties.Element(XName.Get("Framework", namezspace));
IsFramework = frameworkAttribute != null && frameworkAttribute.Value.Equals("True", StringComparison.InvariantCultureIgnoreCase);
}
catch // This will happen if we happen to try to parse non-UWP app manifest
{
IsValid = false;
return;
}
IsValid = true;
var dependencies = manifest.Root.Element(XName.Get("Dependencies", namezspace));
Dependencies = new List<string>();
if (dependencies != null)
{
foreach (var dependencyInfo in dependencies.Descendants())
{
var dependency = new Dependency(dependencyInfo.Attribute("Name").Value, dependencyInfo.Attribute("MinVersion").Value);
AddDependency(dependency, AppxPath);
}
}
}
private void AddDependency(Dependency dependency, string appxPath)
{
var appxFolder = Path.GetDirectoryName(appxPath);
var dependenciesFolder = Path.Combine(appxFolder, "Dependencies");
var searchFolders = new string[] { Path.Combine(dependenciesFolder, CpuArchitecture), dependenciesFolder, appxFolder };
foreach (var searchFolder in searchFolders)
{
if (!Directory.Exists(searchFolder))
continue;
foreach (var file in Directory.GetFiles(searchFolder, "*.appx"))
{
if (AddDependenciesIfMatches(file, dependency))
{
return;
}
}
}
}
private bool AddDependenciesIfMatches(string path, Dependency dependency)
{
var dependencyManifest = Get(path);
if (dependencyManifest.IsValid && dependencyManifest.PackageName.Equals(dependency.Name, StringComparison.InvariantCultureIgnoreCase))
{
Dependencies.AddRange(dependencyManifest.Dependencies);
Dependencies.Add(path);
dependencyManifest.IsDependency = true;
return true;
}
return false;
}
}
}

206
Source/PackageHelper.cs Normal file
Просмотреть файл

@ -0,0 +1,206 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace DevicePortalTool
{
public class AppPackageIdentity
{
public string PackageName { get; private set; }
public string AppId { get; private set; }
public string Publisher { get; private set; }
public string Version { get; private set; }
public Version VersionValue { get; private set; }
public string CpuArchitecture { get; private set; }
public UInt32 CpuArchitectureValue { get; private set; }
public string ResourceId { get; private set; }
public string PackageFullName { get; private set; }
public string PackageFamilyName { get; private set; }
public string LaunchId { get; private set; }
public bool CompleteIdentity { get; private set; }
public AppPackageIdentity(AppxManifest appxData)
{
InitializeInternal(appxData.PackageName, appxData.AppId, appxData.Publisher, appxData.Version, appxData.CpuArchitecture, appxData.ResourceId);
}
public AppPackageIdentity(string packageName, string appId, string publisher, string version)
{
InitializeInternal(packageName, appId, publisher, version, "neutral", "");
}
public AppPackageIdentity(string packageName, string appId, string publisher, string version, string cpuArchitecture, string resourceId)
{
InitializeInternal(packageName, appId, publisher, version, cpuArchitecture, resourceId);
}
private void InitializeInternal(string packageName, string appId, string publisher, string version, string cpuArchitecture, string resourceId)
{
if (String.IsNullOrWhiteSpace(packageName))
throw new System.ArgumentNullException("PackageName is invalid");
if (String.IsNullOrWhiteSpace(appId))
throw new System.ArgumentNullException("AppId is invalid");
if (String.IsNullOrWhiteSpace(publisher))
throw new System.ArgumentNullException("Publisher is invalid");
if (String.IsNullOrWhiteSpace(version))
throw new System.ArgumentNullException("Version is invalid");
if (cpuArchitecture == null)
cpuArchitecture = "neutral";
if (resourceId == null)
resourceId = "";
this.PackageName = packageName;
this.AppId = appId;
this.Publisher = publisher;
this.Version = version;
this.CpuArchitecture = cpuArchitecture;
this.ResourceId = resourceId;
System.Version verObj;
if (System.Version.TryParse(version, out verObj))
{
this.VersionValue = verObj;
}
this.CpuArchitectureValue = PackageHelper.ProcessorArchitectureStringToEnum(cpuArchitecture);
this.PackageFamilyName = PackageHelper.TryGetPackageFamilyName(packageName, publisher);
this.PackageFullName = PackageHelper.TryGetPackageFullName(packageName, publisher, this.VersionValue, this.CpuArchitectureValue);
// If PackageFamilyName was successfully retrieved, construct the "LaunchId" (AUMID)
if (!String.IsNullOrWhiteSpace(this.PackageFamilyName))
{
this.LaunchId = this.PackageFamilyName + "!" + this.AppId;
}
else this.LaunchId = String.Empty;
// If we successfully retrieve FamilyName and FullName then we have package identity is "complete"
// Otherwise one or more properties are missing or invalid
if (!String.IsNullOrWhiteSpace(this.PackageFullName) && !String.IsNullOrWhiteSpace(this.PackageFamilyName))
{
this.CompleteIdentity = true;
}
}
}
internal class PackageHelper
{
public static UInt32 ProcessorArchitectureStringToEnum(string architecture)
{
architecture = architecture.ToLowerInvariant();
switch (architecture)
{
case "x86": return (UInt32)APPX_PACKAGE_ARCHITECTURE.APPX_PACKAGE_ARCHITECTURE_X86;
case "x64": return (UInt32)APPX_PACKAGE_ARCHITECTURE.APPX_PACKAGE_ARCHITECTURE_X64;
case "arm": return (UInt32)APPX_PACKAGE_ARCHITECTURE.APPX_PACKAGE_ARCHITECTURE_ARM;
case "arm64": return (UInt32)APPX_PACKAGE_ARCHITECTURE.APPX_PACKAGE_ARCHITECTURE_ARM64;
}
return (UInt32)APPX_PACKAGE_ARCHITECTURE.APPX_PACKAGE_ARCHITECTURE_NEUTRAL;
}
public static string TryGetPackageFamilyName(string name, string publisherId)
{
string packageFamilyName = String.Empty;
try
{
var packageId = new PACKAGE_ID
{
name = name,
publisher = publisherId,
};
uint packageFamilyNameLength = 0;
// First get the length of the Package Name -> Pass NULL as Output Buffer
if (PackageFamilyNameFromId(packageId, ref packageFamilyNameLength, null) == 122) // ERROR_INSUFFICIENT_BUFFER
{
var packageFamilyNameBuilder = new StringBuilder((int)packageFamilyNameLength);
if (PackageFamilyNameFromId(packageId, ref packageFamilyNameLength, packageFamilyNameBuilder) == 0)
{
packageFamilyName = packageFamilyNameBuilder.ToString();
}
}
}
catch {; }
return packageFamilyName;
}
public static string TryGetPackageFullName(string name, string publisherId, Version appVersion, UInt32 cpuArchitecture)
{
string packageFullName = String.Empty;
try
{
var major = Convert.ToUInt16(appVersion.Major);
var minor = Convert.ToUInt16(appVersion.Minor);
var build = Convert.ToUInt16(appVersion.Build);
var rev = Convert.ToUInt16(appVersion.Revision);
UInt64 packedVersion =
Convert.ToUInt64(rev) << 0 |
Convert.ToUInt64(build) << 16 |
Convert.ToUInt64(minor) << 32 |
Convert.ToUInt64(major) << 48;
var packageId = new PACKAGE_ID
{
name = name,
publisher = publisherId,
processorArchitecture = cpuArchitecture,
version = packedVersion
};
uint packageFullNameLength = 0;
// First get the length of the Package Name -> Pass NULL as Output Buffer
if (PackageFullNameFromId(packageId, ref packageFullNameLength, null) == 122) // ERROR_INSUFFICIENT_BUFFER
{
var packageFullNameBuilder = new StringBuilder((int)packageFullNameLength);
if (PackageFullNameFromId(packageId, ref packageFullNameLength, packageFullNameBuilder) == 0)
{
packageFullName = packageFullNameBuilder.ToString();
}
}
}
catch {; }
return packageFullName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
internal class PACKAGE_ID
{
public UInt32 reserved;
public UInt32 processorArchitecture;
public UInt64 version;
public string name;
public string publisher;
public string resourceId;
public string publisherId;
};
enum APPX_PACKAGE_ARCHITECTURE
{
APPX_PACKAGE_ARCHITECTURE_X86 = 0,
APPX_PACKAGE_ARCHITECTURE_ARM = 5,
APPX_PACKAGE_ARCHITECTURE_X64 = 9,
APPX_PACKAGE_ARCHITECTURE_NEUTRAL = 11,
APPX_PACKAGE_ARCHITECTURE_ARM64 = 12
};
// NOTE: These APIs are only available in Windows 8 and later, and If called on Windows 7 we expect an EntryPointNotFoundException will be thrown.
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern uint PackageFamilyNameFromId(PACKAGE_ID packageId, ref uint packageFullNameLength, StringBuilder packageFullName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern uint PackageFullNameFromId(PACKAGE_ID packageId, ref uint packageFullNameLength, StringBuilder packageFullName);
}
}

123
Source/ParameterHelper.cs Normal file
Просмотреть файл

@ -0,0 +1,123 @@
//----------------------------------------------------------------------------------------------
// <copyright file="ParameterHelper.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
// <copyright file="ParameterHelper.cs" company="Unity Technologies">
// Modified under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace DevicePortalTool
{
public class ParameterHelper
{
public static readonly string HelpFlag = "?";
public static readonly string VerboseFlag = "v";
public static readonly string Operation = "op";
public static readonly string DeviceIpAddress = "ip";
public static readonly string WdpUser = "user";
public static readonly string WdpPassword = "pwd";
public static readonly string StdinCredentials = "stdincred";
public OpperationArea Area { get; private set; } = OpperationArea.None;
private Dictionary<string, string> parameters = new Dictionary<string, string>();
private List<string> flags = new List<string>();
public void AddParameter(string name, string value)
{
this.parameters.Add(name, value);
}
public void AddOrUpdateParameter(string name, string value)
{
if (this.parameters.ContainsKey(name))
{
this.parameters[name] = value;
}
else this.parameters.Add(name, value);
}
public string GetParameterValue(string key)
{
if (this.parameters.ContainsKey(key))
{
return this.parameters[key];
}
else
{
return String.Empty;
}
}
public bool HasParameter(string key)
{
return this.parameters.ContainsKey(key);
}
public bool HasFlag(string flag)
{
return this.flags.Contains(flag);
}
public void ParseCommandLine(string[] args)
{
// If nothing specified then add "help" flag to display usage
if (args.Length == 0)
{
this.flags.Add(ParameterHelper.HelpFlag);
return;
}
// The operation area must always be the 1st parameter
// NOTE: In C# args[0] is program name
Area = Program.OperationAreaStringToEnum(args[0]);
// Parse the command line args
for (int i = 0; i < args.Length; ++i)
{
string arg = args[i];
if (!arg.StartsWith("/") && !arg.StartsWith("-"))
{
// We expect the first parameter to be the "area" which isn't prefixed with a slash
if (i == 0) continue;
throw new Exception(string.Format("Unrecognized argument: {0}", arg));
}
arg = arg.Substring(1);
int valueIndex = arg.IndexOf(':');
string value = null;
// If this contains a colon, separate it into the param and value. Otherwise add it as a flag
if (valueIndex > 0)
{
value = arg.Substring(valueIndex + 1);
arg = arg.Substring(0, valueIndex);
this.parameters.Add(arg.ToLowerInvariant(), value);
}
else
{
this.flags.Add(arg.ToLowerInvariant());
}
}
}
public static string EncodeBase64(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
public static string DecodeBase64(string base64Text)
{
var base64EncodedBytes = System.Convert.FromBase64String(base64Text);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
}
}

342
Source/Program.cs Normal file
Просмотреть файл

@ -0,0 +1,342 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Tools.WindowsDevicePortal;
namespace DevicePortalTool
{
public enum OpperationArea
{
None,
Application,
};
public enum ProgramErrorCodes : int
{
Success = 0,
SuccessHelp = 1,
OperationFailed = -1,
InvalidParameters = -2,
AuthenticationFailed = -3,
ConnectionFailed = -4,
UnexpectedError = -5,
};
class Program
{
private static readonly string GeneralUsageMessage =
"Executes a Windows DevicePortal (WDP) operation on a remote Windows 10 device\n"
+ "\n"
+ "Usage:\n"
+ "DevicePortalTool <operation area> /op:<operation type> [operation parameters]] /ip:<device IP address> [/stdincred | /user:WDP username /pwd:<WDP password] [/v]\n"
+ " <operation area> - must be one of the following values:\n"
+ " app - execute an Application related operation\n"
+ " /op - area specific operation value with additional parameters\n"
+ " /ip - IP address of remote device to operate on\n"
+ " /stdincred - WDP credentials are read from stdin stream instead of command line (no prompts)\n"
+ " credentials read as two separate lines of Base64 encoded strings (user name then password)\n"
+ " /user - Name of WDP user on the device\n"
+ " /pwd - Password of WDP user on the device\n"
+ " /v - Verbose logging output\n"
+ "\n"
+ "DevicePortalTool <operation area> /?\n"
+ " Display area specific usage\n"
+ "\n"
+ "DevicePortalTool /?\n"
+ " Display this usage\n"
;
public static OpperationArea OperationAreaStringToEnum(string areaName)
{
if (String.IsNullOrWhiteSpace(areaName))
{
return OpperationArea.None;
}
if (areaName.Equals("app", StringComparison.OrdinalIgnoreCase))
{
return OpperationArea.Application;
}
return OpperationArea.None;
}
public static int Main(string[] args)
{
var resultCode = ExecuteMain(args);
Console.Out.WriteLine("ExitCode: " + (int)resultCode + " (" + resultCode.ToString() + ")");
return (int)resultCode;
}
private static ProgramErrorCodes ExecuteMain(string[] args)
{
ParameterHelper parameters;
ProgramErrorCodes errorCode;
Uri targetDevice = null;
bool helpFlag;
bool verbose;
errorCode = ParseParametersAndPerformBasicValidation(args, out parameters, out targetDevice, out verbose, out helpFlag);
if (errorCode != ProgramErrorCodes.Success)
return errorCode;
if (!helpFlag && parameters.HasFlag(ParameterHelper.StdinCredentials))
{
try
{
string username;
string password;
if (!TryReadCredentialsFromStdin(out username, out password))
{
Console.Out.WriteLine("Failed to read WDP credentials from stdin");
Console.Out.WriteLine();
return ProgramErrorCodes.InvalidParameters;
}
parameters.AddOrUpdateParameter(ParameterHelper.WdpUser, username);
parameters.AddOrUpdateParameter(ParameterHelper.WdpPassword, password);
}
catch (Exception ex)
{
Console.Out.WriteLine("Fatal error reading WDP credentials from stdin: " + ex.Message);
Console.Out.WriteLine();
return ProgramErrorCodes.UnexpectedError;
}
}
DevicePortal portal;
if (!helpFlag)
{
try
{
if (!TryOpenDevicePortalConnection(targetDevice, parameters, out portal))
{
if (portal != null && portal.ConnectionHttpStatusCode == System.Net.HttpStatusCode.Unauthorized)
{
Console.Out.WriteLine("Aborting due to failed authentication");
Console.Out.WriteLine();
return ProgramErrorCodes.AuthenticationFailed;
}
else
{
Console.Out.WriteLine("Aborting due to failed connection with remote device");
Console.Out.WriteLine();
return ProgramErrorCodes.ConnectionFailed;
}
}
}
catch (Exception ex)
{
Console.Out.WriteLine("Fatal error initializing DevicePortal: " + ex.Message);
Console.Out.WriteLine();
return ProgramErrorCodes.UnexpectedError;
}
}
else
{
// Need to create a dummy DevicePortal so can safely call into area-specific operation processor
// The processor can then output operation specific help info
portal = new DevicePortal(new DefaultDevicePortalConnection("http://0.0.0.0", "", ""));
}
var operationResult = ProgramErrorCodes.Success;
switch (parameters.Area)
{
case OpperationArea.Application:
if (!AppOperation.TryExecuteApplicationOperation(portal, parameters))
{
operationResult = ProgramErrorCodes.OperationFailed;
}
break;
default:
// This case should have already been handled by a parameter check above
operationResult = ProgramErrorCodes.InvalidParameters;
break;
}
// If successful but help switch was set return a different resultCode
if (operationResult == ProgramErrorCodes.Success && helpFlag)
{
operationResult = ProgramErrorCodes.SuccessHelp;
}
// If a debugger is attached, don't close but instead loop here until
// closed.
while (System.Diagnostics.Debugger.IsAttached)
{
System.Threading.Thread.Sleep(0);
}
return operationResult;
}
private static ProgramErrorCodes ParseParametersAndPerformBasicValidation(string[] args, out ParameterHelper parameters, out Uri targetDevice, out bool verbose, out bool helpFlag)
{
parameters = new ParameterHelper();
targetDevice = null;
verbose = false;
helpFlag = false;
try
{
parameters.ParseCommandLine(args);
}
catch (Exception ex)
{
Console.Out.WriteLine("Fatal error parsing command arguments: " + ex.Message);
Console.Out.WriteLine();
return ProgramErrorCodes.UnexpectedError;
}
verbose = parameters.HasFlag(ParameterHelper.VerboseFlag);
helpFlag = parameters.HasFlag(ParameterHelper.HelpFlag);
if (parameters.Area == OpperationArea.None)
{
if (helpFlag)
{
Console.WriteLine(GeneralUsageMessage);
Console.WriteLine();
}
else
{
Console.Out.WriteLine("Invalid parameters: Must specify a valid operation area as the first parameter");
Console.Out.WriteLine();
}
return ProgramErrorCodes.InvalidParameters;
}
if (!helpFlag)
{
string address = parameters.GetParameterValue(ParameterHelper.DeviceIpAddress);
string invalidReason = null;
if (String.IsNullOrWhiteSpace(address))
{
invalidReason = "Must specify IP address of remote Windows Device Portal with /ip switch";
}
else if (!Uri.TryCreate(address, UriKind.Absolute, out targetDevice))
{
invalidReason = "isn't a proper URI";
}
else if (targetDevice.Scheme != "http" && targetDevice.Scheme != "https")
{
invalidReason = "must specify http or https scheme";
}
else if (targetDevice.IsDefaultPort)
{
invalidReason = "doesn't specify a WDP port number";
}
if (invalidReason != null)
{
Console.Out.WriteLine("Invalid parameters: IP address '" + address + "'; " + invalidReason);
Console.Out.WriteLine("IP address must be in the following format: http(s)://<host address>:<WDP port>");
Console.Out.WriteLine("The correct address string can be found under 'Developer' settings on the host device");
Console.Out.WriteLine();
return ProgramErrorCodes.InvalidParameters;
}
}
if (!helpFlag && parameters.HasFlag(ParameterHelper.StdinCredentials))
{
if (parameters.HasParameter(ParameterHelper.WdpUser) || parameters.HasParameter(ParameterHelper.WdpPassword))
{
Console.Out.WriteLine("Invalid parameters: Cannot pass WDP credentials on command line (/user /pwd) when using /stdincred switch; use one method or the other");
Console.Out.WriteLine();
return ProgramErrorCodes.InvalidParameters;
}
}
return ProgramErrorCodes.Success;
}
private static bool TryReadCredentialsFromStdin(out string userName, out string password)
{
userName = String.Empty;
password = String.Empty;
// We expect username/password (base64 encoded) to be passed "piped" in from a calling process
// So there's no prompt and we'll fail if don't read anything after a few seconds
{
var readTask = Console.In.ReadLineAsync();
if (!readTask.Wait(5000))
return false;
userName = ParameterHelper.DecodeBase64(readTask.Result);
}
{
var readTask = Console.In.ReadLineAsync();
if (!readTask.Wait(5000))
return false;
password = ParameterHelper.DecodeBase64(readTask.Result);
}
return true;
}
private static bool TryOpenDevicePortalConnection(Uri targetDevice, ParameterHelper parameters, out DevicePortal portal)
{
string userName = parameters.GetParameterValue(ParameterHelper.WdpUser);
string password = parameters.GetParameterValue(ParameterHelper.WdpPassword);
bool success = true;
portal = new DevicePortal(new DefaultDevicePortalConnection(targetDevice.ToString(), userName, password));
try
{
// We need to handle this event otherwise remote connection will be rejected if
// device isn't trusted by local PC
portal.UnvalidatedCert += DoCertValidation;
var connectTask = portal.ConnectAsync(updateConnection: false);
connectTask.Wait();
if (portal.ConnectionHttpStatusCode != System.Net.HttpStatusCode.OK)
{
if (portal.ConnectionHttpStatusCode == System.Net.HttpStatusCode.Unauthorized)
{
throw new System.UnauthorizedAccessException("Connection rejected due to missing/incorrect credentials; specify valid credentials with /user and /pwd switches");
}
else if (!string.IsNullOrEmpty(portal.ConnectionFailedDescription))
{
throw new System.OperationCanceledException(string.Format("WDP connection failed (HTTP {0}) : {1}", (int)portal.ConnectionHttpStatusCode, portal.ConnectionFailedDescription));
}
else
{
throw new System.OperationCanceledException(string.Format("WDP connection failed (HTTP {0}) : no additional information", (int)portal.ConnectionHttpStatusCode));
}
}
}
catch (Exception ex)
{
bool verbose = parameters.HasFlag(ParameterHelper.VerboseFlag);
Console.Out.WriteLine("Failed to open DevicePortal connection to '" + portal.Address + "'\n" + (verbose ? ex.ToString() : ex.Message));
success = false;
}
return success;
}
private static bool DoCertValidation(DevicePortal sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// We're not validating the remote host
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,30 @@
//----------------------------------------------------------------------------------------------
// <copyright file="AppDeployment.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// MOCK implementation of App Deployment methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting installation status.
/// </summary>
/// <returns>The status</returns>
public async Task<ApplicationInstallStatus> GetInstallStatusAsync()
{
ApplicationInstallStatus status = ApplicationInstallStatus.Completed;
return await Task.FromResult(status);
}
}
}

Просмотреть файл

@ -0,0 +1,160 @@
//----------------------------------------------------------------------------------------------
// <copyright file="ApplicationManager.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Application Management.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// IoT device application list API.
/// </summary>
public static readonly string AppsListApi = "api/iot/appx/default";
/// <summary>
/// IoT device headless application list API.
/// </summary>
public static readonly string HeadlessAppsListApi = "api/iot/appx/listHeadlessApps";
/// <summary>
/// IoT device headless startup application API.
/// </summary>
public static readonly string HeadlessStartupAppApi = "api/iot/appx/startupHeadlessApp";
/// <summary>
/// IoT device package activation API.
/// </summary>
public static readonly string ActivatePackageApi = "api/iot/appx/app";
/// <summary>
/// Gets List of apps.
/// </summary>
/// <returns>Object containing the list of applications.</returns>
public async Task<AppsListInfo> GetAppsListInfoAsync()
{
return await this.GetAsync<AppsListInfo>(AppsListApi);
}
/// <summary>
/// Gets list of headless apps.
/// </summary>
/// <returns>Object containing the list of headless applications.</returns>
public async Task<HeadlessAppsListInfo> GetHeadlessAppsListInfoAsync()
{
return await this.GetAsync<HeadlessAppsListInfo>(HeadlessAppsListApi);
}
/// <summary>
/// Sets selected app as the startup app.
/// </summary>
/// <param name="appId">App Id.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task UpdateStartupAppAsync(string appId)
{
await this.PostAsync(
AppsListApi,
string.Format("appid={0}", Utilities.Hex64Encode(appId)));
}
/// <summary>
/// Sets the selected app as the headless startup app.
/// </summary>
/// <param name="appId">App Id.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task UpdateHeadlessStartupAppAsync(string appId)
{
await this.PostAsync(
HeadlessStartupAppApi,
string.Format("appid={0}", Utilities.Hex64Encode(appId)));
}
/// <summary>
/// Removes the selected app from the headless startup app list.
/// </summary>
/// <param name="appId">App Id.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task RemoveHeadlessStartupAppAsync(string appId)
{
await this.DeleteAsync(
HeadlessStartupAppApi,
string.Format("appid={0}", Utilities.Hex64Encode(appId)));
}
/// <summary>
/// Activiates the selected app package.
/// </summary>
/// <param name="appId">App Id.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task ActivatePackageAsync(string appId)
{
await this.PostAsync(
ActivatePackageApi,
string.Format("appid={0}", Utilities.Hex64Encode(appId)));
}
#region Data contract
/// <summary>
/// Application list info.
/// </summary>
[DataContract]
public class AppsListInfo
{
/// <summary>
/// Gets the default application
/// </summary>
[DataMember(Name = "DefaultApp")]
public string DefaultApp { get; private set; }
/// <summary>
/// Gets the application packages
/// </summary>
[DataMember(Name = "AppPackages")]
public List<AppPackage> AppPackages { get; private set; }
}
/// <summary>
/// Application package.
/// </summary>
[DataContract]
public class AppPackage
{
/// <summary>
/// Gets a value indicating whether the app is the startup app
/// </summary>
[DataMember(Name = "IsStartup")]
public bool IsStartup { get; private set; }
/// <summary>
/// Gets the complate package name
/// </summary>
[DataMember(Name = "PackageFullName")]
public string PackageFullName { get; private set; }
}
/// <summary>
/// Headless app list information.
/// </summary>
[DataContract]
public class HeadlessAppsListInfo
{
/// <summary>
/// Gets the list of headless application packages
/// </summary>
[DataMember(Name = "AppPackages")]
public List<AppPackage> AppPackages { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,125 @@
//----------------------------------------------------------------------------------------------
// <copyright file="CertificateHandling.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// .net 4.x implementation of device certificate handling methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// A manually provided certificate for trust validation.
/// </summary>
private X509Certificate2 manualCertificate = null;
/// <summary>
/// Gets or sets handler for untrusted certificate handling
/// </summary>
public event UnvalidatedCertEventHandler UnvalidatedCert;
/// <summary>
/// Gets the root certificate from the device.
/// </summary>
/// <returns>The device certificate.</returns>
public async Task<X509Certificate2> GetRootDeviceCertificateAsync()
{
X509Certificate2 certificate = null;
Uri uri = Utilities.BuildEndpoint(this.deviceConnection.Connection, RootCertificateEndpoint);
using (Stream stream = await this.GetAsync(uri))
{
using (BinaryReader reader = new BinaryReader(stream))
{
byte[] certData = reader.ReadBytes((int)stream.Length);
certificate = new X509Certificate2(certData);
}
}
return certificate;
}
/// <summary>
/// Sets the manual certificate.
/// </summary>
/// <param name="cert">Manual certificate</param>
private void SetManualCertificate(X509Certificate2 cert)
{
this.manualCertificate = cert;
}
/// <summary>
/// Validate the server certificate
/// </summary>
/// <param name="sender">The sender object</param>
/// <param name="certificate">The server's certificate</param>
/// <param name="chain">The cert chain</param>
/// <param name="sslPolicyErrors">Policy Errors</param>
/// <returns>whether the cert passes validation</returns>
private bool ServerCertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (this.manualCertificate != null)
{
chain.ChainPolicy.ExtraStore.Add(this.manualCertificate);
}
X509Certificate2 certv2 = new X509Certificate2(certificate);
bool isValid = chain.Build(certv2);
// If chain validation failed but we have a manual cert, we can still
// check the chain to see if the server cert chains up to our manual cert
// (or matches it) in which case this is valid.
if (!isValid && this.manualCertificate != null)
{
foreach (X509ChainElement element in chain.ChainElements)
{
foreach (X509ChainStatus status in element.ChainElementStatus)
{
// Check if this is a failure that should cause the chain to be rejected
if (status.Status != X509ChainStatusFlags.NoError &&
status.Status != X509ChainStatusFlags.UntrustedRoot &&
status.Status != X509ChainStatusFlags.RevocationStatusUnknown)
{
return false;
}
}
// This cert chained to our provided cert. Continue walking
// the chain to ensure we don't hit a failure that would
// cause our chain to be rejected.
if (element.Certificate.Issuer == this.manualCertificate.Issuer &&
element.Certificate.Thumbprint == this.manualCertificate.Thumbprint)
{
isValid = true;
break;
}
}
}
// If this still appears invalid, we give the app a chance via a handler
// to override the trust decision.
if (!isValid)
{
bool? overridenIsValid = this.UnvalidatedCert?.Invoke(this, certificate, chain, sslPolicyErrors);
if (overridenIsValid != null && overridenIsValid == true)
{
isValid = true;
}
}
return isValid;
}
}
}

Просмотреть файл

@ -0,0 +1,223 @@
//----------------------------------------------------------------------------------------------
// <copyright file="AppCrashDumpCollection.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for app crash dump collection methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API to retrieve list of the available crash dumps (for sideloaded applications).
/// </summary>
public static readonly string AvailableCrashDumpsApi = "api/debug/dump/usermode/dumps";
/// <summary>
/// API to download or delete a crash dump file (for a sideloaded application).
/// </summary>
public static readonly string CrashDumpFileApi = "api/debug/dump/usermode/crashdump";
/// <summary>
/// API to control the crash dump settings for a sideloaded application.
/// </summary>
public static readonly string CrashDumpSettingsApi = "api/debug/dump/usermode/crashcontrol";
/// <summary>
/// Get a list of app crash dumps on the device.
/// </summary>
/// <returns>List of AppCrashDump objects, which represent crashdumps on the device. </returns>
public async Task<List<AppCrashDump>> GetAppCrashDumpListAsync()
{
AppCrashDumpList cdl = await this.GetAsync<AppCrashDumpList>(AvailableCrashDumpsApi);
return cdl.CrashDumps;
}
/// <summary>
/// Download a sideloaded app's crash dump.
/// </summary>
/// <param name="crashdump"> The AppCrashDump to download</param>
/// <returns>Stream of the crash dump</returns>
public async Task<Stream> GetAppCrashDumpAsync(AppCrashDump crashdump)
{
string queryString = CrashDumpFileApi + string.Format("?packageFullName={0}&fileName={1}", crashdump.PackageFullName, crashdump.Filename);
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
queryString);
return await this.GetAsync(uri);
}
/// <summary>
/// Delete an app crash dump stored on the device.
/// </summary>
/// <param name="crashdump">The crashdump to be deleted</param>
/// <returns>Task tracking completion of the request.</returns>
public async Task DeleteAppCrashDumpAsync(AppCrashDump crashdump)
{
await this.DeleteAsync(
CrashDumpFileApi,
string.Format("packageFullName={0}&fileName={1}", crashdump.PackageFullName, crashdump.Filename));
}
/// <summary>
/// Get the crash settings for a sideloaded app.
/// </summary>
/// <param name="app">The app to get settings for</param>
/// <returns>The crash settings for the app</returns>
public async Task<AppCrashDumpSettings> GetAppCrashDumpSettingsAsync(AppPackage app)
{
return await this.GetAppCrashDumpSettingsAsync(app.PackageFullName);
}
/// <summary>
/// Get the crash settings for a sideloaded app.
/// </summary>
/// <param name="packageFullname">The app to get settings for</param>
/// <returns>The crash settings for the app</returns>
public async Task<AppCrashDumpSettings> GetAppCrashDumpSettingsAsync(string packageFullname)
{
return await this.GetAsync<AppCrashDumpSettings>(
CrashDumpSettingsApi,
string.Format("packageFullName={0}", packageFullname));
}
/// <summary>
/// Set the crash settings for a sideloaded app.
/// </summary>
/// <param name="app">The app to set crash settings for.</param>
/// <param name="enable">Whether to enable or disable crash collection for the app. </param>
/// <returns>Task tracking completion of the request.</returns>
public async Task SetAppCrashDumpSettingsAsync(AppPackage app, bool enable = true)
{
string pfn = app.PackageFullName;
await this.SetAppCrashDumpSettingsAsync(pfn, enable);
}
/// <summary>
/// Set the crash settings for a sideloaded app.
/// </summary>
/// <param name="packageFullName">The app to set crash settings for.</param>
/// <param name="enable">Whether to enable or disable crash collection for the app. </param>
/// <returns>Task tracking completion of the request.</returns>
public async Task SetAppCrashDumpSettingsAsync(string packageFullName, bool enable = true)
{
if (enable)
{
await this.PostAsync(
CrashDumpSettingsApi,
string.Format("packageFullName={0}", packageFullName));
}
else
{
await this.DeleteAsync(
CrashDumpSettingsApi,
string.Format("packageFullName={0}", packageFullName));
}
}
#region Data contract
/// <summary>
/// Per-app crash dump settings.
/// </summary>
[DataContract]
public class AppCrashDumpSettings
{
/// <summary>
/// Gets a value indicating whether crash dumps are enabled for the app
/// </summary>
[DataMember(Name = "CrashDumpEnabled")]
public bool CrashDumpEnabled
{
get;
private set;
}
}
/// <summary>
/// Represents a crash dump collected from a sideloaded app.
/// </summary>
[DataContract]
public class AppCrashDump
{
/// <summary>
/// Gets the timestamp of the crash as a string.
/// </summary>
[DataMember(Name = "FileDate")]
public string FileDateAsString
{
get;
private set;
}
/// <summary>
/// Gets the timestamp of the crash.
/// </summary>
public DateTime FileDate
{
get
{
return DateTime.Parse(this.FileDateAsString);
}
}
/// <summary>
/// Gets the filename of the crash file.
/// </summary>
[DataMember(Name = "FileName")]
public string Filename
{
get;
private set;
}
/// <summary>
/// Gets the size of the crash dump, in bytes
/// </summary>
[DataMember(Name = "FileSize")]
public uint FileSizeInBytes
{
get;
private set;
}
/// <summary>
/// Gets the package full name of the app that crashed.
/// </summary>
[DataMember(Name = "PackageFullName")]
public string PackageFullName
{
get;
private set;
}
}
/// <summary>
/// A list of crash dumps. Internal usage only.
/// </summary>
[DataContract]
private class AppCrashDumpList
{
/// <summary>
/// Gets a list of crash dumps on the device.
/// </summary>
[DataMember(Name = "CrashDumps")]
public List<AppCrashDump> CrashDumps
{
get;
private set;
}
}
#endregion Data contract
}
}

Просмотреть файл

@ -0,0 +1,411 @@
//----------------------------------------------------------------------------------------------
// <copyright file="AppDeployment.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
#if !WINDOWS_UWP
using System.Net;
using System.Net.Http;
#endif // !WINDOWS_UWP
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
#if WINDOWS_UWP
using Windows.Foundation;
using Windows.Security.Credentials;
using Windows.Storage.Streams;
using Windows.Web.Http;
using Windows.Web.Http.Filters;
using Windows.Web.Http.Headers;
#endif
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for App Deployment methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API to retrieve list of installed packages.
/// </summary>
public static readonly string InstalledPackagesApi = "api/app/packagemanager/packages";
/// <summary>
/// Install state API.
/// </summary>
public static readonly string InstallStateApi = "api/app/packagemanager/state";
/// <summary>
/// API for package management.
/// </summary>
public static readonly string PackageManagerApi = "api/app/packagemanager/package";
/// <summary>
/// App Install Status handler.
/// </summary>
public event ApplicationInstallStatusEventHandler AppInstallStatus;
/// <summary>
/// Gets the collection of applications installed on the device.
/// </summary>
/// <returns>AppPackages object containing the list of installed application packages.</returns>
public async Task<AppPackages> GetInstalledAppPackagesAsync()
{
return await this.GetAsync<AppPackages>(InstalledPackagesApi);
}
/// <summary>
/// Installs an application
/// </summary>
/// <param name="appName">Friendly name (ex: Hello World) of the application. If this parameter is not provided, the name of the package is assumed to be the app name.</param>
/// <param name="packageFileName">Full name of the application package file.</param>
/// <param name="dependencyFileNames">List containing the full names of any required dependency files.</param>
/// <param name="certificateFileName">Full name of the optional certificate file.</param>
/// <param name="stateCheckIntervalMs">How frequently we should check the installation state.</param>
/// <param name="timeoutInMinutes">Operation timeout.</param>
/// <param name="uninstallPreviousVersion">Indicate whether or not the previous app version should be uninstalled prior to installing.</param>
/// <remarks>InstallApplication sends ApplicationInstallStatus events to indicate the current progress in the installation process.
/// Some applications may opt to not register for the AppInstallStatus event and await on InstallApplication.</remarks>
/// <returns>Task for tracking completion of install initialization.</returns>
public async Task InstallApplicationAsync(
string appName,
string packageFileName,
List<string> dependencyFileNames,
string certificateFileName = null,
short stateCheckIntervalMs = 500,
short timeoutInMinutes = 15,
bool uninstallPreviousVersion = true)
{
string installPhaseDescription = string.Empty;
try
{
FileInfo packageFile = new FileInfo(packageFileName);
// If appName was not provided, use the package file name
if (string.IsNullOrEmpty(appName))
{
appName = packageFile.Name;
}
// Uninstall the application's previous version, if one exists.
if (uninstallPreviousVersion)
{
installPhaseDescription = string.Format("Uninstalling any previous version of {0}", appName);
this.SendAppInstallStatus(
ApplicationInstallStatus.InProgress,
ApplicationInstallPhase.UninstallingPreviousVersion,
installPhaseDescription);
AppPackages installedApps = await this.GetInstalledAppPackagesAsync();
foreach (PackageInfo package in installedApps.Packages)
{
if (package.Name == appName)
{
await this.UninstallApplicationAsync(package.FullName);
break;
}
}
}
// Create the API endpoint and generate a unique boundary string.
Uri uri;
string boundaryString;
this.CreateAppInstallEndpointAndBoundaryString(
packageFile.Name,
out uri,
out boundaryString);
installPhaseDescription = string.Format("Copying: {0}", packageFile.Name);
this.SendAppInstallStatus(
ApplicationInstallStatus.InProgress,
ApplicationInstallPhase.CopyingFile,
installPhaseDescription);
var content = new HttpMultipartFileContent();
content.Add(packageFile.FullName);
content.AddRange(dependencyFileNames);
content.Add(certificateFileName);
await this.PostAsync(uri, content);
// Poll the status until complete.
ApplicationInstallStatus status = ApplicationInstallStatus.InProgress;
do
{
installPhaseDescription = string.Format("Installing {0}", appName);
this.SendAppInstallStatus(
ApplicationInstallStatus.InProgress,
ApplicationInstallPhase.Installing,
installPhaseDescription);
await Task.Delay(TimeSpan.FromMilliseconds(stateCheckIntervalMs));
status = await this.GetInstallStatusAsync().ConfigureAwait(false);
}
while (status == ApplicationInstallStatus.InProgress);
installPhaseDescription = string.Format("{0} installed successfully", appName);
this.SendAppInstallStatus(
ApplicationInstallStatus.Completed,
ApplicationInstallPhase.Idle,
installPhaseDescription);
}
catch (Exception e)
{
DevicePortalException dpe = e as DevicePortalException;
if (dpe != null)
{
this.SendAppInstallStatus(
ApplicationInstallStatus.Failed,
ApplicationInstallPhase.Idle,
string.Format("Failed to install {0}: {1}", appName, dpe.Reason));
}
else
{
this.SendAppInstallStatus(
ApplicationInstallStatus.Failed,
ApplicationInstallPhase.Idle,
string.Format("Failed to install {0}: {1}", appName, installPhaseDescription));
}
throw;
}
}
/// <summary>
/// Uninstalls the specified application.
/// </summary>
/// <param name="packageName">The name of the application package to uninstall.</param>
/// <returns>Task tracking the uninstall operation.</returns>
public async Task UninstallApplicationAsync(string packageName)
{
await this.DeleteAsync(
PackageManagerApi,
//// NOTE: When uninstalling an app package, the package name is not Hex64 encoded.
string.Format("package={0}", packageName));
}
/// <summary>
/// Builds the application installation Uri and generates a unique boundary string for the multipart form data.
/// </summary>
/// <param name="packageName">The name of the application package.</param>
/// <param name="uri">The endpoint for the install request.</param>
/// <param name="boundaryString">Unique string used to separate the parts of the multipart form data.</param>
private void CreateAppInstallEndpointAndBoundaryString(
string packageName,
out Uri uri,
out string boundaryString)
{
uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
PackageManagerApi,
string.Format("package={0}", packageName));
boundaryString = Guid.NewGuid().ToString();
}
/// <summary>
/// Sends application install status.
/// </summary>
/// <param name="status">Status of the installation.</param>
/// <param name="phase">Current installation phase (ex: Uninstalling previous version)</param>
/// <param name="message">Optional error message describing the install status.</param>
private void SendAppInstallStatus(
ApplicationInstallStatus status,
ApplicationInstallPhase phase,
string message = "")
{
this.AppInstallStatus?.Invoke(
this,
new ApplicationInstallStatusEventArgs(status, phase, message));
}
#region Data contract
/// <summary>
/// Object representing a list of Application Packages
/// </summary>
[DataContract]
public class AppPackages
{
/// <summary>
/// Gets a list of the packages
/// </summary>
[DataMember(Name = "InstalledPackages")]
public List<PackageInfo> Packages { get; private set; }
/// <summary>
/// Presents a user readable representation of a list of AppPackages
/// </summary>
/// <returns>User readable list of AppPackages.</returns>
public override string ToString()
{
string output = "Packages:\n";
foreach (PackageInfo package in this.Packages)
{
output += package;
}
return output;
}
}
/// <summary>
/// Object representing the install state
/// </summary>
[DataContract]
public class InstallState
{
/// <summary>
/// Gets install state code
/// </summary>
[DataMember(Name = "Code")]
public int Code { get; private set; }
/// <summary>
/// Gets message text
/// </summary>
[DataMember(Name = "CodeText")]
public string CodeText { get; private set; }
/// <summary>
/// Gets reason for state
/// </summary>
[DataMember(Name = "Reason")]
public string Reason { get; private set; }
/// <summary>
/// Gets a value indicating whether this was successful
/// </summary>
[DataMember(Name = "Success")]
public bool WasSuccessful { get; private set; }
}
/// <summary>
/// object representing the package information
/// </summary>
[DataContract]
public class PackageInfo
{
/// <summary>
/// Gets package name
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; private set; }
/// <summary>
/// Gets package family name
/// </summary>
[DataMember(Name = "PackageFamilyName")]
public string FamilyName { get; private set; }
/// <summary>
/// Gets package full name
/// </summary>
[DataMember(Name = "PackageFullName")]
public string FullName { get; private set; }
/// <summary>
/// Gets package relative Id
/// </summary>
[DataMember(Name = "PackageRelativeId")]
public string AppId { get; private set; }
/// <summary>
/// Gets package publisher
/// </summary>
[DataMember(Name = "Publisher")]
public string Publisher { get; private set; }
/// <summary>
/// Gets package version
/// </summary>
[DataMember(Name = "Version")]
public PackageVersion Version { get; private set; }
/// <summary>
/// Gets package origin, a measure of how the app was installed.
/// PackageOrigin_Unknown            = 0,
/// PackageOrigin_Unsigned           = 1,
/// PackageOrigin_Inbox              = 2,
/// PackageOrigin_Store              = 3,
/// PackageOrigin_DeveloperUnsigned  = 4,
/// PackageOrigin_DeveloperSigned    = 5,
/// PackageOrigin_LineOfBusiness     = 6
/// </summary>
[DataMember(Name = "PackageOrigin")]
public int PackageOrigin { get; private set; }
/// <summary>
/// Helper method to determine if the app was sideloaded and therefore can be used with e.g. GetFolderContentsAsync
/// </summary>
/// <returns> True if the package is sideloaded. </returns>
public bool IsSideloaded()
{
return this.PackageOrigin == 4 || this.PackageOrigin == 5;
}
/// <summary>
/// Get a string representation of the package
/// </summary>
/// <returns>String representation</returns>
public override string ToString()
{
return string.Format("\t{0}\n\t\t{1}\n", this.FullName, this.AppId);
}
}
/// <summary>
/// Object representing a package version
/// </summary>
[DataContract]
public class PackageVersion
{
/// <summary>
/// Gets version build
/// </summary>
[DataMember(Name = "Build")]
public int Build { get; private set; }
/// <summary>
/// Gets package Major number
/// </summary>
[DataMember(Name = "Major")]
public int Major { get; private set; }
/// <summary>
/// Gets package minor number
/// </summary>
[DataMember(Name = "Minor")]
public int Minor { get; private set; }
/// <summary>
/// Gets package revision
/// </summary>
[DataMember(Name = "Revision")]
public int Revision { get; private set; }
/// <summary>
/// Gets package version
/// </summary>
public Version Version
{
get { return new Version(this.Major, this.Minor, this.Build, this.Revision); }
}
/// <summary>
/// Get a string representation of a version
/// </summary>
/// <returns>String representation</returns>
public override string ToString()
{
return Version.ToString();
}
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,351 @@
//----------------------------------------------------------------------------------------------
// <copyright file="AppFileExplorer.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for App File explorer methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API to upload, download or delete a file in a folder.
/// </summary>
public static readonly string GetFileApi = "api/filesystem/apps/file";
/// <summary>
/// API to rename a file in a folder.
/// </summary>
public static readonly string RenameFileApi = "api/filesystem/apps/rename";
/// <summary>
/// API to retrieve the list of files in a folder.
/// </summary>
public static readonly string GetFilesApi = "api/filesystem/apps/files";
/// <summary>
/// API to retrieve the list of accessible top-level folders.
/// </summary>
public static readonly string KnownFoldersApi = "api/filesystem/apps/knownfolders";
/// <summary>
/// Gets a list of Known Folders on the device.
/// </summary>
/// <returns>List of known folders available on this device.</returns>
public async Task<KnownFolders> GetKnownFoldersAsync()
{
return await this.GetAsync<KnownFolders>(KnownFoldersApi);
}
/// <summary>
/// Gets a list of files in a Known Folder (e.g. LocalAppData).
/// </summary>
/// <param name="knownFolderId">The known folder id for the root of the path.</param>
/// <param name="subPath">An optional subpath to the folder.</param>
/// <param name="packageFullName">The package full name if using LocalAppData.</param>
/// <returns>Contents of the requested folder.</returns>
public async Task<FolderContents> GetFolderContentsAsync(
string knownFolderId,
string subPath = null,
string packageFullName = null)
{
Dictionary<string, string> payload = this.BuildCommonFilePayload(knownFolderId, subPath, packageFullName);
return await this.GetAsync<FolderContents>(GetFilesApi, Utilities.BuildQueryString(payload));
}
/// <summary>
/// Gets a file from LocalAppData or another Known Folder on the device.
/// </summary>
/// <param name="knownFolderId">The known folder id for the root of the path.</param>
/// <param name="filename">The name of the file we are downloading.</param>
/// <param name="subPath">An optional subpath to the folder.</param>
/// <param name="packageFullName">The package full name if using LocalAppData.</param>
/// <returns>Stream to the downloaded file.</returns>
public async Task<Stream> GetFileAsync(
string knownFolderId,
string filename,
string subPath = null,
string packageFullName = null)
{
Dictionary<string, string> payload = this.BuildCommonFilePayload(knownFolderId, subPath, packageFullName);
filename = WebUtility.UrlEncode(filename);
payload.Add("filename", filename);
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
GetFileApi,
Utilities.BuildQueryString(payload));
return await this.GetAsync(uri);
}
/// <summary>
/// Uploads a file to a Known Folder (e.g. LocalAppData)
/// </summary>
/// <param name="knownFolderId">The known folder id for the root of the path.</param>
/// <param name="filepath">The path to the file we are uploading.</param>
/// <param name="subPath">An optional subpath to the folder.</param>
/// <param name="packageFullName">The package full name if using LocalAppData.</param>
/// <returns>Task tracking completion of the upload request.</returns>
public async Task UploadFileAsync(
string knownFolderId,
string filepath,
string subPath = null,
string packageFullName = null)
{
Dictionary<string, string> payload = this.BuildCommonFilePayload(knownFolderId, subPath, packageFullName);
List<string> files = new List<string>();
files.Add(filepath);
await this.PostAsync(GetFileApi, files, Utilities.BuildQueryString(payload));
}
/// <summary>
/// Deletes a file from a Known Folder.
/// </summary>
/// <param name="knownFolderId">The known folder id for the root of the path.</param>
/// <param name="filename">The name of the file we are deleting.</param>
/// <param name="subPath">An optional subpath to the folder.</param>
/// <param name="packageFullName">The package full name if using LocalAppData.</param>
/// <returns>Task tracking completion of the delete request.</returns>
public async Task DeleteFileAsync(
string knownFolderId,
string filename,
string subPath = null,
string packageFullName = null)
{
Dictionary<string, string> payload = this.BuildCommonFilePayload(knownFolderId, subPath, packageFullName);
filename = WebUtility.UrlEncode(filename);
payload.Add("filename", filename);
await this.DeleteAsync(GetFileApi, Utilities.BuildQueryString(payload));
}
/// <summary>
/// Renames a file in a Known Folder.
/// </summary>
/// <param name="knownFolderId">The known folder id for the root of the path.</param>
/// <param name="filename">The name of the file we are renaming.</param>
/// <param name="newFilename">The new name for this file.</param>
/// <param name="subPath">An optional subpath to the folder.</param>
/// <param name="packageFullName">The package full name if using LocalAppData.</param>
/// <returns>Task tracking completion of the rename request.</returns>
public async Task RenameFileAsync(
string knownFolderId,
string filename,
string newFilename,
string subPath = null,
string packageFullName = null)
{
Dictionary<string, string> payload = this.BuildCommonFilePayload(knownFolderId, subPath, packageFullName);
payload.Add("filename", filename);
payload.Add("newfilename", newFilename);
await this.PostAsync(RenameFileApi, Utilities.BuildQueryString(payload));
}
/// <summary>
/// Do some common parsing and validation of file explorer parameters.
/// </summary>
/// <param name="knownFolderId">The known folder id.</param>
/// <param name="subPath">The optional subpath for the folder.</param>
/// <param name="packageFullName">The packagefullname if using LocalAppData.</param>
/// <returns>Dictionary of param name to value.</returns>
private Dictionary<string, string> BuildCommonFilePayload(string knownFolderId, string subPath, string packageFullName)
{
Dictionary<string, string> payload = new Dictionary<string, string>();
payload.Add("knownfolderid", knownFolderId);
if (!string.IsNullOrEmpty(subPath))
{
if (!subPath.StartsWith("/"))
{
subPath = subPath.Insert(0, "/");
}
payload.Add("path", subPath);
}
if (!string.IsNullOrEmpty(packageFullName))
{
payload.Add("packagefullname", packageFullName);
}
else if (string.Equals(knownFolderId, "LocalAppData", StringComparison.OrdinalIgnoreCase))
{
throw new Exception("LocalAppData requires a packageFullName be provided.");
}
return payload;
}
#region Data contract
/// <summary>
/// Known Folders object.
/// </summary>
[DataContract]
public class KnownFolders
{
/// <summary>
/// Gets the list of known folders.
/// </summary>
[DataMember(Name = "KnownFolders")]
public List<string> Folders { get; private set; }
/// <summary>
/// Overridden ToString method providing a user readable
/// list of known folders.
/// </summary>
/// <returns>String representation of the object.</returns>
public override string ToString()
{
if (this.Folders == null)
{
return string.Empty;
}
string contents = string.Empty;
foreach (string folder in this.Folders)
{
contents += folder + '\n';
}
return contents;
}
}
/// <summary>
/// Folder contents object.
/// </summary>
[DataContract]
public class FolderContents
{
/// <summary>
/// Gets the list of folders and files in this folder.
/// </summary>
[DataMember(Name = "Items")]
public List<FileOrFolderInformation> Contents { get; private set; }
/// <summary>
/// Overridden ToString method providing a user readable
/// display of a folder's contents. Tries to match the formatting
/// of regular DIR commands.
/// </summary>
/// <returns>String representation of the object.</returns>
public override string ToString()
{
if (this.Contents == null)
{
return string.Empty;
}
string contents = string.Empty;
foreach (FileOrFolderInformation fileOrFolder in this.Contents)
{
contents += fileOrFolder.ToString();
}
return contents;
}
}
/// <summary>
/// Details about a folder or file.
/// </summary>
[DataContract]
public class FileOrFolderInformation
{
/// <summary>
/// Gets the current directory.
/// </summary>
[DataMember(Name = "CurrentDir")]
public string CurrentDir { get; private set; }
/// <summary>
/// Gets the current directory.
/// </summary>
[DataMember(Name = "DateCreated")]
public long DateCreated { get; private set; }
/// <summary>
/// Gets the Id.
/// </summary>
[DataMember(Name = "Id")]
public string Id { get; private set; }
/// <summary>
/// Gets the Name.
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; private set; }
/// <summary>
/// Gets the SubPath (equivalent to CurrentDir for files).
/// </summary>
[DataMember(Name = "SubPath")]
public string SubPath { get; private set; }
/// <summary>
/// Gets the Type.
/// </summary>
[DataMember(Name = "Type")]
public int Type { get; private set; }
/// <summary>
/// Gets the size of the file (0 for folders).
/// </summary>
[DataMember(Name = "FileSize")]
public long SizeInBytes { get; private set; }
/// <summary>
/// Gets a value indicating whether the current item is a folder by checking for FILE_ATTRIBUTE_DIRECTORY
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx
/// </summary>
public bool IsFolder
{
get
{
return (this.Type & 0x10) == 0x10;
}
}
/// <summary>
/// Overridden ToString method providing a user readable
/// display of a file or folder. Tries to match the formatting
/// of regular DIR commands.
/// </summary>
/// <returns>String representation of the object.</returns>
public override string ToString()
{
DateTime timestamp = DateTime.FromFileTime(this.DateCreated);
// Check if this is a folder.
if (this.IsFolder)
{
return string.Format("{0,-24:MM/dd/yyyy HH:mm tt}{1,-14} {2}\n", timestamp, "<DIR>", this.Name);
}
else
{
return string.Format("{0,-24:MM/dd/yyyy HH:mm tt}{1,14:n0} {2}\n", timestamp, this.SizeInBytes, this.Name);
}
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,103 @@
//----------------------------------------------------------------------------------------------
// <copyright file="DeviceManager.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for device management methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API to retrieve list of installed devices.
/// </summary>
public static readonly string InstalledDevicesApi = "api/devicemanager/devices";
/// <summary>
/// Get a listing of installed devices
/// </summary>
/// <returns>List of installed devices</returns>
public async Task<List<Device>> GetDeviceListAsync()
{
DeviceList deviceList = await this.GetAsync<DeviceList>(InstalledDevicesApi);
return deviceList.Devices;
}
#region Data contract
/// <summary>
/// Object representing a device entry
/// </summary>
[DataContract]
public class DeviceList
{
/// <summary>
/// Gets the Device Class
/// </summary>
[DataMember(Name = "DeviceList")]
public List<Device> Devices { get; private set; }
}
/// <summary>
/// Object representing a device entry
/// </summary>
[DataContract]
public class Device
{
/// <summary>
/// Gets the Device Class
/// </summary>
[DataMember(Name = "Class")]
public string Class { get; private set; }
/// <summary>
/// Gets the Device Description
/// </summary>
[DataMember(Name = "Description")]
public string Description { get; private set; }
/// <summary>
/// Gets the friendly (human-readable) name for the device. Usually more descriptive than Description. Does not apply to all Devices.
/// </summary>
[DataMember(Name = "FriendlyName")]
public string FriendlyName { get; private set; }
/// <summary>
/// Gets the Device ID
/// </summary>
[DataMember(Name = "ID")]
public string ID { get; private set; }
/// <summary>
/// Gets the Device Manufacturer
/// </summary>
[DataMember(Name = "Manufacturer")]
public string Manufacturer { get; private set; }
/// <summary>
/// Gets the Device ParentID, used for pairing
/// </summary>
[DataMember(Name = "ParentID")]
public string ParentID { get; private set; }
/// <summary>
/// Gets the Device Problem Code
/// </summary>
[DataMember(Name = "ProblemCode")]
public int ProblemCode { get; private set; }
/// <summary>
/// Gets the Device Status Code
/// </summary>
[DataMember(Name = "StatusCode")]
public int StatusCode { get; private set; }
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,92 @@
//----------------------------------------------------------------------------------------------
// <copyright file="Dns-Sd.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for DNS methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API to add or delete a tag to the DNS-SD advertisement.
/// </summary>
public static readonly string TagApi = "api/dns-sd/tag";
/// <summary>
/// API to retrieve or delete the currently applied tags for the device.
/// </summary>
public static readonly string TagsApi = "api/dns-sd/tags";
/// <summary>
/// Gets a list of DNS-SD tags being broadcast by this device.
/// </summary>
/// <returns>Array of strings, each one an individual tag.</returns>
public async Task<List<string>> GetServiceTagsAsync()
{
ServiceTags tags = await this.GetAsync<ServiceTags>(TagsApi);
return tags.Tags;
}
/// <summary>
/// Adds a tag to this device's DNS-SD broadcast.
/// </summary>
/// <param name="tagValue">The tag to assign to the device.</param>
/// <returns>Task tracking adding the tag.</returns>
public async Task AddServiceTagAsync(string tagValue)
{
await this.PostAsync(
TagApi,
string.Format("tagValue={0}", tagValue));
}
/// <summary>
/// Delete all tags from the device's DNS-SD broadcast.
/// </summary>
/// <returns>Task tracking deletion of all tags.</returns>
public async Task DeleteAllTagsAsync()
{
await this.DeleteAsync(TagsApi);
}
/// <summary>
/// Delete a specific tag from the device's DNS-SD broadcast.
/// </summary>
/// <param name="tagValue">The tag to delete from the device broadcast.</param>
/// <returns>Task tracking deletion of the tag.</returns>
public async Task DeleteTagAsync(string tagValue)
{
await this.DeleteAsync(
TagApi,
string.Format("tagValue={0}", tagValue));
}
#region Data contract
/// <summary>
/// Service tags object
/// </summary>
[DataContract]
public class ServiceTags
{
/// <summary>
/// Gets the DNS-SD service tags
/// </summary>
[DataMember(Name = "tags")]
public List<string> Tags
{
get; private set;
}
#endregion Data contract
}
}
}

Просмотреть файл

@ -0,0 +1,242 @@
//----------------------------------------------------------------------------------------------
// <copyright file="DumpCollection.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for crash dump collection methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API to retrieve list of the available bugcheck minidumps.
/// </summary>
public static readonly string AvailableBugChecksApi = "api/debug/dump/kernel/dumplist";
/// <summary>
/// API to download a bugcheck minidump file.
/// </summary>
public static readonly string BugcheckFileApi = "api/debug/dump/kernel/dump";
/// <summary>
/// API to control bugcheck minidump settings.
/// </summary>
public static readonly string BugcheckSettingsApi = "api/debug/dump/kernel/crashcontrol";
/// <summary>
/// API to retrieve a live kernel dump.
/// </summary>
public static readonly string LiveKernelDumpApi = "api/debug/dump/livekernel";
/// <summary>
/// API to retrieve a live dump from a running user mode process.
/// </summary>
public static readonly string LiveProcessDumpApi = "api/debug/dump/usermode/live";
/// <summary>
/// Get a list of dumpfiles on the system.
/// </summary>
/// <returns>List of Dumpfiles. Use GetDumpFile to download the file. </returns>
public async Task<List<Dumpfile>> GetDumpfileListAsync()
{
DumpFileList dfl = await this.GetAsync<DumpFileList>(AvailableBugChecksApi);
return dfl.DumpFiles;
}
/// <summary>
/// Download a dumpfile from the system.
/// </summary>
/// <param name="crashdump"> Dumpfile object to be downloaded</param>
/// <returns>Stream of the dump file </returns>
public async Task<Stream> GetDumpFileAsync(Dumpfile crashdump)
{
string queryString = BugcheckFileApi + string.Format("?filename={0}", crashdump.Filename);
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
queryString);
return await this.GetAsync(uri);
}
/// <summary>
/// Takes a live kernel dump of the device. This does not reboot the device.
/// </summary>
/// <returns>Stream of the kernel dump</returns>
public async Task<Stream> GetLiveKernelDumpAsync()
{
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
LiveKernelDumpApi);
return await this.GetAsync(uri);
}
/// <summary>
/// Take a live dump of a process on the system.
/// </summary>
/// <param name="pid"> PID of the process to get a live dump of.</param>
/// <returns>Stream of the process live dump</returns>
public async Task<Stream> GetLiveProcessDumpAsync(int pid)
{
string queryString = LiveProcessDumpApi + string.Format("?pid={0}", pid);
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
queryString);
return await this.GetAsync(uri);
}
/// <summary>
/// Get information on how and when dump files are saved on the device.
/// </summary>
/// <returns>Dumpfile settings object. This can be edited and returned to the device to alter the settings.</returns>
public async Task<DumpFileSettings> GetDumpFileSettingsAsync()
{
return await this.GetAsync<DumpFileSettings>(BugcheckSettingsApi);
}
/// <summary>
/// Set how and when dump files are saved on the device.
/// </summary>
/// <param name="dfs">Altered DumpFileSettings object to set on the device</param>
/// <returns>Task tracking completion of the request</returns>
public async Task SetDumpFileSettingsAsync(DumpFileSettings dfs)
{
string queryParams = string.Format(
"autoreboot={0}&overwrite={1}&dumptype={2}&maxdumpcount={3}",
dfs.AutoReboot ? "1" : "0",
dfs.Overwrite ? "1" : "0",
(int)dfs.DumpType,
dfs.MaxDumpCount);
await this.PostAsync(BugcheckSettingsApi, queryParams);
}
#region Data Contract
/// <summary>
/// DumpFileSettings object. Used to get and set how and when a dump is saved on the device.
/// </summary>
[DataContract]
public class DumpFileSettings
{
/// <summary>
/// The 3 types of dumps that can be saved on the device (or not saved at all).
/// </summary>
public enum DumpTypes
{
/// <summary>
/// Don't collect device crash dumps
/// </summary>
Disabled = 0,
/// <summary>
/// Collect all in use memory
/// </summary>
CompleteMemoryDump = 1,
/// <summary>
/// Don't include usermode memory in the dump
/// </summary>
KernelDump = 2,
/// <summary>
/// Limited kernel dump
/// </summary>
Minidump = 3
}
/// <summary>
/// Gets or sets a value indicating whether the device should restart after a crash dump is taken.
/// </summary>
[DataMember(Name = "autoreboot")]
public bool AutoReboot { get; set; }
/// <summary>
/// Gets or sets the type of dump to be saved when a bugcheck occurs.
/// </summary>
[DataMember(Name = "dumptype")]
public DumpTypes DumpType { get; set; }
/// <summary>
/// Gets or sets the max number of dumps to be saved on the device.
/// </summary>
[DataMember(Name = "maxdumpcount")]
public int MaxDumpCount { get; set; }
/// <summary>
/// Gets or sets a value indicating whether new dumps should overwrite older dumps.
/// </summary>
[DataMember(Name = "overwrite")]
public bool Overwrite { get; set; }
}
/// <summary>
/// Gets a list of kernel dumps on the device.
/// </summary>
[DataContract]
public class DumpFileList
{
/// <summary>
/// Gets a list of kernel dumps on the device.
/// </summary>
[DataMember(Name = "DumpFiles")]
public List<Dumpfile> DumpFiles { get; private set; }
}
/// <summary>
/// Represents a dumpfile stored on the device.
/// </summary>
[DataContract]
public class Dumpfile
{
/// <summary>
/// Gets the timestamp of the crash as a string.
/// </summary>
[DataMember(Name = "FileDate")]
public string FileDateAsString
{
get; private set;
}
/// <summary>
/// Gets the timestamp of the crash.
/// </summary>
public DateTime FileDate
{
get
{
return DateTime.Parse(this.FileDateAsString);
}
}
/// <summary>
/// Gets the filename of the crash file.
/// </summary>
[DataMember(Name = "FileName")]
public string Filename
{
get; private set;
}
/// <summary>
/// Gets the size of the crash dump, in bytes
/// </summary>
[DataMember(Name = "FileSize")]
public uint FileSizeInBytes
{
get; private set;
}
}
#endregion Data Contract
}
}

Просмотреть файл

@ -0,0 +1,317 @@
//----------------------------------------------------------------------------------------------
// <copyright file="Etw.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for ETW methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API to create a realtime ETW session.
/// </summary>
public static readonly string RealtimeEtwSessionApi = "api/etw/session/realtime";
/// <summary>
/// API to get the list of registered custom ETW providers.
/// </summary>
public static readonly string CustomEtwProvidersApi = "api/etw/customproviders";
/// <summary>
/// API to get the list of registered ETW providers.
/// </summary>
public static readonly string EtwProvidersApi = "api/etw/providers";
/// <summary>
/// Web socket to get ETW events.
/// </summary>
private WebSocket<EtwEvents> realtimeEventsWebSocket;
/// <summary>
/// Determines if the event listener has been registered
/// </summary>
private bool isListeningForRealtimeEvents = false;
/// <summary>
/// The ETW event message received handler
/// </summary>
public event WebSocketMessageReceivedEventHandler<EtwEvents> RealtimeEventsMessageReceived;
/// <summary>
/// Returns the current set of registered custom ETW providers.
/// </summary>
/// <returns>EtwProviders object containing a list of providers, friendly name and GUID</returns>
public async Task<EtwProviders> GetCustomEtwProvidersAsync()
{
return await this.GetAsync<EtwProviders>(CustomEtwProvidersApi);
}
/// <summary>
/// Returns the current set of registered ETW providers.
/// </summary>
/// <returns>EtwProviders object containing a list of providers, friendly name and GUID</returns>
public async Task<EtwProviders> GetEtwProvidersAsync()
{
return await this.GetAsync<EtwProviders>(EtwProvidersApi);
}
/// <summary>
/// Toggles the listening state of a specific provider on the realtime events WebSocket.
/// </summary>
/// <param name="etwProvider">The provider to update the listening state of.</param>
/// <param name="isEnabled">Determines whether the listening state should be enabled or disabled.</param>
/// /// <param name="level">Verbosity level - 1 for least, 5 for most verbose. </param>
/// <returns>Task for toggling the listening state of the specified provider.</returns>
public async Task ToggleEtwProviderAsync(EtwProviderInfo etwProvider, bool isEnabled = true, int level = 5)
{
await this.ToggleEtwProviderAsync(etwProvider.GUID, isEnabled, level);
}
/// <summary>
/// Toggles the listening state of a specific provider on the realtime events WebSocket.
/// </summary>
/// <param name="etwProvider">The GUID of the provider to update the listening state of.</param>
/// <param name="isEnabled">Determines whether the listening state should be enabled or disabled.</param>
/// <param name="level">Verbosity level - 1 for least, 5 for most verbose. </param>
/// <returns>Task for toggling the listening state of the specified provider.</returns>
public async Task ToggleEtwProviderAsync(Guid etwProvider, bool isEnabled = true, int level = 5)
{
string action = isEnabled ? "enable" : "disable";
string message = $"provider {etwProvider} {action} {level}";
await this.InitializeRealtimeEventsWebSocketAsync();
await this.realtimeEventsWebSocket.SendMessageAsync(message);
}
/// <summary>
/// Starts listening for ETW events with it being returned via the RealtimeEventsMessageReceived event handler.
/// </summary>
/// <returns>Task for connecting to the WebSocket but not for listening to it.</returns>
public async Task StartListeningForEtwEventsAsync()
{
await this.InitializeRealtimeEventsWebSocketAsync();
if (!this.isListeningForRealtimeEvents)
{
this.isListeningForRealtimeEvents = true;
this.realtimeEventsWebSocket.WebSocketMessageReceived += this.EtwEventsReceivedHandler;
}
await this.realtimeEventsWebSocket.ReceiveMessagesAsync();
}
/// <summary>
/// Stop listening for ETW events.
/// </summary>
/// <returns>Task for stop listening for ETW events and disconnecting from the WebSocket.</returns>
public async Task StopListeningForEtwEventsAsync()
{
if (this.isListeningForRealtimeEvents)
{
this.isListeningForRealtimeEvents = false;
this.realtimeEventsWebSocket.WebSocketMessageReceived -= this.EtwEventsReceivedHandler;
}
await this.realtimeEventsWebSocket.CloseAsync();
}
/// <summary>
/// Creates a new <see cref="WebSocket{EtwEvents}"/> if it hasn't already been initialized.
/// </summary>
/// <returns>Task for connecting the ETW realtime event WebSocket.</returns>
private async Task InitializeRealtimeEventsWebSocketAsync()
{
if (this.realtimeEventsWebSocket == null)
{
#if WINDOWS_UWP
this.realtimeEventsWebSocket = new WebSocket<EtwEvents>(this.deviceConnection);
#else
this.realtimeEventsWebSocket = new WebSocket<EtwEvents>(this.deviceConnection, this.ServerCertificateValidation);
#endif
}
await this.realtimeEventsWebSocket.ConnectAsync(RealtimeEtwSessionApi);
}
/// <summary>
/// Handler for the ETW received event that passes the event to the RealtimeEventsMessageReceived handler.
/// </summary>
/// <param name="sender">The <see cref="WebSocket{EtwEvents}"/> object sending the event.</param>
/// <param name="args">The event data.</param>
private void EtwEventsReceivedHandler(
WebSocket<EtwEvents> sender,
WebSocketMessageReceivedEventArgs<EtwEvents> args)
{
if (args.Message != null)
{
this.RealtimeEventsMessageReceived?.Invoke(
this,
args);
}
}
#region Data contract
/// <summary>
/// ETW Events.
/// </summary>
[DataContract]
public class EtwEvents
{
/// <summary>
/// Saves the downconverted list of events
/// </summary>
private List<EtwEventInfo> stashedList;
/// <summary>
/// Gets the list of ETW Events that occured in the last second.
/// </summary>
public List<EtwEventInfo> Events
{
get
{
if (this.stashedList != null)
{
return this.stashedList;
}
List<EtwEventInfo> events = new List<EtwEventInfo>();
foreach (Dictionary<string, string> dic in this.RawEvents)
{
events.Add(new EtwEventInfo(dic));
}
this.stashedList = events;
return this.stashedList;
}
}
/// <summary>
/// Gets the event frequency.
/// This is always 10 million (10000000) in RS2 devices.
/// </summary>
[DataMember(Name = "Frequency")]
public long Frequency { get; private set; }
/// <summary>
/// Gets or sets the raw list of events. Not for straight usage, as it's entirely unformatted.
/// </summary>
[DataMember(Name = "Events")]
private List<Dictionary<string, string>> RawEvents { get; set; }
}
/// <summary>
/// ETW Events Info. Allows strongly typed access to guaranteed fields
/// like ID or Timestamp, and raw (as string) access to all other
/// payload data, like Latency or PID.
/// </summary>
public class EtwEventInfo : Dictionary<string, string>
{
/// <summary>
/// Initializes a new instance of the <see cref="EtwEventInfo" /> class. Used by the DataContract at access time.
/// </summary>
/// <param name="dictionary">Base dictionary used to populate the object. </param>
internal EtwEventInfo(IDictionary<string, string> dictionary) : base(dictionary)
{
}
/// <summary>
/// Gets the event identifer.
/// </summary>
public ushort ID
{
get
{
return ushort.Parse(this["ID"]);
}
}
/// <summary>
/// Gets the event keyword.
/// </summary>
public ulong Keyword
{
get
{
return ulong.Parse(this["Keyword"]);
}
}
/// <summary>
/// Gets the event level.
/// </summary>
public uint Level
{
get
{
return uint.Parse(this["Level"]);
}
}
/// <summary>
/// Gets the event provider name.
/// </summary>
public string Provider
{
get
{
return this["ProviderName"];
}
}
/// <summary>
/// Gets the event timestamp.
/// </summary>
public ulong Timestamp
{
get
{
return ulong.Parse(this["Timestamp"]);
}
}
}
/// <summary>
/// ETW Providers.
/// </summary>
[DataContract]
public class EtwProviders
{
/// <summary>
/// Gets list of ETW providers
/// </summary>
[DataMember(Name = "Providers")]
public List<EtwProviderInfo> Providers { get; private set; }
}
/// <summary>
/// ETW Provider Info. Contains the Name and GUID.
/// </summary>
[DataContract]
public class EtwProviderInfo
{
/// <summary>
/// Gets provider guid.
/// </summary>
[DataMember(Name = "GUID")]
public Guid GUID { get; private set; }
/// <summary>
/// Gets provider name.
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,166 @@
//----------------------------------------------------------------------------------------------
// <copyright file="Networking.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Networking methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting IP config data.
/// </summary>
public static readonly string IpConfigApi = "api/networking/ipconfig";
/// <summary>
/// Gets the IP configuration data of the device.
/// </summary>
/// <returns>object containing details of the device's network configuration.</returns>
public async Task<IpConfiguration> GetIpConfigAsync()
{
return await this.GetAsync<IpConfiguration>(IpConfigApi);
}
#region Data contract
/// <summary>
/// DHCP object.
/// </summary>
[DataContract]
public class Dhcp
{
/// <summary>
/// Gets the time at which the lease will expire, in ticks.
/// </summary>
[DataMember(Name = "LeaseExpires")]
public long LeaseExpiresRaw { get; private set; }
/// <summary>
/// Gets the time at which the lease was obtained, in ticks.
/// </summary>
[DataMember(Name = "LeaseObtained")]
public long LeaseObtainedRaw { get; private set; }
/// <summary>
/// Gets the name.
/// </summary>
[DataMember(Name = "Address")]
public IpAddressInfo Address { get; private set; }
/// <summary>
/// Gets the lease expiration time.
/// </summary>
public DateTimeOffset LeaseExpires
{
get { return new DateTimeOffset(new DateTime(this.LeaseExpiresRaw)); }
}
/// <summary>
/// Gets the lease obtained time.
/// </summary>
public DateTimeOffset LeaseObtained
{
get { return new DateTimeOffset(new DateTime(this.LeaseObtainedRaw)); }
}
}
/// <summary>
/// IP Address info
/// </summary>
[DataContract]
public class IpAddressInfo
{
/// <summary>
/// Gets the address
/// </summary>
[DataMember(Name = "IpAddress")]
public string Address { get; private set; }
/// <summary>
/// Gets the subnet mask
/// </summary>
[DataMember(Name = "Mask")]
public string SubnetMask { get; private set; }
}
/// <summary>
/// IP Configuration object
/// </summary>
[DataContract]
public class IpConfiguration
{
/// <summary>
/// Gets the list of networking adapters
/// </summary>
[DataMember(Name = "Adapters")]
public List<NetworkAdapterInfo> Adapters { get; private set; }
}
/// <summary>
/// Networking adapter info
/// </summary>
[DataContract]
public class NetworkAdapterInfo
{
/// <summary>
/// Gets the description
/// </summary>
[DataMember(Name = "Description")]
public string Description { get; private set; }
/// <summary>
/// Gets the hardware address
/// </summary>
[DataMember(Name = "HardwareAddress")]
public string MacAddress { get; private set; }
/// <summary>
/// Gets the index
/// </summary>
[DataMember(Name = "Index")]
public int Index { get; private set; }
/// <summary>
/// Gets the name
/// </summary>
[DataMember(Name = "Name")]
public Guid Id { get; private set; }
/// <summary>
/// Gets the type
/// </summary>
[DataMember(Name = "Type")]
public string AdapterType { get; private set; }
/// <summary>
/// Gets DHCP info
/// </summary>
[DataMember(Name = "DHCP")]
public Dhcp Dhcp { get; private set; }
// TODO - WINS
/// <summary>
/// Gets Gateway info
/// </summary>
[DataMember(Name = "Gateways")]
public List<IpAddressInfo> Gateways { get; private set; }
/// <summary>
/// Gets the list of IP addresses
/// </summary>
[DataMember(Name = "IpAddresses")]
public List<IpAddressInfo> IpAddresses { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,272 @@
//----------------------------------------------------------------------------------------------
// <copyright file="OsInformation.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for OS Information.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting the device family.
/// </summary>
public static readonly string DeviceFamilyApi = "api/os/devicefamily";
/// <summary>
/// API for getting the machine name.
/// </summary>
public static readonly string MachineNameApi = "api/os/machinename";
/// <summary>
/// API for getting the OS information.
/// </summary>
public static readonly string OsInfoApi = "api/os/info";
/// <summary>
/// Device portal platforms
/// </summary>
public enum DevicePortalPlatforms
{
/// <summary>
/// Unknown platform
/// </summary>
Unknown = -1,
/// <summary>
/// Windows platform
/// </summary>
Windows = 0,
/// <summary>
/// Mobile platform
/// </summary>
Mobile,
/// <summary>
/// HoloLens platform
/// </summary>
HoloLens,
/// <summary>
/// Xbox One platform
/// </summary>
XboxOne,
/// <summary>
/// Windows IoT on Dragonboard 410c
/// </summary>
IoTDragonboard410c,
/// <summary>
/// Windows IoT on Minnowboard Max
/// </summary>
IoTMinnowboardMax,
/// <summary>
/// Windows IoT on Raspberry Pi 2
/// </summary>
IoTRaspberryPi2,
/// <summary>
/// Windows IoT on Raspberry Pi 3
/// </summary>
IoTRaspberryPi3,
/// <summary>
/// A virtual machine. This may or may not be an emulator.
/// </summary>
VirtualMachine
}
/// <summary>
/// Gets the family name (ex: Windows.Holographic) of the device.
/// </summary>
/// <returns>String containing the device's family.</returns>
public async Task<string> GetDeviceFamilyAsync()
{
DeviceOsFamily deviceFamily = await this.GetAsync<DeviceOsFamily>(DeviceFamilyApi).ConfigureAwait(false);
return deviceFamily.Family;
}
/// <summary>
/// Gets the name of the device.
/// </summary>
/// <returns>String containing the device's name.</returns>
public async Task<string> GetDeviceNameAsync()
{
DeviceName deviceName = await this.GetAsync<DeviceName>(MachineNameApi);
return deviceName.Name;
}
/// <summary>
/// Gets information about the device's operating system.
/// </summary>
/// <returns>OperatingSystemInformation object containing details of the installed operating system.</returns>
public Task<OperatingSystemInformation> GetOperatingSystemInformationAsync()
{
return this.GetAsync<OperatingSystemInformation>(OsInfoApi);
}
/// <summary>
/// Sets the device's name
/// </summary>
/// <param name="name">The name to assign to the device.</param>
/// <remarks>The new name does not take effect until the device has been restarted.</remarks>
/// <returns>Task tracking setting the device name completion.</returns>
public Task SetDeviceNameAsync(string name)
{
return this.PostAsync(
MachineNameApi,
string.Format("name={0}", Utilities.Hex64Encode(name)));
}
#region Data contract
/// <summary>
/// Device name object.
/// </summary>
[DataContract]
public class DeviceName
{
/// <summary>
/// Gets the name.
/// </summary>
[DataMember(Name = "ComputerName")]
public string Name { get; private set; }
}
/// <summary>
/// Device family object.
/// </summary>
[DataContract]
public class DeviceOsFamily
{
/// <summary>
/// Gets the device family name.
/// </summary>
[DataMember(Name = "DeviceType")]
public string Family { get; private set; }
}
/// <summary>
/// Operating system information.
/// </summary>
[DataContract]
public class OperatingSystemInformation
{
/// <summary>
/// Gets the OS name.
/// </summary>
[DataMember(Name = "ComputerName")]
public string Name { get; private set; }
/// <summary>
/// Gets the language
/// </summary>
[DataMember(Name = "Language")]
public string Language { get; private set; }
/// <summary>
/// Gets the edition
/// </summary>
[DataMember(Name = "OsEdition")]
public string OsEdition { get; private set; }
/// <summary>
/// Gets the edition Id
/// </summary>
[DataMember(Name = "OsEditionId")]
public uint OsEditionId { get; private set; }
/// <summary>
/// Gets the OS version
/// </summary>
[DataMember(Name = "OsVersion")]
public string OsVersionString { get; private set; }
/// <summary>
/// Gets the raw platform type
/// </summary>
[DataMember(Name = "Platform")]
public string PlatformName { get; private set; }
/// <summary>
/// Gets the platform
/// </summary>
public DevicePortalPlatforms Platform
{
get
{
DevicePortalPlatforms platform = DevicePortalPlatforms.Unknown;
try
{
// MinnowBoard Max model no. can change based on firmware
if (this.PlatformName.Contains("Minnowboard Max"))
{
return DevicePortalPlatforms.IoTMinnowboardMax;
}
switch (this.PlatformName)
{
case "Xbox One":
platform = DevicePortalPlatforms.XboxOne;
break;
case "SBC":
platform = DevicePortalPlatforms.IoTDragonboard410c;
break;
case "Raspberry Pi 2":
platform = DevicePortalPlatforms.IoTRaspberryPi2;
break;
case "Raspberry Pi 3":
platform = DevicePortalPlatforms.IoTRaspberryPi3;
break;
case "Virtual Machine":
platform = DevicePortalPlatforms.VirtualMachine;
break;
default:
platform = (DevicePortalPlatforms)Enum.Parse(typeof(DevicePortalPlatforms), this.PlatformName);
break;
}
}
catch
{
switch (this.OsEdition)
{
case "Enterprise":
case "Home":
case "Professional":
platform = DevicePortalPlatforms.Windows;
break;
case "Mobile":
platform = DevicePortalPlatforms.Mobile;
break;
default:
platform = DevicePortalPlatforms.Unknown;
break;
}
}
return platform;
}
}
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,549 @@
//----------------------------------------------------------------------------------------------
// <copyright file="PerformanceData.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Performance methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting all running processes
/// </summary>
public static readonly string RunningProcessApi = "api/resourcemanager/processes";
/// <summary>
/// API for getting system performance
/// </summary>
public static readonly string SystemPerfApi = "api/resourcemanager/systemperf";
/// <summary>
/// Web socket to get running processes.
/// </summary>
private WebSocket<RunningProcesses> deviceProcessesWebSocket;
/// <summary>
/// Web socket to get the system perf of the device.
/// </summary>
private WebSocket<SystemPerformanceInformation> systemPerfWebSocket;
/// <summary>
/// The running processes message received handler.
/// </summary>
public event WebSocketMessageReceivedEventHandler<RunningProcesses> RunningProcessesMessageReceived;
/// <summary>
/// The system perf message received handler.
/// </summary>
public event WebSocketMessageReceivedEventHandler<SystemPerformanceInformation> SystemPerfMessageReceived;
/// <summary>
/// Gets the collection of processes running on the device.
/// </summary>
/// <returns>RunningProcesses object containing the list of running processes.</returns>
public async Task<RunningProcesses> GetRunningProcessesAsync()
{
return await this.GetAsync<RunningProcesses>(RunningProcessApi);
}
/// <summary>
/// Starts listening for the running processes on the device with them being returned via the RunningProcessesMessageReceived event handler.
/// </summary>
/// <returns>Task for connecting to the websocket but not for listening to it.</returns>
public async Task StartListeningForRunningProcessesAsync()
{
if (this.deviceProcessesWebSocket == null)
{
#if WINDOWS_UWP
this.deviceProcessesWebSocket = new WebSocket<RunningProcesses>(this.deviceConnection);
#else
this.deviceProcessesWebSocket = new WebSocket<RunningProcesses>(this.deviceConnection, this.ServerCertificateValidation);
#endif
this.deviceProcessesWebSocket.WebSocketMessageReceived += this.RunningProcessesReceivedHandler;
}
else
{
if (this.deviceProcessesWebSocket.IsListeningForMessages)
{
return;
}
}
await this.deviceProcessesWebSocket.ConnectAsync(RunningProcessApi);
await this.deviceProcessesWebSocket.ReceiveMessagesAsync();
}
/// <summary>
/// Stop listening for the running processes on the device.
/// </summary>
/// <returns>Task for stop listening for processes and disconnecting from the websocket .</returns>
public async Task StopListeningForRunningProcessesAsync()
{
await this.deviceProcessesWebSocket.CloseAsync();
}
/// <summary>
/// Gets system performance information for the device.
/// </summary>
/// <returns>SystemPerformanceInformation object containing information such as memory usage.</returns>
public async Task<SystemPerformanceInformation> GetSystemPerfAsync()
{
return await this.GetAsync<SystemPerformanceInformation>(SystemPerfApi);
}
/// <summary>
/// Starts listening for the system performance information for the device with it being returned via the SystemPerfMessageReceived event handler.
/// </summary>
/// <returns>Task for connecting to the websocket but not for listening to it.</returns>
public async Task StartListeningForSystemPerfAsync()
{
if (this.systemPerfWebSocket == null)
{
#if WINDOWS_UWP
this.systemPerfWebSocket = new WebSocket<SystemPerformanceInformation>(this.deviceConnection);
#else
this.systemPerfWebSocket = new WebSocket<SystemPerformanceInformation>(this.deviceConnection, this.ServerCertificateValidation);
#endif
this.systemPerfWebSocket.WebSocketMessageReceived += this.SystemPerfReceivedHandler;
}
else
{
if (this.systemPerfWebSocket.IsListeningForMessages)
{
return;
}
}
await this.systemPerfWebSocket.ConnectAsync(SystemPerfApi);
await this.systemPerfWebSocket.ReceiveMessagesAsync();
}
/// <summary>
/// Stop listening for the system performance information for the device.
/// </summary>
/// <returns>Task for stop listening for system perf and disconnecting from the websocket .</returns>
public async Task StopListeningForSystemPerfAsync()
{
await this.systemPerfWebSocket.CloseAsync();
}
/// <summary>
/// Handler for the processes received event that passes the event to the RunningProcessesMessageReceived handler.
/// </summary>
/// <param name="sender">The <see cref="WebSocket{RunningProcesses}"/> object sending the event.</param>
/// <param name="args">The event data.</param>
private void RunningProcessesReceivedHandler(
WebSocket<RunningProcesses> sender,
WebSocketMessageReceivedEventArgs<RunningProcesses> args)
{
if (args.Message != null)
{
this.RunningProcessesMessageReceived?.Invoke(
this,
args);
}
}
/// <summary>
/// Handler for the system performance information received event that passes the event to the SystemPerfMessageReceived handler.
/// </summary>
/// <param name="sender">The <see cref="WebSocket{SystemPerformanceInformation}"/> object sending the event.</param>
/// <param name="args">The event data.</param>
private void SystemPerfReceivedHandler(
WebSocket<SystemPerformanceInformation> sender,
WebSocketMessageReceivedEventArgs<SystemPerformanceInformation> args)
{
if (args.Message != null)
{
this.SystemPerfMessageReceived?.Invoke(
this,
args);
}
}
#region Device contract
/// <summary>
/// Object representing the app version. Only present if the process is an app.
/// </summary>
[DataContract]
public class AppVersion
{
/// <summary>
/// Gets the major version number
/// </summary>
[DataMember(Name = "Major")]
public uint Major { get; private set; }
/// <summary>
/// Gets the minor version number
/// </summary>
[DataMember(Name = "Minor")]
public uint Minor { get; private set; }
/// <summary>
/// Gets the build number
/// </summary>
[DataMember(Name = "Build")]
public uint Build { get; private set; }
/// <summary>
/// Gets the revision number
/// </summary>
[DataMember(Name = "Revision")]
public uint Revision { get; private set; }
}
/// <summary>
/// Process Info. Contains app information if the process is an app.
/// </summary>
[DataContract]
public class DeviceProcessInfo
{
/// <summary>
/// Gets the app name. Only present if the process is an app.
/// </summary>
[DataMember(Name = "AppName")]
public string AppName { get; private set; }
/// <summary>
/// Gets CPU Usage as a percentage of available CPU resources (0-100)
/// </summary>
[DataMember(Name = "CPUUsage")]
public float CpuUsage { get; private set; }
/// <summary>
/// Gets the image name
/// </summary>
[DataMember(Name = "ImageName")]
public string Name { get; private set; }
/// <summary>
/// Gets the process id (pid)
/// </summary>
[DataMember(Name = "ProcessId")]
public uint ProcessId { get; private set; }
/// <summary>
/// Gets the user the process is running as.
/// </summary>
[DataMember(Name = "UserName")]
public string UserName { get; private set; }
/// <summary>
/// Gets the package full name. Only present if the process is an app.
/// </summary>
[DataMember(Name = "PackageFullName")]
public string PackageFullName { get; private set; }
/// <summary>
/// Gets the Page file usage info, in bytes
/// </summary>
[DataMember(Name = "PageFileUsage")]
public ulong PageFile { get; private set; }
/// <summary>
/// Gets the working set size, in bytes
/// </summary>
[DataMember(Name = "WorkingSetSize")]
public ulong WorkingSet { get; private set; }
/// <summary>
/// Gets package working set, in bytes
/// </summary>
[DataMember(Name = "PrivateWorkingSet")]
public ulong PrivateWorkingSet { get; private set; }
/// <summary>
/// Gets session id
/// </summary>
[DataMember(Name = "SessionId")]
public uint SessionId { get; private set; }
/// <summary>
/// Gets total commit, in bytes
/// </summary>
[DataMember(Name = "TotalCommit")]
public ulong TotalCommit { get; private set; }
/// <summary>
/// Gets virtual size, in bytes
/// </summary>
[DataMember(Name = "VirtualSize")]
public ulong VirtualSize { get; private set; }
/// <summary>
/// Gets a value indicating whether or not the app is running
/// (versus suspended). Only present if the process is an app.
/// </summary>
[DataMember(Name = "IsRunning")]
public bool IsRunning { get; private set; }
/// <summary>
/// Gets publisher. Only present if the process is an app.
/// </summary>
[DataMember(Name = "Publisher")]
public string Publisher { get; private set; }
/// <summary>
/// Gets version. Only present if the process is an app.
/// </summary>
[DataMember(Name = "Version")]
public AppVersion Version { get; private set; }
/// <summary>
/// Gets a value indicating whether or not the package is a XAP
/// package. Only present if the process is an app.
/// </summary>
[DataMember(Name = "IsXAP")]
public bool IsXAP { get; private set; }
/// <summary>
/// String representation of a process
/// </summary>
/// <returns>String representation</returns>
public override string ToString()
{
return string.Format("{0} ({1})", this.AppName, this.Name);
}
}
/// <summary>
/// GPU Adaptors
/// </summary>
[DataContract]
public class GpuAdapter
{
/// <summary>
/// Gets total Dedicated memory in bytes
/// </summary>
[DataMember(Name = "DedicatedMemory")]
public ulong DedicatedMemory { get; private set; }
/// <summary>
/// Gets used Dedicated memory in bytes
/// </summary>
[DataMember(Name = "DedicatedMemoryUsed")]
public ulong DedicatedMemoryUsed { get; private set; }
/// <summary>
/// Gets description
/// </summary>
[DataMember(Name = "Description")]
public string Description { get; private set; }
/// <summary>
/// Gets system memory in bytes
/// </summary>
[DataMember(Name = "SystemMemory")]
public ulong SystemMemory { get; private set; }
/// <summary>
/// Gets memory used in bytes
/// </summary>
[DataMember(Name = "SystemMemoryUsed")]
public ulong SystemMemoryUsed { get; private set; }
/// <summary>
/// Gets engines utilization as percent of maximum.
/// </summary>
[DataMember(Name = "EnginesUtilization")]
public List<double> EnginesUtilization { get; private set; }
}
/// <summary>
/// GPU performance data
/// </summary>
[DataContract]
public class GpuPerformanceData
{
/// <summary>
/// Gets list of available adapters
/// </summary>
[DataMember(Name = "AvailableAdapters")]
public List<GpuAdapter> Adapters { get; private set; }
}
/// <summary>
/// Network performance data
/// </summary>
[DataContract]
public class NetworkPerformanceData
{
/// <summary>
/// Gets current download speed in bytes per second
/// </summary>
[DataMember(Name = "NetworkInBytes")]
public ulong BytesIn { get; private set; }
/// <summary>
/// Gets current upload speed in bytes per second
/// </summary>
[DataMember(Name = "NetworkOutBytes")]
public ulong BytesOut { get; private set; }
}
/// <summary>
/// Running processes
/// </summary>
[DataContract]
public class RunningProcesses
{
/// <summary>
/// Gets list of running processes.
/// </summary>
[DataMember(Name = "Processes")]
public List<DeviceProcessInfo> Processes { get; private set; }
/// <summary>
/// Checks to see if this process Id is in the list of processes
/// </summary>
/// <param name="processId">Process to look for</param>
/// <returns>whether the process id was found</returns>
public bool Contains(int processId)
{
bool found = false;
foreach (DeviceProcessInfo pi in this.Processes)
{
if (pi.ProcessId == processId)
{
found = true;
break;
}
}
return found;
}
/// <summary>
/// Checks for a given package name
/// </summary>
/// <param name="packageName">Name of the package to look for</param>
/// <param name="caseSensitive">Whether we should be case sensitive in our search</param>
/// <returns>Whether the package was found</returns>
public bool Contains(string packageName, bool caseSensitive = true)
{
bool found = false;
foreach (DeviceProcessInfo pi in this.Processes)
{
if (string.Compare(
pi.PackageFullName,
packageName,
caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) == 0)
{
found = true;
break;
}
}
return found;
}
}
/// <summary>
/// System performance information
/// </summary>
[DataContract]
public class SystemPerformanceInformation
{
/// <summary>
/// Gets available pages
/// </summary>
[DataMember(Name = "AvailablePages")]
public ulong AvailablePages { get; private set; }
/// <summary>
/// Gets commit limit in bytes
/// </summary>
[DataMember(Name = "CommitLimit")]
public uint CommitLimit { get; private set; }
/// <summary>
/// Gets committed pages
/// </summary>
[DataMember(Name = "CommittedPages")]
public uint CommittedPages { get; private set; }
/// <summary>
/// Gets CPU load as percent of maximum (0 - 100)
/// </summary>
[DataMember(Name = "CpuLoad")]
public uint CpuLoad { get; private set; }
/// <summary>
/// Gets IO Other Speed in bytes per second
/// </summary>
[DataMember(Name = "IOOtherSpeed")]
public ulong IoOtherSpeed { get; private set; }
/// <summary>
/// Gets IO Read speed in bytes per second.
/// </summary>
[DataMember(Name = "IOReadSpeed")]
public ulong IoReadSpeed { get; private set; }
/// <summary>
/// Gets IO write speed in bytes per second
/// </summary>
[DataMember(Name = "IOWriteSpeed")]
public ulong IoWriteSpeed { get; private set; }
/// <summary>
/// Gets Non paged pool pages
/// </summary>
[DataMember(Name = "NonPagedPoolPages")]
public uint NonPagedPoolPages { get; private set; }
/// <summary>
/// Gets page size
/// </summary>
[DataMember(Name = "PageSize")]
public uint PageSize { get; private set; }
/// <summary>
/// Gets paged pool pages
/// </summary>
[DataMember(Name = "PagedPoolPages")]
public uint PagedPoolPages { get; private set; }
/// <summary>
/// Gets total installed in KB
/// </summary>
[DataMember(Name = "TotalInstalledInKb")]
public ulong TotalInstalledKb { get; private set; }
/// <summary>
/// Gets total pages
/// </summary>
[DataMember(Name = "TotalPages")]
public uint TotalPages { get; private set; }
/// <summary>
/// Gets GPU data
/// </summary>
[DataMember(Name = "GPUData")]
public GpuPerformanceData GpuData { get; private set; }
/// <summary>
/// Gets Networking data
/// </summary>
[DataMember(Name = "NetworkingData")]
public NetworkPerformanceData NetworkData { get; private set; }
}
#endregion // Device contract
}
}

Просмотреть файл

@ -0,0 +1,202 @@
//----------------------------------------------------------------------------------------------
// <copyright file="Power.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Power methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting or setting the active power scheme.
/// </summary>
public static readonly string ActivePowerSchemeApi = "api/power/activecfg";
/// <summary>
/// API for getting battery state.
/// </summary>
public static readonly string BatteryStateApi = "api/power/battery";
/// <summary>
/// API for getting or setting a power scheme's sub-value.
/// </summary>
public static readonly string PowerSchemeSubValueApi = "api/power/cfg";
/// <summary>
/// API for controlling power state.
/// </summary>
public static readonly string PowerStateApi = "api/power/state";
/// <summary>
/// API for getting a sleep study report.
/// </summary>
public static readonly string SleepStudyReportApi = "api/power/sleepstudy/report";
/// <summary>
/// API for getting the list of sleep study reports.
/// </summary>
public static readonly string SleepStudyReportsApi = "api/power/sleepstudy/reports";
/// <summary>
/// API for getting a sleep study report.
/// </summary>
public static readonly string SleepStudyTransformApi = "api/power/sleepstudy/transform";
/// <summary>
/// Returns the current active power scheme.
/// </summary>
/// <returns>The power scheme identifier.</returns>
public async Task<Guid> GetActivePowerSchemeAsync()
{
ActivePowerScheme activeScheme = await this.GetAsync<ActivePowerScheme>(ActivePowerSchemeApi);
return activeScheme.Id;
}
/// <summary>
/// Returns the current state of the device's battery.
/// </summary>
/// <returns>BatteryState object containing details such as the current battery level.</returns>
public async Task<BatteryState> GetBatteryStateAsync()
{
return await this.GetAsync<BatteryState>(BatteryStateApi);
}
/// <summary>
/// Gets the device's current power state.
/// </summary>
/// <returns>PowerState object containing details such as whether or not the device is in low power mode.</returns>
public async Task<PowerState> GetPowerStateAsync()
{
return await this.GetAsync<PowerState>(PowerStateApi);
}
#region Data contract
/// <summary>
/// Battery state.
/// </summary>
[DataContract]
public class ActivePowerScheme
{
/// <summary>
/// Gets the active power scheme identifier.
/// </summary>
[DataMember(Name = "ActivePowerScheme")]
public Guid Id { get; private set; }
}
/// <summary>
/// Battery state.
/// </summary>
[DataContract]
public class BatteryState
{
/// <summary>
/// Gets a value indicating whether the device is on AC power.
/// </summary>
[DataMember(Name = "AcOnline")]
public bool IsOnAcPower { get; private set; }
/// <summary>
/// Gets a value indicating whether a battery is present.
/// </summary>
[DataMember(Name = "BatteryPresent")]
public bool IsBatteryPresent { get; private set; }
/// <summary>
/// Gets a value indicating whether the device is charging.
/// </summary>
[DataMember(Name = "Charging")]
public bool IsCharging { get; private set; }
/// <summary>
/// Gets the default alert.
/// </summary>
[DataMember(Name = "DefaultAlert1")]
public int DefaultAlert1 { get; private set; }
/// <summary>
/// Gets the default alert.
/// </summary>
[DataMember(Name = "DefaultAlert2")]
public int DefaultAlert2 { get; private set; }
/// <summary>
/// Gets estimated battery time left in seconds.
/// </summary>
[DataMember(Name = "EstimatedTime")]
public uint EstimatedTimeRaw { get; private set; }
/// <summary>
/// Gets maximum capacity.
/// </summary>
[DataMember(Name = "MaximumCapacity")]
public int MaximumCapacity { get; private set; }
/// <summary>
/// Gets remaining capacity.
/// </summary>
[DataMember(Name = "RemainingCapacity")]
public int RemainingCapacity { get; private set; }
/// <summary>
/// Gets the battery level as a percentage of the maximum capacity.
/// </summary>
public float Level
{
get
{
// Desktop PCs typically do not have a battery, return 100%
if (this.MaximumCapacity == 0)
{
return 100f;
}
return 100.0f * ((float)this.RemainingCapacity / this.MaximumCapacity);
}
}
/// <summary>
/// Gets the remaining battery time left, as a TimeSpan.
/// Will be 0 if the device has no battery.
/// Will be 0xFFFF,FFFF (around 138 years) if the device is charging.
/// </summary>
public TimeSpan EstimatedTime
{
get
{
return new TimeSpan(0, 0, (int)this.EstimatedTimeRaw);
}
}
}
/// <summary>
/// Power state
/// </summary>
[DataContract]
public class PowerState
{
/// <summary>
/// Gets a value indicating whether the device is in a lower power mode
/// </summary>
[DataMember(Name = "LowPowerState")]
public bool InLowPowerState { get; private set; }
/// <summary>
/// Gets a value indicating whether the device supports a lower power mode
/// </summary>
[DataMember(Name = "LowPowerStateAvailable")]
public bool IsLowPowerStateAvailable { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,48 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RemoteControl.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Remote Control methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for rebooting the device.
/// </summary>
public static readonly string RebootApi = "api/control/restart";
/// <summary>
/// API for shutting down the device.
/// </summary>
public static readonly string ShutdownApi = "api/control/shutdown";
/// <summary>
/// Reboots the device.
/// </summary>
/// <returns>
/// Task tracking reboot completion.
/// </returns>
public async Task RebootAsync()
{
await this.PostAsync(RebootApi);
}
/// <summary>
/// Shuts down the device.
/// </summary>
/// <returns>
/// Task tracking shutdown completion.
/// </returns>
public async Task ShutdownAsync()
{
await this.PostAsync(ShutdownApi);
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
//----------------------------------------------------------------------------------------------
// <copyright file="TaskManager.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Task Manager methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for starting or stopping a modern application.
/// </summary>
public static readonly string TaskManagerApi = "api/taskmanager/app";
/// <summary>
/// Starts running the specified application.
/// </summary>
/// <param name="appid">Application ID</param>
/// <param name="packageName">The name of the application package.</param>
/// <returns>Process identifier for the application instance.</returns>
public async Task<uint> LaunchApplicationAsync(
string appid,
string packageName)
{
string payload = string.Format(
"appid={0}&package={1}",
Utilities.Hex64Encode(appid),
Utilities.Hex64Encode(packageName));
await this.PostAsync(
TaskManagerApi,
payload);
RunningProcesses runningApps = await this.GetRunningProcessesAsync();
uint processId = 0;
foreach (DeviceProcessInfo process in runningApps.Processes)
{
if (string.Compare(process.PackageFullName, packageName) == 0)
{
processId = process.ProcessId;
break;
}
}
return processId;
}
/// <summary>
/// Stops the specified application from running.
/// </summary>
/// <param name="packageName">The name of the application package.</param>
/// <returns>
/// Task for tracking termination completion
/// </returns>
public async Task TerminateApplicationAsync(string packageName)
{
await this.DeleteAsync(
TaskManagerApi,
string.Format("package={0}", Utilities.Hex64Encode(packageName)));
}
}
}

Просмотреть файл

@ -0,0 +1,248 @@
//----------------------------------------------------------------------------------------------
// <copyright file="WiFiManagement.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for WiFi management methods.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting the WiFi interfaces.
/// </summary>
public static readonly string WifiInterfacesApi = "api/wifi/interfaces";
/// <summary>
/// API for the controlling the WiFi network.
/// </summary>
public static readonly string WifiNetworkApi = "api/wifi/network";
/// <summary>
/// API for getting available WiFi networks.
/// </summary>
public static readonly string WifiNetworksApi = "api/wifi/networks";
/// <summary>
/// Connect to a WiFi network using a given network adapter and SSID.
/// </summary>
/// <param name="networkAdapter">Network adaptor GUID.</param>
/// <param name="ssid">SSID of the network.</param>
/// <param name="networkKey">Network key.</param>
/// <returns>Task tracking connection status.</returns>
public async Task ConnectToWifiNetworkAsync(
Guid networkAdapter,
string ssid,
string networkKey)
{
string payload = string.Format(
"interface={0}&ssid={1}&op=connect&createprofile=yes&key={2}",
networkAdapter.ToString(),
Utilities.Hex64Encode(ssid),
Utilities.Hex64Encode(networkKey));
await this.PostAsync(
WifiNetworkApi,
payload);
}
/// <summary>
/// Gets WiFi interfaces.
/// </summary>
/// <returns>List of WiFi interfaces.</returns>
public async Task<WifiInterfaces> GetWifiInterfacesAsync()
{
return await this.GetAsync<WifiInterfaces>(WifiInterfacesApi);
}
/// <summary>
/// Gets WiFi networks as seen from a WiFi interface.
/// </summary>
/// <param name="interfaceGuid">Interface to get networks from.</param>
/// <returns>List of available networks.</returns>
public async Task<WifiNetworks> GetWifiNetworksAsync(Guid interfaceGuid)
{
return await this.GetAsync<WifiNetworks>(
WifiNetworksApi,
string.Format("interface={0}", interfaceGuid.ToString()));
}
#region Data contract
/// <summary>
/// WiFi interface.
/// </summary>
[DataContract]
public class WifiInterface
{
/// <summary>
/// Gets description.
/// </summary>
[DataMember(Name = "Description")]
public string Description { get; private set; }
/// <summary>
/// Gets GUID.
/// </summary>
[DataMember(Name = "GUID")]
public Guid Guid { get; private set; }
/// <summary>
/// Gets index.
/// </summary>
[DataMember(Name = "Index")]
public int Index { get; private set; }
/// <summary>
/// Gets profiles list.
/// </summary>
[DataMember(Name = "ProfilesList")]
public List<WifiNetworkProfile> Profiles { get; private set; }
}
/// <summary>
/// WiFi interfaces.
/// </summary>
[DataContract]
public class WifiInterfaces
{
/// <summary>
/// Gets the list of interfaces.
/// </summary>
[DataMember(Name = "Interfaces")]
public List<WifiInterface> Interfaces { get; private set; }
}
/// <summary>
/// WiFi networks.
/// </summary>
[DataContract]
public class WifiNetworks
{
/// <summary>
/// Gets the list of available networks.
/// </summary>
[DataMember(Name = "AvailableNetworks")]
public List<WifiNetworkInfo> AvailableNetworks { get; private set; }
}
/// <summary>
/// WiFi network info.
/// </summary>
[DataContract]
public class WifiNetworkInfo
{
/// <summary>
/// Gets a value indicating whether the device is already connected to this network.
/// </summary>
[DataMember(Name = "AlreadyConnected")]
public bool IsConnected { get; private set; }
/// <summary>
/// Gets the authentication algorithm.
/// </summary>
[DataMember(Name = "AuthenticationAlgorithm")]
public string AuthenticationAlgorithm { get; private set; }
/// <summary>
/// Gets the channel.
/// </summary>
[DataMember(Name = "Channel")]
public int Channel { get; private set; }
/// <summary>
/// Gets the cipher algorithm.
/// </summary>
[DataMember(Name = "CipherAlgorithm")]
public string CipherAlgorithm { get; private set; }
/// <summary>
/// Gets a value indicating whether this network is connectable
/// </summary>
[DataMember(Name = "Connectable")]
public bool IsConnectable { get; private set; }
/// <summary>
/// Gets the infrastructure type - ad hoc or standard.
/// </summary>
[DataMember(Name = "InfrastructureType")]
public string InfrastructureType { get; private set; }
/// <summary>
/// Gets a value indicating whether a profile is available.
/// </summary>
[DataMember(Name = "ProfileAvailable")]
public bool IsProfileAvailable { get; private set; }
/// <summary>
/// Gets the profile name.
/// </summary>
[DataMember(Name = "ProfileName")]
public string ProfileName { get; private set; }
/// <summary>
/// Gets the SSID.
/// </summary>
[DataMember(Name = "SSID")]
public string Ssid { get; private set; }
/// <summary>
/// Gets a value indicating whether security is enabled.
/// </summary>
[DataMember(Name = "SecurityEnabled")]
public bool IsSecurityEnabled { get; private set; }
/// <summary>
/// Gets the signal quality.
/// </summary>
[DataMember(Name = "SignalQuality")]
public int SignalQuality { get; private set; }
/// <summary>
/// Gets the BSSID.
/// </summary>
[DataMember(Name = "BSSID")]
public List<int> Bssid { get; private set; }
/// <summary>
/// Gets physical types.
/// </summary>
[DataMember(Name = "PhysicalTypes")]
public List<string> NetworkTypes { get; private set; }
}
/// <summary>
/// WiFi network profile.
/// </summary>
[DataContract]
public class WifiNetworkProfile
{
/// <summary>
/// Gets a value indicating whether this is a group policy profile.
/// </summary>
[DataMember(Name = "GroupPolicyProfile")]
public bool IsGroupPolicyProfile { get; private set; }
/// <summary>
/// Gets the name.
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; private set; }
/// <summary>
/// Gets a value indicating whether this is a per user profile.
/// </summary>
[DataMember(Name = "PerUserProfile")]
public bool IsPerUserProfile { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,215 @@
//----------------------------------------------------------------------------------------------
// <copyright file="WindowsErrorReporting.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
namespace Microsoft.Tools.WindowsDevicePortal
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
/// <content>
/// Wrapper for collecting Windows Error Reports from the device.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for downloading a Windows error reporting file.
/// </summary>
public static readonly string WindowsErrorReportingFileApi = "api/wer/report/file";
/// <summary>
/// API for getting the list of files in a Windows error report.
/// </summary>
public static readonly string WindowsErrorReportingFilesApi = "api/wer/report/files";
/// <summary>
/// API for getting the list of Windows error reports.
/// </summary>
public static readonly string WindowsErrorReportsApi = "api/wer/reports";
/// <summary>
/// Gets the list of Windows Error Reporting (WER) reports.
/// </summary>
/// <returns>The list of Windows Error Reporting (WER) reports.</returns>
public async Task<WerDeviceReports> GetWindowsErrorReportsAsync()
{
this.CheckPlatformSupport();
return await this.GetAsync<WerDeviceReports>(WindowsErrorReportsApi);
}
/// <summary>
/// Gets the list of files in a Windows Error Reporting (WER) report.
/// </summary>
/// <param name="user">The user associated with the report.</param>
/// <param name="type">The type of report. This can be either 'queried' or 'archived'.</param>
/// <param name="name">The name of the report.</param>
/// <returns>The list of files.</returns>
public async Task<WerFiles> GetWindowsErrorReportingFileListAsync(string user, string type, string name)
{
this.CheckPlatformSupport();
Dictionary<string, string> payload = new Dictionary<string, string>();
payload.Add("user", user);
payload.Add("type", type);
payload.Add("name", Utilities.Hex64Encode(name));
return await this.GetAsync<WerFiles>(WindowsErrorReportingFilesApi, Utilities.BuildQueryString(payload));
}
/// <summary>
/// Gets the specified file from a Windows Error Reporting (WER) report.
/// </summary>
/// <param name="user">The user associated with the report.</param>
/// <param name="type">The type of report. This can be either 'queried' or 'archived'.</param>
/// <param name="name">The name of the report.</param>
/// <param name="file">The name of the file to download from the report.</param>
/// <returns>Byte array containing the file data</returns>
public async Task<byte[]> GetWindowsErrorReportingFileAsync(string user, string type, string name, string file)
{
this.CheckPlatformSupport();
Dictionary<string, string> payload = new Dictionary<string, string>();
payload.Add("user", user);
payload.Add("type", type);
payload.Add("name", Utilities.Hex64Encode(name));
payload.Add("file", Utilities.Hex64Encode(file));
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
WindowsErrorReportingFileApi,
Utilities.BuildQueryString(payload));
byte[] werFile = null;
using (Stream stream = await this.GetAsync(uri))
{
werFile = new byte[stream.Length];
stream.Read(werFile, 0, werFile.Length);
}
return werFile;
}
/// <summary>
/// Checks if the Windows Error Reporting (WER) APIs are being called on a supported platform.
/// </summary>
private void CheckPlatformSupport()
{
switch (this.Platform)
{
case DevicePortalPlatforms.Mobile:
case DevicePortalPlatforms.XboxOne:
throw new NotSupportedException("This method is only supported on Windows Desktop, HoloLens and IoT platforms.");
}
}
/// <summary>
/// A list of all files contained within a Windows Error Reporting (WER) report.
/// </summary>
[DataContract]
public class WerFiles
{
/// <summary>
/// Gets a list of all files contained within a Windows Error Reporting (WER) report.
/// </summary>
[DataMember(Name = "Files")]
public List<WerFileInformation> Files { get; private set; }
}
/// <summary>
/// Information about a Windows Error Reporting (WER) report file.
/// </summary>
[DataContract]
public class WerFileInformation
{
/// <summary>
/// Gets the name of the file.
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; private set; }
/// <summary>
/// Gets the size of the file (in bytes).
/// </summary>
[DataMember(Name = "Size")]
public int Size { get; private set; }
}
/// <summary>
/// A list of all Windows Error Reporting (WER) reports on a device.
/// </summary>
[DataContract]
public class WerDeviceReports
{
/// <summary>
/// Gets a list of all Windows Error Reporting (WER) reports on a
/// device. The SYSTEM user account usually holds the bulk of the
/// error reports.
/// </summary>
[DataMember(Name = "WerReports")]
public List<WerUserReports> UserReports { get; private set; }
/// <summary>
/// Gets system error reports - Convenience accessor for the System error reports - this is
/// where most error reports end up.
/// </summary>
public WerUserReports SystemErrorReports
{
get
{
return this.UserReports.First(x => x.UserName == "SYSTEM");
}
}
}
/// <summary>
/// A list of Windows Error Reporting (WER) reports for a specific user.
/// </summary>
[DataContract]
public class WerUserReports
{
/// <summary>
/// Gets the user name.
/// </summary>
[DataMember(Name = "User")]
public string UserName { get; private set; }
/// <summary>
/// Gets a list of Windows Error Reporting (WER) reports
/// </summary>
[DataMember(Name = "Reports")]
public List<WerReportInformation> Reports { get; private set; }
}
/// <summary>
/// Information about a Windows Error Reporting (WER) report.
/// </summary>
[DataContract]
public class WerReportInformation
{
/// <summary>
/// Gets the creation time.
/// </summary>
[DataMember(Name = "CreationTime")]
public ulong CreationTime { get; private set; }
/// <summary>
/// Gets the report name (not base64 encoded).
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; private set; }
/// <summary>
/// Gets the report type ("Queue" or "Archive").
/// </summary>
[DataMember(Name = "Type")]
public string Type { get; private set; }
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
//----------------------------------------------------------------------------------------------
// <copyright file="WindowsPerformanceRecorder.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for DNS methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for starting and stopping a Windows performance recorder boot performance trace session.
/// </summary>
public static readonly string WindowsPerformanceBootTraceApi = "api/wpr/boottrace";
/// <summary>
/// API for starting a Windows performance recorder trace using a custom profile.
/// </summary>
public static readonly string WindowsPerformanceCustomTraceApi = "api/wpr/customtrace";
/// <summary>
/// API for starting and stopping a Windows performance recorder trace session.
/// </summary>
public static readonly string WindowsPerformanceTraceApi = "api/wpr/trace";
/// <summary>
/// API for getting the status of a Windows performance recorder trace session.
/// </summary>
public static readonly string WindowsPerformanceTraceStatusApi = "api/wpr/status";
}
}

Просмотреть файл

@ -0,0 +1,204 @@
//----------------------------------------------------------------------------------------------
// <copyright file="DefaultDevicePortalConnection.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using static Microsoft.Tools.WindowsDevicePortal.DevicePortal;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Default implementation of the IDevicePortalConnection interface.
/// This implementation is designed to be compatibile with all device families.
/// </summary>
public class DefaultDevicePortalConnection : IDevicePortalConnection
{
/// <summary>
/// The device's root certificate.
/// </summary>
private X509Certificate2 deviceCertificate = null;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultDevicePortalConnection" /> class.
/// </summary>
/// <param name="address">The fully qualified (ex: "https:/1.2.3.4:4321") address of the device.</param>
/// <param name="userName">The user name used in the connection credentials.</param>
/// <param name="password">The password used in the connection credentials.</param>
public DefaultDevicePortalConnection(
string address,
string userName,
string password)
{
this.Connection = new Uri(address);
if (!string.IsNullOrEmpty(userName) &&
!string.IsNullOrEmpty(password))
{
// append auto- to the credentials to bypass CSRF token requirement on non-Get requests.
this.Credentials = new NetworkCredential(string.Format("auto-{0}", userName), password);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="DefaultDevicePortalConnection"/> class, using a SecureString to secure the password.
/// </summary>
/// <param name="address">device identifier</param>
/// <param name="userName">WDP username</param>
/// <param name="password">WDP password</param>
public DefaultDevicePortalConnection(
string address,
string userName,
System.Security.SecureString password)
{
this.Connection = new Uri(address);
if (!string.IsNullOrEmpty(userName) &&
password != null &&
password.Length > 0)
{
// append auto- to the credentials to bypass CSRF token requirement on non-Get requests.
this.Credentials = new NetworkCredential(string.Format("auto-{0}", userName), password);
}
}
/// <summary>
/// Gets the URI used to connect to the device.
/// </summary>
public Uri Connection
{
get;
private set;
}
/// <summary>
/// Gets Web Socket Connection property
/// </summary>
public Uri WebSocketConnection
{
get
{
if (this.Connection == null)
{
return null;
}
// Convert the scheme from http[s] to ws[s].
string scheme = this.Connection.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) ? "wss" : "ws";
return new Uri(
string.Format(
"{0}://{1}",
scheme,
this.Connection.Authority));
}
}
/// <summary>
/// Gets the credentials used to connect to the device.
/// </summary>
public NetworkCredential Credentials
{
get;
private set;
}
/// <summary>
/// Gets or sets the device's operating system family.
/// </summary>
public string Family
{
get;
set;
}
/// <summary>
/// Gets or sets the operating system information.
/// </summary>
public OperatingSystemInformation OsInfo
{
get;
set;
}
/// <summary>
/// Gets the provided device certificate.
/// </summary>
/// <returns>Stored device certificate.</returns>
public X509Certificate2 GetDeviceCertificate()
{
return this.deviceCertificate;
}
/// <summary>
/// Stores a manually provided device certificate.
/// </summary>
/// <param name="certificate">The device's root certificate.</param>
public void SetDeviceCertificate(X509Certificate2 certificate)
{
this.deviceCertificate = certificate;
}
/// <summary>
/// Updates the device's connection Uri.
/// </summary>
/// <param name="requiresHttps">Indicates whether or not to always require a secure connection.</param>
public void UpdateConnection(bool requiresHttps)
{
this.Connection = new Uri(
string.Format(
"{0}://{1}",
requiresHttps ? "https" : "http",
this.Connection.Authority));
}
/// <summary>
/// Updates the device's connection Uri.
/// </summary>
/// <param name="ipConfig">Object that describes the current network configuration.</param>
/// <param name="requiresHttps">True if an https connection is required, false otherwise.</param>
/// <param name="preservePort">True if the previous connection's port is to continue to be used, false otherwise.</param>
public void UpdateConnection(
IpConfiguration ipConfig,
bool requiresHttps,
bool preservePort)
{
Uri newConnection = null;
foreach (NetworkAdapterInfo adapter in ipConfig.Adapters)
{
foreach (IpAddressInfo addressInfo in adapter.IpAddresses)
{
// We take the first, non-169.x.x.x address we find that is not 0.0.0.0.
if ((addressInfo.Address != "0.0.0.0") && !addressInfo.Address.StartsWith("169."))
{
string address = addressInfo.Address;
if (preservePort)
{
address = string.Format(
"{0}:{1}",
address,
this.Connection.Port);
}
newConnection = new Uri(
string.Format(
"{0}://{1}",
requiresHttps ? "https" : "http",
address));
break;
}
}
if (newConnection != null)
{
this.Connection = newConnection;
break;
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,430 @@
//----------------------------------------------------------------------------------------------
// <copyright file="DeviceInfo.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Device Information.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// IOT device information API.
/// </summary>
public static readonly string IoTOsInfoApi = "api/iot/device/information";
/// <summary>
/// IOT device timezone API.
/// </summary>
public static readonly string TimezoneInfoApi = "api/iot/device/timezones";
/// <summary>
/// IOT device datetime API.
/// </summary>
public static readonly string DateTimeInfoApi = "api/iot/device/datetime";
/// <summary>
/// IOT device Controller Driver API.
/// </summary>
public static readonly string ControllerDriverApi = "api/iot/device/controllersdriver";
/// <summary>
/// IOT display resolution API.
/// </summary>
public static readonly string DisplayResolutionApi = "api/iot/device/displayresolution";
/// <summary>
/// IOT display orientation API.
/// </summary>
public static readonly string DisplayOrientationApi = "api/iot/device/displayorientation";
/// <summary>
/// IOT device name API.
/// </summary>
public static readonly string DeviceNameApi = "api/iot/device/name";
/// <summary>
/// IOT Device password API.
/// </summary>
public static readonly string ResetPasswordApi = "api/iot/device/password";
/// <summary>
/// IOT remote debugging pin API.
/// </summary>
public static readonly string NewRemoteDebuggingPinApi = "api/iot/device/remotedebuggingpin";
/// <summary>
/// IOT set timezone API.
/// </summary>
public static readonly string SetTimeZoneApi = "api/iot/device/settimezone";
/// <summary>
/// Gets the IoT OS Information.
/// </summary>
/// <returns>String containing the OS information.</returns>
public async Task<IoTOSInfo> GetIoTOSInfoAsync()
{
return await this.GetAsync<IoTOSInfo>(IoTOsInfoApi);
}
/// <summary>
/// Gets the Timezone information.
/// </summary>
/// <returns>String containing the timezone information.</returns>
public async Task<TimezoneInfo> GetTimezoneInfoAsync()
{
return await this.GetAsync<TimezoneInfo>(TimezoneInfoApi);
}
/// <summary>
/// Gets the datetime information.
/// </summary>
/// <returns>String containing the datetime information.</returns>
public async Task<DateTimeInfo> GetDateTimeInfoAsync()
{
return await this.GetAsync<DateTimeInfo>(DateTimeInfoApi);
}
/// <summary>
/// Gets the controller driver information.
/// </summary>
/// <returns>String containing the controller driver information.</returns>
public async Task<ControllerDriverInfo> GetControllerDriverInfoAsync()
{
return await this.GetAsync<ControllerDriverInfo>(ControllerDriverApi);
}
/// <summary>
/// Gets the dispaly orientation information.
/// </summary>
/// <returns>String containing the dispaly orientation information.</returns>
public async Task<DisplayOrientationInfo> GetDisplayOrientationInfoAsync()
{
return await this.GetAsync<DisplayOrientationInfo>(DisplayOrientationApi);
}
/// <summary>
/// Gets the dispaly resolution information.
/// </summary>
/// <returns>String containing the dispaly resolution information.</returns>
public async Task<DisplayResolutionInfo> GetDisplayResolutionInfoAsync()
{
return await this.GetAsync<DisplayResolutionInfo>(DisplayResolutionApi);
}
/// <summary>
/// Sets the Device Name.
/// </summary>
/// <param name="name">Name to set for the device.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task SetIoTDeviceNameAsync(string name)
{
await this.PostAsync(DeviceNameApi, string.Format("newdevicename={0}", Utilities.Hex64Encode(name)));
}
/// <summary>
/// Sets a new password.
/// </summary>
/// <param name="oldPassword">Old password.</param>
/// <param name="newPassword">New desired password.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task<ErrorInformation> SetNewPasswordAsync(string oldPassword, string newPassword)
{
return await this.PostAsync<ErrorInformation>(
ResetPasswordApi,
string.Format("oldpassword={0}&newpassword={1}", Utilities.Hex64Encode(oldPassword), Utilities.Hex64Encode(newPassword)));
}
/// <summary>
/// Sets a new remote debugging pin.
/// </summary>
/// <param name="newPin">New pin.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task SetNewRemoteDebuggingPinAsync(string newPin)
{
await this.PostAsync(
NewRemoteDebuggingPinApi,
string.Format("newpin={0}", Utilities.Hex64Encode(newPin)));
}
/// <summary>
/// Sets controllers drivers.
/// </summary>
/// <param name="newDriver">Driver to set.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task<ControllerDriverInfo> SetControllersDriversAsync(string newDriver)
{
return await this.PostAsync<ControllerDriverInfo>(
ControllerDriverApi,
string.Format("newdriver={0}", Utilities.Hex64Encode(newDriver)));
}
/// <summary>
/// Sets Timezone.
/// </summary>
/// <param name="index">Timezone index.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task<ErrorInformation> SetTimeZoneAsync(int index)
{
return await this.PostAsync<ErrorInformation>(
SetTimeZoneApi,
string.Format("index={0}", index));
}
/// <summary>
/// Sets display resolution.
/// </summary>
/// <param name="displayResolution">New display resolution.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task SetDisplayResolutionAsync(string displayResolution)
{
await this.PostAsync(
DisplayResolutionApi,
string.Format("newdisplayresolution={0}", Utilities.Hex64Encode(displayResolution)));
}
/// <summary>
/// Set display orientation.
/// </summary>
/// <param name="displayOrientation">Desired orientation.</param>
/// <returns>Task tracking completion of the REST call.</returns>
public async Task SetDisplayOrientationAsync(string displayOrientation)
{
await this.PostAsync(
DisplayOrientationApi,
string.Format("newdisplayorientation={0}", Utilities.Hex64Encode(displayOrientation)));
}
#region Data contract
/// <summary>
/// Operating system information.
/// </summary>
[DataContract]
public class IoTOSInfo
{
/// <summary>
/// Gets the device model
/// </summary>
[DataMember(Name = "DeviceModel")]
public string Model { get; private set; }
/// <summary>
/// Gets the device name.
/// </summary>
[DataMember(Name = "DeviceName")]
public string Name { get; private set; }
/// <summary>
/// Gets the OS version
/// </summary>
[DataMember(Name = "OSVersion")]
public string OSVersion { get; private set; }
}
/// <summary>
/// Timezone information.
/// </summary>
[DataContract]
public class TimezoneInfo
{
/// <summary>
/// Gets the current timezone
/// </summary>
[DataMember(Name = "Current")]
public Timezone CurrentTimeZone { get; private set; }
/// <summary>
/// Gets the list of all timezones
/// </summary>
[DataMember(Name = "Timezones")]
public List<Timezone> Timezones { get; private set; }
}
/// <summary>
/// Timezone specifications.
/// </summary>
[DataContract]
public partial class Timezone
{
/// <summary>
/// Gets the timezone description
/// </summary>
[DataMember(Name = "Description")]
public string Description { get; private set; }
/// <summary>
/// Gets the timezone index
/// </summary>
[DataMember(Name = "Index")]
public int Index { get; private set; }
/// <summary>
/// Gets the timezone name
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; private set; }
}
/// <summary>
/// DateTime information.
/// </summary>
[DataContract]
public class DateTimeInfo
{
/// <summary>
/// Gets the current date time
/// </summary>
[DataMember(Name = "Current")]
public DateTimeDescription CurrentDateTime { get; private set; }
}
/// <summary>
/// Current Datetime information.
/// </summary>
[DataContract]
public class DateTimeDescription
{
/// <summary>
/// Gets the current day
/// </summary>
[DataMember(Name = "Day")]
public int Day { get; private set; }
/// <summary>
/// Gets the current hour
/// </summary>
[DataMember(Name = "Hour")]
public int Hour { get; private set; }
/// <summary>
/// Gets the current minute
/// </summary>
[DataMember(Name = "Minute")]
public int Min { get; private set; }
/// <summary>
/// Gets the current month
/// </summary>
[DataMember(Name = "Month")]
public int Month { get; private set; }
/// <summary>
/// Gets the current second
/// </summary>
[DataMember(Name = "Second")]
public int Sec { get; private set; }
/// <summary>
/// Gets the current year
/// </summary>
[DataMember(Name = "Year")]
public int Year { get; private set; }
}
/// <summary>
/// Controller Driver information.
/// </summary>
[DataContract]
public class ControllerDriverInfo
{
/// <summary>
/// Gets the current driver information
/// </summary>
[DataMember(Name = "CurrentDriver")]
public string CurrentDriver { get; private set; }
/// <summary>
/// Gets the list of all the controller drivers information
/// </summary>
[DataMember(Name = "ControllersDrivers")]
public List<string> ControllersDrivers { get; private set; }
/// <summary>
/// Gets the request for reboot
/// </summary>
[DataMember(Name = "RequestReboot")]
public string RequestReboot { get; private set; }
}
/// <summary>
/// Dispaly orientation information.
/// </summary>
[DataContract]
public class DisplayOrientationInfo
{
/// <summary>
/// Gets the dispaly orientation information
/// </summary>
[DataMember(Name = "Orientation")]
public int Orientation { get; private set; }
}
/// <summary>
/// Dispaly resolution information.
/// </summary>
[DataContract]
public class DisplayResolutionInfo
{
/// <summary>
/// Gets the current display resolution
/// </summary>
[DataMember(Name = "Current")]
public Resolution CurrentResolution { get; private set; }
/// <summary>
/// Gets the list of resolution specifications
/// </summary>
[DataMember(Name = "Resolutions")]
public List<Resolution> Resolutions { get; private set; }
}
/// <summary>
/// Dispaly resolution specifications.
/// </summary>
[DataContract]
public class Resolution
{
/// <summary>
/// Gets the list of supported display resolutions
/// </summary>
[DataMember(Name = "Resolution")]
public string ResolutionDetail { get; private set; }
/// <summary>
/// Gets the index for the resolution information
/// </summary>
[DataMember(Name = "Index")]
public int Index { get; private set; }
}
/// <summary>
/// Error information if a request fails.
/// </summary>
[DataContract]
public class ErrorInformation
{
/// <summary>
/// Gets the error code
/// </summary>
[DataMember(Name = "ErrorCode")]
public int ErrorCode { get; private set; }
/// <summary>
/// Gets the status of the request
/// </summary>
[DataMember(Name = "Status")]
public string Status { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,496 @@
//----------------------------------------------------------------------------------------------
// <copyright file="DevicePortal.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
#if !WINDOWS_UWP
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
#endif // !WINDOWS_UWP
#if WINDOWS_UWP
using System.Runtime.InteropServices.WindowsRuntime;
#endif // WINDOWS_UWP
#if !WINDOWS_UWP
using System.Security.Cryptography.X509Certificates;
#endif // !WINDOWS_UWP
using System.Threading;
using System.Threading.Tasks;
#if WINDOWS_UWP
using Windows.Security.Cryptography.Certificates;
using Windows.Web.Http;
using Windows.Web.Http.Headers;
#endif // WINDOWS_UWP
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// This is the main DevicePortal object. It contains methods for making HTTP REST calls against
/// all of the WDP endpoints covered by the wrapper project. Different endpoints have their
/// implementation separated out into individual files.
/// </summary>
public partial class DevicePortal
{
/// <summary>
/// Issuer for the device certificate.
/// </summary>
public static readonly string DevicePortalCertificateIssuer = "Microsoft Windows Web Management";
/// <summary>
/// Endpoint used to access the certificate.
/// </summary>
public static readonly string RootCertificateEndpoint = "config/rootcertificate";
/// <summary>
/// Expected number of OS version sections once the OS version is split by period characters
/// </summary>
private static readonly uint ExpectedOSVersionSections = 5;
/// <summary>
/// The target OS version section index once the OS version is split by periods
/// </summary>
private static readonly uint TargetOSVersionSection = 3;
/// <summary>
/// Device connection object.
/// </summary>
private IDevicePortalConnection deviceConnection;
#if !WINDOWS_UWP
/// <summary>
/// Initializes static members of the <see cref="DevicePortal" /> class.
/// </summary>
static DevicePortal()
{
System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
}
#endif
/// <summary>
/// Initializes a new instance of the <see cref="DevicePortal" /> class.
/// </summary>
/// <param name="connection">Implementation of a connection object.</param>
public DevicePortal(IDevicePortalConnection connection)
{
this.deviceConnection = connection;
}
/// <summary>
/// Handler for reporting connection status.
/// </summary>
public event DeviceConnectionStatusEventHandler ConnectionStatus;
/// <summary>
/// HTTP Methods
/// </summary>
public enum HttpMethods
{
/// <summary>
/// The HTTP Get method.
/// </summary>
Get,
/// <summary>
/// The HTTP Put method.
/// </summary>
Put,
/// <summary>
/// The HTTP Post method.
/// </summary>
Post,
/// <summary>
/// The HTTP Delete method.
/// </summary>
Delete,
/// <summary>
/// The HTTP WebSocket method.
/// </summary>
WebSocket
}
/// <summary>
/// Gets the device address.
/// </summary>
public string Address
{
get { return this.deviceConnection.Connection.Authority; }
}
/// <summary>
/// Gets the status code for establishing our connection.
/// </summary>
public HttpStatusCode ConnectionHttpStatusCode
{
get;
private set;
}
/// <summary>
/// Gets a description of why the connection failed.
/// </summary>
public string ConnectionFailedDescription
{
get;
private set;
}
/// <summary>
/// Gets the device operating system family.
/// </summary>
public string DeviceFamily
{
get
{
return (this.deviceConnection.Family != null) ? this.deviceConnection.Family : string.Empty;
}
}
/// <summary>
/// Gets the operating system version.
/// </summary>
public string OperatingSystemVersion
{
get
{
return (this.deviceConnection.OsInfo != null) ? this.deviceConnection.OsInfo.OsVersionString : string.Empty;
}
}
/// <summary>
/// Gets the device platform.
/// </summary>
public DevicePortalPlatforms Platform
{
get
{
return (this.deviceConnection.OsInfo != null) ? this.deviceConnection.OsInfo.Platform : DevicePortalPlatforms.Unknown;
}
}
/// <summary>
/// Gets the device platform name.
/// </summary>
public string PlatformName
{
get
{
return (this.deviceConnection.OsInfo != null) ? this.deviceConnection.OsInfo.PlatformName : "Unknown";
}
}
/// <summary>
/// Connects to the device pointed to by IDevicePortalConnection provided in the constructor.
/// </summary>
/// <param name="ssid">Optional network SSID.</param>
/// <param name="ssidKey">Optional network key.</param>
/// <param name="updateConnection">Indicates whether we should update this connection's IP address after connecting.</param>
/// <param name="manualCertificate">A manually provided X509 Certificate for trust validation against this device.</param>
/// <remarks>Connect sends ConnectionStatus events to indicate the current progress in the connection process.
/// Some applications may opt to not register for the ConnectionStatus event and await on Connect.</remarks>
/// <returns>Task for tracking the connect.</returns>
[method: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:ParameterMustNotSpanMultipleLines", Justification = "manualCertificate param doesn't really span multiple lines, it just has a different type for UWP and .NET implementations.")]
public async Task ConnectAsync(
string ssid = null,
string ssidKey = null,
bool updateConnection = false,
#if WINDOWS_UWP
Certificate manualCertificate = null)
#else
X509Certificate2 manualCertificate = null)
#endif
{
#if WINDOWS_UWP
this.ConnectionHttpStatusCode = HttpStatusCode.Ok;
#else
this.ConnectionHttpStatusCode = HttpStatusCode.OK;
#endif // WINDOWS_UWP
string connectionPhaseDescription = string.Empty;
if (manualCertificate != null)
{
this.SetManualCertificate(manualCertificate);
}
try
{
// Get the device family and operating system information.
connectionPhaseDescription = "Requesting operating system information";
this.SendConnectionStatus(
DeviceConnectionStatus.Connecting,
DeviceConnectionPhase.RequestingOperatingSystemInformation,
connectionPhaseDescription);
this.deviceConnection.Family = await this.GetDeviceFamilyAsync().ConfigureAwait(false);
this.deviceConnection.OsInfo = await this.GetOperatingSystemInformationAsync().ConfigureAwait(false);
// Default to using whatever was specified in the connection.
bool requiresHttps = this.IsUsingHttps();
// HoloLens is the only device that supports the GetIsHttpsRequired method.
if (this.deviceConnection.OsInfo.Platform == DevicePortalPlatforms.HoloLens)
{
// Check to see if HTTPS is required to communicate with this device.
connectionPhaseDescription = "Checking secure connection requirements";
this.SendConnectionStatus(
DeviceConnectionStatus.Connecting,
DeviceConnectionPhase.DeterminingConnectionRequirements,
connectionPhaseDescription);
requiresHttps = await this.GetIsHttpsRequiredAsync().ConfigureAwait(false);
}
// Connect the device to the specified network.
if (!string.IsNullOrWhiteSpace(ssid))
{
connectionPhaseDescription = string.Format("Connecting to {0} network", ssid);
this.SendConnectionStatus(
DeviceConnectionStatus.Connecting,
DeviceConnectionPhase.ConnectingToTargetNetwork,
connectionPhaseDescription);
WifiInterfaces wifiInterfaces = await this.GetWifiInterfacesAsync().ConfigureAwait(false);
// TODO - consider what to do if there is more than one wifi interface on a device
await this.ConnectToWifiNetworkAsync(wifiInterfaces.Interfaces[0].Guid, ssid, ssidKey).ConfigureAwait(false);
}
// Get the device's IP configuration and update the connection as appropriate.
if (updateConnection)
{
connectionPhaseDescription = "Updating device connection";
this.SendConnectionStatus(
DeviceConnectionStatus.Connecting,
DeviceConnectionPhase.UpdatingDeviceAddress,
connectionPhaseDescription);
bool preservePort = true;
// HoloLens and Mobile are the only devices that support USB.
// They require the port to be changed when the connection is updated
// to WiFi.
if ((this.Platform == DevicePortalPlatforms.HoloLens) ||
(this.Platform == DevicePortalPlatforms.Mobile))
{
preservePort = false;
}
this.deviceConnection.UpdateConnection(
await this.GetIpConfigAsync().ConfigureAwait(false),
requiresHttps,
preservePort);
}
this.SendConnectionStatus(
DeviceConnectionStatus.Connected,
DeviceConnectionPhase.Idle,
"Device connection established");
}
catch (Exception e)
{
DevicePortalException dpe = e as DevicePortalException;
if (dpe != null)
{
this.ConnectionHttpStatusCode = dpe.StatusCode;
this.ConnectionFailedDescription = dpe.Message;
}
else
{
this.ConnectionHttpStatusCode = HttpStatusCode.Conflict;
// Get to the innermost exception for our return message.
Exception innermostException = e;
while (innermostException.InnerException != null)
{
innermostException = innermostException.InnerException;
await Task.Yield();
}
this.ConnectionFailedDescription = innermostException.Message;
}
this.SendConnectionStatus(
DeviceConnectionStatus.Failed,
DeviceConnectionPhase.Idle,
string.Format("Device connection failed: {0}, {1}", connectionPhaseDescription, this.ConnectionFailedDescription));
}
}
/// <summary>
/// Helper method used for saving the content of a response to a file.
/// This allows unittests to easily generate real data to use as mock responses.
/// </summary>
/// <param name="endpoint">API endpoint we are calling.</param>
/// <param name="directory">Directory to store our file.</param>
/// <param name="httpMethod">The http method to be performed.</param>
/// <param name="requestBody">An optional stream to use for the request body content.</param>
/// <param name="requestBodyContentType">The content type of the request stream.</param>
/// <returns>Task waiting for HTTP call to return and file copy to complete.</returns>
public async Task SaveEndpointResponseToFileAsync(
string endpoint,
string directory,
HttpMethods httpMethod,
Stream requestBody = null,
string requestBodyContentType = null)
{
Uri uri = new Uri(this.deviceConnection.Connection, endpoint);
// Convert the OS version, such as 14385.1002.amd64fre.rs1_xbox_rel_1608.160709-1700, into a friendly OS version, such as rs1_xbox_rel_1608
string friendlyOSVersion = this.OperatingSystemVersion;
string[] versionParts = friendlyOSVersion.Split('.');
if (versionParts.Length == ExpectedOSVersionSections)
{
friendlyOSVersion = versionParts[TargetOSVersionSection];
}
// Create the filename as DeviceFamily_OSVersion.dat, replacing '/', '.', and '-' with '_' so
// we can create a class with the same name as this Device/OS pair for tests.
string filename = endpoint + "_" + this.Platform.ToString() + "_" + friendlyOSVersion;
if (httpMethod != HttpMethods.Get)
{
filename = httpMethod.ToString() + "_" + filename;
}
Utilities.ModifyEndpointForFilename(ref filename);
filename += ".dat";
string filepath = Path.Combine(directory, filename);
if (HttpMethods.WebSocket == httpMethod)
{
#if WINDOWS_UWP
WebSocket<object> websocket = new WebSocket<object>(this.deviceConnection, true);
#else
WebSocket<object> websocket = new WebSocket<object>(this.deviceConnection, this.ServerCertificateValidation, true);
#endif // WINDOWS_UWP
ManualResetEvent streamReceived = new ManualResetEvent(false);
Stream stream = null;
WindowsDevicePortal.WebSocketStreamReceivedEventInternalHandler<object> streamReceivedHandler =
delegate(WebSocket<object> sender, WebSocketMessageReceivedEventArgs<Stream> args)
{
if (args.Message != null)
{
stream = args.Message;
streamReceived.Set();
}
};
websocket.WebSocketStreamReceived += streamReceivedHandler;
await websocket.ConnectAsync(endpoint);
await websocket.ReceiveMessagesAsync();
streamReceived.WaitOne();
await websocket.CloseAsync();
websocket.WebSocketStreamReceived -= streamReceivedHandler;
using (stream)
{
using (var fileStream = File.Create(filepath))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fileStream);
}
}
}
else if (HttpMethods.Put == httpMethod)
{
#if WINDOWS_UWP
HttpStreamContent streamContent = null;
#else
StreamContent streamContent = null;
#endif // WINDOWS_UWP
if (requestBody != null)
{
#if WINDOWS_UWP
streamContent = new HttpStreamContent(requestBody.AsInputStream());
streamContent.Headers.ContentType = new HttpMediaTypeHeaderValue(requestBodyContentType);
#else
streamContent = new StreamContent(requestBody);
streamContent.Headers.ContentType = new MediaTypeHeaderValue(requestBodyContentType);
#endif // WINDOWS_UWP
}
using (Stream dataStream = await this.PutAsync(uri, streamContent))
{
using (var fileStream = File.Create(filepath))
{
dataStream.Seek(0, SeekOrigin.Begin);
dataStream.CopyTo(fileStream);
}
}
}
else if (HttpMethods.Post == httpMethod)
{
using (Stream dataStream = await this.PostAsync(uri, requestBody, requestBodyContentType))
{
using (var fileStream = File.Create(filepath))
{
dataStream.Seek(0, SeekOrigin.Begin);
dataStream.CopyTo(fileStream);
}
}
}
else if (HttpMethods.Delete == httpMethod)
{
using (Stream dataStream = await this.DeleteAsync(uri))
{
using (var fileStream = File.Create(filepath))
{
dataStream.Seek(0, SeekOrigin.Begin);
dataStream.CopyTo(fileStream);
}
}
}
else if (HttpMethods.Get == httpMethod)
{
using (Stream dataStream = await this.GetAsync(uri))
{
using (var fileStream = File.Create(filepath))
{
dataStream.Seek(0, SeekOrigin.Begin);
dataStream.CopyTo(fileStream);
}
}
}
else
{
throw new NotImplementedException(string.Format("Unsupported HttpMethod {0}", httpMethod.ToString()));
}
}
/// <summary>
/// Sends the connection status back to the caller
/// </summary>
/// <param name="status">Status of the connect attempt.</param>
/// <param name="phase">Current phase of the connection attempt.</param>
/// <param name="message">Optional message describing the connection status.</param>
private void SendConnectionStatus(
DeviceConnectionStatus status,
DeviceConnectionPhase phase,
string message = "")
{
this.ConnectionStatus?.Invoke(this, new DeviceConnectionStatusEventArgs(status, phase, message));
}
/// <summary>
/// Helper method to determine if our connection is using HTTPS
/// </summary>
/// <returns>Whether we are using HTTPS</returns>
private bool IsUsingHttps()
{
return this.deviceConnection.Connection.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase);
}
}
}

Просмотреть файл

@ -0,0 +1,106 @@
//----------------------------------------------------------------------------------------------
// <copyright file="ApplicationInstallStatus.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:FileHeaderFileNameDocumentationMustMatchTypeName", Justification = "Filename matches the enum better than the ApplicationInstallStatusEventArgs class.")]
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Application install status event handler
/// </summary>
/// <param name="sender">sender object</param>
/// <param name="args">install args</param>
public delegate void ApplicationInstallStatusEventHandler(DevicePortal sender, ApplicationInstallStatusEventArgs args);
/// <summary>
/// Application install status
/// </summary>
public enum ApplicationInstallStatus
{
/// <summary>
/// No install status
/// </summary>
None,
/// <summary>
/// Installation is in progress
/// </summary>
InProgress,
/// <summary>
/// Installation is completed
/// </summary>
Completed,
/// <summary>
/// Installation failed
/// </summary>
Failed
}
/// <summary>
/// Install phase
/// </summary>
public enum ApplicationInstallPhase
{
/// <summary>
/// Idle phase
/// </summary>
Idle,
/// <summary>
/// Uninstalling the previous version
/// </summary>
UninstallingPreviousVersion,
/// <summary>
/// Copying the package file
/// </summary>
CopyingFile,
/// <summary>
/// Installing the package
/// </summary>
Installing
}
/// <summary>
/// Application install status event args
/// </summary>
public class ApplicationInstallStatusEventArgs : System.EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationInstallStatusEventArgs"/> class.
/// </summary>
/// <param name="status">Install status</param>
/// <param name="phase">Install phase</param>
/// <param name="message">Install message</param>
internal ApplicationInstallStatusEventArgs(
ApplicationInstallStatus status,
ApplicationInstallPhase phase,
string message = "")
{
this.Status = status;
this.Phase = phase;
this.Message = message;
}
/// <summary>
/// Gets the install status
/// </summary>
public ApplicationInstallStatus Status { get; private set; }
/// <summary>
/// Gets the install phase
/// </summary>
public ApplicationInstallPhase Phase { get; private set; }
/// <summary>
/// Gets the install message
/// </summary>
public string Message { get; private set; }
}
}

Просмотреть файл

@ -0,0 +1,116 @@
//----------------------------------------------------------------------------------------------
// <copyright file="ConnectionStatus.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:FileHeaderFileNameDocumentationMustMatchTypeName", Justification = "Filename matches the enum better than the EventArgs class.")]
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Handler for reporting on device connection status
/// </summary>
/// <param name="sender">sender object</param>
/// <param name="args">connection status details</param>
public delegate void DeviceConnectionStatusEventHandler(DevicePortal sender, DeviceConnectionStatusEventArgs args);
/// <summary>
/// Connection status enumeration
/// </summary>
public enum DeviceConnectionStatus
{
/// <summary>
/// No status
/// </summary>
None,
/// <summary>
/// Currently Connecting
/// </summary>
Connecting,
/// <summary>
/// Connection complete
/// </summary>
Connected,
/// <summary>
/// Connection failed
/// </summary>
Failed,
}
/// <summary>
/// Connection phase enumeration
/// </summary>
public enum DeviceConnectionPhase
{
/// <summary>
/// Idle phase
/// </summary>
Idle,
/// <summary>
/// Acquiring the device certificate
/// </summary>
AcquiringCertificate,
/// <summary>
/// Determining the device connection requirements
/// </summary>
DeterminingConnectionRequirements,
/// <summary>
/// Getting some basic information about the device OS
/// </summary>
RequestingOperatingSystemInformation,
/// <summary>
/// Connecting the device to a provided network
/// </summary>
ConnectingToTargetNetwork,
/// <summary>
/// Updating the device address to reflect the new network
/// </summary>
UpdatingDeviceAddress
}
/// <summary>
/// Contains the details about the connection status
/// </summary>
public class DeviceConnectionStatusEventArgs : System.EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="DeviceConnectionStatusEventArgs"/> class.
/// </summary>
/// <param name="status">Status of the connection</param>
/// <param name="phase">Phase of the connection</param>
/// <param name="message">Optional message describing our connection/phase</param>
internal DeviceConnectionStatusEventArgs(
DeviceConnectionStatus status,
DeviceConnectionPhase phase,
string message = "")
{
this.Status = status;
this.Phase = phase;
this.Message = message;
}
/// <summary>
/// Gets the status of the connection attempt
/// </summary>
public DeviceConnectionStatus Status { get; private set; }
/// <summary>
/// Gets the phase of the connection
/// </summary>
public DeviceConnectionPhase Phase { get; private set; }
/// <summary>
/// Gets a message describing the connection status/phase
/// </summary>
public string Message { get; private set; }
}
}

Просмотреть файл

@ -0,0 +1,40 @@
//----------------------------------------------------------------------------------------------
// <copyright file="WebSocketMessageReceivedEventArgs.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System.IO;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Web socket message received event handler
/// </summary>
/// <param name="sender">Sender <see cref="DevicePortal"/> object</param>
/// <param name="args">Web socket message received args</param>
/// <typeparam name="T">Return type for the websocket messages.</typeparam>
public delegate void WebSocketMessageReceivedEventHandler<T>(DevicePortal sender, WebSocketMessageReceivedEventArgs<T> args);
/// <summary>
/// Web socket message received event args
/// </summary>
/// <typeparam name="T">Return type for the websocket messages.</typeparam>
public class WebSocketMessageReceivedEventArgs<T> : System.EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="WebSocketMessageReceivedEventArgs{T}"/> class.
/// </summary>
/// <param name="message">The message from the web socket.</param>
internal WebSocketMessageReceivedEventArgs(
T message)
{
this.Message = message;
}
/// <summary>
/// Gets the web socket message
/// </summary>
public T Message { get; private set; }
}
}

Просмотреть файл

@ -0,0 +1,238 @@
//----------------------------------------------------------------------------------------------
// <copyright file="DevicePortalException.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
#if !WINDOWS_UWP
using System.Net;
using System.Net.Http;
#endif // !WINDOWS_UWP
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Security;
using System.Threading.Tasks;
#if WINDOWS_UWP
using Windows.Foundation;
using Windows.Storage.Streams;
using Windows.Web.Http;
#endif // WINDOWS_UWP
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Base exception class for a Device Portal exception
/// </summary>
#if !WINDOWS_UWP
[Serializable]
#endif // !WINDOWS_UWP
public class DevicePortalException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="DevicePortalException"/> class.
/// </summary>
/// <param name="statusCode">The Http status code.</param>
/// <param name="errorResponse">Http parsed error response message.</param>
/// <param name="requestUri">Request URI which threw the exception.</param>
/// <param name="message">Optional exception message.</param>
/// <param name="innerException">Optional inner exception.</param>
public DevicePortalException(
HttpStatusCode statusCode,
HttpErrorResponse errorResponse,
Uri requestUri = null,
string message = "",
Exception innerException = null) : this(
statusCode,
errorResponse.Reason,
requestUri,
message,
innerException)
{
this.HResult = errorResponse.ErrorCode;
this.Reason = errorResponse.ErrorMessage;
// If we didn't get the Hresult and reason from these properties, try the other ones.
if (this.HResult == 0)
{
this.HResult = errorResponse.Code;
}
if (string.IsNullOrEmpty(this.Reason))
{
this.Reason = errorResponse.Reason;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="DevicePortalException"/> class.
/// </summary>
/// <param name="statusCode">Http status code.</param>
/// <param name="reason">Reason for exception.</param>
/// <param name="requestUri">Request URI which threw the exception.</param>
/// <param name="message">Optional message.</param>
/// <param name="innerException">Optional inner exception.</param>
public DevicePortalException(
HttpStatusCode statusCode,
string reason,
Uri requestUri = null,
string message = "",
Exception innerException = null) : base(
message,
innerException)
{
this.StatusCode = statusCode;
this.Reason = reason;
this.RequestUri = requestUri;
}
/// <summary>
/// Gets the HTTP Status code.
/// </summary>
public HttpStatusCode StatusCode { get; private set; }
/// <summary>
/// Gets a reason for the exception.
/// </summary>
public string Reason { get; private set; }
/// <summary>
/// Gets the request URI that threw the exception.
/// </summary>
public Uri RequestUri { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="DevicePortalException"/> class.
/// </summary>
/// <param name="responseMessage">Http response message.</param>
/// <param name="message">Optional exception message.</param>
/// <param name="innerException">Optional inner exception.</param>
/// <returns>async task</returns>
public static async Task<DevicePortalException> CreateAsync(
HttpResponseMessage responseMessage,
string message = "",
Exception innerException = null)
{
DevicePortalException error = new DevicePortalException(
responseMessage.StatusCode,
responseMessage.ReasonPhrase,
responseMessage.RequestMessage != null ? responseMessage.RequestMessage.RequestUri : null,
message,
innerException);
try
{
if (responseMessage.Content != null)
{
Stream dataStream = null;
#if !WINDOWS_UWP
using (HttpContent content = responseMessage.Content)
{
dataStream = new MemoryStream();
await content.CopyToAsync(dataStream).ConfigureAwait(false);
// Ensure we point the stream at the origin.
dataStream.Position = 0;
}
#else // WINDOWS_UWP
IBuffer dataBuffer = null;
using (IHttpContent messageContent = responseMessage.Content)
{
dataBuffer = await messageContent.ReadAsBufferAsync();
if (dataBuffer != null)
{
dataStream = dataBuffer.AsStream();
}
}
#endif // WINDOWS_UWP
if (dataStream != null)
{
HttpErrorResponse errorResponse = DevicePortal.ReadJsonStream<HttpErrorResponse>(dataStream);
if (errorResponse != null)
{
error.HResult = errorResponse.ErrorCode;
error.Reason = errorResponse.ErrorMessage;
// If we didn't get the Hresult and reason from these properties, try the other ones.
if (error.HResult == 0)
{
error.HResult = errorResponse.Code;
}
if (string.IsNullOrEmpty(error.Reason))
{
error.Reason = errorResponse.Reason;
}
}
}
}
}
catch (Exception)
{
// Do nothing if we fail to get additional error details from the response body.
}
return error;
}
#if !WINDOWS_UWP
/// <summary>
/// Get object data override
/// </summary>
/// <param name="info">Serialization info</param>
/// <param name="context">Streaming context</param>
[SecurityCritical]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
#endif // !WINDOWS_UWP
#region data contract
/// <summary>
/// Object containing additional error information from
/// an HTTP response.
/// </summary>
[DataContract]
public class HttpErrorResponse
{
/// <summary>
/// Gets the ErrorCode
/// </summary>
[DataMember(Name = "ErrorCode")]
public int ErrorCode { get; private set; }
/// <summary>
/// Gets the Code (used by some endpoints instead of ErrorCode).
/// </summary>
[DataMember(Name = "Code")]
public int Code { get; private set; }
/// <summary>
/// Gets the ErrorMessage
/// </summary>
[DataMember(Name = "ErrorMessage")]
public string ErrorMessage { get; private set; }
/// <summary>
/// Gets the Reason (used by some endpoints instead of ErrorMessage).
/// </summary>
[DataMember(Name = "Reason")]
public string Reason { get; private set; }
/// <summary>
/// Gets a value indicating whether the operation succeeded. For an error this should generally be false if present.
/// </summary>
[DataMember(Name = "Success")]
public bool Success { get; private set; }
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,270 @@
// <copyright file="HolographicOs.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Holographic OS methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting or setting Interpupilary distance
/// </summary>
public static readonly string HolographicIpdApi = "api/holographic/os/settings/ipd";
/// <summary>
/// API for getting a list of running HoloLens specific services.
/// </summary>
public static readonly string HolographicServicesApi = "api/holographic/os/services";
/// <summary>
/// API for getting or setting HTTPS setting
/// </summary>
public static readonly string HolographicWebManagementHttpSettingsApi = "api/holographic/os/webmanagement/settings/https";
/// <summary>
/// Enumeration describing the status of a process
/// </summary>
public enum ProcessStatus
{
/// <summary>
/// The process is running
/// </summary>
Running = 0,
/// <summary>
/// The process is stopped
/// </summary>
Stopped
}
/// <summary>
/// Gets the status of the Holographic Services on this HoloLens.
/// </summary>
/// <returns>HolographicServices object describing the state of the Holographic services.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<HolographicServices> GetHolographicServiceState()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
return await this.GetAsync<HolographicServices>(HolographicServicesApi);
}
/// <summary>
/// Gets the interpupilary distance registered on the device.
/// </summary>
/// <returns>Interpupilary distance, in millimeters.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<float> GetInterPupilaryDistanceAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
InterPupilaryDistance ipd = await this.GetAsync<InterPupilaryDistance>(HolographicIpdApi);
return ipd.Ipd;
}
/// <summary>
/// Sets the WiFi http security requirements for communication with the device.
/// </summary>
/// <param name="httpsRequired">Desired value for HTTPS communication</param>
/// <returns>True if WiFi based communication requires a secure connection, false otherwise.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task SetIsHttpsRequiredAsync(bool httpsRequired)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
await this.PostAsync(
HolographicWebManagementHttpSettingsApi,
string.Format("required={0}", httpsRequired));
this.deviceConnection.UpdateConnection(httpsRequired);
}
/// <summary>
/// Sets the interpupilary distance registered on the device.
/// </summary>
/// <param name="ipd">Interpupilary distance, in millimeters.</param>
/// <returns>Task for tracking the POST call</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task SetInterPupilaryDistanceAsync(float ipd)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format("ipd={0}", (int)(ipd * 1000.0f));
await this.PostAsync(
HolographicIpdApi,
payload);
}
/// <summary>
/// Gets the WiFi http security requirements for communication with the device.
/// </summary>
/// <returns>True if WiFi based communication requires a secure connection, false otherwise.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<bool> GetIsHttpsRequiredAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
WebManagementHttpSettings httpSettings = await this.GetAsync<WebManagementHttpSettings>(HolographicWebManagementHttpSettingsApi);
return httpSettings.IsHttpsRequired;
}
#region Data contract
/// <summary>
/// Object reporesentation of the status of the Holographic services
/// </summary>
[DataContract]
public class HolographicServices
{
/// <summary>
/// Gets the status for the collection of holographic services
/// </summary>
[DataMember(Name = "SoftwareStatus")]
public HolographicSoftwareStatus Status { get; private set; }
}
/// <summary>
/// Object representation of the collection of holographic services.
/// </summary>
[DataContract]
public class HolographicSoftwareStatus
{
/// <summary>
/// Gets the status of dwm.exe
/// </summary>
[DataMember(Name = "dwm.exe")]
public ServiceStatus Dwm { get; private set; }
/// <summary>
/// Gets the status of holoshellapp.exe
/// </summary>
[DataMember(Name = "holoshellapp.exe")]
public ServiceStatus HoloShellApp { get; private set; }
/// <summary>
/// Gets the status of holosi.exe
/// </summary>
[DataMember(Name = "holosi.exe")]
public ServiceStatus HoloSi { get; private set; }
/// <summary>
/// Gets the status of mixedrealitycapture.exe
/// </summary>
[DataMember(Name = "mixedrealitycapture.exe")]
public ServiceStatus MixedRealitytCapture { get; private set; }
/// <summary>
/// Gets the status of sihost.exe
/// </summary>
[DataMember(Name = "sihost.exe")]
public ServiceStatus SiHost { get; private set; }
/// <summary>
/// Gets the status of spectrum.exe
/// </summary>
[DataMember(Name = "spectrum.exe")]
public ServiceStatus Spectrum { get; private set; }
}
/// <summary>
/// Object representation for Interpupilary distance
/// </summary>
[DataContract]
public class InterPupilaryDistance
{
/// <summary>
/// Gets the raw interpupilary distance
/// </summary>
[DataMember(Name = "ipd")]
public int IpdRaw { get; private set; }
/// <summary>
/// Gets or sets the interpupilary distance
/// </summary>
public float Ipd
{
get { return this.IpdRaw / 1000.0f; }
set { this.IpdRaw = (int)(value * 1000); }
}
}
/// <summary>
/// Object representation of the status of a service
/// </summary>
[DataContract]
public class ServiceStatus
{
/// <summary>
/// Gets the raw value returned for the expected service status
/// </summary>
[DataMember(Name = "Expected")]
public string ExpectedRaw { get; private set; }
/// <summary>
/// Gets the raw value returned for the observed service status
/// </summary>
[DataMember(Name = "Observed")]
public string ObservedRaw { get; private set; }
/// <summary>
/// Gets the the expected service status
/// </summary>
public ProcessStatus Expected
{
get
{
return (this.ExpectedRaw == "Running") ? ProcessStatus.Running : ProcessStatus.Stopped;
}
}
/// <summary>
/// Gets the the observed service status
/// </summary>
public ProcessStatus Observed
{
get
{
return (this.ObservedRaw == "Running") ? ProcessStatus.Running : ProcessStatus.Stopped;
}
}
}
/// <summary>
/// Object representation for HTTP settings
/// </summary>
[DataContract]
public class WebManagementHttpSettings
{
/// <summary>
/// Gets a value indicating whether HTTPS is required
/// </summary>
[DataMember(Name = "httpsRequired")]
public bool IsHttpsRequired { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,195 @@
//----------------------------------------------------------------------------------------------
// <copyright file="HolographicPerception.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Holographic Perception methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for running a Perception client.
/// </summary>
public static readonly string HolographicPerceptionClient = "api/holographic/perception/client";
/// <summary>
/// API for getting or setting the Holographic Perception Simulation control mode.
/// </summary>
public static readonly string HolographicSimulationModeApi = "api/holographic/simulation/control/mode";
/// <summary>
/// API for controlling the Holographic Perception Simulation control stream.
/// </summary>
public static readonly string HolographicSimulationStreamApi = "api/holographic/simulation/control/stream";
/// <summary>
/// Enumeration defining the control modes used by the Holographic Perception Simulation.
/// </summary>
public enum SimulationControlMode
{
/// <summary>
/// Default mode.
/// </summary>
Default = 0,
/// <summary>
/// Simulation mode.
/// </summary>
Simulation
}
/// <summary>
/// Enumeration defining the priority levels for the Holographic Perception Simulation control stream.
/// </summary>
public enum SimulationControlStreamPriority
{
/// <summary>
/// Low priority.
/// </summary>
Low = 0,
/// <summary>
/// Normal priority.
/// </summary>
Normal
}
/// <summary>
/// Creates a simulation control stream.
/// </summary>
/// <param name="priority">The control stream priority.</param>
/// <returns>The identifier of the created stream.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<string> CreatePerceptionSimulationControlStreamAsync(SimulationControlStreamPriority priority)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
if (!(await this.VerifySimulationControlModeAsync(SimulationControlMode.Simulation)))
{
throw new InvalidOperationException("The simulation control mode on the target HoloLens must be 'Simulation'.");
}
string payload = string.Format(
"priority={0}",
(int)priority);
PerceptionSimulationControlStreamId controlStreamId = await this.GetAsync<PerceptionSimulationControlStreamId>(
HolographicSimulationStreamApi,
payload);
return controlStreamId.StreamId;
}
/// <summary>
/// Deletes a simulation control stream.
/// </summary>
/// <param name="streamId">The identifier of the stream to be deleted.</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task DeletePerceptionSimulationControlStreamAsync(string streamId)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
if (!(await this.VerifySimulationControlModeAsync(SimulationControlMode.Simulation)))
{
throw new InvalidOperationException("The simulation control mode on the target HoloLens must be 'Simulation'.");
}
string payload = string.Format(
"streamId={0}",
streamId);
await this.DeleteAsync(
HolographicSimulationStreamApi,
payload);
}
/// <summary>
/// Gets the perception simulation control mode.
/// </summary>
/// <returns>The simulation control mode.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<SimulationControlMode> GetPerceptionSimulationControlModeAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
PerceptionSimulationControlMode controlMode = await this.GetAsync<PerceptionSimulationControlMode>(HolographicSimulationModeApi);
return controlMode.Mode;
}
/// <summary>
/// Sets the perception simulation control mode.
/// </summary>
/// <param name="mode">The simulation control mode.</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task SetPerceptionSimulationControlModeAsync(SimulationControlMode mode)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"mode={0}",
(int)mode);
await this.PostAsync(HolographicSimulationModeApi, payload);
}
/// <summary>
/// Compares the current simulation control mode with the expected mode.
/// </summary>
/// <param name="expectedMode">The simulation control mode that we expect the device to be in.</param>
/// <returns>The simulation control mode.</returns>
private async Task<bool> VerifySimulationControlModeAsync(SimulationControlMode expectedMode)
{
SimulationControlMode simMode = await this.GetPerceptionSimulationControlModeAsync();
return simMode == expectedMode;
}
#region Data contract
/// <summary>
/// Object representation of Perception Simulation control mode.
/// </summary>
[DataContract]
public class PerceptionSimulationControlMode
{
/// <summary>
/// Gets the control mode.
/// </summary>
[DataMember(Name = "mode")]
public SimulationControlMode Mode { get; private set; }
}
/// <summary>
/// Object representation of the response recevied when creating a Perception Simulation control stream.
/// </summary>
[DataContract]
public class PerceptionSimulationControlStreamId
{
/// <summary>
/// Gets the stream identifier.
/// </summary>
[DataMember(Name = "streamId")]
public string StreamId { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,109 @@
//----------------------------------------------------------------------------------------------
// <copyright file="HolographicThermal.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Thermal State enumeration
/// </summary>
public enum ThermalStages
{
/// <summary>
/// No thermal stage
/// </summary>
Normal,
/// <summary>
/// Warm stage
/// </summary>
Warm,
/// <summary>
/// Critical stage
/// </summary>
Critical,
/// <summary>
/// Unknown stage
/// </summary>
Unknown = 9999
}
/// <content>
/// Wrappers for Holographic Thermal methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting the thermal stage
/// </summary>
public static readonly string ThermalStageApi = "api/holographic/thermal/stage";
/// <summary>
/// Gets the current thermal stage reading from the device.
/// </summary>
/// <returns>ThermalStages enum value.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<ThermalStages> GetThermalStageAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
ThermalStage thermalStage = await this.GetAsync<ThermalStage>(ThermalStageApi);
return thermalStage.Stage;
}
#region Data contract
/// <summary>
/// Object representation of thermal stage
/// </summary>
[DataContract]
public class ThermalStage
{
/// <summary>
/// Gets the raw stage value
/// </summary>
[DataMember(Name = "CurrentStage")]
public int StageRaw { get; private set; }
/// <summary>
/// Gets the enumeration value of the thermal stage
/// </summary>
public ThermalStages Stage
{
get
{
ThermalStages stage = ThermalStages.Unknown;
try
{
stage = (ThermalStages)Enum.ToObject(typeof(ThermalStages), this.StageRaw);
if (!Enum.IsDefined(typeof(ThermalStages), stage))
{
stage = ThermalStages.Unknown;
}
}
catch
{
stage = ThermalStages.Unknown;
}
return stage;
}
}
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,723 @@
//----------------------------------------------------------------------------------------------
// <copyright file="MixedRealityCapture.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Mixed reality capture methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting or deleting a Mixed Reality Capture file.
/// </summary>
public static readonly string MrcFileApi = "api/holographic/mrc/file";
/// <summary>
/// API for getting the list of Holographic Mixed Reality Capture files.
/// </summary>
public static readonly string MrcFileListApi = "api/holographic/mrc/files";
/// <summary>
/// API for taking a Mixed Reality Capture photo.
/// </summary>
public static readonly string MrcPhotoApi = "api/holographic/mrc/photo";
/// <summary>
/// API for getting or setting the default Mixed Reality Capture settings.
/// </summary>
public static readonly string MrcSettingsApi = "api/holographic/mrc/settings";
/// <summary>
/// API for starting a Holographic Mixed Reality Capture recording.
/// </summary>
public static readonly string MrcStartRecordingApi = "api/holographic/mrc/video/control/start";
/// <summary>
/// API for getting the Holographic Mixed Reality Capture status.
/// </summary>
public static readonly string MrcStatusApi = "api/holographic/mrc/status";
/// <summary>
/// API for stopping a Holographic Mixed Reality Capture recording.
/// </summary>
public static readonly string MrcStopRecordingApi = "api/holographic/mrc/video/control/stop";
/// <summary>
/// API for getting a live Holographic Mixed Reality Capture stream.
/// </summary>
public static readonly string MrcLiveStreamApi = "api/holographic/stream/live.mp4";
/// <summary>
/// API for getting a high resolution live Holographic Mixed Reality Capture stream.
/// </summary>
public static readonly string MrcLiveStreamHighResApi = "api/holographic/stream/live_high.mp4";
/// <summary>
/// API for getting a low resolution live Holographic Mixed Reality Capture stream.
/// </summary>
public static readonly string MrcLiveStreamLowResApi = "api/holographic/stream/live_low.mp4";
/// <summary>
/// API for getting a medium resolution live Holographic Mixed Reality Capture stream.
/// </summary>
public static readonly string MrcLiveStreamMediumResApi = "api/holographic/stream/live_med.mp4";
/// <summary>
/// API for getting a mixed reality capture thumbnail
/// </summary>
public static readonly string MrcThumbnailApi = "api/holographic/mrc/thumbnail";
/// <summary>
/// Removes a Mixed Reality Capture file from the device's local storage.
/// </summary>
/// <param name="fileName">The name of the file to be deleted.</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task DeleteMrcFileAsync(string fileName)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
await this.DeleteAsync(
MrcFileApi,
string.Format("filename={0}", Utilities.Hex64Encode(fileName)));
}
/// <summary>
/// Retrieve the Uri for the high resolution Mixed Reality Capture live stream.
/// </summary>
/// <param name="includeHolograms">Specifies whether or not to include holograms</param>
/// <param name="includeColorCamera">Specifies whether or not to include the color camera</param>
/// <param name="includeMicrophone">Specifies whether or not to include microphone data</param>
/// <param name="includeAudio">Specifies whether or not to include audio data</param>
/// <returns>Uri used to retreive the Mixed Reality Capture stream.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public Uri GetHighResolutionMrcLiveStreamUri(
bool includeHolograms = true,
bool includeColorCamera = true,
bool includeMicrophone = true,
bool includeAudio = true)
{
string payload = string.Format(
"holo={0}&pv={1}&mic={2}&loopback={3}",
includeHolograms,
includeColorCamera,
includeMicrophone,
includeAudio).ToLower();
return Utilities.BuildEndpoint(
this.deviceConnection.Connection,
MrcLiveStreamHighResApi,
payload);
}
/// <summary>
/// Retrieve the Uri for the low resolution Mixed Reality Capture live stream.
/// </summary>
/// <param name="includeHolograms">Specifies whether or not to include holograms</param>
/// <param name="includeColorCamera">Specifies whether or not to include the color camera</param>
/// <param name="includeMicrophone">Specifies whether or not to include microphone data</param>
/// <param name="includeAudio">Specifies whether or not to include audio data</param>
/// <returns>Uri used to retreive the Mixed Reality Capture stream.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public Uri GetLowResolutionMrcLiveStreamUri(
bool includeHolograms = true,
bool includeColorCamera = true,
bool includeMicrophone = true,
bool includeAudio = true)
{
string payload = string.Format(
"holo={0}&pv={1}&mic={2}&loopback={3}",
includeHolograms,
includeColorCamera,
includeMicrophone,
includeAudio).ToLower();
return Utilities.BuildEndpoint(
this.deviceConnection.Connection,
MrcLiveStreamLowResApi,
payload);
}
/// <summary>
/// Retrieve the Uri for the medium resolution Mixed Reality Capture live stream.
/// </summary>
/// <param name="includeHolograms">Specifies whether or not to include holograms</param>
/// <param name="includeColorCamera">Specifies whether or not to include the color camera</param>
/// <param name="includeMicrophone">Specifies whether or not to include microphone data</param>
/// <param name="includeAudio">Specifies whether or not to include audio data</param>
/// <returns>Uri used to retreive the Mixed Reality Capture stream.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public Uri GetMediumResolutionMrcLiveStreamUri(
bool includeHolograms = true,
bool includeColorCamera = true,
bool includeMicrophone = true,
bool includeAudio = true)
{
string payload = string.Format(
"holo={0}&pv={1}&mic={2}&loopback={3}",
includeHolograms,
includeColorCamera,
includeMicrophone,
includeAudio).ToLower();
return Utilities.BuildEndpoint(
this.deviceConnection.Connection,
MrcLiveStreamMediumResApi,
payload);
}
/// <summary>
/// Gets the capture file data
/// </summary>
/// <param name="fileName">Name of the file to retrieve.</param>
/// <param name="isThumbnailRequest">Specifies whether or not we are requesting a thumbnail image.</param>
/// <returns>Byte array containing the file data.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<byte[]> GetMrcFileDataAsync(
string fileName,
bool isThumbnailRequest = false)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
byte[] dataBytes = null;
string apiPath = isThumbnailRequest ? MrcThumbnailApi : MrcFileApi;
string payload = string.Format("filename={0}", Utilities.Hex64Encode(fileName));
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
apiPath,
payload);
using (Stream dataStream = await this.GetAsync(uri))
{
dataBytes = new byte[dataStream.Length];
dataStream.Read(dataBytes, 0, dataBytes.Length);
}
return dataBytes;
}
/// <summary>
/// Gets the list of capture files
/// </summary>
/// <returns>List of the capture files</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<MrcFileList> GetMrcFileListAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
MrcFileList mrcFileList = await this.GetAsync<MrcFileList>(MrcFileListApi);
foreach (MrcFileInformation mfi in mrcFileList.Files)
{
try
{
mfi.Thumbnail = await this.GetMrcThumbnailDataAsync(mfi.FileName);
}
catch
{
}
}
return mrcFileList;
}
/// <summary>
/// Retrieve the Uri for the Mixed Reality Capture live stream using the default resolution.
/// </summary>
/// <param name="includeHolograms">Specifies whether or not to include holograms</param>
/// <param name="includeColorCamera">Specifies whether or not to include the color camera</param>
/// <param name="includeMicrophone">Specifies whether or not to include microphone data</param>
/// <param name="includeAudio">Specifies whether or not to include audio data</param>
/// <returns>Uri used to retreive the Mixed Reality Capture stream.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public Uri GetMrcLiveStreamUri(
bool includeHolograms = true,
bool includeColorCamera = true,
bool includeMicrophone = true,
bool includeAudio = true)
{
string payload = string.Format(
"holo={0}&pv={1}&mic={2}&loopback={3}",
includeHolograms,
includeColorCamera,
includeMicrophone,
includeAudio).ToLower();
return Utilities.BuildEndpoint(
this.deviceConnection.Connection,
MrcLiveStreamApi,
payload);
}
/// <summary>
/// Gets the current Mixed Reality Capture settings
/// </summary>
/// <returns>MrcSettings object containing the current settings</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<MrcSettings> GetMrcSettingsAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
return await this.GetAsync<MrcSettings>(MrcSettingsApi);
}
/// <summary>
/// Gets the status of the reality capture
/// </summary>
/// <returns>Status of the capture</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<MrcStatus> GetMrcStatusAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
return await this.GetAsync<MrcStatus>(MrcStatusApi);
}
/// <summary>
/// Gets thumbnail data for the capture
/// </summary>
/// <param name="fileName">Name of the capture file</param>
/// <returns>Byte array containing the thumbnail image data</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<byte[]> GetMrcThumbnailDataAsync(string fileName)
{
// GetMrcFileData checks for the appropriate platform. We do not need to duplicate the check here.
return await this.GetMrcFileDataAsync(fileName, true);
}
/// <summary>
/// Sets the default Mixed Reality Capture settings
/// </summary>
/// <param name="settings">Mixed Reality Capture settings to be used as the default.</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task SetMrcSettingsAsync(MrcSettings settings)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"holo={0}&pv={1}&mic={2}&appAudio={3}&vstabbuffer={4}",
settings.IncludeHolograms.ToString().ToLower(),
settings.IncludeColorCamera.ToString().ToLower(),
settings.IncludeMicrophone.ToString().ToLower(),
settings.IncludeAudio.ToString().ToLower(),
settings.VideoStabilizationBuffer);
await this.PostAsync(
MrcSettingsApi,
payload);
}
/// <summary>
/// Starts a Mixed Reality Capture recording.
/// </summary>
/// <param name="includeHolograms">Specifies whether or not to include holograms</param>
/// <param name="includeColorCamera">Specifies whether or not to include the color camera</param>
/// <param name="includeMicrophone">Specifies whether or not to include microphone data</param>
/// <param name="includeAudio">Specifies whether or not to include audio data</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task StartMrcRecordingAsync(
bool includeHolograms = true,
bool includeColorCamera = true,
bool includeMicrophone = true,
bool includeAudio = true)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"holo={0}&pv={1}&mic={2}&loopback={3}",
includeHolograms,
includeColorCamera,
includeMicrophone,
includeAudio).ToLower();
await this.PostAsync(
MrcStartRecordingApi,
payload);
}
/// <summary>
/// Stops the Mixed Reality Capture recording
/// </summary>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task StopMrcRecordingAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
await this.PostAsync(MrcStopRecordingApi);
}
/// <summary>
/// Take a Mixed Reality Capture photo
/// </summary>
/// <param name="includeHolograms">Specifies whether or not to include holograms</param>
/// <param name="includeColorCamera">Specifies whether or not to include the color camera</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task TakeMrcPhotoAsync(
bool includeHolograms = true,
bool includeColorCamera = true)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
await this.PostAsync(
MrcPhotoApi,
string.Format("holo={0}&pv={1}", includeHolograms, includeColorCamera).ToLower());
}
#region Data contract
/// <summary>
/// Object representation of the capture file list
/// </summary>
[DataContract]
public class MrcFileList
{
/// <summary>
/// Gets the list of files
/// </summary>
[DataMember(Name = "MrcRecordings")]
public List<MrcFileInformation> Files { get; private set; }
}
/// <summary>
/// Object representation of an individual capture file
/// </summary>
[DataContract]
public class MrcFileInformation
{
/// <summary>
/// Gets the raw creation time
/// </summary>
[DataMember(Name = "CreationTime")]
public long CreationTimeRaw { get; private set; }
/// <summary>
/// Gets the filename
/// </summary>
[DataMember(Name = "FileName")]
public string FileName { get; private set; }
/// <summary>
/// Gets the file size
/// </summary>
[DataMember(Name = "FileSize")]
public uint FileSize { get; private set; }
/// <summary>
/// Gets the thumbnail
/// </summary>
public byte[] Thumbnail { get; internal set; }
/// <summary>
/// Gets the creation time
/// </summary>
public DateTime Created
{
get { return new DateTime(this.CreationTimeRaw); }
}
}
/// <summary>
/// Object representation of the Capture status
/// </summary>
[DataContract]
public class MrcStatus
{
/// <summary>
/// Gets a value indicating whether the device is recording
/// </summary>
[DataMember(Name = "IsRecording")]
public bool IsRecording { get; private set; }
/// <summary>
/// Gets the recording status
/// </summary>
[DataMember(Name = "ProcessStatus")]
public MrcProcessStatus Status { get; private set; }
}
/// <summary>
/// Object representation of the Mixed Reality Capture process status
/// </summary>
[DataContract]
public class MrcProcessStatus
{
/// <summary>
/// Gets the raw data for the Mixed Reality Capture process status
/// </summary>
[DataMember(Name = "MrcProcess")]
public string MrcProcessRaw { get; private set; }
/// <summary>
/// Gets the status of the Mixed Reality Capture process
/// </summary>
public ProcessStatus MrcProcess
{
get
{
return (this.MrcProcessRaw == "Running") ? ProcessStatus.Running : ProcessStatus.Stopped;
}
}
}
/// <summary>
/// Object representation of a Mixed Reality Capture setting.
/// </summary>
[DataContract]
public class MrcSetting
{
/// <summary>
/// Gets or sets the name of the setting
/// </summary>
[DataMember(Name = "Setting")]
public string Setting { get; set; }
/// <summary>
/// Gets or sets the value of the setting
/// </summary>
[DataMember(Name = "Value")]
public object Value { get; set; }
}
/// <summary>
/// Object representing the collection of Mixed Reality Capture settings
/// </summary>
[DataContract]
public class MrcSettings
{
/// <summary>
/// Gets the collection of settings
/// </summary>
[DataMember(Name = "MrcSettings")]
public List<MrcSetting> Settings { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether or not holograms are included.
/// </summary>
public bool IncludeHolograms
{
get
{
object setting = this.GetSetting("EnableHolograms");
if (setting == null)
{
return true;
}
return (bool)setting;
}
set
{
this.SetSetting(
"EnableHolograms",
value);
}
}
/// <summary>
/// Gets or sets a value indicating whether or not color camera data is included.
/// </summary>
public bool IncludeColorCamera
{
get
{
object setting = this.GetSetting("EnableCamera");
if (setting == null)
{
return true;
}
return (bool)setting;
}
set
{
this.SetSetting(
"EnableCamera",
value);
}
}
/// <summary>
/// Gets or sets a value indicating whether or not microphone audio is included.
/// </summary>
public bool IncludeMicrophone
{
get
{
object setting = this.GetSetting("EnableMicrophone");
if (setting == null)
{
return true;
}
return (bool)setting;
}
set
{
this.SetSetting(
"EnableMicrophone",
value);
}
}
/// <summary>
/// Gets or sets a value indicating whether or not audio is included.
/// </summary>
public bool IncludeAudio
{
get
{
object setting = this.GetSetting("EnableSystemAudio");
if (setting == null)
{
return true;
}
return (bool)setting;
}
set
{
this.SetSetting(
"EnableSystemAudio",
value);
}
}
/// <summary>
/// Gets or sets the size, in frames, of the video stabilization buffer.
/// </summary>
public int VideoStabilizationBuffer
{
get
{
object setting = this.GetSetting("VideoStabilizationBuffer");
if (setting == null)
{
return 0;
}
return (int)setting;
}
set
{
if (value < 0)
{
throw new ArgumentException("The video stabilization buffer value must be >= 0");
}
this.SetSetting(
"VideoStabilizationBuffer",
value);
}
}
/// <summary>
/// Gets the value of a setting
/// </summary>
/// <param name="settingName">The name of the setting</param>
/// <returns>The value of the setting, or if not found, null.</returns>
private object GetSetting(string settingName)
{
object value = null;
foreach (MrcSetting setting in this.Settings)
{
if (setting.Setting == settingName)
{
value = setting.Value;
break;
}
}
return value;
}
/// <summary>
/// Sets the value of a Mixed Reality Capture setting.
/// </summary>
/// <param name="settingName">The name of the setting</param>
/// <param name="value">The value of the setting</param>
private void SetSetting(
string settingName,
object value)
{
// If the setting exists, update the value, otherwise create a new one.
MrcSetting mrcSetting = null;
foreach (MrcSetting setting in this.Settings)
{
if (setting.Setting == settingName)
{
mrcSetting = setting;
break;
}
}
if (mrcSetting != null)
{
mrcSetting.Value = value;
}
else
{
mrcSetting = new MrcSetting();
mrcSetting.Setting = settingName;
mrcSetting.Value = value;
this.Settings.Add(mrcSetting);
}
}
}
#endregion Data contract
}
}

Просмотреть файл

@ -0,0 +1,426 @@
//----------------------------------------------------------------------------------------------
// <copyright file="PerceptionSimulationPlayback.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Perception Simulation Playback methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for loading or unloading a Holographic Perception Simulation recording.
/// </summary>
public static readonly string HolographicSimulationPlaybackSessionFileApi = "api/holographic/simulation/playback/session/file";
/// <summary>
/// API for pausing a Holographic Perception Simulation recording.
/// </summary>
public static readonly string HolographicSimulationPlaybackPauseApi = "api/holographic/simulation/playback/session/pause";
/// <summary>
/// API for uploading or deleting a Holographic Perception Simulation recording file.
/// </summary>
public static readonly string HolographicSimulationPlaybackFileApi = "api/holographic/simulation/playback/file";
/// <summary>
/// API for retrieving a list of a Holographic Perception Simulation recording files.
/// </summary>
public static readonly string HolographicSimulationPlaybackFilesApi = "api/holographic/simulation/playback/files";
/// <summary>
/// API for starting playback of a Holographic Perception Simulation recording.
/// </summary>
public static readonly string HolographicSimulationPlaybackPlayApi = "api/holographic/simulation/playback/session/play";
/// <summary>
/// API for getting the list of loaded Holographic Perception Simulation files.
/// </summary>
public static readonly string HolographicSimulationPlaybackSessionFilesApi = "api/holographic/simulation/playback/session/files";
/// <summary>
/// API for retrieving the playback state of a Holographic Perception Simulation recording.
/// </summary>
public static readonly string HolographicSimulationPlaybackStateApi = "api/holographic/simulation/playback/session";
/// <summary>
/// API for starting playback of a Holographic Perception Simulation recording.
/// </summary>
public static readonly string HolographicSimulationPlaybackStopApi = "api/holographic/simulation/playback/session/stop";
/// <summary>
/// API for retrieving the types of data in a Holographic Perception Simulation recording.
/// </summary>
public static readonly string HolographicSimulationPlaybackDataTypesApi = "api/holographic/simulation/playback/session/types";
/// <summary>
/// Enumeration describing the available Holgraphic Simulation playback states.
/// </summary>
public enum HolographicSimulationPlaybackStates
{
/// <summary>
/// The simulation has been stopped.
/// </summary>
Stopped = 0,
/// <summary>
/// The simulation is playing.
/// </summary>
Playing,
/// <summary>
/// The simulation has been paused.
/// </summary>
Paused,
/// <summary>
/// Playback has completed.
/// </summary>
Complete,
/// <summary>
/// Playback is in an unexpected / unknown state.
/// </summary>
Unexpected = 9999
}
/// <summary>
/// Deletes the specified Holographic Simulation recording.
/// </summary>
/// <param name="name">The name of the recording to delete (ex: testsession.xef).</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task DeleteHolographicSimulationRecordingAsync(string name)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"recording={0}",
name);
await this.DeleteAsync(HolographicSimulationPlaybackFileApi, payload);
}
/// <summary>
/// Gets the collection of Holographic Perception Simulation files on this HoloLens.
/// </summary>
/// <returns>HolographicSimulationPlaybackFiles object representing the files on the HoloLens</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<HolographicSimulationPlaybackFiles> GetHolographicSimulationPlaybackFilesAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
return await this.GetHolographicSimulationPlaybackFilesPrivateAsync(false);
}
/// <summary>
/// Gets the collection of loaded Holographic Perception Simulation files on this HoloLens.
/// </summary>
/// <returns>HolographicSimulationPlaybackFiles object representing the files loaded on the HoloLens</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<HolographicSimulationPlaybackFiles> GetHolographicSimulationPlaybackSessionFilesAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
return await this.GetHolographicSimulationPlaybackFilesPrivateAsync(true);
}
/// <summary>
/// Gets the types of data that are in a loaded Holographic Perception Simulation file.
/// </summary>
/// <param name="recordingName">Name of the recording file, with extension.</param>
/// <returns>HolographicSimulationDataTypes object representing they types of data in the file</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<HolographicSimulationDataTypes> GetHolographicSimulationPlaybackSessionDataTypesAsync(string recordingName)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"recording={0}",
recordingName);
return await this.GetAsync<HolographicSimulationDataTypes>(
HolographicSimulationPlaybackDataTypesApi,
payload);
}
/// <summary>
/// Gets the playback state of a Holographic Simulation recording.
/// </summary>
/// <param name="name">The name of the recording (ex: testsession.xef).</param>
/// <returns>HolographicSimulationPlaybackStates enum value describing the state of the recording.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<HolographicSimulationPlaybackStates> GetHolographicSimulationPlaybackStateAsync(string name)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
HolographicSimulationPlaybackStates playbackState = HolographicSimulationPlaybackStates.Unexpected;
string payload = string.Format(
"recording={0}",
name);
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
HolographicSimulationPlaybackStateApi,
payload);
using (Stream dataStream = await this.GetAsync(uri))
{
if ((dataStream != null) &&
(dataStream.Length != 0))
{
// Try to get the session state.
try
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HolographicSimulationPlaybackSessionState));
HolographicSimulationPlaybackSessionState sessionState = (HolographicSimulationPlaybackSessionState)serializer.ReadObject(dataStream);
playbackState = sessionState.State;
}
catch
{
// We did not receive the session state, check to see if we received a simulation error.
dataStream.Position = 0;
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HolographicSimulationError));
HolographicSimulationError error = (HolographicSimulationError)serializer.ReadObject(dataStream);
throw new InvalidOperationException(error.Reason);
}
}
}
return playbackState;
}
/// <summary>
/// Loads the specified Holographic Simulation recording.
/// </summary>
/// <param name="recordingName">The name of the recording to load (ex: testsession.xef).</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task LoadHolographicSimulationRecordingAsync(string recordingName)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"recording={0}",
recordingName);
await this.PostAsync(HolographicSimulationPlaybackSessionFileApi, payload);
}
/// <summary>
/// Pauses playback of a Holographic Perception Simulation recording
/// </summary>
/// <param name="recordingName">The name of the recording to pause</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task PauseHolographicSimulationRecordingAsync(string recordingName)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"recording={0}",
recordingName);
await this.PostAsync(HolographicSimulationPlaybackPauseApi, payload);
}
/// <summary>
/// Starts playback of a Holographic Perception Simulation recording
/// </summary>
/// <param name="recordingName">The name of the recording to play</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task PlayHolographicSimulationRecordingAsync(string recordingName)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"recording={0}",
recordingName);
await this.PostAsync(HolographicSimulationPlaybackPlayApi, payload);
}
/// <summary>
/// Stops playback of a Holographic Perception Simulation recording
/// </summary>
/// <param name="recordingName">The name of the recording to stop</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task StopHolographicSimulationRecordingAsync(string recordingName)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"recording={0}",
recordingName);
await this.PostAsync(HolographicSimulationPlaybackStopApi, payload);
}
/// <summary>
/// Unloads the specified Holographic Simulation recording.
/// </summary>
/// <param name="recordingName">The name of the recording to unload (ex: testsession.xef).</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task UnloadHolographicSimulationRecordingAsync(string recordingName)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"recording={0}",
recordingName);
await this.DeleteAsync(HolographicSimulationPlaybackSessionFileApi, payload);
}
/// <summary>
/// Gets the collection of Holographic Perception Simulation files
/// </summary>
/// <param name="session">Value indicating whether or not to return loaded files.</param>
/// <returns>Collection of Holographic Perception simulation file names</returns>
private async Task<HolographicSimulationPlaybackFiles> GetHolographicSimulationPlaybackFilesPrivateAsync(bool session)
{
string apiPath = session ? HolographicSimulationPlaybackSessionFilesApi : HolographicSimulationPlaybackFilesApi;
return await this.GetAsync<HolographicSimulationPlaybackFiles>(apiPath);
}
#region Data contract
/// <summary>
/// Object representing the data types in a Holographic Perception Simulation file
/// </summary>
[DataContract]
public class HolographicSimulationDataTypes
{
/// <summary>
/// Gets a value indicating whether or not the file contains hand data.
/// </summary>
[DataMember(Name = "hands")]
public bool IncludesHands { get; private set; }
/// <summary>
/// Gets a value indicating whether or not the file contains head data.
/// </summary>
[DataMember(Name = "head")]
public bool IncludesHead { get; private set; }
/// <summary>
/// Gets a value indicating whether or not the file contains environmentatl data.
/// </summary>
[DataMember(Name = "environment")]
public bool IncludesEnvironment { get; private set; }
/// <summary>
/// Gets a value indicating whether or not the file contains spatial mapping data.
/// </summary>
[DataMember(Name = "spatialMapping")]
public bool IncludesSpatialMapping { get; private set; }
}
/// <summary>
/// Object representation of the Holographic Perception Simulation files collection
/// </summary>
[DataContract]
public class HolographicSimulationPlaybackFiles
{
/// <summary>
/// Gets the list of recording file names.
/// </summary>
[DataMember(Name = "recordings")]
public List<string> Files { get; private set; }
}
/// <summary>
/// Object representation of the Holographic Perception Simulation playback state
/// </summary>
[DataContract]
public class HolographicSimulationPlaybackSessionState
{
/// <summary>
/// Gets the state value as a string
/// </summary>
[DataMember(Name = "state")]
public string StateRaw { get; private set; }
/// <summary>
/// Gets the playback session state
/// </summary>
public HolographicSimulationPlaybackStates State
{
get
{
HolographicSimulationPlaybackStates state = HolographicSimulationPlaybackStates.Unexpected;
switch (this.StateRaw)
{
case "stopped":
state = HolographicSimulationPlaybackStates.Stopped;
break;
case "playing":
state = HolographicSimulationPlaybackStates.Playing;
break;
case "paused":
state = HolographicSimulationPlaybackStates.Paused;
break;
case "end":
state = HolographicSimulationPlaybackStates.Complete;
break;
default:
state = HolographicSimulationPlaybackStates.Unexpected;
break;
}
return state;
}
}
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,162 @@
//----------------------------------------------------------------------------------------------
// <copyright file="PerceptionSimulationRecording.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Wrappers for Perception Simulation Recording methods
/// </content>
public partial class DevicePortal
{
/// <summary>
/// API for getting a Holographic Perception Simulation recording status.
/// </summary>
public static readonly string HolographicSimulationRecordingStatusApi = "api/holographic/simulation/recording/status";
/// <summary>
/// API for starting a Holographic Perception Simulation recording.
/// </summary>
public static readonly string StartHolographicSimulationRecordingApi = "api/holographic/simulation/recording/start";
/// <summary>
/// API for stopping a Holographic Perception Simulation recording.
/// </summary>
public static readonly string StopHolographicSimulationRecordingApi = "api/holographic/simulation/recording/stop";
/// <summary>
/// Gets the holographic simulation recording status.
/// </summary>
/// <returns>True if recording, false otherwise.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<bool> GetHolographicSimulationRecordingStatusAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
HolographicSimulationRecordingStatus status = await this.GetAsync<HolographicSimulationRecordingStatus>(HolographicSimulationRecordingStatusApi);
return status.IsRecording;
}
/// <summary>
/// Starts a Holographic Simulation recording session.
/// </summary>
/// <param name="name">The name of the recording.</param>
/// <param name="recordHead">Should head data be recorded? The default value is true.</param>
/// <param name="recordHands">Should hand data be recorded? The default value is true.</param>
/// <param name="recordSpatialMapping">Should Spatial Mapping data be recorded? The default value is true.</param>
/// <param name="recordEnvironment">Should environment data be recorded? The default value is true.</param>
/// <returns>Task tracking completion of the REST call.</returns>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task StartHolographicSimulationRecordingAsync(
string name,
bool recordHead = true,
bool recordHands = true,
bool recordSpatialMapping = true,
bool recordEnvironment = true)
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
string payload = string.Format(
"head={0}&hands={1}&spatialMapping={2}&environment={3}&name={4}",
recordHead ? 1 : 0,
recordHands ? 1 : 0,
recordSpatialMapping ? 1 : 0,
recordEnvironment ? 1 : 0,
name);
await this.PostAsync(StartHolographicSimulationRecordingApi, payload);
}
/// <summary>
/// Stops a Holographic Simulation recording session.
/// </summary>
/// <returns>Byte array containing the recorded data.</returns>
/// <exception cref="InvalidOperationException">No recording was in progress.</exception>
/// <remarks>This method is only supported on HoloLens.</remarks>
public async Task<byte[]> StopHolographicSimulationRecordingAsync()
{
if (!Utilities.IsHoloLens(this.Platform, this.DeviceFamily))
{
throw new NotSupportedException("This method is only supported on HoloLens.");
}
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
StopHolographicSimulationRecordingApi);
byte[] dataBytes = null;
using (Stream dataStream = await this.GetAsync(uri))
{
if ((dataStream != null) &&
(dataStream.Length != 0))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HolographicSimulationError));
HolographicSimulationError error = null;
try
{
// Try to get / interpret an error response.
error = (HolographicSimulationError)serializer.ReadObject(dataStream);
}
catch
{
}
if (error != null)
{
// We received an error response.
throw new InvalidOperationException(error.Reason);
}
// Getting here indicates that we have file data to return.
dataBytes = new byte[dataStream.Length];
dataStream.Read(dataBytes, 0, dataBytes.Length);
}
}
return dataBytes;
}
#region Data contract
/// <summary>
/// Object representation of a Holographic Simulation (playback or recording) error.
/// </summary>
[DataContract]
public class HolographicSimulationError
{
/// <summary>
/// Gets the Reason string.
/// </summary>
[DataMember(Name = "Reason")]
public string Reason { get; private set; }
}
/// <summary>
/// Object representation of Holographic Simulation recording status.
/// </summary>
[DataContract]
public class HolographicSimulationRecordingStatus
{
/// <summary>
/// Gets a value indicating whether the simulation is recording.
/// </summary>
[DataMember(Name = "recording")]
public bool IsRecording { get; private set; }
}
#endregion // Data contract
}
}

Просмотреть файл

@ -0,0 +1,141 @@
//----------------------------------------------------------------------------------------------
// <copyright file="HttpMultipartFileContent.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// This class mimicks <see cref="HttpMultipartContent"/>, with two main differences
/// 1. Simplifies posting files by taking file names instead of managing streams.
/// 2. Does not quote the boundaries, due to a bug in the device portal
/// </summary>
internal sealed class HttpMultipartFileContent : HttpContent
{
/// <summary>
/// List of items to transfer
/// </summary>
private List<string> items = new List<string>();
/// <summary>
/// Boundary string
/// </summary>
private string boundaryString;
/// <summary>
/// Initializes a new instance of the <see cref="HttpMultipartFileContent" /> class.
/// </summary>
public HttpMultipartFileContent() : this(Guid.NewGuid().ToString())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpMultipartFileContent" /> class.
/// </summary>
/// <param name="boundary">The boundary string for file content.</param>
public HttpMultipartFileContent(string boundary)
{
this.boundaryString = boundary;
Headers.TryAddWithoutValidation("Content-Type", string.Format("multipart/form-data; boundary={0}", this.boundaryString));
}
/// <summary>
/// Adds a file to the list of items to transfer
/// </summary>
/// <param name="filename">The name of the file to add</param>
public void Add(string filename)
{
if (filename != null)
{
this.items.Add(filename);
}
}
/// <summary>
/// Adds a range of files to the list of items to transfer
/// </summary>
/// <param name="filenames">List of files to add</param>
public void AddRange(IEnumerable<string> filenames)
{
if (filenames != null)
{
this.items.AddRange(filenames);
}
}
/// <summary>
/// Serializes the stream.
/// </summary>
/// <param name="outStream">Serialized Stream</param>
/// <param name="context">The Transport Context</param>
/// <returns>Task tracking progress</returns>
protected override async Task SerializeToStreamAsync(Stream outStream, TransportContext context)
{
var boundary = Encoding.ASCII.GetBytes($"--{boundaryString}\r\n");
var newline = Encoding.ASCII.GetBytes("\r\n");
foreach (var item in this.items)
{
outStream.Write(boundary, 0, boundary.Length);
var headerdata = GetFileHeader(new FileInfo(item));
outStream.Write(headerdata, 0, headerdata.Length);
using (var file = File.OpenRead(item))
{
await file.CopyToAsync(outStream);
}
outStream.Write(newline, 0, newline.Length);
await outStream.FlushAsync();
}
// Close the installation request data.
boundary = Encoding.ASCII.GetBytes($"--{boundaryString}--\r\n");
outStream.Write(boundary, 0, boundary.Length);
await outStream.FlushAsync();
}
/// <summary>
/// Computes required length for the transfer.
/// </summary>
/// <param name="length">The computed length value</param>
/// <returns>Whether or not the length was successfully computed</returns>
protected override bool TryComputeLength(out long length)
{
length = 0;
var boundaryLength = Encoding.ASCII.GetBytes(string.Format("--{0}\r\n", this.boundaryString)).Length;
foreach (var item in this.items)
{
var headerdata = GetFileHeader(new FileInfo(item));
length += boundaryLength + headerdata.Length + new FileInfo(item).Length + 2;
}
length += boundaryLength + 2;
return true;
}
/// <summary>
/// Gets the file header for the transfer
/// </summary>
/// <param name="info">Information about the file</param>
/// <returns>A byte array with the file header information</returns>
private static byte[] GetFileHeader(FileInfo info)
{
string contentType = "application/octet-stream";
if (info.Extension.ToLower() == ".cer")
{
contentType = "application/x-x509-ca-cert";
}
return Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{0}\"\r\nContent-Type: {1}\r\n\r\n", info.Name, contentType));
}
}
}

Просмотреть файл

@ -0,0 +1,152 @@
//----------------------------------------------------------------------------------------------
// <copyright file="HttpHeadersHelper.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
#if !WINDOWS_UWP
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading.Tasks;
#else
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Web.Http;
using Windows.Web.Http.Headers;
#endif // !WINDOWS_UWP
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Methods for working with Http headers.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Header name for Content Type of a request body.
/// </summary>
private static readonly string ContentTypeHeaderName = "Content-Type";
/// <summary>
/// Header name for a CSRF-Token.
/// </summary>
private static readonly string CsrfTokenName = "CSRF-Token";
/// <summary>
/// Header name for a User-Agent.
/// </summary>
private static readonly string UserAgentName = "User-Agent";
/// <summary>
/// Header value for User-Agent for the WDPW Open Source project.
/// </summary>
private static readonly string UserAgentValue = "WindowsDevicePortalWrapper";
/// <summary>
/// CSRF token retrieved by GET calls and used on subsequent POST/DELETE/PUT calls.
/// This token is intended to prevent a security vulnerability from cross site forgery.
/// </summary>
private string csrfToken = string.Empty;
/// <summary>
/// Applies the CSRF token to the HTTP client.
/// </summary>
/// <param name="client">The HTTP client on which to have the header set.</param>
/// <param name="method">The HTTP method (ex: POST) that will be called on the client.</param>
private void ApplyCSRFHeader(
HttpClient client,
HttpMethods method)
{
string headerName = "X-" + CsrfTokenName;
string headerValue = this.csrfToken;
if (string.Compare(method.ToString(), "get", true) == 0)
{
headerName = CsrfTokenName;
headerValue = string.IsNullOrEmpty(this.csrfToken) ? "Fetch" : headerValue;
}
#if WINDOWS_UWP
HttpRequestHeaderCollection headers = client.DefaultRequestHeaders;
#else
HttpRequestHeaders headers = client.DefaultRequestHeaders;
#endif // WINDOWS_UWP
headers.Add(headerName, headerValue);
}
/// <summary>
/// Applies any needed headers to the HTTP client.
/// </summary>
/// <param name="client">The HTTP client on which to have the headers set.</param>
/// <param name="method">The HTTP method (ex: POST) that will be called on the client.</param>
private void ApplyHttpHeaders(
HttpClient client,
HttpMethods method)
{
this.ApplyUserAgentHeader(client);
this.ApplyCSRFHeader(client, method);
}
/// <summary>
/// Adds the User-Agent string to a request to identify it
/// as coming from the WDPW Open Source project.
/// </summary>
/// <param name="client">The HTTP client on which to have the header set.</param>
private void ApplyUserAgentHeader(HttpClient client)
{
string userAgentValue = UserAgentValue;
#if WINDOWS_UWP
Assembly asm = this.GetType().GetTypeInfo().Assembly;
userAgentValue += "-v" + asm.GetName().Version.ToString();
userAgentValue += "-UWP";
HttpRequestHeaderCollection headers = client.DefaultRequestHeaders;
#else
Assembly asm = Assembly.GetExecutingAssembly();
userAgentValue += "-v" + asm.GetName().Version.ToString();
userAgentValue += "-dotnet";
HttpRequestHeaders headers = client.DefaultRequestHeaders;
#endif // WINDOWS_UWP
headers.Add(UserAgentName, userAgentValue);
}
/// <summary>
/// Retrieves the CSRF token from the HTTP response and stores it.
/// </summary>
/// <param name="response">The HTTP response from which to retrieve the header.</param>
private void RetrieveCsrfToken(HttpResponseMessage response)
{
// If the response sets a CSRF token, store that for future requests.
#if WINDOWS_UWP
string cookie;
if (response.Headers.TryGetValue("Set-Cookie", out cookie))
{
string csrfTokenNameWithEquals = CsrfTokenName + "=";
if (cookie.StartsWith(csrfTokenNameWithEquals))
{
this.csrfToken = cookie.Substring(csrfTokenNameWithEquals.Length);
}
}
#else
IEnumerable<string> cookies;
if (response.Headers.TryGetValues("Set-Cookie", out cookies))
{
foreach (string cookie in cookies)
{
string csrfTokenNameWithEquals = CsrfTokenName + "=";
if (cookie.StartsWith(csrfTokenNameWithEquals))
{
this.csrfToken = cookie.Substring(csrfTokenNameWithEquals.Length);
}
}
}
#endif
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RequestHelpers.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System.IO;
using System.Runtime.Serialization;
using System.Text;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Methods for working with Http requests.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Copies a file to the specified stream and prepends the necessary content information
/// required to be part of a multipart form data request.
/// </summary>
/// <param name="file">The file to be copied.</param>
/// <param name="stream">The stream to which the file will be copied.</param>
private static void CopyFileToRequestStream(
FileInfo file,
Stream stream)
{
byte[] data;
string contentDisposition = string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n", file.Name, file.Name);
string contentType = "Content-Type: application/octet-stream\r\n\r\n";
data = Encoding.ASCII.GetBytes(contentDisposition);
stream.Write(data, 0, data.Length);
data = Encoding.ASCII.GetBytes(contentType);
stream.Write(data, 0, data.Length);
using (FileStream fs = File.OpenRead(file.FullName))
{
fs.CopyTo(stream);
}
}
}
}

Просмотреть файл

@ -0,0 +1,115 @@
//----------------------------------------------------------------------------------------------
// <copyright file="ResponseHelpers.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text.RegularExpressions;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// Methods for working with Http responses.
/// </content>
public partial class DevicePortal
{
/// <summary>
/// The prefix for the <see cref="SystemPerformanceInformation" /> JSON formatting error.
/// </summary>
private static readonly string SysPerfInfoErrorPrefix = "{\"Reason\" : \"";
/// <summary>
/// The postfix for the <see cref="SystemPerformanceInformation" /> JSON formatting error.
/// </summary>
private static readonly string SysPerfInfoErrorPostfix = "\"}";
/// <summary>
/// Reads dataStream as T.
/// </summary>
/// <typeparam name="T">Return type for the JSON message</typeparam>
/// <param name="dataStream">The stream that contains the JSON message to be checked.</param>
/// <param name="settings">Optional settings for JSON serialization.</param>
/// <returns>Read data</returns>
public static T ReadJsonStream<T>(Stream dataStream, DataContractJsonSerializerSettings settings = null)
{
T data = default(T);
object response = null;
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings);
using (dataStream)
{
if ((dataStream != null) &&
(dataStream.Length != 0))
{
JsonFormatCheck<T>(dataStream);
try
{
response = serializer.ReadObject(dataStream);
}
catch (SerializationException)
{
// Assert on serialization failure.
Debug.Assert(false, "Serialization failure encountered. Check DataContract types for a possible mismatch between expectations and reality");
throw;
}
data = (T)response;
}
}
return data;
}
/// <summary>
/// Checks the JSON for any known formatting errors and fixes them.
/// </summary>
/// <typeparam name="T">Return type for the JSON message</typeparam>
/// <param name="jsonStream">The stream that contains the JSON message to be checked.</param>
private static void JsonFormatCheck<T>(Stream jsonStream)
{
if (typeof(T) == typeof(SystemPerformanceInformation))
{
StreamReader read = new StreamReader(jsonStream);
string rawJsonString = read.ReadToEnd();
// Recover from an error in which SystemPerformanceInformation is returned with an incorrect prefix, postfix and the message converted into JSON a second time.
if (rawJsonString.StartsWith(SysPerfInfoErrorPrefix, StringComparison.OrdinalIgnoreCase) && rawJsonString.EndsWith(SysPerfInfoErrorPostfix, StringComparison.OrdinalIgnoreCase))
{
// Remove the incorrect prefix and postfix from the JSON message.
rawJsonString = rawJsonString.Substring(SysPerfInfoErrorPrefix.Length, rawJsonString.Length - SysPerfInfoErrorPrefix.Length - SysPerfInfoErrorPostfix.Length);
// Undo the second JSON conversion.
rawJsonString = Regex.Replace(rawJsonString, "\\\\\"", "\"", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
rawJsonString = Regex.Replace(rawJsonString, "\\\\\\\\", "\\", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
// Overwrite the stream with the fixed JSON.
jsonStream.SetLength(0);
var sw = new StreamWriter(jsonStream);
sw.Write(rawJsonString);
sw.Flush();
}
jsonStream.Seek(0, SeekOrigin.Begin);
}
}
#region Data contract
/// <summary>
/// A null response class when we don't care about the response
/// body.
/// </summary>
[DataContract]
private class NullResponse
{
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,53 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestDelete.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// HTTP DELETE Wrapper
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Calls the specified API with the provided payload. This signature leaves
/// off the optional response so callers who don't need a response body
/// don't need to specify a type for it.
/// </summary>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>Task tracking the HTTP completion.</returns>
public async Task DeleteAsync(
string apiPath,
string payload = null)
{
await this.DeleteAsync<NullResponse>(apiPath, payload);
}
/// <summary>
/// Calls the specified API with the provided payload.
/// </summary>
/// <typeparam name="T">The type of the data for the HTTP response body (if present).</typeparam>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>Task tracking the HTTP completion.</returns>
public async Task<T> DeleteAsync<T>(
string apiPath,
string payload = null) where T : new()
{
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
apiPath,
payload);
return ReadJsonStream<T>(await this.DeleteAsync(uri));
}
}
}

Просмотреть файл

@ -0,0 +1,42 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestGet.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Serialization.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// HTTP GET Wrapper
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Calls the specified API with the provided payload.
/// </summary>
/// <typeparam name="T">Return type for the GET call</typeparam>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>An object of the specified type containing the data returned by the request.</returns>
public async Task<T> GetAsync<T>(
string apiPath,
string payload = null) where T : new()
{
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
apiPath,
payload);
return ReadJsonStream<T>(await this.GetAsync(uri).ConfigureAwait(false));
}
}
}

Просмотреть файл

@ -0,0 +1,83 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestPost.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// HTTP POST Wrapper
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Calls the specified API with the provided body. This signature leaves
/// off the optional response so callers who don't need a response body
/// don't need to specify a type for it.
/// </summary>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="files">List of files that we want to include in the post request.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>Task tracking the POST completion.</returns>
public async Task PostAsync(
string apiPath,
List<string> files,
string payload = null)
{
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
apiPath,
payload);
var content = new HttpMultipartFileContent();
content.AddRange(files);
await this.PostAsync(uri, content);
}
/// <summary>
/// Calls the specified API with the provided body. This signature leaves
/// off the optional response so callers who don't need a response body
/// don't need to specify a type for it.
/// </summary>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>Task tracking the POST completion.</returns>
public async Task PostAsync(
string apiPath,
string payload = null)
{
await this.PostAsync<NullResponse>(apiPath, payload);
}
/// <summary>
/// Calls the specified API with the provided payload.
/// </summary>
/// <typeparam name="T">The type of the data for the HTTP response body (if present).</typeparam>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <param name="requestStream">Optional stream containing data for the request body.</param>
/// <param name="requestStreamContentType">The type of that request body data.</param>
/// <returns>Task tracking the POST completion.</returns>
public async Task<T> PostAsync<T>(
string apiPath,
string payload = null,
Stream requestStream = null,
string requestStreamContentType = null) where T : new()
{
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
apiPath,
payload);
return ReadJsonStream<T>(await this.PostAsync(uri, requestStream, requestStreamContentType));
}
}
}

Просмотреть файл

@ -0,0 +1,98 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestPut.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
#if !WINDOWS_UWP
using System.Net.Http;
using System.Net.Http.Headers;
#endif // !WINDOWS_UWP
using System.Runtime.Serialization.Json;
using System.Threading.Tasks;
#if WINDOWS_UWP
using Windows.Foundation;
using Windows.Networking;
using Windows.Security.Credentials;
using Windows.Storage.Streams;
using Windows.Web.Http;
using Windows.Web.Http.Filters;
using Windows.Web.Http.Headers;
#endif // WINDOWS_UWP
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// HTTP PUT Wrapper
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Calls the specified API with the provided body. This signature leaves
/// off the optional response so callers who don't need a response body
/// don't need to specify a type for it, which also would force them
/// to explicitly declare their bodyData type instead of letting it
/// be implied implicitly.
/// </summary>
/// <typeparam name="K">The type of the data for the HTTP request body.</typeparam>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="bodyData">The data to be used for the HTTP request body.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>Task tracking the PUT completion.</returns>
public async Task PutAsync<K>(
string apiPath,
K bodyData,
string payload = null) where K : class
{
await this.PutAsync<NullResponse, K>(apiPath, bodyData, payload);
}
/// <summary>
/// Calls the specified API with the provided body.
/// </summary>
/// <typeparam name="T">The type of the data for the HTTP response body (if present).</typeparam>
/// <typeparam name="K">The type of the data for the HTTP request body.</typeparam>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="bodyData">The data to be used for the HTTP request body.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>Task tracking the PUT completion, optional response body.</returns>
public async Task<T> PutAsync<T, K>(
string apiPath,
K bodyData = null,
string payload = null) where T : new()
where K : class
{
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.Connection,
apiPath,
payload);
#if WINDOWS_UWP
HttpStreamContent streamContent = null;
#else
StreamContent streamContent = null;
#endif // WINDOWS_UWP
if (bodyData != null)
{
// Serialize the body to a JSON stream
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(K));
Stream stream = new MemoryStream();
serializer.WriteObject(stream, bodyData);
stream.Seek(0, SeekOrigin.Begin);
#if WINDOWS_UWP
streamContent = new HttpStreamContent(stream.AsInputStream());
streamContent.Headers.ContentType = new HttpMediaTypeHeaderValue("application/json");
#else
streamContent = new StreamContent(stream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
#endif // WINDOWS_UWP
}
return ReadJsonStream<T>(await this.PutAsync(uri, streamContent));
}
}
}

Просмотреть файл

@ -0,0 +1,170 @@
//----------------------------------------------------------------------------------------------
// <copyright file="WebSocket.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Internal Web socket message received event handler
/// </summary>
/// <param name="sender">sender <see cref="WebSocket{T}"/> object</param>
/// <param name="args">Web socket message received args</param>
/// <typeparam name="T">Return type for the websocket messages.</typeparam>
internal delegate void WebSocketMessageReceivedEventInternalHandler<T>(WebSocket<T> sender, WebSocketMessageReceivedEventArgs<T> args);
/// <summary>
/// Internal Web socket stream received event handler
/// </summary>
/// <param name="sender">sender <see cref="WebSocket{T}"/> object</param>
/// <param name="args">Web socket message received args</param>
/// <typeparam name="T">Return type for the websocket.</typeparam>
internal delegate void WebSocketStreamReceivedEventInternalHandler<T>(WebSocket<T> sender, WebSocketMessageReceivedEventArgs<Stream> args);
/// <summary>
/// HTTP Websocket Wrapper
/// </summary>
/// <typeparam name="T">Return type for the websocket messages.</typeparam>
internal partial class WebSocket<T>
{
/// <summary>
/// The device that the <see cref="WebSocket{T}" /> is connected to.
/// </summary>
private IDevicePortalConnection deviceConnection;
/// <summary>
/// Indicates whether the web socket should send streams instead of parsed <see cref="T" /> objects
/// </summary>
private bool sendStreams = false;
/// <summary>
/// Gets or sets the message received handler.
/// </summary>
public event WebSocketMessageReceivedEventInternalHandler<T> WebSocketMessageReceived;
/// <summary>
/// Gets or sets the stream received handler.
/// </summary>
public event WebSocketStreamReceivedEventInternalHandler<T> WebSocketStreamReceived;
/// <summary>
/// Gets a value indicating whether the web socket is connected.
/// </summary>
public bool IsConnected
{
get;
private set;
}
/// <summary>
/// Gets a value indicating whether the web socket is listening for messages.
/// </summary>
public bool IsListeningForMessages
{
get;
private set;
}
/// <summary>
/// Initialize a connection to the websocket.
/// </summary>
/// <param name="apiPath">The relative portion of the uri path that specifies the API to call.</param>
/// <param name="payload">The query string portion of the uri path that provides the parameterized data.</param>
/// <returns>The task of opening the websocket connection.</returns>
internal async Task ConnectAsync(string apiPath, string payload = null)
{
if (this.IsConnected)
{
return;
}
Uri uri = Utilities.BuildEndpoint(
this.deviceConnection.WebSocketConnection,
apiPath,
payload);
await this.ConnectInternalAsync(uri);
}
/// <summary>
/// Closes the connection to the websocket and stop listening for messages.
/// </summary>
/// <returns>The task of closing the websocket connection.</returns>
internal async Task CloseAsync()
{
if (this.IsConnected)
{
if (this.IsListeningForMessages)
{
await this.StopListeningForMessagesInternalAsync();
}
await this.CloseInternalAsync();
}
}
/// <summary>
/// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised.
/// </summary>
/// <returns>The task of listening for messages from the websocket.</returns>
internal async Task ReceiveMessagesAsync()
{
if (this.IsConnected && !this.IsListeningForMessages)
{
await this.StartListeningForMessagesInternalAsync();
}
}
/// <summary>
/// Sends a message to the server.
/// </summary>
/// <param name="message">The message to send.</param>
/// <returns>The task of sending a message to the websocket.</returns>
internal async Task SendMessageAsync(string message)
{
if (this.IsConnected)
{
await this.SendMessageInternalAsync(message);
}
}
/// <summary>
/// Converts received stream to a parsed <see cref="T" /> object and passes it to
/// the WebSocketMessageReceived handler. The sendstreams property can be used to
/// override this and send the <see cref="Stream" /> instead via the WebSocketStreamReceived handler.
/// </summary>
/// <param name="stream">The received stream.</param>
private void ConvertStreamToMessage(Stream stream)
{
if (stream != null && stream.Length != 0)
{
if (this.sendStreams)
{
this.WebSocketStreamReceived?.Invoke(
this,
new WebSocketMessageReceivedEventArgs<Stream>(stream));
}
else
{
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings()
{
UseSimpleDictionaryFormat = true
};
T message = DevicePortal.ReadJsonStream<T>(stream, settings);
this.WebSocketMessageReceived?.Invoke(
this,
new WebSocketMessageReceivedEventArgs<T>(message));
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,64 @@
//----------------------------------------------------------------------------------------------
// <copyright file="IDevicePortalConnection.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Net;
#if !WINDOWS_UWP
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
#endif
using static Microsoft.Tools.WindowsDevicePortal.DevicePortal;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Interface for creating a connection with the device portal.
/// </summary>
public interface IDevicePortalConnection
{
/// <summary>
/// Gets the base uri (ex: http://localhost) used to communicate with the device.
/// </summary>
Uri Connection { get; }
/// <summary>
/// Gets the base uri (ex: ws://localhost) used to communicate with web sockets on the device.
/// </summary>
Uri WebSocketConnection { get; }
/// <summary>
/// Gets the credentials used when communicating with the device.
/// </summary>
NetworkCredential Credentials { get; }
/// <summary>
/// Gets or sets the family of the device (ex: Windows.Holographic).
/// </summary>
string Family { get; set; }
/// <summary>
/// Gets or sets information describing the operating system installed on the device.
/// </summary>
OperatingSystemInformation OsInfo { get; set; }
/// <summary>
/// Updates the http security requirements for device communication.
/// </summary>
/// <param name="requiresHttps">True if an https connection is required, false otherwise.</param>
void UpdateConnection(bool requiresHttps);
/// <summary>
/// Updates the connection details (IP address) and http security requirements used when communicating with the device.
/// </summary>
/// <param name="ipConfig">Object that describes the current network configuration.</param>
/// <param name="requiresHttps">True if an https connection is required, false otherwise.</param>
/// <param name="preservePort">True if the previous connection's port is to continue to be used, false otherwise.</param>
void UpdateConnection(
IpConfiguration ipConfig,
bool requiresHttps,
bool preservePort);
}
}

Просмотреть файл

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Просмотреть файл

@ -0,0 +1,65 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestDelete.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// .net 4.x implementation of HTTP DeleteAsync
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Submits the http delete request to the specified uri.
/// </summary>
/// <param name="uri">The uri to which the delete request will be issued.</param>
/// <returns>Task tracking HTTP completion</returns>
public async Task<Stream> DeleteAsync(Uri uri)
{
MemoryStream dataStream = null;
WebRequestHandler requestSettings = new WebRequestHandler();
requestSettings.UseDefaultCredentials = false;
requestSettings.Credentials = this.deviceConnection.Credentials;
requestSettings.ServerCertificateValidationCallback = this.ServerCertificateValidation;
using (HttpClient client = new HttpClient(requestSettings))
{
this.ApplyHttpHeaders(client, HttpMethods.Delete);
using (HttpResponseMessage response = await client.DeleteAsync(uri).ConfigureAwait(false))
{
if (!response.IsSuccessStatusCode)
{
throw await DevicePortalException.CreateAsync(response);
}
this.RetrieveCsrfToken(response);
if (response.Content != null)
{
using (HttpContent content = response.Content)
{
dataStream = new MemoryStream();
await content.CopyToAsync(dataStream).ConfigureAwait(false);
// Ensure we return with the stream pointed at the origin.
dataStream.Position = 0;
}
}
}
}
return dataStream;
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestGet.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// .net 4.x implementation of HTTP GetAsync
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Submits the http get request to the specified uri.
/// </summary>
/// <param name="uri">The uri to which the get request will be issued.</param>
/// <returns>Response data as a stream.</returns>
public async Task<Stream> GetAsync(
Uri uri)
{
MemoryStream dataStream = null;
WebRequestHandler handler = new WebRequestHandler();
handler.UseDefaultCredentials = false;
handler.Credentials = this.deviceConnection.Credentials;
handler.ServerCertificateValidationCallback = this.ServerCertificateValidation;
using (HttpClient client = new HttpClient(handler))
{
this.ApplyHttpHeaders(client, HttpMethods.Get);
using (HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false))
{
if (!response.IsSuccessStatusCode)
{
throw await DevicePortalException.CreateAsync(response);
}
this.RetrieveCsrfToken(response);
using (HttpContent content = response.Content)
{
dataStream = new MemoryStream();
await content.CopyToAsync(dataStream).ConfigureAwait(false);
// Ensure we return with the stream pointed at the origin.
dataStream.Position = 0;
}
}
}
return dataStream;
}
}
}

Просмотреть файл

@ -0,0 +1,92 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestPost.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// .net 4.x implementation of HTTP PostAsync
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Submits the http post request to the specified uri.
/// </summary>
/// <param name="uri">The uri to which the post request will be issued.</param>
/// <param name="requestStream">Optional stream containing data for the request body.</param>
/// <param name="requestStreamContentType">The type of that request body data.</param>
/// <returns>Task tracking the completion of the POST request</returns>
public async Task<Stream> PostAsync(
Uri uri,
Stream requestStream = null,
string requestStreamContentType = null)
{
StreamContent requestContent = null;
if (requestStream != null)
{
requestContent = new StreamContent(requestStream);
requestContent.Headers.Remove(ContentTypeHeaderName);
requestContent.Headers.TryAddWithoutValidation(ContentTypeHeaderName, requestStreamContentType);
}
return await this.PostAsync(uri, requestContent);
}
/// <summary>
/// Submits the http post request to the specified uri.
/// </summary>
/// <param name="uri">The uri to which the post request will be issued.</param>
/// <param name="requestContent">Optional content containing data for the request body.</param>
/// <returns>Task tracking the completion of the POST request</returns>
public async Task<Stream> PostAsync(
Uri uri,
HttpContent requestContent)
{
MemoryStream responseDataStream = null;
WebRequestHandler requestSettings = new WebRequestHandler();
requestSettings.UseDefaultCredentials = false;
requestSettings.Credentials = this.deviceConnection.Credentials;
requestSettings.ServerCertificateValidationCallback = this.ServerCertificateValidation;
using (HttpClient client = new HttpClient(requestSettings))
{
this.ApplyHttpHeaders(client, HttpMethods.Post);
using (HttpResponseMessage response = await client.PostAsync(uri, requestContent).ConfigureAwait(false))
{
if (!response.IsSuccessStatusCode)
{
throw await DevicePortalException.CreateAsync(response);
}
this.RetrieveCsrfToken(response);
if (response.Content != null)
{
using (HttpContent responseContent = response.Content)
{
responseDataStream = new MemoryStream();
await responseContent.CopyToAsync(responseDataStream).ConfigureAwait(false);
// Ensure we return with the stream pointed at the origin.
responseDataStream.Position = 0;
}
}
}
}
return responseDataStream;
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
//----------------------------------------------------------------------------------------------
// <copyright file="RestPut.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <content>
/// .net 4.x implementation of HTTP PutAsync
/// </content>
public partial class DevicePortal
{
/// <summary>
/// Submits the http put request to the specified uri.
/// </summary>
/// <param name="uri">The uri to which the put request will be issued.</param>
/// <param name="body">The HTTP content comprising the body of the request.</param>
/// <returns>Task tracking the PUT completion.</returns>
public async Task<Stream> PutAsync(
Uri uri,
HttpContent body = null)
{
MemoryStream dataStream = null;
WebRequestHandler requestSettings = new WebRequestHandler();
requestSettings.UseDefaultCredentials = false;
requestSettings.Credentials = this.deviceConnection.Credentials;
requestSettings.ServerCertificateValidationCallback = this.ServerCertificateValidation;
using (HttpClient client = new HttpClient(requestSettings))
{
this.ApplyHttpHeaders(client, HttpMethods.Put);
// Send the request
using (HttpResponseMessage response = await client.PutAsync(uri, body).ConfigureAwait(false))
{
if (!response.IsSuccessStatusCode)
{
throw await DevicePortalException.CreateAsync(response);
}
this.RetrieveCsrfToken(response);
if (response.Content != null)
{
using (HttpContent content = response.Content)
{
dataStream = new MemoryStream();
await content.CopyToAsync(dataStream).ConfigureAwait(false);
// Ensure we return with the stream pointed at the origin.
dataStream.Position = 0;
}
}
}
}
return dataStream;
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
//----------------------------------------------------------------------------------------------
// <copyright file="UnvalidatedCert.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Handler for when an unvalidated cert is received.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="certificate">The server's certificate.</param>
/// <param name="chain">The cert chain.</param>
/// <param name="sslPolicyErrors">Policy Errors.</param>
/// <returns>whether the cert should still pass validation.</returns>
public delegate bool UnvalidatedCertEventHandler(DevicePortal sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors);
}

Просмотреть файл

@ -0,0 +1,107 @@
//----------------------------------------------------------------------------------------------
// <copyright file="Utilities.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using static Microsoft.Tools.WindowsDevicePortal.DevicePortal;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// Utility class for common functions
/// </summary>
internal static class Utilities
{
/// <summary>
/// Constructs a fully formed REST API endpoint uri.
/// </summary>
/// <param name="baseUri">The base uri (typically, just scheme and authority).</param>
/// <param name="path">The path to the REST API method (ex: api/control/restart).</param>
/// <param name="payload">Parameterized data required by the REST API.</param>
/// <returns>Uri object containing the complete path and query string required to issue the REST API call.</returns>
public static Uri BuildEndpoint(
Uri baseUri,
string path,
string payload = null)
{
string relativePart = !string.IsNullOrWhiteSpace(payload) ?
string.Format("{0}?{1}", path, payload) : path;
return new Uri(baseUri, relativePart);
}
/// <summary>
/// Builds a query string from key value pairs.
/// </summary>
/// <param name="payload">The key value pairs containing the query parameters.</param>
/// <returns>Properly formatted query string.</returns>
public static string BuildQueryString(Dictionary<string, string> payload)
{
string query = string.Empty;
foreach (KeyValuePair<string, string> pair in payload)
{
query += pair.Key + "=" + pair.Value + "&";
}
// Trim off the final ampersand.
query = query.Trim('&');
return query;
}
/// <summary>
/// Encodes the specified string as a base64 value.
/// </summary>
/// <param name="str">The string to encode.</param>
/// <returns>Base64 encoded version of the string data.</returns>
public static string Hex64Encode(string str)
{
return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(str));
}
/// <summary>
/// Checks if this device is a hololens.
/// </summary>
/// <param name="platform">The platform.</param>
/// <param name="deviceFamily">The device family.</param>
/// <returns>Whether this is a hololens.</returns>
public static bool IsHoloLens(
DevicePortalPlatforms platform,
string deviceFamily)
{
bool isHoloLens = false;
if ((platform == DevicePortalPlatforms.HoloLens) ||
((platform == DevicePortalPlatforms.VirtualMachine) && (deviceFamily == "Windows.Holographic")))
{
isHoloLens = true;
}
return isHoloLens;
}
/// <summary>
/// Modifies an endpoint to match the way we store it in a file.
/// This involves replacing a number of characters with underscores.
/// </summary>
/// <param name="endpoint">The endpoint that is being modified.</param>
public static void ModifyEndpointForFilename(ref string endpoint)
{
char[] invalidChars = Path.GetInvalidFileNameChars();
foreach (char character in invalidChars)
{
endpoint = endpoint.Replace(character, '_');
}
endpoint = endpoint.Replace('-', '_');
endpoint = endpoint.Replace('.', '_');
endpoint = endpoint.Replace('=', '_');
endpoint = endpoint.Replace('&', '_');
}
}
}

Просмотреть файл

@ -0,0 +1,220 @@
//----------------------------------------------------------------------------------------------
// <copyright file="WebSocket.cs" company="Microsoft Corporation">
// Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Tools.WindowsDevicePortal
{
/// <summary>
/// HTTP Websocket Wrapper
/// </summary>
/// <typeparam name="T">Return type for the websocket messages.</typeparam>
internal partial class WebSocket<T>
{
/// <summary>
/// The hresult for the connection being reset by the peer.
/// </summary>
private const int WSAECONNRESET = 0x2746;
/// <summary>
/// The maximum number of bytes that can be received in a single chunk.
/// </summary>
private static readonly uint MaxChunkSizeInBytes = 1024;
/// <summary>
/// The <see cref="ClientWebSocket" /> that is being wrapped.
/// </summary>
private ClientWebSocket websocket;
/// <summary>
/// <see cref="Task" /> for receiving messages.
/// </summary>
private Task receivingMessagesTask;
/// <summary>
/// The handler used to validate server certificates.
/// </summary>
private Func<object, X509Certificate, X509Chain, SslPolicyErrors, bool> serverCertificateValidationHandler;
/// <summary>
/// Initializes a new instance of the <see cref="WebSocket{T}" /> class.
/// </summary>
/// <param name="connection">Implementation of a connection object.</param>
/// <param name="serverCertificateValidationHandler">Server certificate handler.</param>
/// <param name="sendStreams">specifies whether the web socket should send streams (useful for creating mock data).</param>
public WebSocket(IDevicePortalConnection connection, Func<object, X509Certificate, X509Chain, SslPolicyErrors, bool> serverCertificateValidationHandler, bool sendStreams = false)
{
this.sendStreams = sendStreams;
this.deviceConnection = connection;
this.IsListeningForMessages = false;
this.serverCertificateValidationHandler = serverCertificateValidationHandler;
}
/// <summary>
/// Opens a connection to the specified websocket API.
/// </summary>
/// <param name="endpoint">The uri that the weboscket should connect to</param>
/// <returns>The task of opening a connection to the websocket.</returns>
private async Task ConnectInternalAsync(
Uri endpoint)
{
this.websocket = new ClientWebSocket();
this.websocket.Options.UseDefaultCredentials = false;
this.websocket.Options.Credentials = this.deviceConnection.Credentials;
this.websocket.Options.SetRequestHeader("Origin", this.deviceConnection.Connection.AbsoluteUri);
// There is no way to set a ServerCertificateValidationCallback for a single web socket, hence the workaround.
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors)
{
return this.serverCertificateValidationHandler(sender, cert, chain, policyErrors);
};
await this.websocket.ConnectAsync(endpoint, CancellationToken.None);
this.IsConnected = true;
}
/// <summary>
/// Closes the connection to the websocket.
/// </summary>
/// <returns>The task of closing the websocket connection.</returns>
private async Task CloseInternalAsync()
{
await Task.Run(() =>
{
this.websocket.Dispose();
this.websocket = null;
this.IsConnected = false;
});
}
/// <summary>
/// Stops listening for messages.
/// </summary>
/// <returns>The task of closing the websocket connection.</returns>
private async Task StopListeningForMessagesInternalAsync()
{
await this.websocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
// Wait for web socket to no longer be receiving messages.
if (this.IsListeningForMessages)
{
await this.receivingMessagesTask;
this.receivingMessagesTask = null;
}
}
/// <summary>
/// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised.
/// </summary>
/// <returns>The task of listening for messages from the websocket.</returns>
private async Task StartListeningForMessagesInternalAsync()
{
await Task.Run(() =>
{
this.StartListeningForMessagesInternal();
});
}
/// <summary>
/// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised.
/// </summary>
private void StartListeningForMessagesInternal()
{
this.IsListeningForMessages = true;
this.receivingMessagesTask = Task.Run(async () =>
{
try
{
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[MaxChunkSizeInBytes]);
// Once close message is sent do not try to get any more messages as WDP will abort the web socket connection.
while (this.websocket.State == WebSocketState.Open)
{
// Receive single message in chunks.
using (var ms = new MemoryStream())
{
WebSocketReceiveResult result;
do
{
result = await this.websocket.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false);
if (result.MessageType == WebSocketMessageType.Close)
{
await this.websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
return;
}
if (result.Count > MaxChunkSizeInBytes)
{
throw new InvalidOperationException("Buffer not large enough");
}
ms.Write(buffer.Array, buffer.Offset, result.Count);
}
while (!result.EndOfMessage);
ms.Seek(0, SeekOrigin.Begin);
if (result.MessageType == WebSocketMessageType.Text)
{
Stream stream = new MemoryStream();
await ms.CopyToAsync(stream);
// Ensure we return with the stream pointed at the origin.
stream.Position = 0;
this.ConvertStreamToMessage(stream);
}
}
}
}
catch (WebSocketException e)
{
// If WDP aborted the web socket connection ignore the exception.
SocketException socketException = e.InnerException?.InnerException as SocketException;
if (socketException != null)
{
if (socketException.NativeErrorCode == WSAECONNRESET)
{
return;
}
}
throw;
}
finally
{
this.IsListeningForMessages = false;
}
});
}
/// <summary>
/// Sends a message to the server.
/// </summary>
/// <param name="message">The message to send.</param>
/// <returns>The task of sending a message to the websocket.</returns>
private async Task SendMessageInternalAsync(string message)
{
byte[] bytes = Encoding.UTF8.GetBytes(message);
ArraySegment<byte> buffer = new ArraySegment<byte>(bytes);
await this.websocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}