Skip to content

Fix sparse-packaged apps unable to discover module-specific PRI files - #6376

Merged
Gordon Lam (yeelam-gordon) merged 7 commits into
mainfrom
fix/sparse-app-pri-discovery
Apr 9, 2026
Merged

Gordon Lam (yeelam-gordon) merged 7 commits into
mainfrom
fix/sparse-app-pri-discovery

Conversation

@yeelam-gordon

@yeelam-gordon Gordon Lam (yeelam-gordon) commented Apr 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes sparse-packaged apps (unpackaged + self-contained WinUI3 with identity via AddPackageByUriAsync) failing to locate module-specific PRI files (e.g., MyApp.pri set via ProjectPriFileName).

Why This Matters

Not an regression, it doesn't work from the first day.

While unpackaged apps that need package-identity-only APIs (e.g., Windows AI APIs) must use sparse app registration (AddPackageByUriAsync) to acquire identity. When multiple such apps share the same output directory, each needs its own module-specific PRI (via ProjectPriFileName) to avoid resource conflicts. This fix unblocks that scenario - without it, any sparse-packaged app with a custom PRI name fails to load resources.

Problem

GetDefaultPriFile() determines isPackaged=true for sparse apps because they have package identity. This causes GetDefaultPriFileForCurentModule to pass "resources.pri" to MrmGetFilePathFromName, which only searches for that exact filename - skipping the [modulename].pri fallback that unpackaged apps (the one leverage sparse app) receive.

When multiple apps share the same output directory, each needs its own module-specific PRI to avoid conflicts, but sparse apps could only find resources.pri.

Fix

When the packaged "resources.pri" search fails for apps with identity, fall back to the unpackaged discovery path (pass nullptr to MrmGetFilePathFromName) which triggers the broader search including "[modulename].pri" derived from the process executable name.

The fallback is gated on IsResourceNotFound(hr) to ensure only genuine not-found conditions trigger it, avoiding masking real errors (e.g., E_OUTOFMEMORY, E_ACCESSDENIED). If the fallback also fails, the original HRESULT from the "resources.pri" search is returned for backward compatibility so callers checking for specific errors (e.g., ERROR_FILE_NOT_FOUND) are not broken.

Changed file

  • dev/MRTCore/mrt/Microsoft.Windows.ApplicationModel.Resources/src/Helper.cpp

Existing test coverage

  • DefaultResourceManagerWithExePri — validates the nullptr fallback mechanism

  • DefaultResourceManager — validates error behavior when no PRI exists

  • DefaultResourceManagerWithResourcePri — validates the normal packaged path

A full sparse-app integration test requires actual sparse package registration (AddPackageByUriAsync) which is beyond unit test scope.

Validation

  • Builds clean with no warnings

  • /azp run pipeline validation


A microsoft employee must use /azp run to validate using the pipelines below.

WARNING:

Comments made by azure-pipelines bot maybe inaccurate.

Please see pipeline link to verify that the build is being ran.

For status checks on the main branch, please use TransportPackage-Foundation-PR

(https://microsoft.visualstudio.com/ProjectReunion/_build?definitionId=81063&_a=summary)

and run the build against your PR branch with the default parameters.

Sparse-packaged apps have package identity (via AddPackageByUriAsync) but
deploy resources as loose files next to the executable. GetDefaultPriFile()
determines isPackaged=true for these apps (since they have identity), causing
GetDefaultPriFileForCurentModule to pass "resources.pri" to
MrmGetFilePathFromName — which only searches for that exact filename,
skipping the [modulename].pri fallback that unpackaged apps receive.

When the "resources.pri" search fails for apps with identity, fall back to
the unpackaged discovery path (pass nullptr to MrmGetFilePathFromName) which
triggers the broader search including "[modulename].pri" derived via
PathCchRenameExtension from the process executable name.

If the fallback also fails, return the original HRESULT from the
"resources.pri" search so callers that check for specific errors
(e.g., ERROR_FILE_NOT_FOUND) are not broken.

Existing test coverage:
- DefaultResourceManagerWithExePri validates the nullptr fallback mechanism
- DefaultResourceManager validates error behavior when no PRI exists
- DefaultResourceManagerWithResourcePri validates the normal packaged path

A full sparse-app integration test requires actual sparse package
registration (AddPackageByUriAsync) which is beyond unit test scope.

Fixes: microsoft/microsoft-ui-xaml#10856

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@yeelam-gordon

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Copilot AI left a comment

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.

Pull request overview

This PR updates MRTCore’s default PRI discovery logic to support sparse-packaged apps (apps with package identity but deploying PRI files as loose files next to the executable), ensuring module-specific PRI files (e.g., [modulename].pri) can be discovered when resources.pri is not present.

Changes:

  • After a failed packaged-module lookup ("resources.pri"), add a fallback to the unpackaged discovery path (MrmGetFilePathFromName(nullptr, ...)) for apps with identity.
  • Preserve the original HRESULT from the packaged-module lookup when the fallback also fails.

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

@yeelam-gordon

Copy link
Copy Markdown
Contributor Author

Copilot review

Copilot AI commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Copilot review

Reviewed the current state of Helper.cpp. No bugs or concerns in the changed code — the fallback is correctly gated on IsResourceNotFound(hr), the original HRESULT is preserved for backward compatibility when both paths fail, and comments are concise single-line statements.

@yeelam-gordon

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

@yeelam-gordon
Gordon Lam (yeelam-gordon) merged commit d5afea6 into main Apr 9, 2026
53 checks passed
@yeelam-gordon
Gordon Lam (yeelam-gordon) deleted the fix/sparse-app-pri-discovery branch April 9, 2026 01:44
@khmyznikov

Copy link
Copy Markdown

Scott Jones (@Scottj1s) when it will be considered for the release? High priority for PowerToys.

Felipe G (guimafelipe) added a commit that referenced this pull request May 28, 2026
…-specific PRI files (#6376) (#6493)

* Fix sparse-packaged apps unable to discover module-specific PRI files (#6376)

* Fix sparse-packaged apps unable to discover module-specific PRI files

Sparse-packaged apps have package identity (via AddPackageByUriAsync) but
deploy resources as loose files next to the executable. GetDefaultPriFile()
determines isPackaged=true for these apps (since they have identity), causing
GetDefaultPriFileForCurentModule to pass "resources.pri" to
MrmGetFilePathFromName — which only searches for that exact filename,
skipping the [modulename].pri fallback that unpackaged apps receive.

When the "resources.pri" search fails for apps with identity, fall back to
the unpackaged discovery path (pass nullptr to MrmGetFilePathFromName) which
triggers the broader search including "[modulename].pri" derived via
PathCchRenameExtension from the process executable name.

If the fallback also fails, return the original HRESULT from the
"resources.pri" search so callers that check for specific errors
(e.g., ERROR_FILE_NOT_FOUND) are not broken.

Existing test coverage:
- DefaultResourceManagerWithExePri validates the nullptr fallback mechanism
- DefaultResourceManager validates error behavior when no PRI exists
- DefaultResourceManagerWithResourcePri validates the normal packaged path

A full sparse-app integration test requires actual sparse package
registration (AddPackageByUriAsync) which is beyond unit test scope.

Fixes: microsoft/microsoft-ui-xaml#10856

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review: simplify flow, shorten comment, gate on file-not-found

* Add backward-compatibility comment on return hr

* Move backward-compat comment to fallback block for clarity

* Keep comment sentences on single lines

* Use existing IsResourceNotFound() helper instead of manual HRESULT checks

* Merge two-line comment into single line

Agent-Logs-Url: https://github.com/microsoft/WindowsAppSDK/sessions/ed36cfc6-52ad-414b-8e1a-dc16f413a97e

Co-authored-by: yeelam-gordon <73506701+yeelam-gordon@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* Add containment for sparse PRI fallback fix

Bug 62382643: gate the sparse PRI fallback with containment so
it can be rolled back if needed.

* Fix containment version to WinAppSDK_2_1_5

* Fix bug ID in containment comment

* Contain all changed code inside containment gate

* Use IsPackagedProcess() for package detection inside containment

* Add MRTCore_SparsePackagedPriFallback to RuntimeCompatibilityChange enum

* Revert "Add MRTCore_SparsePackagedPriFallback to RuntimeCompatibilityChange enum"

This reverts commit 068135d.

---------

Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: agniuks <41223743+agniuks@users.noreply.github.com>
Felipe G (guimafelipe) added a commit that referenced this pull request May 28, 2026
…fic PRI files (#6376) (#6492)

* Fix sparse-packaged apps unable to discover module-specific PRI files (#6376)

* Fix sparse-packaged apps unable to discover module-specific PRI files

Sparse-packaged apps have package identity (via AddPackageByUriAsync) but
deploy resources as loose files next to the executable. GetDefaultPriFile()
determines isPackaged=true for these apps (since they have identity), causing
GetDefaultPriFileForCurentModule to pass "resources.pri" to
MrmGetFilePathFromName — which only searches for that exact filename,
skipping the [modulename].pri fallback that unpackaged apps receive.

When the "resources.pri" search fails for apps with identity, fall back to
the unpackaged discovery path (pass nullptr to MrmGetFilePathFromName) which
triggers the broader search including "[modulename].pri" derived via
PathCchRenameExtension from the process executable name.

If the fallback also fails, return the original HRESULT from the
"resources.pri" search so callers that check for specific errors
(e.g., ERROR_FILE_NOT_FOUND) are not broken.

Existing test coverage:
- DefaultResourceManagerWithExePri validates the nullptr fallback mechanism
- DefaultResourceManager validates error behavior when no PRI exists
- DefaultResourceManagerWithResourcePri validates the normal packaged path

A full sparse-app integration test requires actual sparse package
registration (AddPackageByUriAsync) which is beyond unit test scope.

Fixes: microsoft/microsoft-ui-xaml#10856

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review: simplify flow, shorten comment, gate on file-not-found

* Add backward-compatibility comment on return hr

* Move backward-compat comment to fallback block for clarity

* Keep comment sentences on single lines

* Use existing IsResourceNotFound() helper instead of manual HRESULT checks

* Merge two-line comment into single line

Agent-Logs-Url: https://github.com/microsoft/WindowsAppSDK/sessions/ed36cfc6-52ad-414b-8e1a-dc16f413a97e

Co-authored-by: yeelam-gordon <73506701+yeelam-gordon@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* Add containment for sparse PRI fallback fix

Bug 62382643: gate the sparse PRI fallback with containment so
it can be rolled back if needed.

* Fix containment version to WinAppSDK_2_1_5

* Fix bug ID in containment comment

* Contain all changed code inside containment gate

* Use IsPackagedProcess() for package detection inside containment

* Add MRTCore_SparsePackagedPriFallback to RuntimeCompatibilityChange enum

* Revert "Add MRTCore_SparsePackagedPriFallback to RuntimeCompatibilityChange enum"

This reverts commit 73d274f.

---------

Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: agniuks <41223743+agniuks@users.noreply.github.com>
Scott Jones (Scottj1s) added a commit that referenced this pull request Jul 10, 2026
…(supersedes #5828) (#6603)

* Add fallback for MrmGetFilePathFromName

* Update dev/MRTCore/mrt/Core/src/MRM.cpp

Co-authored-by: Rafael Rivera <rafael@withinwindows.com>

* Complete MrmGetFilePathFromName fallback: return S_OK with a valid path

PR #5828 added a DefaultFallback search pass to restore the documented
"always succeed, return a best-effort path" contract of MrmGetFilePathFromName
(regressed in #4965 to return ERROR_FILE_NOT_FOUND, breaking ResourceManager
creation when no resources.pri exists - #5814 / #5940).

However DefaultFallback was unreachable: both terminal passes
(ParentPathForFileName, exeDirForModulePri) jumped to Final, skipping it. The
function therefore returned S_OK with *filePath == nullptr, which crashes
callers that dereference the path (e.g. MrmCreateResourceManager rejects an
empty path with E_INVALIDARG, and the GetFilePath unit test AVs on wcsstr).

Changes:
- MRM.cpp: route both terminal passes into DefaultFallback so it runs as the
  final pass; return S_OK with a non-null best-effort path (the provided
  filename, else resources.pri) under the base directory (if set) otherwise the
  module directory - matching pre-#4965 (legacy) behavior. Preserve the module
  directory since the parent-folder search mutates exeDir in place.
- Helper.cpp (GetDefaultPriFile): the sparse-packaged [modulename].pri fallback
  gated on IsResourceNotFound(hr); since MrmGetFilePathFromName now returns
  S_OK, gate on actual file existence (CheckFile) instead, and only adopt the
  unpackaged result if it resolves to an existing file (avoids re-regressing
  #6376 / microsoft-ui-xaml#10856).
- MrmTests.cpp: strengthen GetFilePath to assert the fallback path is under the
  module directory.
- BypassTests.json: remove the now-passing UnitTest::BasicTest::GetFilePath and
  MrtCoreUnpackagedTests.TestClass.DefaultResourceManager entries so they gate
  again (they were bypassed in #5498 / #5919 after the #4965 regression, which
  is why the regression shipped undetected).

Verified locally (x64 Debug): MrmUnitTest 21/21, MrtCoreUnpackagedTests 27/27.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: 王宇逸 <Strawberry_Str@hotmail.com>
Co-authored-by: Rafael Rivera <rafael@withinwindows.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MRT ResourceManager default constructor doesn't find [modulename].pri for sparse-packaged apps

5 participants