Closed DrusTheAxe closed 3 years ago
Looks pretty reasonable. A few issues:
enum class Foo : uint32_t
when declaring enumerations (also lets you drop the Foo_Bar
prefix on everything.)...NounAreEquivalent
should be ...PluralnounAreEquivalent
PinPackageDependencyOptions
needs a default ctor so it can be constructed...Options
types be struct
typed since we're being a little looser with versioning here?struct ThingContextId { UInt64 Id; }
and then there's methods like static ThingContext GetFromContextId(ThingContextId)
, etc. ... it's not clear the public surface needs to convert to-from a HANDLE
here anyhow, can that be removed?AreEquivalent
method and keep a bool Equals
member method instead.IClosable
- see internal Patterns document for more on this (coming to the public soon!)Thanks Jon!
Approved. Updated the proposal
Rejected:
{ return id1.CompareNoCase(id2) }
if folks feel that's useful. This is independent of equivalency methodsOpen issues:
runtimeclass FooOptions2 { struct Foo Foo; ...new v2 properties... }
where v1=struct v2=runtimeclass that inconsistency isn't a big deal (if/when that day comes)?Q: Will this work downlevel?
Q: Will this work downlevel?
Yes! The target is down to whatever releases Project Reunion supports.
A design proposal is coming shortly. That'll explain 'how' we can make the magic happen. Please stay tuned
How exactly does this work from an end user perspective? As an app developer, do I have to call these APIs in my main method? Or will there be something in the SxS manifest that declares the package dependency?
Question about this design. I presume the AppX packages that would be referenced using this API would be framework packages. That’s perfectly OK, until we start implementing APIs with broker processes that might need to be run in full trust. Per that linked doc, framework packages cannot declare either applications (making it awfully hard to run the broker as a separate app for boundary purposes) or capabilities (meaning we are limited to AppContainer-only APIs, since we can’t specify runFullTrust
). As I understand it, only framework packages can be listed as dependencies in AppX manifests, and modification/optional packages must specify the exact PFN that they expect to be loaded into, so those won‘t work either. Could someone please clarify this? Thanks!
@stevenbrix Yes, these are APIs apps would call at (un)install and runtime. The basic flow is
For a rough comparison to an MSIX (packaged) application...
A packaged app has a package graph from process creation until process termination. In fact, all process have a package graph, it's just empty/null for non-packaged processes. Either way, a process' package graph is set at process creation (be it a list of packages or empty/null) and is is constant until process termination. Dynamic Dependencies supplements that static package graph with the ability to dynamically alter a process' package graph at runtime.
Unfortunately SxS is no help as Windows lacks the hooks to setup the necessary information at process creation. But let's assume for a moment we could change Windows
@wjk Yes, your understanding is correct.
The Dynamic Dependency API targets Framework packages. That's both a pressing need and a natural fit for how framework packages work -- used by other packages, don't provide their own process/runtime identity etc.
Can you elaborate on what you'd like to accomplish (that frameworks don't suffice)?
@DrusTheAxe See #62 for an example. If that proposal were to be implemented, we would not be able to use a framework package to deploy it, as it needs to supply a full-trust helper application that uses App Services to communicate with the main app, and framework packages cannot define those. Thanks!
@wjk For some of our planned features we need brokering so AppContainer/Universal apps can get access (with appropriate control) to other user content. We’ve discussed a model like this:
In this picture the framework and main package work collectively to provide the end-to-end integrated solution. Better together :-)
Yay for packaged applications. The DynamicDependencies proposal goes one step further, providing access for unpackaged Win32 (MSI, setup.exe, xcopy-deploy, ...) apps as well as packaged apps.
We're still mulling over whether AppService is the right technology for this, and how to ensure your Win32 application can co-deploy that Framework Package and its Main Package helpers. Stay tuned real soon for more on that front.
I've posted a PR with a spec for this proposal => Spec for MSIX Dynamic Dependencies #108
Feedback is welcome 😃
@DrusTheAxe would it be possible to use the same manifest file for unpackaged apps? This seems like the appropriate time and place to converge the appxmanifest and sxs manifest into something that works in all scenarios. Then we don't need to put an extra burden on the app author, or tooling to support multiple different techs, which I imagine will just continually diverge over time. The build can just produce a ProjectReunionManifest.xml, which is an appxmanifest.xml with some metadata that says "this app isn't running in a container". Then when the app launches, the system reads the manifest file and does the whole pinning of dependencies that you are describing.
@stevenbrix > would it be possible to use the same manifest file for unpackaged apps? This seems like the appropriate time and place to converge the appxmanifest and sxs manifest into something that works in all scenarios
Are you asking/suggesting if it's possible to make the Dynamic Dependency behavior declarative and automagically happen at process creation, with the right declarative work by a developer?
Or could we merge SxS and MSIX manifests to have a single artifact?
Or could we leverage SxS to avoid the need for a helper 'sidecar' Main package?
Or something else?
Are you aware of the recent SxS support to add package identity to a non-packaged app via <msix>
? Is that related to your query? Though that's more a complementary use of SxS and MSIX than a merging of manifests.
Hoping to get a clearer idea what's on your mind before trying to answer.
Are you aware of the recent SxS support to add package identity to a non-packaged app via
? Is that related to your query? Though that's more a complementary use of SxS and MSIX than a merging of manifests.
@DrusTheAxe I was not aware that already existed, that's amazing!! That's a huge part of the puzzle, so it's cool to see that already working. Could this be extended to make https://github.com/microsoft/ProjectReunion/issues/55 a reality?
Or could we merge SxS and MSIX manifests to have a single artifact?
Or could we leverage SxS to avoid the need for a helper 'sidecar' Main package?
Yes and yes 😄
Ultimately, it would be nice to have a single manifest, so that we can have a single build pipeline, single winrt activation story, single manifest designer in visual studio, and single inner-loop workflow for developers, and single next best thing.
IMO, a perfect design wouldn't require me (as an app developer) to do anything extra, like having to pin dependencies or give myself identity in an unpackaged app. All of this should be in the manifest (which it already is for packaged apps), and the system should just take care of it for me when my app launches.
Here's some rough c++ psuedo-code to describe what I'm talking about:
bool hasManifest = ExistsReunionManifest();
if (hasManifest && !IsRegistered())
{
// App isn't registered. Loose register, this can do 3 different things:
// 1. Install MSIX dependencies
// 2. Grant identity
// 3. Run in container
RegisterDynamicReunionManifest();
}
And yes, any one of these things can fail during startup, so there needs to be an elegant way the app can handle this. I don't show this, but I know we can figure that out. Even if it were designed in such a way that higher level frameworks like WPF or WinUI handle this special behavior, that's fine by me.
This makes our story very simple:
@stevenbrix
a perfect design wouldn't require me (as an app developer) to do anything extra, like having to pin dependencies or give myself identity in an unpackaged app
Of course. ProjectReunion is striving to unify and expand the Windows platform but, much like Rome, Windows is big and broad and wasn't built in a day. It'll take us time to bring some features to where we all desire them to be. And some features are more difficult than others to make available as smooth and seamlessly as we'd like, without at least some changes to Windows itself. That's why feedback like yours is so important, to help us understand where we should focus our efforts. To identify where we can provide great solutions today, and where we need to invest in foundational improvements to improve them going forward.
Here's some rough c++ psuedo-code to describe what I'm talking about:
What if I said you could do that today?... 🤨
When we built the plumbing described in Identity, Registration and Activation of Non-packaged Win32 Apps we also identified how it plays well with the 'XCOPY-install' model -- no 'install', just dump some files on a machine, run an app and have it discover on the fly if work's needed and handle it. I don't see this mentioned in the blog post (or elsewhere); I'll follow up on that. But it's actually quite easy to do, given the new features outlined in that blog:
if (not running with identity) then { Install(mysparsepackage.msix); relaunch self }
Step 2 is the key here
Historically, activating a packaged app required Windows (ultimately) go through ActivateApplication() to start the process appropriately (package identity, RuntimeBehavior, TrustLevel and other manifest activation information. Let's call this Activation-via-ActivationManager.
You could make a DesktopBridge application in the classic Win32 sense (Winmain etc) instead of a Universal app, but you couldn't just run the executable. CreateProcess("foo.exe") would fail.(a)
(a) Yes we've added <Extension Category="windows.appExecutionAlias"....>
that can make some of those scenarios work. A handy improvement, but not a complete solution. Not quite the same as just double-click an exe in Explorer and run.
The <msix>
element is the game changer.
During process creation, if the executable has a SxS manifest containing <msix>
we check if that package is registered for the user and if so, we create the process just like ActivateApplication()
would! Let's call this Activation-via-CreateProcess 😄 Regardless whether you CreateProcess("kittens.exe")
or ActivateApplication("Kittens_1.2.3.4_x86__1234567890abc!App")
you get the same result. The same process with the same package identity and other properties.
Now here's the the critical piece.
If process creation sees <msix>
in the SxS manifest but that package is not registered, we create the process anyway, as if there was no <msix>
in the SxS manifest. We don't fail.
This is where step 3 comes in.
The kittens.exe process will be created whether or not the Kittens package is registered for the user. You can detect this in your app and adapt e.g.
int main()
{
// Are we running with package identity?
UINT32 n = 0;
if (GetCurrentPackageFullName(&n, nullptr) == ERROR_NO_APPMODEL_PACKAGE)
{
// Nope. Let's correct that...
...install kittens package e.g. packageManager.AddPackageByUriAsync()...
...launch self...
...quit...
}
// We're running with package identity! Let's roll
...
}
Per your checklist...
Voila! All the runtime benefits, with no explicit, user-driven or visible 'install' step.
You can further extend this with Dynamic Dependencies per your pseudo-code's RegisterDynamicReunionManifest()
. they're complementary techniques.
One caveat of this 'XCOPY-install friendly' technique is it requires Windows >= 10.0.19041.0 (aka May 2020 Update aka 20H1).
How does that sound?
When we built the plumbing described in Identity, Registration and Activation of Non-packaged Win32 Apps we also identified how it plays well with the 'XCOPY-install' model -- no 'install', just dump some files on a machine, run an app and have it discover on the fly if work's needed and handle it. I don't see this mentioned in the blog post (or elsewhere); I'll follow up on that. But it's actually quite easy to do, given the new features outlined in that blog:
Thanks for sharing a link to this blog. The blog mentions that there is no VS support for this, are there any plans to add VS tooling for all of this to make it smoother?
The meta point I'm trying to make is a) I don't see anything in these specs about how these can be tooled so that they are easy for developers to accomplish and b) I don't want there to be separate tooling for unpackaged and packaged apps. I'm glad this all seems to be doable, but I don't understand how I can accomplish it, and it looks like way too much work to do.
@stevenbrix
are there any plans to add VS tooling for all of this to make it smoother?
Are you referring to the blog post, this Dynamic Dependencies feature or something else? Just want to make sure I understand the question
a) I don't see anything in these specs about how these can be tooled so that they are easy for developers to accomplish
Agreed, it's not just a matter of it can work, it should also be easy to accomplish. Tooling's been an active area of discussion and investigation. What sort of tooling would you desire? Is something akin to VS' current new-UWP-project what you have in mind? GUI property pages etc to add/edit? Is VS your environment of interest? VSCode? Other?
b) I don't want there to be separate tooling for unpackaged and packaged apps
I expect not. After all, it's all just Windows development. We're actively trying to erase the lines between those development experiences :-)
I'm glad this all seems to be doable, but I don't understand how I can accomplish it, and it looks like way too much work to do.
Which part(s) seem too-much-work?
are there any plans to add VS tooling for all of this to make it smoother?
Are you referring to the blog post, this Dynamic Dependencies feature or something else? Just want to make sure I understand the question
@DrusTheAxe I was referring to the blog post.
? a) I don't see anything in these specs about how these can be tooled so that they are easy for developers to accomplish
Agreed, it's not just a matter of it can work, it should also be easy to accomplish. Tooling's been an active area of discussion and investigation. What sort of tooling would you desire? Is something akin to VS' current new-UWP-project what you have in mind? GUI property pages etc to add/edit? Is VS your environment of interest? VSCode? Other?
Developers shouldn't have to write code in their main method. Maybe this is fine for frameworks where the app author owns the main method. But in WPF and WinUI, the main method is generated by tooling.
I'm glad this all seems to be doable, but I don't understand how I can accomplish it, and it looks like way too much work to do.
Which part(s) seem too-much-work?
The whole end-to-end, I don't understand what steps I need to go through in order for this to work. It would be helpful to see a doc that outlines how a customer will be successful.
Also, how does this play into the concept of .NET Core SelfContained
apps? The promise there is that the app carries all of it's dependencies with it and the app author has full control. This is currently the recommended approach from the .NET team. If an app author sets SelfContained
to true in their project file, will that ensure the framework package is never updated? How do we educate developers in this scenario that some of their dependencies are bundled with the app, but then some are not?
We're discussing tooling and integration options to make this as friction-free as possible. The end-of-end story spans multiple parts - Dynamic Dependencies, the #156 Deployment Information API and some tooling and additional APIs to further shrinkwrap the picture.
The generated-main issue is a good one. I'll follow up on that.
Some folks are looking specifically at the .NETCore space. I've heard SelfContained
come up but I don't know the latest thinking (yet :P). StayTuned.
@marb2000 can help with SelfContained
, he has done a lot of work in this space already.
The generated-main issue is a good one. I'll follow up on that.
XAML ask => Proposal: Extensibility hooks in generated main #3408
WPF ask => Proposal: Extensibility hooks in generated main #3632
@DrusTheAxe #158 seems to have been lost in the transition from master
to main
. It shows as merged on GitHub, but is not present in the main branch (and the master branch it was merged into has been deleted). Since the source branch has since been deleted also, it is now impossible to use the MSIX DD code, as it now exists only in the form of diffs on the PR page; it cannot be checked out now. Could someone please resuscitate this code and get it into main
? Thanks!
@wjk Yes something went weird with the branching. I've killed the PR. Rebase'ing against main shows some merge conflicts :-(
Let me get the branch back up and verified it's current.
OK I made a new branch user/drustheaxe/dyndep that's rebased against the latest in main. All code preserved and builds and tests pass so yay.
Feedback from recent design reviews centered on the 'helper' main package and asks to do something simpler / more streamlined for developers. So we made a change to plan - Project Reunion will provide the 'helper' main package for folks using Dynamic Dependencies. Thus is born the 'Dynamic Dependencies Lifetime Manager'.
The branch has the core implementation. I'm working on an update to the spec about this, plus some remaining work e.g. integrate the DDLM into the CI/Build pipeline. Coming soon
Mission accomplished, just forgot to resolve the issue
Proposal: MSIX Dynamic Dependencies (aka DynamicDependencies aka DynDep)
Summary
Provide APIs to enable access to packaged content at runtime, regardless if the caller is packaged or not. This supplements the MSIX appmodel's current static dependency support (via in appxmanifest.xml) with a dynamic runtime equivalent. It also allows non-packaged processes (which have no appxmanifest.xml) to use packaged content.
Microsoft-internal task 23447728
Rationale
This aligns with Project Reunion's roadmap:
Scope
Important Notes
All processes have a package graph. A process may be created with entries in its package graph; this is referred to as the 'static package graph'.
Packaged processes (i.e. a process with package identity) are created with a static package graph per their AppxManifest.xml. A process' static package graph cannot be altered, but it can be supplemented at runtime via the Dynamic Dependency API.
Processes without package identity have an no static package graph. They can modify their package graph using the Dynamic Dependency API.
MddPinPackageDependency
defines a package dependency.MddAddPackageDependency
determines a package that satisfies a package dependency and updates the caller's process. This includes adding the resolved package to the process' package graph, updating the Loader to include the resolved package in the DLL Search Order, etc. The package dependency is resolved to a specific package if not already resolved.A resolved PackageDependency is represented by
MDD_PACKAGE_DEPENDENCY_CONTEXT
.Once a PackageDependency is resolved to a package all further
MddAddPackageDependency
calls yield the same result until the package dependency is unresolved. Resolved package dependencies are tracked by User + PackageDependencyId. This ensures multiple overlapping calls toMddAddPackageDependency
yield the same result. A package dependency is unresolved when the lastMDD_PACKAGE_DEPENDENCY_CONTEXT
is closed (viaMddRemovePackageDependency
or process termination).MddRemovePackageDependency
removes the resolved PackageDependency from the calling process' package graph.MddUnpinPackageDependency
undefines a package dependency previously defined viaPinPackageDependency
.PackageDependency definitions and usage are tracked and managed on a per-user basis.
PackageDependency definitions are not persisted or tracked across reboots if
MddPinPackageDependency
is called withMddPinPackageDependency_LifecycleHint_Process
. SpecifyMddPinPackageDependency_LifecycleHint_FileOrPath
orMddPinPackageDependency_LifecycleHint_RegistrySubkey
forPinPackageDependency
to persist the definition until explicitly removed viaMddUnpinPackageDependency
or the specified lifetime artifact is deleted.If concurrent processes need the same package resolution for a defined criteria they should share the packageDependencyId returned by
MddPinPackageDependency
. Concurrent processes running as the same user callingMddAddPackageDependency
with the same packageDependencyId get the same resolved package added to their package graph. This enables multiple concurrent processes needing the same package resolution get a consistent answer.Package dependencies can only be resolved to packages registered for a user. As packages cannot be registered for LocalSystem the Dynamic Dependencies feature is not available to callers running as LocalSystem.
Win32 API
MsixDynamicDependency.hpp
NOTE: All APIs prefixed with Mdd/MDD for MSIX Dynamic Dependencies.
WinRI API
Open Questions
Q: Package dependencies are only resolved for Framework packages. Should other package types (Main, Resource, Optional) be supported?
Q: A package dependency's critieria includes user, package family, minimum version, and processor architecture. Are there other qualifiers we should be consider?
Q:WinRT: How should 'lifetimeArtifact' and MddPinPackageDependency_LifecycleHint* be expressed in the WinRT API? Some ideas:
String file; String regkey;
if both null then it's ProcessString lifetimeArtifact; boolean isFile; boolean isReg; boolean isProcess;
and only 1 can be trueILifetimeArtifact
property with multiple implementations e.g. FileLifetimeArtifact ={ string file; }
vs RegistryLifetimeArtifact ={ HKEY root; string subkey; }
vs Process=null