Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 179 additions & 7 deletions specs/dynamicdependencies/DynamicDependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ to use packaged content.
- [6.1.1. MsixDynamicDependency.h](#611-msixdynamicdependencyh)
- [6.1.2. MddBootstrap.h](#612-mddbootstraph)
- [6.1.3. MddLifetimeManagement.h](#613-mddlifetimemanagementh)
- [6.1.4. Microsoft.Windows.ApplicationModel.DynamicDependency (C#)](#614-microsoftwindowsapplicationmodeldynamicdependency-c)
- [6.2. WinRT API](#62-winrt-api)
- [7. Static Package Dependency Resolution Algorithm](#7-static-package-dependency-resolution-algorithm)
- [7.1. Frequently Asked Questions (FAQ)](#71-frequently-asked-questions-faq)
Expand Down Expand Up @@ -794,6 +795,7 @@ Samples illustrating the DynamicDependency APIs
- [Sample 6](sample-6.md) - LolzKitten Installer / Uninstaller defining a 32bit PackageDependency [\[Win32\]](sample-6.md#win32) [\[WinRT\]](sample-6.md#winrt)
- [Sample 7](sample-7.md) - LolzKitten app ordering Packages in PackageGraph [\[Win32\]](sample-7.md#win32) [\[WinRT\]](sample-7.md#winrt)
- [Sample 8](sample-8.md) - LolzKitten app ordering Packages in PackageGraph with prepend [\[Win32\]](sample-8.md#win32) [\[WinRT\]](sample-8.md#winrt)
- [Sample B.1](sample-b.1.md) - HelloWorld console app using the Boostrap API [\[Win32\]](sample-b.1.md#win32) [\[C#\]](sample-b.1.md#cs) [\[C# (no throw)\]](sample-b.1.md#cs_nothrow)

# 5. Remarks

Expand Down Expand Up @@ -1163,7 +1165,7 @@ STDAPI MddTryCreatePackageDependency(
MddPackageDependencyLifetimeKind lifetimeKind,
PCWSTR lifetimeArtifact,
MddCreatePackageDependencyOptions options,
_Outptr_result_maybenull_ PWSTR* packageDependencyId);
_Outptr_result_maybenull_ PWSTR* packageDependencyId) noexcept;

/// Undefine a package dependency. Removing a pin on a PackageDependency is typically done at uninstall-time.
/// This implicitly occurs if the package dependency's 'lifetime artifact' (specified via MddTryCreatePackageDependency)
Expand All @@ -1172,7 +1174,7 @@ STDAPI MddTryCreatePackageDependency(
/// @warn MddDeletePackageDependency() requires the caller have administrative privileges
/// if the package dependency was pinned with MddCreatePackageDependencyOptions::ScopeIsSystem.
STDAPI_(void) MddDeletePackageDependency(
_In_ PCWSTR packageDependencyId);
_In_ PCWSTR packageDependencyId) noexcept;

/// Resolve a previously-pinned PackageDependency to a specific package and
/// add it to the invoking process' package graph. Once the dependency has
Expand Down Expand Up @@ -1218,7 +1220,7 @@ STDAPI MddAddPackageDependency(
INT32 rank,
MddAddPackageDependencyOptions options,
_Out_ MDD_PACKAGEDEPENDENCY_CONTEXT* packageDependencyContext,
_Outptr_opt_result_maybenull_ PWSTR* packageFullName);
_Outptr_opt_result_maybenull_ PWSTR* packageFullName) noexcept;

/// Remove a resolved PackageDependency from the current process' package graph
/// (i.e. undo MddAddPackageDependency). Used at runtime (i.e. the moral equivalent
Expand All @@ -1229,7 +1231,7 @@ STDAPI MddAddPackageDependency(
/// to be used; future file resolution will fail to see the removed
/// package dependency.
STDAPI_(void) MddRemovePackageDependency(
_In_ MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext);
_In_ MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext) noexcept;

/// Return the package full name that would be used if the
/// PackageDependency were to be resolved. Does not add the
Expand All @@ -1240,7 +1242,7 @@ STDAPI_(void) MddRemovePackageDependency(
/// succeeds but packageFullName is nullptr.
STDAPI MddGetResolvedPackageFullNameForPackageDependency(
_In_ PCWSTR packageDependencyId,
_Outptr_result_maybenull_ PWSTR* packageFullName);
_Outptr_result_maybenull_ PWSTR* packageFullName) noexcept;

/// Return the package dependency for the context.
///
Expand All @@ -1249,15 +1251,18 @@ STDAPI MddGetResolvedPackageFullNameForPackageDependency(
/// the function succeeds but packageDependencyId is nullptr.
STDAPI MddGetIdForPackageDependencyContext(
_In_ MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext,
_Outptr_result_maybenull_ PWSTR* packageDependencyId);
_Outptr_result_maybenull_ PWSTR* packageDependencyId) noexcept;

/// Return the package graph's current generation id.
STDAPI_(UINT32) MddGetGenerationId() noexcept;
```

### 6.1.2. MddBootstrap.h

This header contains the Bootstrap API

```c++
/// Iniitalize the calling process to use Windows App SDK's framework package.
/// Initialize the calling process to use Windows App SDK's framework package.
///
/// Find a Windows App SDK framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
Expand Down Expand Up @@ -1289,6 +1294,157 @@ This header contains the Lifetime Management API
STDAPI MddLifetimeManagementGC() noexcept;
```

### 6.1.4. Microsoft.Windows.ApplicationModel.DynamicDependency (C#)

This C# assembly contains the Bootstrap API for C#

```c#
namespace Microsoft.Windows.ApplicationModel.DynamicDependency
{
// The version of an MSIX package. This is logically `Major.Minor.Build.Revision` and can be expressed as...
// * individual `ushort` values (uint16)
// * an unsigned `ulong` value (uint64)
// * a dot-string notation ("major.minor.build.revision")
public struct PackageVersion
{
public ushort Major;
public ushort Minor;
public ushort Build;
public ushort Revision;

// Create an instance with the value `major.0.0.0`.
public PackageVersion(ushort major);

// Create an instance with the value `major.minor.0.0`.
public PackageVersion(ushort major, ushort minor);

// Create an instance with the value `major.minor.build.0`.
public PackageVersion(ushort major, ushort minor, ushort build);

// Create an instance with the value `major.minor.build.revision`.
public PackageVersion(ushort major, ushort minor, ushort build, ushort revision);

// Create an instance from a version as a uint64.
public PackageVersion(ulong version);

// Return the version as a uint64.
public ulong ToVersion();

// Return the string as a formatted value "major.minor.build.revision".
public override string ToString();
};

// The Windows App SDK bootstrap initialization API.
public class Bootstrap
{
/// Initialize the calling process to use Windows App SDK's framework package.
///
/// Find a Windows App SDK framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
/// candidate is selected.
///
/// This is equivalent to `Initialize(majorMinorVersion, null, new PackageVersion())`.
///
/// @param majorMinorVersion major and minor version of Windows App SDK's framework package, encoded as `0xMMMMNNNN` where M=Major, N=Minor (e.g. 1.2 == 0x00010002).
/// @see Initialize(uint, string)
/// @see Initialize(uint, string, PackageVersion)
/// @see Shutdown()
public static void Initialize(uint majorMinorVersion);

/// Initialize the calling process to use Windows App SDK's framework package.
///
/// Find a Windows App SDK framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
/// candidate is selected.
///
/// This is equivalent to `Initialize(majorMinorVersion, versionTag, new PackageVersion())`.
///
/// @param majorMinorVersion major and minor version of Windows App SDK's framework package, encoded as `0xMMMMNNNN` where M=Major, N=Minor (e.g. 1.2 == 0x00010002).
/// @param versionTag version tag (if any), e.g. "preview1".
/// @see Initialize(uint)
/// @see Initialize(uint, string, PackageVersion)
/// @see Shutdown()
public static void Initialize(uint majorMinorVersion, string versionTag);

/// Initialize the calling process to use Windows App SDK's framework package.
///
/// Find a Windows App SDK framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
/// candidate is selected.
///
/// @param majorMinorVersion major and minor version of Windows App SDK's framework package, encoded as `0xMMMMNNNN` where M=Major, N=Minor (e.g. 1.2 == 0x00010002).
/// @param versionTag version tag (if any), e.g. "preview1".
/// @param minVersion the minimum version to use
/// @see Initialize(uint)
/// @see Initialize(uint, string)
/// @see Shutdown()
public static void Initialize(uint majorMinorVersion, string versionTag, PackageVersion minVersion);

/// Initialize the calling process to use Windows App SDK's framework package.
/// Failure returns false with the failure HRESULT in the hresult parameter.
///
/// Find a Windows App SDK framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
/// candidate is selected.
///
/// This is equivalent to `TryInitialize(majorMinorVersion, null, new PackageVersion(), hresult)`.
///
/// @param majorMinorVersion major and minor version of Windows App SDK's framework package, encoded as `0xMMMMNNNN` where M=Major, N=Minor (e.g. 1.2 == 0x00010002).
/// @retval true if successful, otherwise false is returned.
/// @see TryInitialize(uint, string, out int)
/// @see TryInitialize(uint, string, PackageVersion, out int)
/// @see Shutdown()
public static bool TryInitialize(uint majorMinorVersion, out int hresult);

/// Initialize the calling process to use Windows App SDK's framework package.
/// Failure returns false with the failure HRESULT in the hresult parameter.
///
/// Find a Windows App SDK framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
/// candidate is selected.
///
/// This is equivalent to `TryInitialize(majorMinorVersion, versionTag, new PackageVersion(), hresult)`.
///
/// @param majorMinorVersion major and minor version of Windows App SDK's framework package, encoded as `0xMMMMNNNN` where M=Major, N=Minor (e.g. 1.2 == 0x00010002).
/// @param versionTag version tag (if any), e.g. "preview1".
/// @retval true if successful, otherwise false is returned.
/// @see TryInitialize(uint, out int)
/// @see TryInitialize(uint, string, PackageVersion, out int)
/// @see Shutdown()
public static bool TryInitialize(uint majorMinorVersion, string versionTag, out int hresult);

/// Initialize the calling process to use Windows App SDK's framework package.
/// Failure returns false with the failure HRESULT in the hresult parameter.
///
/// Find a Windows App SDK framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
/// candidate is selected.
///
/// @param majorMinorVersion major and minor version of Windows App SDK's framework package, encoded as `0xMMMMNNNN` where M=Major, N=Minor (e.g. 1.2 == 0x00010002).
/// @param versionTag version tag (if any), e.g. "preview1".
/// @param minVersion the minimum version to use.
/// @param hresult the error code if an error occurred.
/// @retval true if successful, otherwise false is returned.
/// @see TryInitialize(uint, out int)
/// @see TryInitialize(uint, string, out int)
/// @see Shutdown()
public static bool TryInitialize(uint majorMinorVersion, string versionTag, PackageVersion minVersion, out int hresult);

/// Undo the changes made by Initialize().
///
/// @warning Packages made available via `Initialize()` and
/// the Dynamic Dependencies API should not be used after this call.
/// @see Initialize(uint)
/// @see Initialize(uint, string)
/// @see Initialize(uint, string, PackageVersion)
/// @see TryInitialize(uint, out int)
/// @see TryInitialize(uint, string, out int)
/// @see TryInitialize(uint, string, PackageVersion, out int)
public static void Shutdown();
}
}
```

## 6.2. WinRT API

```c# (but really MIDL3)
Expand Down Expand Up @@ -1511,6 +1667,10 @@ runtimeclass PackageDependency
///
/// Calls to Add() can be balanced by a PackageDependencyContext.Remove()
/// to remove the entry from the package graph.
///
/// Successful calls change the package graph's current generation id.
///
/// @see GenerationId
PackageDependencyContext Add();

/// Resolve a previously pinned PackageDependency to a specific package and
Expand Down Expand Up @@ -1546,7 +1706,14 @@ runtimeclass PackageDependency
///
/// Calls to Add() can be balanced by a PackageDependencyContext.Remove() (or object destruction)
/// to remove the entry from the package graph.
///
/// Successful calls change the package graph's current generation id.
///
/// @see GenerationId
PackageDependencyContext Add(AddPackageDependencyOptions options);

/// Return the package graph's current generation id.
static UInt32 GenerationId{ get; };
}

/// A unique identifier for a resolved package dependency
Expand Down Expand Up @@ -1576,6 +1743,11 @@ runtimeclass PackageDependencyContext
/// Returns the package full name of the resolved package for this context
String PackageFullName { get; }

/// Remove from the package graph a package dependency previously added via PackageDependency.Add().
///
/// Successful calls change the package graph's current generation id.
///
/// @see PackageDependency.GenerationId
void Remove();
}
}
Expand Down
107 changes: 107 additions & 0 deletions specs/dynamicdependencies/sample-b.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Sample B.1 - HelloWorld console app using the Boostrap API

At runtime, HelloWorld wants to use the Windows App SDK Framework 1.0-preview1 package, so it calls the Bootstrap API.

## Win32

```c++
#include <iostream>

#include <MddBootstrap.h>

int main()
{
// Initialize access to Windows App SDK
const uint32_t c_majorMinorVersion{ 0x00010000 };
PCWSTR c_versionTag{ L"preview1" };
const PACKAGE_VERSION c_minVersion{};
wprintf(L"MddBootstrapInitialize(0x%08X, \"%s\", %hu.%hu.%hu.%hu)...\n",
c_majorMinorVersion, c_versionTag, c_minVersion.Major, c_minVersion.Minor, c_minVersion.Build, c_minVersion.Revision);
HRESULT hr{ MddBootstrapInitialize(c_majorMinorVersion, c_versionTag, c_minVersion) };
if (FAILED(hr))
{
wprintf(L"Error 0x%X in MddBootstrapInitialize(0x%08X, \"%s\", %hu.%hu.%hu.%hu)\n",
hr, c_majorMinorVersion, c_versionTag,
c_minVersion.Major, c_minVersion.Minor, c_minVersion.Build, c_minVersion.Revision);
return hr;
}

// Do interesting stuff...
std::cout << "Hello World!\n";

// Cleanup
MddBootstrapShutdown();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider showing adding a wil::unique_call here that handles calling this shutdown method, even on exceptions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed on the other PR, failure to call shutdown is a minor nit. Is automatic cleanup important? And if so, is that just a C++ thing or should we also provide it for C#?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good to show proper lifecycle behavior in a sample, even if someone chooses to not do it.

Maybe a wrapper type for C# that implements IDisposable as a sample, so you can say "var winappsdkref = Bootstrap.Initialize()" or something

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was thinking of IDisposable. Will cook something up

Please check the related implementation PR #1274. Currently blocked by you. Feedback's been addressed. Would be helpful to get that and add this wrapper in a subsequent (smaller) PR than have it all await perfection.

return 0;
}
```

## C# (Throw Exception on Error)

```c#
using System;
using Microsoft.Windows.ApplicationModel.DynamicDependency;

namespace HelloWorldCS
{
class Program
{
static void Main(string[] args)
{
// Initialize access to Windows App SDK
uint majorMinorVersion = 0x00010000;
string versionTag = "preview1";
var minVersion = new PackageVersion();
try
{
Console.WriteLine($"Bootstrap.Initialize({majorMinorVersion:X08}, \"{versionTag}\", {minVersion.Major}.{minVersion.Minor}.{minVersion.Build}.{minVersion.Revision})...");
Bootstrap.Initialize(majorMinorVersion, versionTag, minVersion);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Environment.Exit(e.HResult);
}

// Do interesting stuff...
Console.WriteLine("Hello World!");

// Cleanup
Bootstrap.Shutdown();
}
}
}
```

## C# (Return HRESULT on Error)

```c#
using System;
using Microsoft.Windows.ApplicationModel.DynamicDependency;

namespace HelloWorldCS_NoThrow
{
class Program
{
static void Main(string[] args)
{
// Initialize access to Windows App SDK
uint majorMinorVersion = 0x00010000;
string versionTag = "preview1";
var minVersion = new PackageVersion();
Console.WriteLine($"Bootstrap_NoThrow.Initialize({majorMinorVersion:X08}, \"{versionTag}\", {minVersion.Major}.{minVersion.Minor}.{minVersion.Build}.{minVersion.Revision})...");
int hr = 0;
if (!Bootstrap.TryInitialize(majorMinorVersion, versionTag, minVersion, out hr))
{
Console.WriteLine($"Error 0x{hr:X08} in Bootstrap_NoThrow.Initialize(0x{majorMinorVersion:X08}, \"{versionTag}\", {minVersion.Major}.{minVersion.Minor}.{minVersion.Build}.{minVersion.Revision})");
Environment.Exit(hr);
}

// Do interesting stuff...
Console.WriteLine("Hello World!");

// Cleanup
Bootstrap.Shutdown();
}
}
}
```