-
Notifications
You must be signed in to change notification settings - Fork 328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
App Lifecycle #111
Comments
Would this be a good place to consider how the app behaves when an App Update is installed from the store, to prevent apps restarting in the middle of the user working on an unsaved document? Also could this allow WinUI Desktop apps to opt-in to the UWP Permissions system? |
Should #9 be closed in favor of this? |
@mdtauk permissions are mostly relevant only within the context of an AppContainer, so this proposal alone won't do anything to enable Win32 apps to have the same experience as UWP apps. We're interested in doing more here but it should be a separate issue. |
As a part of these changes, it would be great if UWP app activation no longer relied upon |
@dremin - we're definitely aware of that! Is there a specific use case you're looking for that it'd enable for you? |
@jonwis For me, that would be fantastic to allow for alternate shells (such as one I maintain) to continue to exist in the future. As more functionality moves into UWP applications, alternate shells have unfortunately become far less feasible (and running an alternate shell atop Explorer brings its own set of issues). |
Feedback
Questions
Answers to open questions
Target 1809. 1809 EOL is May 11, 2021. Users will not be able to min-target 2004 until ~May 10, 2022.
It's not clear what "undocking the whole of Windows a-la-carte" refers to. But I suggest re-using the appxmanifest approach as outlined in Activation kinds above. Beware of certain areas in Windows that perform caller identity checking as a security mechanism.
Would be great if desktop applications could opt-into behaviors that realized connection costs/limits/metering (e.g. a la Windows.Networking.Connectivity.ConnectionCost). |
@jonwis Sometimes I have the need to kill File Explorer via Task Manager. Or update an app that restarts the shell to update a shell extension. When I do these things, app modern apps often terminate. |
@riverar FYI clean shutdown of explorer is possible when its not hung, in the shutdown dialog (e.g. click the taskbar and Alt+F4) hold ctrl+alt+shift and press cancel. This is documented for shell extension authors to test their extensions without rebooting. (for anyone else coming across this in the future: to restart it run Never tested this before so I don't know if its consistent across versions but UWP apps seem to stay open and stop rendering when you close explorer this way, when you restart explorer they all seem to close. In general very poor behavior. |
@weltkante TIP: Ctrl+Shift then right-clicking Taskbar also offers a Exit Explorer option. Where is this officially documented? It doesn't work properly on Windows 10 Version 2004. My Mail app terminated. Further, starting a new File Explorer process resulted in a shell load/crash loop that I had to resolve manually. |
Thanks, wasn't aware.
That was years ago when I had to write an explorer extension, seems the docs are updated to use the method you mentioned, but they still list the other method as well, even if not recommending it. [edit] here is the version in the new doc system I mostly just chime in when someone speaks of "killing explorer" because clean shutdown is often preferrable to killing processes. Will mention your method in the future since it seems preferable. |
@stevewri I see the "needs-triage" tag was removed. Can you provide more details as to what that means for proposals as such? I have unanswered questions above. |
Needs-triage is the bot-assigned label for new issues; triage is just the process of routing it to the right area label and owner.
|
@andreww-msft Reading this proposal made me think about Microsoft Edge's use of sparse MSIX to register PWAs/better integrate with Windows. (Currently behind a flag) When you say any "app technology", are you referring to scenarios like this? In other words, is it possible for sparse MSIX packages registered by Edge to take advantage of the WinRT app lifecycle improvements? |
Would it be possible for an app to declare, as a UWP app declares capabilities, it's need to remain open until the user shuts it down? This is important for business apps. I know most of these currently are Win32/Web, but moving forward this capability should be available I think. |
Hi @VagueGit, can you be more specific? Apps can already use |
@ptorr-msft Currently we have to deal with UWP apps being suspended and request an extendedExecution, but this might be denied. It would be nice not to have to deal with it at all. If the app could declare on install that it doesn't want Windows to suspend it and give the user the option to accept that, then the issue goes away. We have both UWP and Win32 versions of our business app and our customers are used to leaving the app open all day. |
Thanks for the extra info. I agree it's a useful feature to add (whether it's a manifest opt-in or something else that is easier to manage than "sessions"). In the meantime, as long as your users are on Desktop and not running on battery, the Extended Execution should run forever. If they are running on battery power, there is a setting they can use to still enable the app to run forever (see Run with Extended Execution) |
Well, that is after all one of the benefits of MSIX packaging and deployment, and an unpackaged app misses out on those benefits. For the platform to be able to fully track unpackaged app uninstall/move/delete would require significant infrastructure that would probably parallel the MSIX infrastructure. |
Different apps have different requirements here. We want to be very careful not to force all apps into one path. So instead we provide tools where each app can choose how it wants to behave - and in fact, can make this choice dynamically. This leaves control firmly in the hands of the app itself. |
Yes - as they already are. If you're an MSIX package then your "installer" is Windows (and more specifically, the MSIX deployment pipeline in windows). If you're not an MSIX package then you have an install model (run foosetup.exe or foo.msi, unzip foo.zip, even XCOPY has an install model -- copy foo.exe(+friends)). You can use Project Reunion to supplement your app. Like many components and frameworks, Project Reunion has 'install' needs to 'register' appropriately with Windows (and uninstall/deregister on the flip side).
Of course that assumes the app that installed ProjectReunion is the only one using it. That's unlikely (especially over time). You wouldn't want to remove ProjectReunion if any app on the system needs it. OTOH if an in-app deployment model is used then any registrations and the app's directory is deleted (or at least Project Reunion's files) then whatever's wired up won't be found if/when Windows tries to access it. Impacts depend on the nature of the registration and can range from nothing (inert, not used) to error dialogs ("I need to run X but can't find it"), broken functionality or other negative user experiences.
Generally MSIX packages can be uninstalled (if it wouldn't break things). Some people have asked Project Reunion to not be uninstallable, as they view it as critical infrastructure where the cons of being uninstallable (causing problems and support headaches) outweighs the value of being uninstallable. Project Reunion 0.5 is uninstallable (if no one's dependent on it). We are of course interested in developers needs and desires so the feedback is being considered (as is all input). Good discussion but we're drifting off the App Lifecycle topic. If there's additional questions or issues we should discuss them under new Issues or Discussions more focused on the matter(s). |
@DrusTheAxe in the case on in-app deployment, I see the lack of automated cleanup to be a nasty side effect. I get that the whole point of having installers (and ideally MSIX) it to provide for cleanup but if we're providing a mechanism for apps to register (for the in-app deployment scenario), can't we provide a mechanism where cleanup can be done automatically if the app or folder is deleted, or moved. I guess this is something that the community could provide...... |
@nickrandolph can't we provide a mechanism where cleanup can be done automatically if the app or folder is deleted, or moved Yes, I think we can. In particular I'm thinking of the Ideally we'd want Doing something equivalent in Project Reunion is possible, at least in part. I'll need to think on it some more and talk with a few folks but, yes. This does look very promising. Thanks for the suggestion! |
Some additional things I would like to see in new App Lifecycle management for multi-instance scenario:
|
Question: which WinUI preview release was the to include support for lifecycle management in UWP apps? Note: a lot of the notes on changes to the lifecycle API are only present in versioned GitHub links to transient versions of MarkDown documents in the repo that are no longer available in HEAD/master. Is there a single "source of truth" document that survives that includes the changes to the APIs? |
I believe that would be docs.microsoft.com. Process lifetime management is a broad set of related apis and has changed/grown over time, so you’ll want to look up a specific api endpoint to see when it was introduced. For example, https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.activation.appointmentsprovideraddappointmentactivatedeventargs?view=winrt-20348 was introduced in v1.0, 10.0.10240.0 |
Is the |
RequestRestartAsync is only for UWP apps. In a desktop app, you can start a new instance of your app and terminate the current one. |
I have observed I have a hunch that this might be because the kind of activation is one of the ~34 other types that @andreww-msft didn't explicitly mention (Launch, File, Protocol, StartupTask, ToastNotification, ShareTarget, CommandLine, Restart). Is there no way for packaged (or unpackaged) WinMain apps to handle being activated in response to such an event, even if it requires manually piecing together an instance of |
Why was this issue closed @aeloros when it hasn't been fully implemented yet. |
This issue was closed because it is fully implemented. If there are bugs in the code, then they should be filed as separate issues. This issue was for the initial implementation which is done. |
@aeloros How did activation get handled? Did that "subset of the full MSIX manifest" idea pan out? |
@riverar, |
@aeloros The proposal here refers to potentially creating a "subset of the full MSIX manifest" to give unpackaged apps identity. Did that happen? Or do we need to use MSIX sparse packages? Just asking because my proposal had a dependency on the creation of this new manifest. |
I don't think anything has been decided yet with regards to identity. It's a fairly complex problem and will take considerable thinking. |
@aeloros Hm ok. I guess I misunderstood your "This issue was closed because it is fully implemented" then. |
Folks Although this issue is closed, I wanted to clarify a few points. This was the very first Reunion/Windows App SDK feature document to be published - at a time when we were still very much feeling our way through a whole bunch of new processes around how we would publish docs and specs. We wanted to get the doc out very early, to gather feedback - and we have indeed received a lot of feedback - so many thanks for that! Reading through all the comments again, it's clear there's been a little confusion: it's worth calling out that the doc was to some extent aspirational - it was a set of proposals rather than a committed detailed design. The more detailed API specs for AppLifecycle features were published much later. This initial doc therefore contains some ideas that we ended up changing as we fine-tuned the designs, and some ideas that got re-shuffled in the schedule, for example, the Restart support is now targeting 1.1. Another example: we originally proposed unpackaged support for 8 activation kinds, but it turns out that Launch and CommandLine are indistinguishable for unpackaged apps, so we combined these, and we deferred ToastNotification and ShareTarget to later releases. Going forward, I'll leave this document as is, for historical reference. The aforementioned API specs and the docs.microsoft.com documentation should be considered more accurate references. Also, for suggestions for improving or extending the AppLifecycle support, we'll file new issues. For example, I've just created a new issue 1709 for improvements to the instancing redirection APIs. Of course, we're also considering issues that other folks file, such as 126 Loosen import redirection restrictions from Rafael. |
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.
❌ 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:
Rationale
This aligns with Project Reunion's roadmap:
Scope
Important Notes
Rich Activation Behaviors
The activation part of the AppLifecycle component is focused on the
following 4 functional requirements:
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:
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:
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:
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:
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.
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:
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.
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.
The proposed features for this part of the AppLifecycle component are as follows:
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:
Final implementation TBD.
Open Questions
-- and avoid the need for a broker -- until a later release when apps might be OK with targeting only down to 2004?
The text was updated successfully, but these errors were encountered: