Skip to content

[Fix] Locating Microsoft.WindowsAppRuntime.pri in framework usage #5582

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

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 2 additions & 2 deletions dev/Interop/StoragePickers/FileOpenPicker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation
auto logTelemetry{ StoragePickersTelemetry::FileOpenPickerPickSingleFile::Start(m_telemetryHelper) };

PickerCommon::PickerParameters parameters{};
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey);
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey, parameters.AllFilesText);

CaptureParameters(parameters);

Expand Down Expand Up @@ -129,7 +129,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation

// capture parameters to avoid using get strong referece of picker
PickerCommon::PickerParameters parameters{};
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey);
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey, parameters.AllFilesText);

CaptureParameters(parameters);

Expand Down
2 changes: 1 addition & 1 deletion dev/Interop/StoragePickers/FileSavePicker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation
auto logTelemetry{ StoragePickersTelemetry::FileSavePickerPickSingleFile::Start(m_telemetryHelper) };

PickerCommon::PickerParameters parameters{};
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey);
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey, parameters.AllFilesText);

CaptureParameters(parameters);

Expand Down
2 changes: 1 addition & 1 deletion dev/Interop/StoragePickers/FolderPicker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation
auto logTelemetry{ StoragePickersTelemetry::FolderPickerPickSingleFolder::Start(m_telemetryHelper) };

PickerCommon::PickerParameters parameters{};
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey);
parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey, parameters.AllFilesText);

CaptureParameters(parameters);

Expand Down
115 changes: 110 additions & 5 deletions dev/Interop/StoragePickers/PickerLocalization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,119 @@
#include "PickerLocalization.h"
#include <winrt\base.h>
#include <winrt\Microsoft.Windows.ApplicationModel.Resources.h>
#include <iostream>

// find framework package related code from https://github.com/microsoft/microsoft-ui-xaml/blob/main/src/controls/dev/ResourceHelper/ResourceAccessor.cpp
// and https://github.com/microsoft/microsoft-ui-xaml/blob/main/src/controls/dev/dll/SharedHelpers.cpp

namespace {
constexpr wchar_t c_reunionPackageNamePrefix[] = L"Microsoft.WindowsAppRuntime";
constexpr int c_reunionPackageNamePrefixLength = ARRAYSIZE(c_reunionPackageNamePrefix) - 1;

// Tries to retrieve the current package graph. Will return true if a package graph exists
// and is non-empty, else false.
bool TryGetCurrentPackageGraph(
const std::uint32_t flags,
std::uint32_t& packageCount,
const PACKAGE_INFO*& packageGraph,
std::unique_ptr<BYTE[]>& buffer)
{
std::uint32_t bufferLength{};
LONG rc{ ::GetCurrentPackageInfo(flags, &bufferLength, nullptr, nullptr) };
if ((rc == APPMODEL_ERROR_NO_PACKAGE) || (rc == ERROR_SUCCESS))
{
// No/empty package graph
return false;
}
else if (rc != ERROR_INSUFFICIENT_BUFFER)
{
if (FAILED(HRESULT_FROM_WIN32(rc)))
{
return false;
}
}

buffer = std::make_unique<BYTE[]>(bufferLength);
if (SUCCEEDED(HRESULT_FROM_WIN32(::GetCurrentPackageInfo(flags, &bufferLength, buffer.get(), &packageCount))))
{
packageGraph = reinterpret_cast<PACKAGE_INFO*>(buffer.get());
return true;
}

return false;
}

bool IsInFrameworkPackageImpl(winrt::hstring& frameworkInstallLocation)
{
// We could be running in an app that is using WinUI "In-App" (i.e. the WinUI dlls are deployed as part of the app).
// Or we could be running in the Project Reunion Framework Package that an app takes a dependency on.
// We determine if we are in a Framework Package by looking at the App's package dependencies. If the app has a Project Reunion dependency
// then we must be running in a Framework Package.
const UINT32 c_filter{ PACKAGE_FILTER_HEAD | PACKAGE_FILTER_DIRECT | PACKAGE_FILTER_STATIC | PACKAGE_FILTER_DYNAMIC | PACKAGE_INFORMATION_BASIC };
std::uint32_t packageCount{};
const PACKAGE_INFO* packageGraph{};
std::unique_ptr<BYTE[]> packageBuffer;
if (TryGetCurrentPackageGraph(c_filter, packageCount, packageGraph, packageBuffer))
{
if (packageGraph)
{
for (std::uint32_t index = 0; index < packageCount; index++)
{
const PACKAGE_INFO& packageInfo{ packageGraph[index] };

// We use FamilyName here and not DisplayName because DisplayName is a localized string, but FamilyName is a stable identifier.
auto packageFamilyName = packageInfo.packageFamilyName;
int nameLength{ static_cast<int>(wcslen(packageFamilyName)) };

if (nameLength < c_reunionPackageNamePrefixLength)
{
continue;
}

if (
CompareStringOrdinal(
packageFamilyName,
c_reunionPackageNamePrefixLength,
c_reunionPackageNamePrefix,
c_reunionPackageNamePrefixLength,
TRUE) == CSTR_EQUAL)
{
frameworkInstallLocation = winrt::hstring(packageInfo.path);
return true;
}
}
}
}
return false;
}
}
bool PickerLocalization::IsInFrameworkPackage(winrt::hstring& frameworkPackageInstallLocation)
{
static winrt::hstring s_frameworkInstallLocation;
static bool isInFrameworkPackage = IsInFrameworkPackageImpl(s_frameworkInstallLocation);
frameworkPackageInstallLocation = s_frameworkInstallLocation;
return isInFrameworkPackage;
}
namespace PickerLocalization {
const winrt::hstring priPath = L"Microsoft.WindowsAppRuntime.pri";
winrt::hstring GetStoragePickersLocalizationText(winrt::hstring key)
const winrt::hstring priFileName = L"Microsoft.WindowsAppRuntime.pri";
winrt::hstring GetStoragePickersLocalizationText(winrt::hstring key, winrt::hstring fallback)
{
auto manager = winrt::Microsoft::Windows::ApplicationModel::Resources::ResourceManager(priPath);
return manager.MainResourceMap().GetValue(key).ValueAsString();
// adding try-catch to prevent localization error break picker experience on first shipping
// TODO: remove try-catch after stabilization period
try
{
winrt::hstring frameworkInstallLocation{};
auto priPath = IsInFrameworkPackage(frameworkInstallLocation)
? (frameworkInstallLocation + L"\\" + priFileName)
: priFileName;
auto manager = winrt::Microsoft::Windows::ApplicationModel::Resources::ResourceManager(priPath);
return manager.MainResourceMap().GetValue(key).ValueAsString();
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return fallback;
}
}
}


4 changes: 3 additions & 1 deletion dev/Interop/StoragePickers/PickerLocalization.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "PickerCommon.h"

namespace PickerLocalization {
winrt::hstring GetStoragePickersLocalizationText(winrt::hstring key);
winrt::hstring GetStoragePickersLocalizationText(winrt::hstring key, winrt::hstring fallback);

bool IsInFrameworkPackage(winrt::hstring& frameworkPackageInstallLocation);
}

15 changes: 1 addition & 14 deletions dev/Interop/StoragePickers/StoragePickers.vcxitems
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,6 @@
<Midl Include="$(MSBuildThisFileDirectory)Microsoft.Windows.Storage.Pickers.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\cs-CZ\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\de-DE\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\en-US\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\es-ES\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\fr-FR\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\it-IT\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\ja-JP\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\ko-KR\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\pl-PL\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\pt-BR\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\ru-RU\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\tr-TR\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\zh-CN\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\zh-TW\StoragePickers.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\*\*.resw" />
</ItemGroup>
</Project>