8000 App Lifecycle · Issue #111 · microsoft/WindowsAppSDK · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
App Lifecycle #111
Closed
Closed
@andreww-msft

Description

@andreww-msft
Summary Status

Customer / job-to-be-done: App developers using WPF, WinForms, or Win32 C++ need to register for certain activation types like file associations and startup activation, need to selectively single-instance their apps, and need to use power states to selectively suspend their apps.

Problem: Non-packaged apps use a different set of APIs to achieve all of the above, and they also don't have access to power state notifications

How does our solution make things easier? All types of apps will benefit from the same single API regardless of app type, apps can now listen to power states, and WIn32 apps have a built-in easy way to single instance.

✅ Problem validation
❌ Docs
🔄 Dev spec
❌ Solution validation
❌ Implementation
❌ Samples

Summary

Provide a core set of functionality related to app activation and lifecycle. The initial release focuses on the following 4 main feature areas:

  1. Activation.
  2. Selective Multi-instancing.
  3. Restart and Recovery.
  4. Power/State Notifications.

Rationale

  • Enable all apps to register for the core activation contracts, consistently in the same way.
  • Enable all apps to use platform support for single-instancing, or selective multi-instancing.
  • Enable all apps to request immediate restart, or to opt in to recovery/restart in the event of app failure or system reboot.
  • Enable all apps to get power/battery/user/system-state change notifications so that they can be better power/battery citizens.

This aligns with Project Reunion's roadmap:

  • Enable all applications (packaged and not-packaged) to use the same mechanisms for managing app activation and lifecycle.

Scope

Capability Priority
This proposal will allow developers to use the same mechanism to register for different activation kinds regardless of app technology (classic Win32, Windows Forms, WPF, UWP or WinUI). Must
This proposal will allow developers to use the same mechanism to get rich activation event arguments regardless of app technology. Must
This proposal will allow users to activate any app by double-clicking it in file explorer, or typing in a path in a command window. Must
This proposal will allow developers to use a consistent mechanism to single-instance or multi-instance their apps, based on app-defined logic. Must
This proposal will allow developers to use a consistent mechanism to take part in app and system restart and recovery. Must
This proposal will allow developers to use a consistent mechanism to get power and system state notifications. Must
This proposal will allow apps to better tune their operations to improve power/battery usage. Must
This proposal will allow developers to take part in system resource management Could
This proposal will allow developers to take part in app suspend/resume behavior Could

Important Notes

  • This is not "yet another app framework" -- rather it is an easier way for all app types to use features in the platform consistently.
  • In some cases, we'll be providing parallels to existing APIs -- and the benefit is that converging traditional Win32 APIs with modern WinRT APIs into one component allows all app types to use the same APIs (instead of one app type using Win32 APIs, and another app type using WinRT APIs) consistently. This will also make it easier for app developers to move from one app type to another as appropriate -- they can continue to use the same AppLifecycle component regardless.
  • We will not wrap existing public APIs as this would result in a performance penalty -- rather, we will provide parallel APIs that use the same underlying primitives.
  • Whatever we build, we strive to make it as easy as possible for app developers to use. We aim not to introduce new hurdles, new development paradigms, or new architectural concepts that would cause friction. We also won't make the developer change what they're doing just because we version the Reunion component. As we add improvements/new features over time, these should "just work" seamlessly.

Rich Activation Behaviors

The activation part of the AppLifecycle component is focused on the
following 4 functional requirements:

  • Enable activation of any app (including Desktop Bridge and UWP) via CreateProcess on its path.
  • Enable all apps to register for any of the most common activation kinds.
  • Provide a GetActivatedEventArgs API that all apps can use to get any of the supported rich arguments for any of the supported activation kinds.

CreateProcess activation

In the Win32 world, it is normal to activate an app by calling CreateProcess (or ShellExecute), specifying the filesystem path to the executable file. When the user double-clicks an executable file in File Explorer, this uses that same mechanism. When the user types in an executable file name into a command window, the same thing happens. In contrast, up until now, when a UWP or Desktop Bridge app is activated, this is done via the app's Application User Model ID (AUMID). For example, when an app calls Launcher.LaunchUriAsync, it specifies the target AUMID -- or a file or protocol, which causes the platform to look up registered AUMIDs in its State Repository database, before activating
the app.

The Windows a-la-carte features introduced in 2004 include the ability to activate an app via CreateProcess on its path, as well as by the UWP AUMID lookup. The gap here is that the CreateProcess mechanism only works for Win32 apps -- as part of Reunion, we will bring this to packaged (Desktop Bridge and UWP) apps also.

With this, you will be able to CreateProcess a UWP app by specifying the path to its executable, or navigate to the filesystem location and double-click it there.

Activation kinds

UWP supports ~40 different activation kinds. Traditional Win32 apps have a much smaller set available -- the most common being file-type associations and protocol handling. In UWP both of these are formalized into specific activation contracts (ActivationKind.File and ActivationKind.Protocol) and when the app is activated in this way, the platform passes in rich FileActivatedEventArgs or ProtocolActivatedEventArgs. The same applies for command-line activation, activation at user login (startup activation), and so on.

Of the many different kinds of activation that UWP supports, 6 of the most commonly-used kinds are also supported for Desktop Bridge apps, and almost the same set is supported for Windows a-la-carte apps. We will support this core set of activation kinds for unpackaged Win32 apps also, plus command-line and restart activation. So the initial list is as follows:

  • Launch
  • File
  • Protocol
  • StartupTask
  • ToastNotification
  • ShareTarget
  • CommandLine
  • Restart

How should we ask developers to specify their registrations for the various activation kinds? For the activation kinds that are supported in Win32 -- such as file and protocol -- apps commonly write registry keys. Conversely, UWP apps write morally equivalent extension entries in their regular MSIX manifest.

Apps that write regkeys on install sometimes don't clean them up on uninstall, and this is a primary source of winrot. This is one of the main benefits of MSIX. Therefore, the proposal is to encourage app developers to specify their activation registrations in an XML manifest rather than in the registry.

An additional advantage of the manifest approach is that it also provides an easy way for the app to get identity. The proposal is that we ask an app to craft some subset of an MSIX manifest -- sufficient for the activation extension registrations, plus identity. We will not require that the app is MSIX-packaged, merely that they have a simple manifest. While this will be a subset of the full MSIX manifest, it must use the same schemas. The platform can then use the same Deployment Extension Handlers (DEHs) on deployment to register these extensions.

Activation and ActivatedEventArgs

Traditional Win32 apps expect to get their arguments passed into WinMain in the form of a string array. Windows Forms apps expect to call Environment.GetCommandLineArgs to return a string array. ulti-instanced UWP and Desktop Bridge apps can call the AppInstance.GetActivatedEventArgs API to return strongly-typed objects for each activation kind. We will provide a converged GetActivatedEventArgs which will get all args, regardless of activation kind -- including both traditional command-line activation and also the richer UWP activation objects. This will be available to all apps.

Activation broker

Two of the proposed activation kinds present particular problems: ToastNotification and ShareTarget. Both of these will likely require some additional component at runtime to act as a broker between the source and target of the activation.

The a-la-carte model has paved the way for apps of all kinds to get a system-recognized identity. This then provides several benefits when using modern features -- including the activation kinds under consideration for the undocked AppLifecycle component. We would like to use the a-la-carte model for undocked activation. However, there are a few issues:

  • The a-la-carte implementation is unfortunately very tightly coupled with the OS and the app platform, and it is infeasible to undock it in the timeframe of Reunion v1.
  • The model is only available from OS version 2004, but there is a desire for the undocked AppLifecycle functionality to be available down to OS version 1809.
  • For the same reasons that it is infeasible to undock a-la-carte, it is also infeasible to service the OS down to 1809 to include a-la-carte.
  • For ShareTarget specifically, the model uses a small MSIX broker which must be authored for each app. From 2004, ShareTarget can be achieved with just the manifest registration alone, no broker.

Identity

Both UWP and Desktop Bridge apps have an identity that is registered on the platform, and on which multiple APIs in the platform rely. Unpackaged Win32 apps do not have such an identity, and therefore cannot use any of the APIs that require identity.

In the Windows a-la-carte model, the app creates an XML manifest specifying its identity, and registers this with the system during install (or at runtime) via new PackageManager APIs.

There is an initiative in the Cobalt timeframe to enable the Store to serve up unpackaged Win32 apps in addition to UWP and Desktop Bridge apps. This is part of a general strategy of de-fragmenting the user experience of apps. Right now, there are several places where packaged apps are treated differently from unpackaged apps. A user experience example is the Apps & features page, where the options are differentiated. A more critical example is in the API surface where many WinRT APIs require the caller to have identity.

Crucially, some WinRT APIs behave differently depending on whether or not the caller has identity. Given that even unpackaged Win32 apps are already using WinRT APIs, we cannot apply identity unless the app specifically registers for identity. Instead, if an app wants to call an API, we should simply document whether or not that API requires identity, and guide the app developer to creating the appropriate manifest if they need it.

Single/Multi-Instancing

In the Win32 world, apps are multi-instance by default. That is, if the user launches Notepad 3 times in a row, they get 3 separate instances of the app. Conversely, in the UWP world, apps are single-instance by default: if the user launches Maps times in a row, the first request causes the Maps app to launch, and the second and subsequent requests cause the platform to make another activation call into the first (and only) instance.

Since Windows OS version 1803, a UWP app can opt in to multi-instancing via a manifest declaration. This feature also includes APIs to allow an app to declare itself to be multi-instanced, and yet to choose dynamically for each instance that is activated whether in fact it wants to redirect that activation to an existing instance instead. In this way, the app has the freedom to choose among several options:

  • constrain itself to a single instance
  • constrain itself to an app-defined finite set of instances
  • allow for an open-ended set of multiple instances.

Win32 apps have existing mechanisms (most commonly, named mutexes and named pipes) which they can use to achieve single-instancing in this context. There's also a Visual Basic Application Model which Windows Forms apps (whether written in Visual Basic or not) can use for single-instancing. The undocked AppLifecycle component will include a consistent, platform-supported API for selective
multi/single-instancing. This updates the UWP mechanism and enables it for use by Desktop Bridge and unpackaged Win32 apps.

The specific multi-instance redirection APIs that we will provide for all apps are all based on the existing WinRT
AppInstance class. The proposal is to expose equivalents to almost all AppInstance methods and properties from the new AppLifecycle class.

The existing APIs allow an app to intercept each activation, find any other running instances, and choose which instance to handle the new activation (either the current instance, or any other). The app doesn't control the activation event args -- these are simply directed to the chosen instance. In the Win32 world, apps have the opportunity to intercept the args, and therefore have the opportunity to modify/suppress/replace these args before forwarding them on. So, we propose to enhance the existing WinRT APIs to allow each activation to pass in additional payload to the target instance (at a lower priority, possibly deferred to a later release). This payload is in addition to the ActivatedEventArgs that the system originally passed in, and which the system will pass on to the target activated instance.

The multi-instancing part of the AppLifecycle component is focused on the following functional requirements:

  • Provide an undocked implementation of the existing AppInstance multi-instance redirection APIs so they are also usable by Desktop Bridge and unpackaged Win32 apps.
  • Enhance the multi-instance APIs to enable apps to intercept the activation args and manipulate them in any way they require: including modifying, suppressing or replacing them.
  • Provide an Activated event on AppLifecycle, specifically so that apps that are using multi/single-instancing can handle being the target of a redirection.

App Restart and Recovery

The CoreApplication WinRT class exposes the RequestRestartAsync method, which allows a UWP app to terminate and restart itself immediately, on request, and to provide an arbitrary command-line string for the restarted instance.

A related API exists in the Win32 world: RegisterApplicationRestart
and the matching UnregisterApplicationRestart API. This is intended for an app to register itself to be restarted if it was running when a system update occurs, or if it crashed or hung. This behavior is currently not available to UWP apps.

The use-cases for these 2 APIs are different, but related enough that it makes sense to offer both from the same place in the undocked
AppLifecycle.

Related to RegisterApplicationRestart, there are 2 further Win32 APIs that enable an app to save state or perform other clean-up operations prior to termination. As part of this recovery, the app can choose to update the command-line to be used when the app is restarted:

  • Let-me-save-state-before-termination. An app can call
    RegisterApplicationRecoveryCallback
    to register a callback for the system to call before terminating the app. If an application encounters an unhandled exception or becomes unresponsive, Windows Error Reporting (WER) calls the specified recovery callback, where the app can save state information. The system pings the app every n seconds to make sure that it hasn't hung in its callback. The app can specify the ping interval when it registers the callback. In its recovery callback, the app can call RegisterApplicationRestart a second time, to update the command-line.

  • Recovery-in-progress. While the app is doing work in its recovery callback, the app must periodically call
    ApplicationRecoveryInProgress
    -- if it doesn't call within the registered ping interval, WER will terminate the process.

These APIs will also be included in the Converged AppLifecycle component.

  • Undock RequestRestartAsync, to enable any app to terminate and restart itself, and to provide an arbitrary command-line string for the restarted instance.
  • For consistency with other activation scenarios, provide a new Restart ActivationKind and RestartActivatedEventArgs. This will encapsulate the app-supplied command-line payload. For old apps this is surfaced in the LaunchActivatedEventArgs. For new apps, they can use the new RestartActivatedEventArgs.
  • Alongside the RequestRestartAsync API, expose an additional RegisterApplicationRestart, which mirrors the Win32 app to allow an app to register itself to be restarted if it was running when a system update occurs. Also provide an associated UnregisterApplicationRestart.
  • AppLifecycle should include functionality equivalent to RegisterApplicationRecoveryCallback and ApplicationRecoveryInProgress.

Improved Power Usage

For UWP apps, a major factor in improving device power usage and battery life is that the platform can suspend an app if the user is not actively engaged in it. UWP apps are familiar with the suspend/resume aspects of app lifecycle. That said, the suspend/resume behavior is not without problems. Power usage and battery life is an important factor for users, and there are 2 main aspects in this spec:

  1. System state changes: notifications that the system sends to an app when interesting battery/power changes occur (eg, switching
    between AC and DC, critical power level changes, critical battery level changes, etc). In addition to power state, we'll send
    notifications for other interesting events such as user inactive, screen off, and so on -- since these can also be used by an app to
    better tune its work. All of these are simply informative notifications: the app can do whatever it likes with the information.

  2. Suspend/resume or resource throttling: for UWP apps, the platform has heuristics to determine when to suspend or resume an
    app, including whether the main window is minimized, whether certain critical background triggers are fired, and so on. The pattern here is more than a simple notification, specifically:
    a. There is a notification that suspension is imminent.
    b. There is an opportunity for the app to defer suspension for a finite period so that it can complete some arbitrary work. After
    this period, the app will be suspended.
    c. There is a notification that the app is being resumed from suspension.

Note: it will likely be difficult for many Win32 apps to adopt UWP-style suspend/resume, but more apps might want to take part in resource throttling, and all apps could listen for interesting power/system-state notifications if they wish.

The proposed features for this part of the AppLifecycle component are as follows:

  • A new undocked API that provides equivalent functionality to Windows.System.Power.PowerManager.
  • Additional events in the PowerManager type, based off events used in PowerSettingRegisterNotification.
  • A new API for suspend/resume/throttle, plus a ResourceUsageChange notification. Merging with the RegisterForReducedUsage API -- that is, an API where the app can opt in and say "you can throttle me", or "you can suspend me".
  • Apps & features "good resource citizen" badge.

Power state changes

There are several existing APIs which apps can use to detect changes in battery/power status, in order to participate in improved battery life.

These APIs cover most if not all of the battery/power state change scenarios that apps would care about -- but there's no single API that is consumable by all app types. There's also no single API that's undocked from the OS. The proposal is to incorporate a clone of the W.S.P.PowerManager API in the undocked AppLifecycle component, and augment it with additional notifications based on the Win32
PowerSettingRegisterNotification API.

Suspend/resume and throttling

In UWP-style suspend/resume behavior, the constraints of the UWP app-container allow the system to suspend the app safely. Conversely,
traditional Win32 apps can't always reliably be suspended because they might be using system-wide resources (file handles, named pipes, etc), so suspending the app would lock these resources, and the platform has no mechanism to deal with this scenario. Such apps would not opt in to UWP suspend/resume.

What does suspend/resume actually mean? When an app is suspended it remains in memory, but its threads are not scheduled to run -- this
allows the system to restore it quickly when needed.

When does an app get suspended? In the UWP world, the most obvious manifestation is when the app's main window is minimized this is usually (but not always) following by the app getting suspended. The system can use its own heuristics and policies to decide when is a good time to suspend an app, including (but not limited to) when the app has no foreground, un-obscured or un-minimized windows.

What should an app do in its suspending handler? The key reason an app wants to know when it is about to be suspended is that there's no
guarantee that the system will ever resume it, and might terminate the app at any time while it is suspended -- therefore the suspending event is the app's opportunity to save state such that it can pick up again seamlessly when it is next activated. If a suspended UWP app is holding a file open, the system can terminate the app if necessary to release the lock.

Even some UWP apps have found that the suspend/resume behavior can be difficult to work with. One mitigation that the platform offers is the option to take a SuspendingDeferral
in the Suspending event handler. The app is given a SuspendingEventArgs which includes a method to request a deferral. On top of that, beyond a simple deferral, an app can request an ExtendedExecution -- including potentially an indefinite extension (although this is rarely granted, by policy).

Given the difficulties that UWP apps experience with suspend/resume, and the added complexities of Win32 apps which perform operations outside the control or awareness of an app-container context, it is most likely that very few complex Win32 apps would be able to use UWP-style suspend/resume, and a trivially simply Win32 app is unlikely to be a significant resource-hog. However, there are likely more apps that could take part in some form of throttling -- and especially if the throttling is under app control. Exactly how this throttling might work is TBD.

Recognition for good citizens

Apps will want to opt in to lifecycle and resource management because they want to be good citizens. To further incentivize apps, we should also surface this to the user in some way. One approach would be to add a "battery-aware" or "good resource citizen" badge in the Apps & features list in Settings, similar to the badge used in the traditional TaskManager (although this badge is conferred simply whenever the app is suspended).

We could apply the badge for any app that opts in to throttling and/or suspension. Note: simply registering for power state change
notifications is not enough to qualify: the app could respond to low power/battery states by doing less work -- or it could respond by doing more work.

It has been pointed out that neither TaskManager nor the Apps & Features page in Settings are highly visible, especially to a non-technical user. Two other options present for consideration:

  • Show a notification for excessive usage -- more of a stick than a carrot.
  • Put the good-citizen badge on the app's icon in the taskbar and/or in the app's window chrome (if it has any).

Final implementation TBD.

Open Questions

  • Should we include the broker model in v1 of Reunion so that apps can target down to 1809, or do we defer support for ShareTarget
    -- and avoid the need for a broker -- until a later release when apps might be OK with targeting only down to 2004?
  • Can we provide a way for unpackaged apps to have identity without undocking the whole of Windows a-la-carte?
  • Is there any suspend/resume or resource (CPU, network) throttling behavior that would actually be useful for a traditional Win32 app to take part in, or is this just too difficult to work with?

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    0