Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions BuildAll.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ Try {
Copy-Item -path "BuildOutput\$configurationForMrtAndAnyCPU\$platformToRun\Microsoft.Windows.ApplicationModel.Resources.Projection\Microsoft.Windows.ApplicationModel.Resources.Projection.dll" -destination "$BasePath\lib\net6.0-windows10.0.17763.0" -force
Copy-Item -path "BuildOutput\$configurationForMrtAndAnyCPU\$platformToRun\Microsoft.Windows.ApplicationModel.Resources.Projection\Microsoft.Windows.ApplicationModel.Resources.Projection.pdb" -destination "$BasePath\lib\net6.0-windows10.0.17763.0" -force
Copy-Item -path "BuildOutput\$configurationForMrtAndAnyCPU\$platformToRun\Microsoft.Windows.ApplicationModel.Resources\Microsoft.Windows.ApplicationModel.Resources.winmd" -destination "$BasePath\lib\uap10.0" -force
Copy-Item -path "BuildOutput\$configurationForMrtAndAnyCPU\$platformToRun\Microsoft.Windows.ApplicationModel.Resources\Microsoft.Windows.Globalization.winmd" -destination "$BasePath\lib\uap10.0" -force
}
}

Expand Down
1 change: 1 addition & 0 deletions build/NuSpecs/AppxManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<ActivatableClass ActivatableClassId="Microsoft.Windows.ApplicationModel.Resources.ResourceCandidate" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="Microsoft.Windows.ApplicationModel.Resources.ResourceLoader" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="Microsoft.Windows.ApplicationModel.Resources.ResourceManager" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="Microsoft.Windows.Globalization.ApplicationLanguages" ThreadingModel="both" />
</InProcessServer>
</Extension>
<Extension Category="windows.activatableClass.inProcessServer">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
Expand All @@ -21,7 +21,7 @@
</ItemGroup>

<PropertyGroup>
<CSWinRTIncludes>Microsoft.Windows.ApplicationModel.Resources</CSWinRTIncludes>
<CSWinRTIncludes>Microsoft.Windows.Globalization;Microsoft.Windows.ApplicationModel.Resources</CSWinRTIncludes>
<CSWinRTWindowsMetadata>10.0.17763.0</CSWinRTWindowsMetadata>
<NoWarn>MSB3271</NoWarn>
</PropertyGroup>
Expand All @@ -30,10 +30,11 @@
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
</PropertyGroup>

<ItemGroup>
<CsWinRTInputs Include="$(OutDir)/**/*.winmd" />
<CsWinRTInputs Include="$(BaseOutputPath)/Microsoft.Windows.ApplicationModel.Resources/Microsoft.Windows.Globalization.winmd" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#include "pch.h"
#include "ApplicationLanguages.h"
#include "Microsoft.Windows.Globalization.ApplicationLanguages.g.cpp"

#include <winrt/Windows.Globalization.h>

#include <AppModel.Identity.h>

#include "Helper.h"

namespace winrt::Microsoft::Windows::Globalization::implementation
{
hstring ApplicationLanguages::m_language;

winrt::Windows::Foundation::Collections::IVectorView<hstring> ApplicationLanguages::Languages()
{
return winrt::Windows::Globalization::ApplicationLanguages::Languages();
}

winrt::Windows::Foundation::Collections::IVectorView<hstring> ApplicationLanguages::ManifestLanguages()
{
if (AppModel::Identity::IsPackagedProcess())
{
return winrt::Windows::Globalization::ApplicationLanguages::ManifestLanguages();
}
else
{
return {};
}
}

hstring ApplicationLanguages::PrimaryLanguageOverride()
{
static wil::srwlock lock;

auto criticalSection{ lock.lock_shared() };
return m_language;
}

void ApplicationLanguages::PrimaryLanguageOverride(hstring const& language)
{
bool isValidLanguageTag = IsWellFormedLanguageTag(language.c_str());

THROW_HR_IF_MSG(E_INVALIDARG, !isValidLanguageTag, "The parameter is incorrect");

static wil::srwlock lock;
Copy link
Member

Choose a reason for hiding this comment

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

You have to use the same lock for reading and writing. Otherwise the setter could take an exclusive lock, but another thread could still read the data.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤦 fixed


auto criticalSection {lock.lock_exclusive()};
m_language = language;

if (AppModel::Identity::IsPackagedProcess())
{
winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride(language);
}
}
} // namespace winrt::Microsoft::Windows::Globalization::implementation
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#pragma once
#include "Microsoft.Windows.Globalization.ApplicationLanguages.g.h"

namespace winrt::Microsoft::Windows::Globalization::implementation
{
struct ApplicationLanguages
{
ApplicationLanguages() = delete;

static winrt::Windows::Foundation::Collections::IVectorView<hstring> Languages();
static winrt::Windows::Foundation::Collections::IVectorView<hstring> ManifestLanguages();
static hstring PrimaryLanguageOverride();
static void PrimaryLanguageOverride(hstring const& language);

private:
static hstring m_language;
};
} // namespace winrt::Microsoft::Windows::Globalization::implementation

namespace winrt::Microsoft::Windows::Globalization::factory_implementation
{
struct ApplicationLanguages : ApplicationLanguagesT<ApplicationLanguages, implementation::ApplicationLanguages>
{
};
} // namespace winrt::Microsoft::Windows::Globalization::factory_implementation
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,31 @@ HRESULT GetDefaultPriFile(winrt::hstring& filePath)
bool isPackaged = (hr != HRESULT_FROM_WIN32(APPMODEL_ERROR_NO_PACKAGE));
return GetDefaultPriFileForCurentModule(isPackaged, filePath);
}

bool IsWellFormedLanguageTag(const wchar_t* languageTag)
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done. I don't have this header, so I reused the logic to load the DLL manually as in platform.h/.cpp. Tested. Works as expected.

Copy link
Contributor

Choose a reason for hiding this comment

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

the header should be in SDK. The LoadLibrary code was written before it's added into SDK.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

according to the docs it should be there since Windows 10 Build 17763, but I don't have it on machine (OS version 10.0.22631 Build 22631), VS 22 installed with multiple SDK versions. I double-checked on another machine with the same software versions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I double-checked again, the header is present in SDK v10.0.20348.0 (and later), even though docs says it should be there before

Copy link
Contributor Author

@stefansjfw stefansjfw Jun 28, 2024

Choose a reason for hiding this comment

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

Is bumping WindowsTargetPlatformVersion to 10.0.20348.0 for this specific project ok to do ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I pushed the change (including WindowsTargetPlatformVersion bump). I'll revert if it's not ok

{
// Implementation taken from Platform.h, without accepting semi-colon, as it must be a single language tag
if (languageTag == nullptr || *languageTag == L'\0')
{
return FALSE;
}

BOOLEAN ok = TRUE;
PCWSTR test = languageTag;

while (ok && (*test != L'\0'))
{
// we accept letters, numbers and dash
if (((*test >= L'0') && (*test <= L'9')) || ((*test >= L'a') && (*test <= L'z')) || ((*test >= L'A') && (*test <= L'Z')) ||
(*test == L'-'))
{
test++;
}
else
{
ok = FALSE;
}
}

return ok && ((test - languageTag) >= 2);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
#pragma once
bool IsResourceNotFound(HRESULT hr);
HRESULT GetDefaultPriFile(winrt::hstring& path);
bool IsWellFormedLanguageTag(const wchar_t* languageTag);
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,18 @@ namespace Microsoft.Windows.ApplicationModel.Resources
static String Theme { get; };
}
} // namespace Microsoft.Windows.ApplicationModel.Resources

namespace Microsoft.Windows.Globalization
{
[contractversion(1)]
apicontract MrtCoreContract{};

[contract(MrtCoreContract, 2)]
runtimeclass ApplicationLanguages
{
static IVectorView<String> Languages { get; };
static IVectorView<String> ManifestLanguages { get; };
static String PrimaryLanguageOverride;
}

} // namespace Microsoft.Windows.Globalization
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
</DisableSpecificWarnings>
<PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalIncludeDirectories>$(RepoRoot)\dev\Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
Expand All @@ -109,6 +110,7 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ApplicationLanguages.h" />
<ClInclude Include="Helper.h" />
<ClInclude Include="KnownResourceQualifierName.h" />
<ClInclude Include="pch.h" />
Expand All @@ -120,6 +122,7 @@
<ClInclude Include="ResourceNotFoundEventArgs.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ApplicationLanguages.cpp" />
<ClCompile Include="Helper.cpp" />
<ClCompile Include="KnownResourceQualifierName.cpp" />
<ClCompile Include="pch.cpp">
Expand All @@ -140,7 +143,7 @@
<None Include="Microsoft.Windows.ApplicationModel.Resources.def" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Microsoft.Windows.ApplicationModel.Resources.rc">
<ResourceCompile Include="Microsoft.Windows.ApplicationModel.Resources.rc">
<AdditionalIncludeDirectories>$(RepoRoot);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemGroup>
Expand Down Expand Up @@ -171,6 +174,7 @@
<Import Project="$(NugetPackageDirectory)\Microsoft.SourceLink.Common.$(MicrosoftSourceLinkCommonVersion)\build\Microsoft.SourceLink.Common.targets" Condition="Exists('$(NugetPackageDirectory)\Microsoft.SourceLink.Common.$(MicrosoftSourceLinkCommonVersion)\build\Microsoft.SourceLink.Common.targets')" />
<Import Project="$(NugetPackageDirectory)\Microsoft.Build.Tasks.Git.$(MicrosoftBuildTasksGitVersion)\build\Microsoft.Build.Tasks.Git.targets" Condition="Exists('$(NugetPackageDirectory)\Microsoft.Build.Tasks.Git.$(MicrosoftBuildTasksGitVersion)\build\Microsoft.Build.Tasks.Git.targets')" />
<Import Project="$(NugetPackageDirectory)\Microsoft.SourceLink.GitHub.$(MicrosoftSourceLinkGitHubVersion)\build\Microsoft.SourceLink.GitHub.targets" Condition="Exists('$(NugetPackageDirectory)\Microsoft.SourceLink.GitHub.$(MicrosoftSourceLinkGitHubVersion)\build\Microsoft.SourceLink.GitHub.targets')" />
<Import Project="$(NugetPackageDirectory)\Microsoft.Windows.ImplementationLibrary.$(MicrosoftWindowsImplementationLibraryVersion)\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(NugetPackageDirectory)\Microsoft.Windows.ImplementationLibrary.$(MicrosoftWindowsImplementationLibraryVersion)\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
Expand All @@ -185,5 +189,6 @@
<Error Condition="!Exists('$(NugetPackageDirectory)\Microsoft.Build.Tasks.Git.$(MicrosoftBuildTasksGitVersion)\build\Microsoft.Build.Tasks.Git.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NugetPackageDirectory)\Microsoft.Build.Tasks.Git.$(MicrosoftBuildTasksGitVersion)\build\Microsoft.Build.Tasks.Git.targets'))" />
<Error Condition="!Exists('$(NugetPackageDirectory)\Microsoft.SourceLink.GitHub.$(MicrosoftSourceLinkGitHubVersion)\build\Microsoft.SourceLink.GitHub.props')" Text="$([System.String]::Format('$(ErrorText)', '$(NugetPackageDirectory)\Microsoft.SourceLink.GitHub.$(MicrosoftSourceLinkGitHubVersion)\build\Microsoft.SourceLink.GitHub.props'))" />
<Error Condition="!Exists('$(NugetPackageDirectory)\Microsoft.SourceLink.GitHub.$(MicrosoftSourceLinkGitHubVersion)\build\Microsoft.SourceLink.GitHub.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NugetPackageDirectory)\Microsoft.SourceLink.GitHub.$(MicrosoftSourceLinkGitHubVersion)\build\Microsoft.SourceLink.GitHub.targets'))" />
<Error Condition="!Exists('$(NugetPackageDirectory)\Microsoft.Windows.ImplementationLibrary.$(MicrosoftWindowsImplementationLibraryVersion)\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NugetPackageDirectory)\Microsoft.Windows.ImplementationLibrary.$(MicrosoftWindowsImplementationLibraryVersion)\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<ClCompile Include="KnownResourceQualifierName.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ApplicationLanguages.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
Expand Down Expand Up @@ -58,6 +61,9 @@
<ClInclude Include="KnownResourceQualifierName.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ApplicationLanguages.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="Microsoft.Windows.ApplicationModel.Resources.idl" />
Expand All @@ -75,4 +81,7 @@
<UniqueIdentifier>{54696afe-f42a-494a-bc24-3fd0d4192122}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Microsoft.Windows.ApplicationModel.Resources.rc" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

const wchar_t c_languageQualifierName[] = L"Language";

#include "ApplicationLanguages.h"

using namespace winrt::Microsoft::Windows::Globalization;

namespace winrt::Microsoft::Windows::ApplicationModel::Resources::implementation
{
void ResourceContext::InitializeQualifierNames()
Expand Down Expand Up @@ -97,6 +101,10 @@ void ResourceContext::Apply()
winrt::check_hresult(MrmSetQualifier(m_resourceContext, eachValue.Key().c_str(), eachValue.Value().c_str()));
}
}
if (!ApplicationLanguages::PrimaryLanguageOverride().empty())
Copy link
Contributor

Choose a reason for hiding this comment

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

PrimaryLanguageOverride

explicit language context should trump PrimaryLanguageOverride

Copy link
Contributor Author

@stefansjfw stefansjfw Jun 26, 2024

Choose a reason for hiding this comment

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

With W.G.PrimaryLanguageOverride set, newly created ResourceContext takes PrimaryLanguageOverride into account, i.e. does not override language QualifierValue to default language.

Copy link
Contributor

Choose a reason for hiding this comment

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

e.g. if default language is "en-US", PrimaryLanguageOverride is "de-DE", and ResourceContext["Lanauge"]="fr-FR", what's the language that will take effect? I'd think "fr-FR"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

according to this logic, no, PrimaryLanguageOverride should take precedence over ResourceContext["Language"]. GetLangugageContext returns Windows::Globalization::ApplicationLanguages::Languages() which is affected if W.G.PrimaryLanguageOverride is set. This behavior remains the same with this change

Copy link
Contributor

Choose a reason for hiding this comment

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

I think in that logic ResourceContext["Language"] still takes precedence. It would be strange to the caller that an explicit request is not honored

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it now. You're right. Fixed by latest change. Now explicit set of context Language is not overridden by PrimaryLanguageOverride.

I tested with this WinAppSDK sample. Default language is PrimaryLanguageOverride - de-DE, but fetching resources with m_overrideResourceContext passed is fetching it-IT resources:

image

{
winrt::check_hresult(MrmSetQualifier(m_resourceContext, c_languageQualifierName, ApplicationLanguages::PrimaryLanguageOverride().c_str()));
}
}

hstring ResourceContext::GetLangugageContext()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
<package id="Microsoft.SourceLink.Common" version="1.1.1" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.SourceLink.GitHub" version="1.1.1" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.230706.1" targetFramework="native" />
</packages>
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.220914.1" targetFramework="native" />
</packages>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <winrt/Windows.Foundation.Collections.h>
#include "..\..\core\src\MRM.h"

#include <wil/result_macros.h>
#include <wil/resource.h>

struct StringResourceFreer
{
void operator()(wchar_t* resource) { MrmFreeResource(resource); }
Expand Down