Microsoft.Testing.Extensions.PackagedApp 2.5.1

Prefix Reserved
dotnet add package Microsoft.Testing.Extensions.PackagedApp --version 2.5.1
                    
NuGet\Install-Package Microsoft.Testing.Extensions.PackagedApp -Version 2.5.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Microsoft.Testing.Extensions.PackagedApp" Version="2.5.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Microsoft.Testing.Extensions.PackagedApp" Version="2.5.1" />
                    
Directory.Packages.props
<PackageReference Include="Microsoft.Testing.Extensions.PackagedApp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Microsoft.Testing.Extensions.PackagedApp --version 2.5.1
                    
#r "nuget: Microsoft.Testing.Extensions.PackagedApp, 2.5.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Microsoft.Testing.Extensions.PackagedApp@2.5.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Microsoft.Testing.Extensions.PackagedApp&version=2.5.1
                    
Install as a Cake Addin
#tool nuget:?package=Microsoft.Testing.Extensions.PackagedApp&version=2.5.1
                    
Install as a Cake Tool

Microsoft.Testing.Extensions.PackagedApp

Microsoft.Testing.Extensions.PackagedApp is an extension for Microsoft.Testing.Platform that starts Windows test hosts which cannot simply be Process.Started. In its Windows build (net*-windows) a packaged MSIX host is registered with the OS and activated by Application User Model ID (AUMID); a non-packaged (loose-layout) host can optionally be deployed into an isolated directory and launched from there.

It is the consumer of the platform's ITestHostLauncher extension point for Windows test hosts. Packaged Windows apps require package identity and ship as MSIX; VSTest exposes a single UwpTestHostRuntimeProvider for the equivalent scenario, built on Visual-Studio-internal deployment components, whereas this extension uses only public, redistributable Windows APIs:

  • Packaged AUMID activation (Windows build): a packaged (MSIX) layout is registered in place with the PackageManager and the app is activated by AUMID via IApplicationActivationManager. packagedClassicApp/win32App hosts receive the platform-prepared command line through argv, including when their trust level is appContainer. windowsApp/UWP hosts receive one opaque launch string and restore the exact logical argument array through PackagedAppExtensions.GetTestApplicationArguments(args.Arguments) in Application.OnLaunched (see #10485). Registering an unsigned build-output layout requires Developer Mode (or sideloading). The plain net8.0/net9.0 build rejects a packaged layout with an actionable error pointing at the Windows TFM.
  • Deploy + launch loose layout (opt-in): a non-packaged app — one without an AppxManifest.xml — is deployed to a deployment directory and the produced executable is launched from there.

Switching packaged build layouts

Before activation, the launcher verifies that the current user's main package registration points to the directory containing the requested AppxManifest.xml. Windows can report registration success while retaining a different layout with the same package identity and version. In that case, the launcher removes only the stale development registration, using PreserveApplicationData, and registers the requested layout again. Repeated runs from the same layout do not remove its registration.

Launchers serialize the entire registration-and-activation sequence per user and package family, including across processes. Another launcher cannot switch the layout between verification and AUMID activation. Waiting for another launch respects cancellation.

The manifest is held open for reading throughout that sequence, preventing a concurrent rebuild from changing its identity or application declarations. The same parsed identity determines the family lock, registration verification, handoff storage, and activation AUMID.

If cancellation is observed before stale-registration removal starts, no destructive change is made. Once removal completes, the launcher finishes registering and verifying the replacement before it surfaces cancellation, so an interrupted run does not intentionally leave the package unregistered.

If the conflicting package is not a development registration, removal does not actually unregister it, or the requested location still cannot be registered, the launcher reports an error instead of activating another build. A failed replacement can leave the package unregistered, but its application data is retained; resolve the reported deployment error and retry. Callers do not need to uninstall their app or change its version for each build.

When the launcher takes over

Registering an enabled test host launcher switches the run to the test host controller (process restart) model, because a launcher only has an effect when an out-of-process test host is started. To avoid charging that cost to apps that do not need it, the launcher enables itself only when it has real work to do:

Situation Enabled?
Not Windows no
Packaged layout (an AppxManifest.xml that describes this app) yes
Any other layout, including unpackaged WinUI and ordinary console test apps no

A manifest in the app's own directory is taken as the app's layout. A manifest in an ancestor directory is used only when one of its <Application> entries declares an Executable resolving back to the app directory (and, at launch, to that exact executable), which supports Application/@Executable pointing into a package subdirectory at any depth while keeping a stray manifest in a shared build root from classifying an unrelated app as packaged.

So referencing this package from an unpackaged app costs nothing: no extra process, and no copy of the build output.

Controller pipe access for AppContainer hosts

The platform's test host controller talks to the test host over a named pipe created with the equivalent of PipeOptions.CurrentUserOnly: it is owned by the creating token's owner SID and its DACL grants only that SID, which is what keeps another user — or a differently-elevated process of the same user — out of the run.

A UWP or AppContainer-configured WinUI host cannot connect to such a pipe. An AppContainer runs with a restricted token and Windows grants access only when the normal access check and the restricted-SID check both succeed; the restricting SIDs contain the app's package SID, so a DACL naming only the user denies the host even though it belongs to the same signed-in user.

When the layout is a packaged app that declares an AppContainer application, this extension derives that package's own AppContainer SID from its package family name and asks the platform to authorize it on the pipe before the pipe is created. Sandbox membership is deliberately classified separately from argument delivery: a packagedClassicApp at TrustLevel="appContainer" receives plain argv but still needs the SID grant, while a windowsApp explicitly at TrustLevel="mediumIL" uses launch activation without running in an AppContainer. The grant is minimal and scoped:

  • only that package SID is added — ALL APPLICATION PACKAGES (S-1-15-2-1) is never granted, and the platform rejects any request for a user, a group or Everyone;
  • it receives read, write, read-security and synchronize rights only, so it can never create another instance of the pipe, change its DACL, or delete it;
  • the DACL stays protected and the pipe rejects remote clients;
  • authorization-enabled pipes use the Windows-required LOCAL\ namespace (\\.\pipe\LOCAL\<name>);
  • the pipe keeps the controller's own integrity level, so Mandatory Integrity Control stays a second gate behind the DACL;
  • a packaged full-trust host, an unpackaged app, and every non-Windows run keep the existing pipe unchanged.

Set TESTINGPLATFORM_PACKAGEDAPP_PIPEAUTHORIZATION to never to disable the grant entirely, or to always to request it for any packaged layout; auto (the default) probes the manifest.

Set TESTINGPLATFORM_PACKAGEDAPP_LAUNCHER to override that decision — always opts a non-packaged layout into deploy-and-launch, never keeps the launcher out of the way entirely, and auto (the default) probes the layout.

Launch-activation bootstrap

A windowsApp/UWP app has no Main(string[] args) receiving the activation string as process arguments. Its OnLaunched override must restore the MTP argument array before creating the builder. A packagedClassicApp/win32App uses its normal process arguments instead, even when its trust level is appContainer:

protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
    string[] cliArgs = PackagedAppExtensions.GetTestApplicationArguments(args.Arguments);
    ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(cliArgs);
    builder.AddSelfRegisteredExtensions(cliArgs);
    using ITestApplication app = await builder.BuildAsync();
    Environment.ExitCode = await app.RunAsync();
    Exit();
}

The handoff is versioned and length-prefixed, so empty values, whitespace, quotes, backslashes, Unicode, repeated options, and option order round-trip exactly. Payloads within Windows' documented 2,048-character launch-argument envelope stay entirely in the activation string. Larger payloads are written to package LocalState only as authenticated ciphertext; the one-shot key remains in the activation string, and both the host and launcher delete the file at the earliest cleanup point. User filters, runsettings, and other arguments are never persisted in plaintext.

The one-shot connect-back handshake file in LocalState carries only the explicit protocol metadata that AUMID activation cannot inherit, including the native dotnet test execution ID. This keeps the activated host on the controller's existing .NET 10+ test session while continuing to exclude arbitrary environment values, filters, runsettings, credentials, and secrets.

Argument restoration, connect-back metadata, and exact package-SID pipe authorization are implemented. Microsoft.Testing.Extensions.PackagedApp.MSBuild uses them from an ordinary full-trust sidecar controller, so classic UWP, modern UWP, and AppContainer-configured WinUI applications execute through Microsoft.Testing.Platform without a VSTest runtime or deployment provider. MSTest.Sdk references that integration automatically; metapackage and adapter-only projects can reference it explicitly with EnableMSTestRunner=true.

Microsoft.Testing.Platform is open source. You can find Microsoft.Testing.Extensions.PackagedApp code in the microsoft/testfx GitHub repository.

Install the package

dotnet add package Microsoft.Testing.Extensions.PackagedApp

Usage

Referencing this package automatically registers its test-host launcher. Packaged layouts are detected automatically; set TESTINGPLATFORM_PACKAGEDAPP_LAUNCHER=always to opt a non-packaged loose layout into deployment, or never to disable the launcher.

For a windowsApp/UWP host, restore the launch activation arguments with PackagedAppExtensions.GetTestApplicationArguments before creating the test application builder, as shown in Launch-activation bootstrap.

About

This package extends Microsoft.Testing.Platform with:

  • Registration + activation: registers a packaged MSIX test host and activates it by AUMID (see #9933 and #10485); optionally stages a non-packaged (loose-layout) Windows test host payload into an isolated directory and launches the deployed copy.
  • Mechanism-agnostic monitoring: returns an ITestHostHandle that exposes only the lifecycle the platform needs (surfacing the activated process id for the packaged path, and none for the deployed loose-layout path).

Documentation

For comprehensive documentation, see https://aka.ms/testingplatform.

Feedback & contributing

Microsoft.Testing.Platform is an open source project. Provide feedback or report issues in the microsoft/testfx GitHub repository.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net8.0-windows10.0.19041 is compatible.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net9.0-windows10.0.19041 is compatible.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Microsoft.Testing.Extensions.PackagedApp:

Package Downloads
Microsoft.Testing.Extensions.PackagedApp.MSBuild

Run packaged Windows test applications through a full-trust Microsoft.Testing.Platform controller. This package supplies the controller executable, its runtime dependencies, and shared MSBuild targets for WinUI, modern UWP, and classic UWP. Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.5.1 320 10/7/2026
2.5.0 135 10/6/2026
2.4.1 514 9/16/2026
2.4.0 357 9/2/2026
1.0.0-alpha.26377.5 99 7/28/2026
1.0.0-alpha.26363.8 90 7/13/2026
1.0.0-alpha.26358.12 106 7/8/2026
1.0.0-alpha.26357.13 98 7/7/2026