From 3d60ccdc317b048c21eaaa3491fb29991618fd02 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Wed, 28 Aug 2019 14:19:41 -0700 Subject: [PATCH] [UWP] Use platform character conversion functions instead of codecvt (#3400) * [UWP] Use platform character conversion functions instead of codecvt Fixes #3398 * Use [w]string_view instead of [w]string * Move HRESULT-returning methods to noexcept --- .../Renderer/lib/AdaptiveOpenUrlAction.cpp | 7 +- source/uwp/Renderer/lib/DateTimeParser.h | 1 - source/uwp/Renderer/lib/Util.cpp | 101 ++++++++++++------ source/uwp/Renderer/lib/Util.h | 23 ++-- .../Visualizer/ViewModel/DocumentViewModel.cs | 64 +++++------ 5 files changed, 115 insertions(+), 81 deletions(-) diff --git a/source/uwp/Renderer/lib/AdaptiveOpenUrlAction.cpp b/source/uwp/Renderer/lib/AdaptiveOpenUrlAction.cpp index 971b5ef601..a64b810787 100644 --- a/source/uwp/Renderer/lib/AdaptiveOpenUrlAction.cpp +++ b/source/uwp/Renderer/lib/AdaptiveOpenUrlAction.cpp @@ -28,11 +28,12 @@ namespace AdaptiveNamespace ComPtr uriActivationFactory; RETURN_IF_FAILED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriActivationFactory)); - std::wstring imageUri = StringToWstring(sharedOpenUrlAction->GetUrl()); + HString asHstring; + RETURN_IF_FAILED(UTF8ToHString(sharedOpenUrlAction->GetUrl(), asHstring.GetAddressOf())); - if (!imageUri.empty()) + if (asHstring.IsValid()) { - RETURN_IF_FAILED(uriActivationFactory->CreateUri(HStringReference(imageUri.c_str()).Get(), m_url.GetAddressOf())); + RETURN_IF_FAILED(uriActivationFactory->CreateUri(asHstring.Get(), m_url.GetAddressOf())); } InitializeBaseElement(std::static_pointer_cast(sharedOpenUrlAction)); diff --git a/source/uwp/Renderer/lib/DateTimeParser.h b/source/uwp/Renderer/lib/DateTimeParser.h index c302dc4760..846a621a9d 100644 --- a/source/uwp/Renderer/lib/DateTimeParser.h +++ b/source/uwp/Renderer/lib/DateTimeParser.h @@ -3,7 +3,6 @@ #pragma once #include "DateTimePreparser.h" -#include #include namespace AdaptiveNamespace diff --git a/source/uwp/Renderer/lib/Util.cpp b/source/uwp/Renderer/lib/Util.cpp index 05d32ad50b..84be14a392 100644 --- a/source/uwp/Renderer/lib/Util.cpp +++ b/source/uwp/Renderer/lib/Util.cpp @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" -#include -#include #include #include @@ -54,42 +52,91 @@ using namespace AdaptiveNamespace; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; -HRESULT WStringToHString(const std::wstring& in, _Outptr_ HSTRING* out) +HRESULT WStringToHString(const std::wstring_view& in, _Outptr_ HSTRING* out) noexcept try { if (out == nullptr) { return E_INVALIDARG; } - return WindowsCreateString(in.c_str(), static_cast(in.length()), out); + else if (in.empty()) + { + return WindowsCreateString(L"", 0, out); + } + else + { + return WindowsCreateString(&in[0], static_cast(in.length()), out); + } } +CATCH_RETURN; -HRESULT UTF8ToHString(const std::string& in, _Outptr_ HSTRING* out) +std::string WstringToString(const std::wstring_view& in) { - if (out == nullptr) + if (!in.empty()) { - return E_INVALIDARG; + const size_t requiredSize = + WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, &in[0], (int)in.length(), nullptr, 0, nullptr, nullptr); + std::string converted(requiredSize, 0); + + if (WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, &in[0], (int)in.length(), &converted[0], (int)requiredSize, nullptr, nullptr) == 0) + { + throw bad_string_conversion(); + } + return converted; } - std::wstring_convert> converter; - std::wstring wide = converter.from_bytes(in); - return WindowsCreateString(wide.c_str(), static_cast(wide.length()), out); + return ""; } -HRESULT HStringToUTF8(const HSTRING& in, std::string& out) +std::wstring StringToWstring(const std::string_view& in) { - if (in == nullptr) + if (!in.empty()) + { + // TODO: safer casts + const size_t requiredSize = + MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, &in[0], (int)in.length(), (LPWSTR) nullptr, 0); + std::wstring wide(requiredSize, 0); + + if (MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, &in[0], (int)in.length(), &wide[0], (int)requiredSize) == 0) + { + throw bad_string_conversion(); + } + + return wide; + } + return L""; +} + +HRESULT UTF8ToHString(const std::string_view& in, _Outptr_ HSTRING* out) noexcept try +{ + if (out == nullptr) { return E_INVALIDARG; } - std::wstring_convert> converter; - out = converter.to_bytes(WindowsGetStringRawBuffer(in, nullptr)); + else + { + std::wstring wide = StringToWstring(in); + return WindowsCreateString(wide.c_str(), static_cast(wide.length()), out); + } +} +CATCH_RETURN; + +HRESULT HStringToUTF8(const HSTRING& in, std::string& out) noexcept try +{ + out = WstringToString(WindowsGetStringRawBuffer(in, nullptr)); return S_OK; } +CATCH_RETURN; std::string HStringToUTF8(const HSTRING& in) { std::string typeAsKey; - HRESULT hr = HStringToUTF8(in, typeAsKey); - return FAILED(hr) ? "" : typeAsKey; + if (SUCCEEDED(HStringToUTF8(in, typeAsKey))) + { + return typeAsKey; + } + else + { + return ""; + } } template @@ -1207,8 +1254,9 @@ CATCH_RETURN; HRESULT StringToJsonObject(const std::string& inputString, _COM_Outptr_ IJsonObject** result) { - std::wstring asWstring = StringToWstring(inputString); - return HStringToJsonObject(HStringReference(asWstring.c_str()).Get(), result); + HString asHstring; + RETURN_IF_FAILED(UTF8ToHString(inputString, asHstring.GetAddressOf())); + return HStringToJsonObject(asHstring.Get(), result); } HRESULT HStringToJsonObject(const HSTRING& inputHString, _COM_Outptr_ IJsonObject** result) @@ -1246,8 +1294,9 @@ HRESULT JsonObjectToHString(_In_ IJsonObject* inputJson, _Outptr_ HSTRING* resul HRESULT StringToJsonValue(const std::string inputString, _COM_Outptr_ IJsonValue** result) { - std::wstring asWstring = StringToWstring(inputString); - return HStringToJsonValue(HStringReference(asWstring.c_str()).Get(), result); + HString asHstring; + RETURN_IF_FAILED(UTF8ToHString(inputString, asHstring.GetAddressOf())); + return HStringToJsonValue(asHstring.Get(), result); } HRESULT HStringToJsonValue(const HSTRING& inputHString, _COM_Outptr_ IJsonValue** result) @@ -1327,18 +1376,6 @@ HRESULT IsBackgroundImageValid(_In_ ABI::AdaptiveNamespace::IAdaptiveBackgroundI return S_OK; } -std::wstring StringToWstring(const std::string& in) -{ - std::wstring_convert, wchar_t> utfConverter; - return utfConverter.from_bytes(in); -} - -std::string WstringToString(const std::wstring& input) -{ - std::wstring_convert, wchar_t> utfConverter; - return utfConverter.to_bytes(input); -} - void RemoteResourceElementToRemoteResourceInformationVector(_In_ ABI::AdaptiveNamespace::IAdaptiveElementWithRemoteResources* remoteResourceElement, std::vector& resourceUris) { diff --git a/source/uwp/Renderer/lib/Util.h b/source/uwp/Renderer/lib/Util.h index 680682d915..48f460fec2 100644 --- a/source/uwp/Renderer/lib/Util.h +++ b/source/uwp/Renderer/lib/Util.h @@ -25,24 +25,33 @@ using namespace InternalNamespace; #endif -HRESULT WStringToHString(const std::wstring& in, _Outptr_ HSTRING* out); +class bad_string_conversion : public std::exception +{ +public: + bad_string_conversion() : _dwErr(GetLastError()) {} + +private: + DWORD _dwErr; +}; + +HRESULT WStringToHString(const std::wstring_view& in, _Outptr_ HSTRING* out) noexcept; -std::string WstringToString(const std::wstring& in); -std::wstring StringToWstring(const std::string& in); +std::string WstringToString(const std::wstring_view& in); +std::wstring StringToWstring(const std::string_view& in); // This function is needed to deal with the fact that non-windows platforms handle Unicode without the need for wchar_t. // (which has a platform specific implementation) It converts a std::string to an HSTRING. -HRESULT UTF8ToHString(const std::string& in, _Outptr_ HSTRING* out); +HRESULT UTF8ToHString(const std::string_view& in, _Outptr_ HSTRING* out) noexcept; // This function is needed to deal with the fact that non-windows platforms handle Unicode without the need for wchar_t. // (which has a platform specific implementation) It converts from HSTRING to a standard std::string. -HRESULT HStringToUTF8(const HSTRING& in, std::string& out); +HRESULT HStringToUTF8(const HSTRING& in, std::string& out) noexcept; std::string HStringToUTF8(const HSTRING& in); -inline bool Boolify(const boolean value) +inline bool Boolify(const boolean value) noexcept { - return value > 0 ? true : false; + return (value > 0); } HRESULT GetColorFromString(const std::string& colorString, _Out_ ABI::Windows::UI::Color* color) noexcept; diff --git a/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs b/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs index bf2f78b591..cb92ced85f 100644 --- a/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs +++ b/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs @@ -221,50 +221,38 @@ public string SerializeActionEventArgsToString(AdaptiveActionEventArgs args) public static void InitializeRenderer(AdaptiveHostConfig hostConfig) { - try + _renderer = new AdaptiveCardRenderer(); + if (hostConfig != null) { - _renderer = new AdaptiveCardRenderer(); - if (hostConfig != null) - { - _renderer.HostConfig = hostConfig; - } - - // Add a feature representing this version of the visualizer. used for test cards. - _renderer.FeatureRegistration.Set("acTest", "1.0"); + _renderer.HostConfig = hostConfig; + } - if (Settings.UseFixedDimensions) - { - _renderer.SetFixedDimensions(320, 180); - } + // Add a feature representing this version of the visualizer. used for test cards. + _renderer.FeatureRegistration.Set("acTest", "1.0"); - // Custom resource resolvers - _renderer.ResourceResolvers.Set("symbol", new MySymbolResourceResolver()); + if (Settings.UseFixedDimensions) + { + _renderer.SetFixedDimensions(320, 180); + } - /* - *Example on how to override the Action Positive and Destructive styles - Style positiveStyle = new Style(typeof(Button)); - positiveStyle.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.LawnGreen))); - Style destructiveStyle = new Style(typeof(Button)); - destructiveStyle.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.Red))); - Style otherStyle = new Style(typeof(Button)); - otherStyle.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.Yellow))); - otherStyle.Setters.Add(new Setter(Button.ForegroundProperty, new SolidColorBrush(Windows.UI.Colors.DarkRed))); + // Custom resource resolvers + _renderer.ResourceResolvers.Set("symbol", new MySymbolResourceResolver()); - _renderer.OverrideStyles = new ResourceDictionary(); - _renderer.OverrideStyles.Add("Adaptive.Action.Positive", positiveStyle); - _renderer.OverrideStyles.Add("Adaptive.Action.Destructive", destructiveStyle); - _renderer.OverrideStyles.Add("Adaptive.Action.other", otherStyle); - */ + /* + *Example on how to override the Action Positive and Destructive styles + Style positiveStyle = new Style(typeof(Button)); + positiveStyle.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.LawnGreen))); + Style destructiveStyle = new Style(typeof(Button)); + destructiveStyle.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.Red))); + Style otherStyle = new Style(typeof(Button)); + otherStyle.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.Yellow))); + otherStyle.Setters.Add(new Setter(Button.ForegroundProperty, new SolidColorBrush(Windows.UI.Colors.DarkRed))); - } - catch - { - if (Debugger.IsAttached) - { - Debugger.Break(); - } - throw; - } + _renderer.OverrideStyles = new ResourceDictionary(); + _renderer.OverrideStyles.Add("Adaptive.Action.Positive", positiveStyle); + _renderer.OverrideStyles.Add("Adaptive.Action.Destructive", destructiveStyle); + _renderer.OverrideStyles.Add("Adaptive.Action.other", otherStyle); + */ } } }