Skip to content

MSIX FindPackageFile*() API#6200

Open
DrusTheAxe wants to merge 45 commits intomainfrom
user/drustheaxe/FindPackageFile
Open

MSIX FindPackageFile*() API#6200
DrusTheAxe wants to merge 45 commits intomainfrom
user/drustheaxe/FindPackageFile

Conversation

@DrusTheAxe
Copy link
Copy Markdown
Member

Implementation of MSIX FindPackageFile*() API Spec#5885

https://task.ms/59424502

NOTE: SearchStatic/DynamicDependencies options are dropped from spec because Windows doesn't expose this information. Get[Current]PackageInfo*() accepts an input to optionally filter package graph entries but output doesn't indicate if a package was statically or dynamically added to the package graph.

@DrusTheAxe DrusTheAxe self-assigned this Feb 9, 2026
@DrusTheAxe DrusTheAxe added the area-PackageManagement MSIX deployment technology (eg PackageDeploymentManager) label Feb 9, 2026
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
<Logo>logo.png</Logo>
<uap15:DependencyTarget>true</uap15:DependencyTarget>
<uap10:AllowExternalContent>true</uap10:AllowExternalContent>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AllowExternalContent

What's the difference between the UserExternal package and the MachineExternal package?

else
{
return GetPackageFilePathOptions_SearchInstallPath |
nonLocationOptions;
Copy link
Copy Markdown
Contributor

@letao-msft letao-msft Apr 1, 2026

Choose a reason for hiding this comment

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

;

Should be |
(or delete the next line, since it's redundant with nonLocationOptions)

inline void initialize()
{
wil::unique_hmodule dll;
if (::ExportLoader::Load(L"api-ms-win-appmodel-runtime-l1-1-3.dll", wil::out_param(dll)))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Load

This function returns HRESULT but it's checked as if a bool

{

__pragma(detect_mismatch("ODR_violation_WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_APPLICATIONMODEL_FEATURE_PACKAGERUNTIME_ENABLED_mismatch", "AlwaysEnabled"))
struct Feature_PackageRuntime
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feature_PackageRuntime

The feature is defined but isn't used in code anywhere?

PCWSTR filename,
PackagePathType packageType)
{
const auto path{ ::AppModel::Package::GetPath<std::wstring>(packageFullName, packageType) };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

GetPath

This might return empty string - should that be handled as a special case?
std::filesystem::absolute("" / filename) might return a bogus result.

namespace TD = ::Test::Diagnostics;
namespace TB = ::Test::Bootstrap;
namespace TP = ::Test::Packages;
namespace TD = ::Test::Diagnostics;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is already defined on line 10

namespace TD = ::Test::Diagnostics;
namespace TB = ::Test::Bootstrap;
namespace TP = ::Test::Packages;
namespace TD = ::Test::Diagnostics;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is already defined on line 8

<ActivatableClass ActivatableClassId="Microsoft.Windows.ApplicationModel.DynamicDependency.PackageDependencyRank" ThreadingModel="both" />

<!-- Package -->
<ActivatableClass ActivatableClassId="Microsoft.Windows.ApplicationModel.Package" ThreadingModel="both" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Package

Should PackageGraph also be added here?

std::call_once(g_onceFlag, initialize);
RETURN_HR_IF_NULL(E_NOTIMPL, g_getPackagePathByFullName2);

RETURN_IF_FAILED(g_getPackagePathByFullName2(packageFullName, packagePathType, pathLength, path));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

g_getPackagePathByFullName2

This returns win32 error (LONG), not HRESULT

// Only Install location is supported by the current system
// Effective is thus equivalent to Install
// Either way, rock it old school...
RETURN_IF_FAILED(::GetPackagePathByFullName(packageFullName, pathLength, path));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

GetPackagePathByFullName

This returns win32 error (LONG), not HRESULT

{
return options | maskPackageTypes;
}
return options;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The IDL says

        /// @note If SearchInstallPath, SearchMutablePath, SearchMachineExternalPath and SearchUserExternalPath
        ///        are omitted then all locations are searched (i.e. specify all or none yields the same result).

But this path doesn't add all those options if none of them is specified (i.e. when only SearchPackages is specified, but none of SearchPath is specified).

PCWSTR packageFullName,
_In_ PCWSTR filename,
_In_ GetPackageFilePathOptions options,
_Outptr_result_maybenull_ PWSTR* packageFile) noexcept try
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

packageFile

For public APIs, validate if parameter is null before using (applies throughout).

[flags]
enum GetFilePathOptions
{
/// Default behavior
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Describe what "default behavior" is (e.g. "Searches all paths and package types")


// It's bigger than a breadbox. Allocate memory
std::unique_ptr<WCHAR[]> pathBuffer{ std::make_unique<WCHAR[]>(pathLength) };
THROW_IF_WIN32_ERROR_MSG(details::GetPackagePathByFullName2IfSupported(packageFullName, packagePathType, &pathLength, pathBuffer.get()),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

GetPackagePathByFullName2IfSupported

This function returns HRESULT but is checked for win32 error

const auto& packageInfo{ packageInfos[index] };

// Does the package's properties match our search criteria?
const auto packageInfoFlags{ appmodel::package_properties_to_options(packageInfo.flags) };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

packageInfoFlags

After conversion in package_properties_to_options, this should be called packagePathOptions (of type GetPackageFilePathOptions). It is no longer packageInfoFlags (of type uint32_t).


inline bool is_match_for_package_properties(
_In_ GetPackageFilePathOptions options,
std::uint32_t packageInfoFlags)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

std::uint32_t packageInfoFlags

This parameter should be
GetPackageFilePathOptions packagePathOptions

"uint32_t packageInfoFlags" can easily be confused with PACKAGE_INFO.flags, which is the wrong type.

// * PackagePathType_Install is available since Win8
// * PackagePathType_Mutable is available since 19H1
// * PackagePathType_Effective is available since 19H1
// * PackagePathType_MachineExternalLocation is available since 20H1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PackagePathType_MachineExternalLocation

The value is called "PackagePathType_MachineExternal" (without "Location"). Similarly for PackagePathType_UserExternal and PackagePathType_EffectiveExternal below.

// * PackagePathType_MachineExternalLocation is available since 20H1
// * PackagePathType_UserExternalLocation is available since 20H1
// * PackagePathType_EffectiveExternalLocation is available since 20H1
// GetPackagePathByFullName() is available since Win8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The value is called "PackagePathType_MachineExternal" (without "Location"). Similarly for PackagePathType_UserExternal and PackagePathType_EffectiveExternal below.

@letao-msft
Copy link
Copy Markdown
Contributor

path = package.GetInstallPath()

This pseudocode doesn't match the implementation.
package_runtime.cpp line 205 checks if SearchInstallPath is in options before getting install path, but here it's unconditional.


Refers to: specs/package/Package.md:150 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)

/// @note If a package has a UserExternal location then MachineExternal location is not checked (even if the package has one).
/// @see https://learn.microsoft.com/en-us/windows/win32/api/appmodel/nf-appmodel-getpackagepathbyfullname2
/// @see PackageGraph.GetFilePath
/// @see PackageGraph.GetFilePathOptions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

GetFilePathOptions

Is this referring to GetFilePathWithOptions?
(Add the word "With")
PackageGraph.GetFilePathOptions doesn't exist.

/// @note If a package has a UserExternal location then MachineExternal location is not checked (even if the package has one).
/// @see https://learn.microsoft.com/en-us/windows/win32/api/appmodel/nf-appmodel-getpackagepathbyfullname2
/// @see Package.GetFilePath
/// @see GetPackageFilePathOptions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

GetPackageFilePathOptions

Is this referring to Package.GetFilePathInPackageWithOptions?
Or enum GetFilePathOptions?

@letao-msft
Copy link
Copy Markdown
Contributor

var options = GetPackageFilePathOptions.SearchInstallPath |

The IDL only defines GetFilePathOptions.
GetPackageFilePathOptions is an internal C++ impl type.

Same applies throughout.


Refers to: specs/package/Package.md:281 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)

@letao-msft
Copy link
Copy Markdown
Contributor

if (absoluteFilename == null)

The API returns empty string - does that really map to null in C#?
Consider using string.IsNullOrEmpty(absoluteFileName).


Refers to: specs/package/Package.md:285 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)

@@ -377,9 +377,7 @@ string GetXamlWinMD()
GetPackageFilePathOptions.SearchUserExternalPath |
GetPackageFilePathOptions.SearchMainPackages |
GetPackageFilePathOptions.SearchFrameworkPath |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SearchFrameworkPath

SearchFrameworkPackages?
Similarly for SearchOptionalPackages.
Same applies throughout.

@letao-msft
Copy link
Copy Markdown
Contributor

    wprintf(L"ERROR: Microsoft.UI.Xaml.winmd not found\n", hr);

There is no % specifier that matches this argument.


Refers to: specs/package/Package.md:356 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)

@letao-msft
Copy link
Copy Markdown
Contributor

    wprintf(L"ERROR: resources.pri not found for %ls\n", hr, packageFullName);

There is no % specifier that matches this argument


Refers to: specs/package/Package.md:262 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)

@letao-msft
Copy link
Copy Markdown
Contributor

    wprintf(L"ERROR: resources.pri not found for %ls\n", hr, packageFullName);

There is no % specifier that matches this argument


Refers to: specs/package/Package.md:312 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)

@letao-msft
Copy link
Copy Markdown
Contributor

    wprintf(L"ERROR: Microsoft.UI.Xaml.winmd not found\n", hr);

There is no % specifier that matches this argument


Refers to: specs/package/Package.md:412 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)

@letao-msft
Copy link
Copy Markdown
Contributor

std::wstring GetResourcesPri(PCWSTR packageFullName)

This parameter is never used


Refers to: specs/package/Package.md:205 in 50e998b. [](commit_id = 50e998b, deletion_comment = False)


Locate `Microsoft.UI.Xaml.winmd` in the current process' package graph but ignore Mutable locations,
Resource packages and HostRuntime dependencies.
Resource and Resource packages and HostRuntime dependencies.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Resource and Resource

Typo?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-PackageManagement MSIX deployment technology (eg PackageDeploymentManager) needs-triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants