In Windows 10 version 2004, we are introducing the concept of Hosted Apps to the Windows App Model. Hosted apps are registered as independent apps on Windows, but require a host process in order to run. An example would be a script file which requires its host (eg: Powershell or Python) to be installed. By itself, it is just a file and does not have any way to appear as an app to Windows. With the Hosted App Model, an app can declare itself as a host, and then packages can declare a dependency upon that host and are known as hosted apps. When the hosted app is launched, the host executable is then launched with the identity of the hosted app package instead of its own identity. This allows the host to be able to access the contents of the hosted app package and when calling APIs it does so with the hosted app identity.
Modern apps are defined to Windows via signed MSIX packages. A package provides identity, so it is known to the system and contains all the files, assets, and registration information for the app it contains. Many apps have scenarios where they want to host content and binaries, such as extensibility points, from other apps. There are also scenarios where the host app is more of a runtime engine that loads script content. On top of it all, there is a desire to have these hosted apps to look and behave like a separate app on the system – where it has its own start tile, identity, and deep integration with Windows features such as BackgroundTasks, Notifications, and Share. Using the Hosted App Model, a retail kiosk app can easily be rebranded, or a Python or Powershell script can now be treated as a separate app.
Developers attempt to accomplish this today in either of two ways. First, they simply use a shortcut on the desktop to launch the host. But this experience does not have any deep integration with Windows and the shell, as the ‘app’ is the host executable not the script. To get a more deeply integrated experience, the alternative is for developers to create a packaged app that includes the host binaries within the package. While the package would now be a separate app and have the ability for deep Windows integration, this approach is inefficient as each app would need to redistribute the host and can have potential servicing and licensing issues.
The Hosted App Model solves the needs of these hosted apps. The Hosted App Model is dependent upon two pieces, a “Host” which is made available to other apps, and a “Hosted App” that declares a dependency upon the host. When a hosted app is launched, the result is that the host is then running under the identity of the hosted app package, so it can load visual assets, content from the Hosted App package location, and when it calls APIs it does so with the identity declared in the Hosted App. The Hosted App gets the intersection of capabilities declared between the Host and Hosted App – this means that if a Hosted App cannot ask for more capabilities than what the Host provides. In this initial release of the Hosted App Model packaged desktop apps are supported, and we will be expanding support to UWP hosts in future releases.
What is a Host and a Hosted App?
More specifically, a Host is the executable in a package declared by the HostRuntime extension which points to the main executable or runtime process for the hosted app. The HostRuntime extension has an Id attribute, and this identifier is referenced as a dependency by the Hosted App in its package manifest. A host can determine the package identity it is currently running under by referring to the Windows.ApplicationModel.Package.Current api.
A Hosted App is an app that declares a package dependency on a Host, and leverages the HostRuntime Id for activation instead of specifying an Entrypoint executable in its own package. It typically contains content, visual assets, scripts, or binaries that may be accessed by the host. Hosted App packages can be Signed or Unsigned:
- Signed packages may contain executable files. This is useful in scenarios that have an extension mechanism, allowing the host to load a dll or registered component in the hosted app package.
- Unsigned packages can only contain non-executable files. This is useful in scenarios where the hostruntime only needs to load images, assets and content such as script files. Unsigned packages must include a special Unsigned Publisher OID in their Identity or they won’t be allowed to register. This prevents unsigned packages from spoofing a signed package identity.
Declaring a Host
Declaring a Host is quite simple. All you need to do is to declare the HostRuntime package extension in your AppxManifest.xml. The HostRuntime extension is package-wide and so is declared as a child of the package element. Below is an excerpt from an example AppxManifest.xml showing the HostRuntime entry that declares an app as a Host with Id “PythonHost.”
- hostRuntime – a package-wide extension defining runtime information used when activating a Hosted App.
- Executable – The executable binary that will be the host process
- RuntimeBehavior and TrustLevel – A hosted app will run with the definitions expressed in the extension. For example, a hosted app using the Host declared above will run the executable PyScriptEngine.exe, at mediumIL trust level.
- HostRuntime Id – A unique identifier used to specify a Host in a package. A package can have multiple Host Apps, and each must have a unique HostRuntime Id. This identifier is referenced by the Hosted App.
Declaring a Hosted App
A hosted app must declare a package dependency upon the host, and specify the HostId to use. If the package is unsigned, it must include the Unsigned Publisher OID to ensure the package identity does not conflict with a signed package. Also the TargetDeviceFamily should match the host so it does not attempt to deploy on devices that are not supported by the host. The following is an example of a manifest for a Hosted App that takes a dependency upon the Python host.
- Unsigned Publisher OID – 2.25.311729368913984317654407730594956997722=1 This identifier is required when a Hosted App will be unsigned. The identifier ensures any unsigned package cannot spoof the identity of a signed package.
- HostRuntimeDependency – A Hosted App package must declare a HostRuntimeDependency on the Host app. This consists of the Name and Publisher of the Host package, and the min version it depends on. These can be found under the <Identity> element in the Host package. When deployed, if the HostRuntimeDependency cannot be found, the registration fails.
- HostId – Instead of declaring the usual Executable and EntryPoint for an app or extension, the HostId attribute expresses a dependency on a Host app. As a result, the Hosted App inherits the Executable, EntryPoint and runtime attributes of the Host with the specified HostId. When registered, if the HostId is not found, the deployment fails.
- Parameters (optional)– parameters that are passed on the command line to the host app. The host needs to know what to do with these parameters, and so there is an implied contract between the host and hosted app.
Dynamic Registration for Unsigned Hosted Apps
One of the advantages of the new HostRuntime is that it enables a host to dynamically register a hosted app package at runtime. This dynamically registered package does not need to be signed. This allows a host to dynamically generate the content and manifest for the hosted app package and then register it. We are working with the new Microsoft Edge browser to take advantage of the Hosted App Model for Progressive Web Apps (PWAs) – converting the web app manifest into an app manifest, package the additional web content into an MSIX package and register it. In this model, a PWA is its own independent app registered to the system even though it is being hosted by Edge.
The new APIs for registering a package are:
- Management.Deployment.PackageManager.AddPackageByUriAsync() is used for registering an MSIX package
- Management.Deployment.PackageManager.RegisterPackageByUriAsync() is used for registering a loose file AppxManifest.xml file.
In the case where the hosted app is unsigned, its manifest must meet the following requirements:
- The unsigned package cannot contain any Executable attributes in its Application or Extension elements (e.g.: no <Application Executable=…> or <Extension Executable=…>), and it can’t specify any other activation data (Executable, TrustLevel, etc). The Application node only supports the HostId and Parameters elements.
- An unsigned package must be a Main package type – it cannot be a Bundle, Framework, Resource or Optional package.
In turn, the host process registering an unsigned hosted app package must meet the following requirements:
- The process must have package identity
- The process must have the package management capability <rescap:Capability Name=”packageManagement”/>
A Host and Hosted App Examples
Let’s have a look at two examples. The first, WinFormsToastHost, is a Host with a signed Hosted App that shows how to include an extension that is dynamically loaded into the host. The second, NumberGuesser, an example of using python as a host and a script file as a hosted app package. You can find the sample code for both at https://aka.ms/hostedappsample.
The host in this example is a simple Windows Forms app that displays its package identity, location, and calls the ToastNotification APIs. It also has the capability to load a binary extension from a hosted app package. When run under its own identity, it does not display the extension information. The app is packaged with the Windows App Packaging Project which includes the manifest declarations for being a host.
The hosted app is a .NET dll that implements an extension mechanism for the host to load. It also includes a packaging project that declares its identity and dependency upon the hostruntime. You will see this identity reflected in the values displayed when the app is run. When registered, the hostruntime has access to the hostedapp’s package location and thus can load the extension.
Running the sample
You can load the source code in Visual Studio as follows:
- Open WinformsToastHost.sln in VS2019
- Build and deploy WinformsToastHost.Package
- Build and deploy HostedAppExtension
- Goto Start menu and launch ‘WinformsToastHost’
- Goto Start menu and launch ‘Hosted WinformsToastHost Extension‘
Here is a screenshot of the host running. Notice its package identity and path, and the UX for loading an assembly is not available because it is not running as a hosted app.
Now launch the hosted app. Notice the identity and path have changed, and that the UX for dynamically loading an extension assembly is enabled.
When the “Run hosted” button is pressed, you will get a dialog from the binary extension:
Here is the Task Manager details view showing both apps running at the same time. Notice that the host binary is the executable for both:
And when clicking on the Show Toast button for each app, the system recognizes the two different identities in the action center:
NumberGuesser – A Python Host and Game
In this example, the host is comprised of 2 projects – first is PyScriptEngine which is wrapper written in C# and makes use of the Python nuget package to run python scripts. This wrapper parses the command line and has the capability to dynamically register a manifest as well as launch the python executable with a path to a script file. The second project is PyScriptEnginePackage which is a Windows App Packaging Project that installs PyScriptEngine and registers the manifest that includes the HostRuntime extension.
The Hosted App
The Hosted App is made up of a python script, NumberGuesser.py, and visual assets. It doesn’t contain any PE files. It has an app manifest where the declarations for HostRuntimeDependency and HostId are declared that identifies PyScriptEngine as its Host. The manifest also contains the Unsigned Publisher OID entry that is required for an unsigned package.
Running the sample
To run this sample you first need to build and deploy the host, then you can use the host from the commandline to dynamically register the hosted app.
- Open PyScriptEngine.sln solution in Visual Studio
- Set PyScriptEnginePackage as the Startup project
- Build PyScriptEnginePackage
- Deploy PyScriptEnginePackage
- Because the host app declares an appexecutionalias, you will be able to go to a command prompt and run “pyscriptengine” to get the usage notice:
6. Use the python host to register the NumberGuesser game from the commandline:
7. Now, click on “Number Guesser (Manifest)” in your start menu, and run the game! See how many tries it takes you to guess the number:
Let’s confirm what is running. Notice how PyScriptEngine is executing under the package identity of NumberGuesser!
Wrapping it up
In summary, we are pleased to bring you more power and features to the windows platform, and we are excited to see what creative ideas you have for the Hosted App Model. In addition to Microsoft Edge, we are working with teams across the company and expect to see more apps leveraging the Hosted App Model in the future.