Introduce a WinRT utils library and "checked resources" (#3350)
This commit introduces a C++/WinRT utility library and moves ScopedResourceLoader into it. I decided to get uppity and introduce something I like to call "checked resources." The idea is that every resource reference from a library is knowable at compile time, and we should be able to statically ensure that all resources exist. This is 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. It exposes two new (preprocessor) APIs: * `RS_(wchar_t)`: loads a localizable string resource by name. * `USES_RESOURCE(wchar_t)`: marks a resource key as used, but is intended for loading images or passing static resource keys as parameters to functions that will look them up later. 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 a 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. Fixes #2146. Macros are the only way to do something this cool, incidentally. ## Validation Steps Performed Made references to a bunch of bad resources, tried to break it a lot. It looks like this when it fails: ### App.cpp ``` 36 static const std::array<std::wstring_view, 2> settingsLoadErrorsLabels { 37 USES_RESOURCE(L"NoProfilesText"), 38 USES_RESOURCE(L"AllProfilesHiddenText_HA_JUST_KIDDING") 39 }; ``` ``` WinRTUtils\LibraryResources.cpp(68)\TerminalApp.dll: FailFast(1) tid(1034) 8000FFFF Catastrophic failure Msg:[Resource AllProfilesHiddenText_HA_JUST_KIDDING not found in scope TerminalApp/Resources (App.cpp:38)] [EnsureAllResourcesArePresent] ```
This commit is contained in:
parent
a34c47a493
commit
15505337d8
|
@ -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}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "App.h"
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
#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<std::wstring_view, 3> settingsLoadWarningsLabels {
|
||||
L"MissingDefaultProfileText",
|
||||
L"DuplicateProfileText",
|
||||
L"UnknownColorSchemeText"
|
||||
USES_RESOURCE(L"MissingDefaultProfileText"),
|
||||
USES_RESOURCE(L"DuplicateProfileText"),
|
||||
USES_RESOURCE(L"UnknownColorSchemeText")
|
||||
};
|
||||
static const std::array<std::wstring_view, 2> settingsLoadErrorsLabels {
|
||||
L"NoProfilesText",
|
||||
L"AllProfilesHiddenText"
|
||||
USES_RESOURCE(L"NoProfilesText"),
|
||||
USES_RESOURCE(L"AllProfilesHiddenText")
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
@ -46,15 +48,14 @@ static const std::array<std::wstring_view, 2> 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<std::size_t N>
|
||||
static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_view, N> keys, ScopedResourceLoader& loader)
|
||||
static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_view, N> 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<std::wstring_vi
|
|||
// - The warning should have an entry in settingsLoadWarningsLabels.
|
||||
// Arguments:
|
||||
// - warning: the SettingsLoadWarnings 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 warning
|
||||
static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warning, ScopedResourceLoader& loader)
|
||||
static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warning)
|
||||
{
|
||||
return _GetMessageText(static_cast<uint32_t>(warning), settingsLoadWarningsLabels, loader);
|
||||
return _GetMessageText(static_cast<uint32_t>(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<uint32_t>(error), settingsLoadErrorsLabels, loader);
|
||||
return _GetMessageText(static_cast<uint32_t>(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<ScopedResourceLoader>(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<TerminalPage>(_resourceLoader);
|
||||
_root = winrt::make_self<TerminalPage>();
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
|
|
|
@ -58,8 +58,6 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
|
||||
|
||||
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
|
||||
|
||||
HRESULT _settingsLoadedResult;
|
||||
winrt::hstring _settingsLoadExceptionText{};
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "TerminalPage.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
#include "TerminalPage.g.cpp"
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
|
@ -32,14 +34,10 @@ namespace winrt
|
|||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalPage::TerminalPage() {}
|
||||
|
||||
TerminalPage::TerminalPage(std::shared_ptr<ScopedResourceLoader> 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 });
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "Tab.h"
|
||||
#include "CascadiaSettings.h"
|
||||
#include "Profile.h"
|
||||
#include "ScopedResourceLoader.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalControl.h>
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
|
@ -24,8 +23,6 @@ namespace winrt::TerminalApp::implementation
|
|||
public:
|
||||
TerminalPage();
|
||||
|
||||
TerminalPage(std::shared_ptr<ScopedResourceLoader> resourceLoader);
|
||||
|
||||
void SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI);
|
||||
|
||||
void Create();
|
||||
|
@ -60,8 +57,6 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
std::vector<std::shared_ptr<Tab>> _tabs;
|
||||
|
||||
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
|
||||
|
||||
void _ShowAboutDialog();
|
||||
void _ShowCloseWarningDialog();
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
// 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")
|
||||
|
|
|
@ -114,7 +114,6 @@
|
|||
<ClCompile Include="../JsonUtils.cpp" />
|
||||
<ClCompile Include="../Utils.cpp" />
|
||||
<ClCompile Include="../DefaultProfileUtils.cpp" />
|
||||
<ClCompile Include="../ScopedResourceLoader.cpp" />
|
||||
<ClCompile Include="../PowershellCoreProfileGenerator.cpp" />
|
||||
<ClCompile Include="../WslDistroGenerator.cpp" />
|
||||
<ClCompile Include="../AzureCloudShellGenerator.cpp" />
|
||||
|
@ -184,6 +183,11 @@
|
|||
-->
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WinRTUtils\WinRTUtils.vcxproj">
|
||||
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
|
||||
<!-- For whatever reason, we can't include the TerminalControl and
|
||||
TerminalSettings projects' winmds via project references. So we'll have to
|
||||
manually include the winmds as References below -->
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include <hstring.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/windows.ui.core.h>
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
|
||||
<!-- ========================= CppWinRT Metadata ======================== -->
|
||||
<PropertyGroup>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="inc\ScopedResourceLoader.h" />
|
||||
<ClInclude Include="inc\LibraryResources.h" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
|
||||
<ClCompile Include="ScopedResourceLoader.cpp" />
|
||||
<ClCompile Include="LibraryResources.cpp" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= idl Files ======================== -->
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Misc Files ======================== -->
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ====================== Compiler & Linker Flags ===================== -->
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<!-- ========================= Globals ======================== -->
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>WinRTUtils</RootNamespace>
|
||||
<ProjectName>WinRTUtils</ProjectName>
|
||||
<TargetName>WinRTUtils</TargetName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
DON'T REDIRECT OUR OUTPUT.
|
||||
Setting this will tell cppwinrt.build.post.props to copy our output from
|
||||
the default OutDir up one level, so the wapproj will be able to find it.
|
||||
-->
|
||||
<NoOutputRedirection>true</NoOutputRedirection>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
</Project>
|
|
@ -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;
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
</packages>
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "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 <LibraryIncludes.h>
|
||||
// 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 <wil/cppwinrt.h>
|
||||
|
||||
#include <unknwn.h>
|
||||
|
||||
#include <hstring.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.System.h>
|
|
@ -126,7 +126,7 @@
|
|||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\src\cascadia\WinRTUtils\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
Loading…
Reference in New Issue