diff --git a/OpenConsole.sln b/OpenConsole.sln index e1cfad695..36ebf7121 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -256,6 +256,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty", "src\winconpty\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererUia", "src\renderer\uia\lib\uia.vcxproj", "{48D21369-3D7B-4431-9967-24E81292CF63}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinRTUtils", "src\cascadia\WinRTUtils\WinRTUtils.vcxproj", "{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -1270,6 +1272,24 @@ Global {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.Build.0 = Release|x64 {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x86.ActiveCfg = Release|Win32 {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x86.Build.0 = Release|Win32 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|Any CPU.ActiveCfg = Release|x64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|ARM64.ActiveCfg = Release|ARM64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|x64.ActiveCfg = Release|x64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|x86.ActiveCfg = Release|Win32 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|ARM64.Build.0 = Debug|ARM64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x64.ActiveCfg = Debug|x64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x64.Build.0 = Debug|x64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x86.ActiveCfg = Debug|Win32 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x86.Build.0 = Debug|Win32 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|Any CPU.ActiveCfg = Release|Win32 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|ARM64.ActiveCfg = Release|ARM64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|ARM64.Build.0 = Release|ARM64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x64.ActiveCfg = Release|x64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x64.Build.0 = Release|x64 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x86.ActiveCfg = Release|Win32 + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1335,6 +1355,7 @@ Global {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {59840756-302F-44DF-AA47-441A9D673202} {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} {48D21369-3D7B-4431-9967-24E81292CF63} = {05500DEF-2294-41E3-AF9A-24E580B82836} + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} = {59840756-302F-44DF-AA47-441A9D673202} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 9a99091d9..72b6ae388 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -5,6 +5,8 @@ #include "App.h" #include +#include + #include "App.g.cpp" using namespace winrt::Windows::ApplicationModel::DataTransfer; @@ -27,13 +29,13 @@ namespace winrt // Make sure that these keys are in the same order as the // SettingsLoadWarnings/Errors enum is! static const std::array settingsLoadWarningsLabels { - L"MissingDefaultProfileText", - L"DuplicateProfileText", - L"UnknownColorSchemeText" + USES_RESOURCE(L"MissingDefaultProfileText"), + USES_RESOURCE(L"DuplicateProfileText"), + USES_RESOURCE(L"UnknownColorSchemeText") }; static const std::array settingsLoadErrorsLabels { - L"NoProfilesText", - L"AllProfilesHiddenText" + USES_RESOURCE(L"NoProfilesText"), + USES_RESOURCE(L"AllProfilesHiddenText") }; // clang-format on @@ -46,15 +48,14 @@ static const std::array settingsLoadErrorsLabels { // Arguments: // - key: the value to use to look for a resource key in the given map // - map: A map of keys->Resource keys. -// - loader: the ScopedResourceLoader to use to look up the localized string. // Return Value: // - the localized string for the given type, if it exists. template -static winrt::hstring _GetMessageText(uint32_t index, std::array keys, ScopedResourceLoader& loader) +static winrt::hstring _GetMessageText(uint32_t index, std::array keys) { if (index < keys.size()) { - return loader.GetLocalizedString(keys.at(index)); + return GetLibraryResourceString(keys.at(index)); } return {}; } @@ -65,12 +66,11 @@ static winrt::hstring _GetMessageText(uint32_t index, std::array(warning), settingsLoadWarningsLabels, loader); + return _GetMessageText(static_cast(warning), settingsLoadWarningsLabels); } // Function Description: @@ -79,12 +79,11 @@ static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warnin // - The warning should have an entry in settingsLoadErrorsLabels. // Arguments: // - error: the SettingsLoadErrors value to get the localized text for. -// - loader: the ScopedResourceLoader to use to look up the localized string. // Return Value: // - localized text for the given error -static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error, ScopedResourceLoader& loader) +static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error) { - return _GetMessageText(static_cast(error), settingsLoadErrorsLabels, loader); + return _GetMessageText(static_cast(error), settingsLoadErrorsLabels); } // Function Description: @@ -128,12 +127,10 @@ namespace winrt::TerminalApp::implementation // Initialize will become protected or be deleted when GH#1339 (workaround for MSFT:22116519) are fixed. Initialize(); - _resourceLoader = std::make_shared(L"TerminalApp/Resources"); - // The TerminalPage has to be constructed during our construction, to // make sure that there's a terminal page for callers of // SetTitleBarContent - _root = winrt::make_self(_resourceLoader); + _root = winrt::make_self(); } // Method Description: @@ -222,8 +219,8 @@ namespace winrt::TerminalApp::implementation const winrt::hstring& contentKey, HRESULT settingsLoadedResult) { - auto title = _resourceLoader->GetLocalizedString(titleKey); - auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); + auto title = GetLibraryResourceString(titleKey); + auto buttonText = RS_(L"Ok"); Controls::TextBlock warningsTextBlock; // Make sure you can copy-paste @@ -232,7 +229,7 @@ namespace winrt::TerminalApp::implementation warningsTextBlock.TextWrapping(TextWrapping::Wrap); winrt::Windows::UI::Xaml::Documents::Run errorRun; - const auto errorLabel = _resourceLoader->GetLocalizedString(contentKey); + const auto errorLabel = GetLibraryResourceString(contentKey); errorRun.Text(errorLabel); warningsTextBlock.Inlines().Append(errorRun); @@ -246,7 +243,7 @@ namespace winrt::TerminalApp::implementation // Add a note that we're using the default settings in this case. winrt::Windows::UI::Xaml::Documents::Run usingDefaultsRun; - const auto usingDefaultsText = _resourceLoader->GetLocalizedString(L"UsingDefaultSettingsText"); + const auto usingDefaultsText = RS_(L"UsingDefaultSettingsText"); usingDefaultsRun.Text(usingDefaultsText); warningsTextBlock.Inlines().Append(usingDefaultsRun); @@ -266,8 +263,8 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details void App::_ShowLoadWarningsDialog() { - auto title = _resourceLoader->GetLocalizedString(L"SettingsValidateErrorTitle"); - auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); + auto title = RS_(L"SettingsValidateErrorTitle"); + auto buttonText = RS_(L"Ok"); Controls::TextBlock warningsTextBlock; // Make sure you can copy-paste @@ -279,7 +276,7 @@ namespace winrt::TerminalApp::implementation for (const auto& warning : warnings) { // Try looking up the warning message key for each warning. - const auto warningText = _GetWarningText(warning, *_resourceLoader); + const auto warningText = _GetWarningText(warning); if (!warningText.empty()) { warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, Resources())); @@ -307,8 +304,8 @@ namespace winrt::TerminalApp::implementation { if (FAILED(_settingsLoadedResult)) { - const winrt::hstring titleKey = L"InitialJsonParseErrorTitle"; - const winrt::hstring textKey = L"InitialJsonParseErrorText"; + const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText"); _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); } else if (_settingsLoadedResult == S_FALSE) @@ -432,7 +429,7 @@ namespace winrt::TerminalApp::implementation catch (const ::TerminalApp::SettingsException& ex) { hr = E_INVALIDARG; - _settingsLoadExceptionText = _GetErrorText(ex.Error(), *_resourceLoader); + _settingsLoadExceptionText = _GetErrorText(ex.Error()); } catch (...) { @@ -561,8 +558,8 @@ namespace winrt::TerminalApp::implementation if (FAILED(_settingsLoadedResult)) { _root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { - const winrt::hstring titleKey = L"ReloadJsonParseErrorTitle"; - const winrt::hstring textKey = L"ReloadJsonParseErrorText"; + const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); }); diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 3896c412f..770e81081 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -58,8 +58,6 @@ namespace winrt::TerminalApp::implementation std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr }; - std::shared_ptr _resourceLoader{ nullptr }; - HRESULT _settingsLoadedResult; winrt::hstring _settingsLoadExceptionText{}; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 4e0a38247..cb0c3f6bc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -5,6 +5,8 @@ #include "TerminalPage.h" #include "Utils.h" +#include + #include "TerminalPage.g.cpp" #include @@ -32,14 +34,10 @@ namespace winrt namespace winrt::TerminalApp::implementation { - TerminalPage::TerminalPage() {} - - TerminalPage::TerminalPage(std::shared_ptr resourceLoader) : + TerminalPage::TerminalPage() : _tabs{} { InitializeComponent(); - - _resourceLoader = resourceLoader; } void TerminalPage::SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI) @@ -102,9 +100,9 @@ namespace winrt::TerminalApp::implementation void TerminalPage::ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey) { - auto title = _resourceLoader->GetLocalizedString(titleKey); - auto message = _resourceLoader->GetLocalizedString(contentKey); - auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); + auto title = GetLibraryResourceString(titleKey); + auto message = GetLibraryResourceString(contentKey); + auto buttonText = RS_(L"Ok"); WUX::Controls::ContentDialog dialog; dialog.Title(winrt::box_value(title)); @@ -120,14 +118,14 @@ namespace winrt::TerminalApp::implementation // Notes link. void TerminalPage::_ShowAboutDialog() { - const auto title = _resourceLoader->GetLocalizedString(L"AboutTitleText"); - const auto versionLabel = _resourceLoader->GetLocalizedString(L"VersionLabelText"); - const auto gettingStartedLabel = _resourceLoader->GetLocalizedString(L"GettingStartedLabelText"); - const auto documentationLabel = _resourceLoader->GetLocalizedString(L"DocumentationLabelText"); - const auto releaseNotesLabel = _resourceLoader->GetLocalizedString(L"ReleaseNotesLabelText"); - const auto gettingStartedUriValue = _resourceLoader->GetLocalizedString(L"GettingStartedUriValue"); - const auto documentationUriValue = _resourceLoader->GetLocalizedString(L"DocumentationUriValue"); - const auto releaseNotesUriValue = _resourceLoader->GetLocalizedString(L"ReleaseNotesUriValue"); + const auto title = RS_(L"AboutTitleText"); + const auto versionLabel = RS_(L"VersionLabelText"); + const auto gettingStartedLabel = RS_(L"GettingStartedLabelText"); + const auto documentationLabel = RS_(L"DocumentationLabelText"); + const auto releaseNotesLabel = RS_(L"ReleaseNotesLabelText"); + const auto gettingStartedUriValue = RS_(L"GettingStartedUriValue"); + const auto documentationUriValue = RS_(L"DocumentationUriValue"); + const auto releaseNotesUriValue = RS_(L"ReleaseNotesUriValue"); const auto package = winrt::Windows::ApplicationModel::Package::Current(); const auto packageName = package.DisplayName(); const auto version = package.Id().Version(); @@ -171,7 +169,7 @@ namespace winrt::TerminalApp::implementation winrt::hstring aboutText{ aboutTextStream.str() }; about.Text(aboutText); - const auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); + const auto buttonText = RS_(L"Ok"); WUX::Controls::TextBlock aboutTextBlock; aboutTextBlock.Inlines().Append(about); @@ -197,9 +195,9 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details void TerminalPage::_ShowCloseWarningDialog() { - auto title = _resourceLoader->GetLocalizedString(L"CloseWindowWarningTitle"); - auto primaryButtonText = _resourceLoader->GetLocalizedString(L"CloseAll"); - auto secondaryButtonText = _resourceLoader->GetLocalizedString(L"Cancel"); + auto title = RS_(L"CloseWindowWarningTitle"); + auto primaryButtonText = RS_(L"CloseAll"); + auto secondaryButtonText = RS_(L"Cancel"); WUX::Controls::ContentDialog dialog; dialog.Title(winrt::box_value(title)); @@ -279,7 +277,7 @@ namespace winrt::TerminalApp::implementation { // Create the settings button. auto settingsItem = WUX::Controls::MenuFlyoutItem{}; - settingsItem.Text(_resourceLoader->GetLocalizedString(L"SettingsMenuItem")); + settingsItem.Text(RS_(L"SettingsMenuItem")); WUX::Controls::SymbolIcon ico{}; ico.Symbol(WUX::Controls::Symbol::Setting); @@ -296,7 +294,7 @@ namespace winrt::TerminalApp::implementation // Create the feedback button. auto feedbackFlyout = WUX::Controls::MenuFlyoutItem{}; - feedbackFlyout.Text(_resourceLoader->GetLocalizedString(L"FeedbackMenuItem")); + feedbackFlyout.Text(RS_(L"FeedbackMenuItem")); WUX::Controls::FontIcon feedbackIcon{}; feedbackIcon.Glyph(L"\xE939"); @@ -308,7 +306,7 @@ namespace winrt::TerminalApp::implementation // Create the about button. auto aboutFlyout = WUX::Controls::MenuFlyoutItem{}; - aboutFlyout.Text(_resourceLoader->GetLocalizedString(L"AboutMenuItem")); + aboutFlyout.Text(RS_(L"AboutMenuItem")); WUX::Controls::SymbolIcon aboutIcon{}; aboutIcon.Symbol(WUX::Controls::Symbol::Help); @@ -509,7 +507,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_FeedbackButtonOnClick(const IInspectable&, const RoutedEventArgs&) { - const auto feedbackUriValue = _resourceLoader->GetLocalizedString(L"FeedbackUriValue"); + const auto feedbackUriValue = RS_(L"FeedbackUriValue"); winrt::Windows::System::Launcher::LaunchUriAsync({ feedbackUriValue }); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index dafec743f..04d08a25d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -9,7 +9,6 @@ #include "Tab.h" #include "CascadiaSettings.h" #include "Profile.h" -#include "ScopedResourceLoader.h" #include #include @@ -24,8 +23,6 @@ namespace winrt::TerminalApp::implementation public: TerminalPage(); - TerminalPage(std::shared_ptr resourceLoader); - void SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI); void Create(); @@ -60,8 +57,6 @@ namespace winrt::TerminalApp::implementation std::vector> _tabs; - std::shared_ptr _resourceLoader{ nullptr }; - void _ShowAboutDialog(); void _ShowCloseWarningDialog(); diff --git a/src/cascadia/TerminalApp/init.cpp b/src/cascadia/TerminalApp/init.cpp index d6b7eb06a..5f79877f3 100644 --- a/src/cascadia/TerminalApp/init.cpp +++ b/src/cascadia/TerminalApp/init.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT license. #include "pch.h" +#include // Note: Generate GUID using TlgGuid.exe tool TRACELOGGING_DEFINE_PROVIDER( @@ -29,3 +30,5 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) return TRUE; } + +UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"TerminalApp/Resources") diff --git a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj index d86234966..d689c7c6c 100644 --- a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj @@ -114,7 +114,6 @@ - @@ -184,6 +183,11 @@ --> + + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} + false + + diff --git a/src/cascadia/TerminalApp/lib/pch.h b/src/cascadia/TerminalApp/lib/pch.h index eb90d80a5..11348a47b 100644 --- a/src/cascadia/TerminalApp/lib/pch.h +++ b/src/cascadia/TerminalApp/lib/pch.h @@ -24,7 +24,6 @@ #include -#include #include #include #include diff --git a/src/cascadia/WinRTUtils/LibraryResources.cpp b/src/cascadia/WinRTUtils/LibraryResources.cpp new file mode 100644 index 000000000..dee269bd7 --- /dev/null +++ b/src/cascadia/WinRTUtils/LibraryResources.cpp @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "ScopedResourceLoader.h" +#include "LibraryResources.h" + +/* +CHECKED RESOURCES +This is the support infrastructure for "checked resources", a system that lets +us immediately failfast (on launch) when a library makes a static reference to +a resource that doesn't exist at runtime. + +Resource checking relies on diligent use of USES_RESOURCE() and RS_() (which +uses USES_RESOURCE), but can make sure we don't ship something that'll blow up +at runtime. + +It works like this: +** IN DEBUG MODE ** +- All resource names referenced through USES_RESOURCE() are emitted alongside + their referencing filenames and line numbers into a static section of the + binary. + That section is named .util$res$m. + +- We emit two sentinel values into two different sections, .util$res$a and + .util$res$z. + +- The linker sorts all sections alphabetically before crushing them together + into the final binary. + +- When we first construct our library's scoped resource loader, we iterate over + every resource reference between $a and $z and check residency. + +** IN RELEASE MODE ** +- All checked resource code is compiled out. +*/ + +extern const wchar_t* g_WinRTUtilsLibraryResourceScope; + +#ifdef _DEBUG +#pragma detect_mismatch("winrt_utils_debug", "1") + +#pragma section(".util$res$a", read) +#pragma section(".util$res$z", read) + +__declspec(allocate(".util$res$a")) static const ::Microsoft::Console::Utils::StaticResource* debugResFirst{ nullptr }; +__declspec(allocate(".util$res$z")) static const ::Microsoft::Console::Utils::StaticResource* debugResLast{ nullptr }; + +static void EnsureAllResourcesArePresent(const ScopedResourceLoader& loader) +{ + for (auto resp = &debugResFirst; resp != &debugResLast; ++resp) + { + if (*resp) + { + const auto& res = **resp; + if (!loader.HasResourceWithName(res.resourceKey)) + { + auto filename = wcsrchr(res.filename, L'\\'); + if (!filename) + { + filename = res.filename; + } + else + { + filename++; // skip the '\' + } + + FAIL_FAST_MSG("Resource %ls not found in scope %ls (%ls:%u)", res.resourceKey, g_WinRTUtilsLibraryResourceScope, filename, res.line); + } + } + } +} +#else // _DEBUG + +#pragma detect_mismatch("winrt_utils_debug", "0") + +#endif + +static ScopedResourceLoader GetLibraryResourceLoader() UTILS_NONDEBUG_NOEXCEPT +{ + ScopedResourceLoader loader{ g_WinRTUtilsLibraryResourceScope }; +#ifdef _DEBUG + EnsureAllResourcesArePresent(loader); +#endif + return loader; +} + +winrt::hstring GetLibraryResourceString(const std::wstring_view key) UTILS_NONDEBUG_NOEXCEPT +{ + static auto loader{ GetLibraryResourceLoader() }; + return loader.GetLocalizedString(key); +} diff --git a/src/cascadia/TerminalApp/ScopedResourceLoader.cpp b/src/cascadia/WinRTUtils/ScopedResourceLoader.cpp similarity index 70% rename from src/cascadia/TerminalApp/ScopedResourceLoader.cpp rename to src/cascadia/WinRTUtils/ScopedResourceLoader.cpp index 4212984ba..4981e7fc6 100644 --- a/src/cascadia/TerminalApp/ScopedResourceLoader.cpp +++ b/src/cascadia/WinRTUtils/ScopedResourceLoader.cpp @@ -17,7 +17,7 @@ ScopedResourceLoader::ScopedResourceLoader(const std::wstring_view resourceLocat // - Gets the resource map associated with the scoped resource subcompartment. // Return Value: // - the resource map associated with the scoped resource subcompartment. -ResourceMap ScopedResourceLoader::GetResourceMap() +ResourceMap ScopedResourceLoader::GetResourceMap() const { return _resourceMap; } @@ -31,7 +31,18 @@ ResourceMap ScopedResourceLoader::GetResourceMap() // - resourceName: the key up by which to look the resource // Return Value: // - The final localized string for the given key. -winrt::hstring ScopedResourceLoader::GetLocalizedString(const std::wstring_view resourceName) +winrt::hstring ScopedResourceLoader::GetLocalizedString(const std::wstring_view resourceName) const { return _resourceMap.GetValue(resourceName, _resourceContext).ValueAsString(); } + +// Method Description: +// - Returns whether this resource loader can find a resource with the given key. +// Arguments: +// - resourceName: the key up by which to look the resource +// Return Value: +// - A boolean indicating whether the resource was found +bool ScopedResourceLoader::HasResourceWithName(const std::wstring_view resourceName) const +{ + return _resourceMap.HasKey(resourceName); +} diff --git a/src/cascadia/TerminalApp/ScopedResourceLoader.h b/src/cascadia/WinRTUtils/ScopedResourceLoader.h similarity index 78% rename from src/cascadia/TerminalApp/ScopedResourceLoader.h rename to src/cascadia/WinRTUtils/ScopedResourceLoader.h index 1d6dbf75e..b92fbf4ff 100644 --- a/src/cascadia/TerminalApp/ScopedResourceLoader.h +++ b/src/cascadia/WinRTUtils/ScopedResourceLoader.h @@ -7,8 +7,9 @@ class ScopedResourceLoader { public: ScopedResourceLoader(const std::wstring_view resourceLocatorBase); - winrt::Windows::ApplicationModel::Resources::Core::ResourceMap GetResourceMap(); - winrt::hstring GetLocalizedString(const std::wstring_view resourceName); + winrt::Windows::ApplicationModel::Resources::Core::ResourceMap GetResourceMap() const; + winrt::hstring GetLocalizedString(const std::wstring_view resourceName) const; + bool HasResourceWithName(const std::wstring_view resourceName) const; private: winrt::Windows::ApplicationModel::Resources::Core::ResourceMap _resourceMap; diff --git a/src/cascadia/WinRTUtils/WinRTUtils.vcxproj b/src/cascadia/WinRTUtils/WinRTUtils.vcxproj new file mode 100644 index 000000000..ab4078c28 --- /dev/null +++ b/src/cascadia/WinRTUtils/WinRTUtils.vcxproj @@ -0,0 +1,71 @@ + + + + + + + + StaticLibrary + Console + true + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + pch.h + $(MSBuildThisFileDirectory)\inc;%(AdditionalIncludeDirectories) + + + + + + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} + Win32Proj + WinRTUtils + WinRTUtils + WinRTUtils + + + + + true + + + + diff --git a/src/cascadia/WinRTUtils/inc/LibraryResources.h b/src/cascadia/WinRTUtils/inc/LibraryResources.h new file mode 100644 index 000000000..a6aadaea7 --- /dev/null +++ b/src/cascadia/WinRTUtils/inc/LibraryResources.h @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +/* +USING RESOURCES +To use PRI resources that are included alongside your library: +- In one file, use + UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"ResourceScope"). The + provided scope will be used as the basename for all library + resource lookups with RS_() or GetLibraryLocalizedString(). +- Use RS_(L"ResourceName") for all statically-known resource + names. +- Use GetLibraryResourceString(string) for all resource lookups + for keys known only at runtime. +- For any static resource lookups that are deferred through + another function call, use USES_RESOURCE(L"Key") to ensure the + key is tracked. +*/ + +#ifdef _DEBUG + +/* +The definitions in this section exist to support checked resources. +Check out the comment in LibraryResources.cpp to learn more. +*/ + +// Don't let non-debug and debug builds live together. +#pragma detect_mismatch("winrt_utils_debug", "1") + +#pragma section(".util$res$m", read) + +namespace Microsoft::Console::Utils +{ + struct StaticResource + { + const wchar_t* resourceKey; + const wchar_t* filename; + unsigned int line; + }; +} + +#define USES_RESOURCE(x) ([]() { \ + static const ::Microsoft::Console::Utils::StaticResource res{ \ + (x), __FILEW__, __LINE__ \ + }; \ + __declspec(allocate(".util$res$m")) static auto pRes{ &res }; \ + return pRes->resourceKey; \ +}()) +#define RS_(x) GetLibraryResourceString(USES_RESOURCE(x)) +#define UTILS_NONDEBUG_NOEXCEPT + +#else // _DEBUG + +#pragma detect_mismatch("winrt_utils_debug", "0") + +#define USES_RESOURCE(x) (x) +#define RS_(x) GetLibraryResourceString((x)) +#define UTILS_NONDEBUG_NOEXCEPT noexcept + +#endif + +#define UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(x) \ + __declspec(selectany) extern const wchar_t* g_WinRTUtilsLibraryResourceScope{ (x) }; + +winrt::hstring GetLibraryResourceString(const std::wstring_view key) UTILS_NONDEBUG_NOEXCEPT; diff --git a/src/cascadia/WinRTUtils/packages.config b/src/cascadia/WinRTUtils/packages.config new file mode 100644 index 000000000..e345a6ccd --- /dev/null +++ b/src/cascadia/WinRTUtils/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/src/cascadia/WinRTUtils/pch.cpp b/src/cascadia/WinRTUtils/pch.cpp new file mode 100644 index 000000000..3c27d44d5 --- /dev/null +++ b/src/cascadia/WinRTUtils/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/src/cascadia/WinRTUtils/pch.h b/src/cascadia/WinRTUtils/pch.h new file mode 100644 index 000000000..3f0eb83a4 --- /dev/null +++ b/src/cascadia/WinRTUtils/pch.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// pch.h +// Header for platform projection include files +// + +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#include + +#include + +#include + +#include +#include +#include +#include diff --git a/src/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props index 0b839063f..02ef97381 100644 --- a/src/cppwinrt.build.pre.props +++ b/src/cppwinrt.build.pre.props @@ -126,7 +126,7 @@ - $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories) + $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\src\cascadia\WinRTUtils\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories) WindowsApp.lib;%(AdditionalDependencies)