diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/TextCompositionManager.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/TextCompositionManager.cs index 5dd95d987c7..a23e839010a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/TextCompositionManager.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/TextCompositionManager.cs @@ -1,23 +1,34 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Text; using System.Windows.Threading; -using System.Runtime.InteropServices; -using MS.Win32; +using System.ComponentModel; using Microsoft.Win32; +using MS.Win32; namespace System.Windows.Input { - // - // Modes of AltNumpad. - // + /// + /// Modes when entering characters via Alt+Numpad keys. + /// internal enum AltNumpadConversionMode { - DefaultCodePage, // ACP code page encoding. Alt+Numpad0+NumpadX - OEMCodePage, // OEM code page encoding. Alt+NumpadX - HexDefaultCodePage, // HEX value in ACP. Alt+NumpadDOT+NumpadX - HexUnicode, // HEX value in Unicode. Alt+NumpadPlus+NumpadX + /// + /// ACP code page encoding: Alt+Numpad0+NumpadX + /// + DefaultCodePage, + /// + /// OEM code page encoding: Alt+NumpadX + /// + OEMCodePage, + /// + /// HEX value in ACP: Alt+NumpadDOT+NumpadX + /// + HexDefaultCodePage, + /// + /// HEX value in Unicode: Alt+NumpadPlus+NumpadX + /// + HexUnicode, } /// @@ -374,31 +385,33 @@ private static bool UnsafeCompleteComposition(TextComposition composition) private static string GetCurrentOEMCPEncoding(int code) { - int cp = UnsafeNativeMethods.GetOEMCP(); - return CharacterEncoding(cp, code); - } - - // Convert code to the string based on the code page. - private static string CharacterEncoding(int cp, int code) - { - Byte[] bytes = ConvertCodeToByteArray(code); - StringBuilder sbuilder = new StringBuilder(EncodingBufferLen); + int codePage = UnsafeNativeMethods.GetOEMCP(); - // Win32K uses MB_PRECOMPOSED | MB_USEGLYPHCHARS. - int nret = UnsafeNativeMethods.MultiByteToWideChar(cp, - UnsafeNativeMethods.MB_PRECOMPOSED | UnsafeNativeMethods.MB_USEGLYPHCHARS, - bytes, bytes.Length, - sbuilder, EncodingBufferLen); + return CharacterEncoding(codePage, code); + } - if (nret == 0) - { - int win32Err = Marshal.GetLastWin32Error(); - throw new System.ComponentModel.Win32Exception(win32Err); + /// + /// Convert to a string based on the . + /// + private static unsafe string CharacterEncoding(int codePage, int code) + { + ReadOnlySpan multiByte = ConvertCodeToByteArray(code, stackalloc byte[2]); + Span outputChars = stackalloc char[EncodingBufferLen]; // 4 + + int charsWritten; + // Since we do not use [LibraryImport], Span marshallers are not available by default + fixed (byte* ptrMultiByte = multiByte) + fixed (char* ptrOutputChars = outputChars) + { // Win32K uses MB_PRECOMPOSED | MB_USEGLYPHCHARS. + charsWritten = UnsafeNativeMethods.MultiByteToWideChar(codePage, UnsafeNativeMethods.MB_PRECOMPOSED | UnsafeNativeMethods.MB_USEGLYPHCHARS, + ptrMultiByte, multiByte.Length, ptrOutputChars, outputChars.Length); } - // set the length as MultiByteToWideChar returns. - sbuilder.Length = nret; - return sbuilder.ToString(); + if (charsWritten == 0) + throw new Win32Exception(); // Initializes with Marshal.GetLastPInvokeError() + + // Set the length as MultiByteToWideChar returns + return new string(outputChars.Slice(0, charsWritten)); } // PreProcessInput event handler @@ -470,40 +483,37 @@ private void PreProcessInput(object sender, PreProcessInputEventArgs e) private void PostProcessInput(object sender, ProcessInputEventArgs e) { // KeyUp - if(e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent) + if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent) { - KeyEventArgs keyArgs = (KeyEventArgs) e.StagingItem.Input; - if(!keyArgs.Handled) + KeyEventArgs keyArgs = (KeyEventArgs)e.StagingItem.Input; + if (!keyArgs.Handled) { - if(keyArgs.RealKey == Key.LeftAlt || keyArgs.RealKey == Key.RightAlt) + if (keyArgs.RealKey is Key.LeftAlt or Key.RightAlt) { // Make sure both Alt keys are up. ModifierKeys modifiers = keyArgs.KeyboardDevice.Modifiers; - if((modifiers & ModifierKeys.Alt) == 0) + if ((modifiers & ModifierKeys.Alt) == 0) { - if(_altNumpadEntryMode) + if (_altNumpadEntryMode) { _altNumpadEntryMode = false; // Generate the Unicode equivalent if we // actually entered a number via the numpad. - if(_altNumpadEntry != 0) + if (_altNumpadEntry != 0) { _altNumpadcomposition.ClearTexts(); - if (_altNumpadConversionMode == AltNumpadConversionMode.OEMCodePage) + if (_altNumpadConversionMode is AltNumpadConversionMode.OEMCodePage) { _altNumpadcomposition.SetText(GetCurrentOEMCPEncoding(_altNumpadEntry)); } - else if ((_altNumpadConversionMode == AltNumpadConversionMode.DefaultCodePage) || - (_altNumpadConversionMode == AltNumpadConversionMode.HexDefaultCodePage)) + else if (_altNumpadConversionMode is AltNumpadConversionMode.DefaultCodePage or AltNumpadConversionMode.HexDefaultCodePage) { _altNumpadcomposition.SetText(CharacterEncoding(InputLanguageManager.Current.CurrentInputLanguage.TextInfo.ANSICodePage, _altNumpadEntry)); } - else if (_altNumpadConversionMode == AltNumpadConversionMode.HexUnicode) + else if (_altNumpadConversionMode is AltNumpadConversionMode.HexUnicode) { - Char[] chars = new Char[1]; - chars[0] = (Char) _altNumpadEntry; - _altNumpadcomposition.SetText(new string(chars)); + _altNumpadcomposition.SetText(((char)_altNumpadEntry).ToString()); } } } @@ -839,22 +849,25 @@ private int GetNewEntry(Key key, int scanCode) return NumpadScanCode.DigitFromScanCode(scanCode); } - // Convert the code to byte array for DBCS/SBCS. - private static Byte[] ConvertCodeToByteArray(int codeEntry) + /// + /// Convert the code to byte array for DBCS/SBCS. + /// + private static ReadOnlySpan ConvertCodeToByteArray(int codeEntry, Span destination) { - Byte[] bytes; - if (codeEntry > 0xff) + Debug.Assert(destination.Length == 2, "Invalid buffer length"); + + if (codeEntry > 0xFF) { - bytes = new Byte[2]; - bytes[0] = (Byte)(codeEntry >> 8); - bytes[1] = (Byte)codeEntry; + destination[0] = (byte)(codeEntry >> 8); + destination[1] = (byte)codeEntry; } else { - bytes = new Byte[1]; - bytes[0] = (Byte)codeEntry; + destination = destination.Slice(0, 1); + destination[0] = (byte)codeEntry; } - return bytes; + + return destination; } // clear the altnumpad composition object and reset entry. @@ -875,13 +888,7 @@ private void ClearAltnumpadComposition() // Return true if we're in hex conversion mode. private bool HexConversionMode { - get - { - if ((_altNumpadConversionMode == AltNumpadConversionMode.HexDefaultCodePage) || - (_altNumpadConversionMode == AltNumpadConversionMode.HexUnicode)) - return true; - return false; - } + get => _altNumpadConversionMode is AltNumpadConversionMode.HexDefaultCodePage or AltNumpadConversionMode.HexUnicode; } /// @@ -893,15 +900,11 @@ private static bool IsHexNumpadEnabled { if (!_isHexNumpadRegistryChecked) { - object obj; - RegistryKey key; - - key = Registry.CurrentUser.OpenSubKey("Control Panel\\Input Method"); + RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Input Method"); if (key != null) { - obj = key.GetValue("EnableHexNumpad"); - - if ((obj is string) && ((string)obj != "0")) + object obj = key.GetValue("EnableHexNumpad"); + if (obj is string value && value != "0") { _isHexNumpadEnabled = true; } @@ -909,6 +912,7 @@ private static bool IsHexNumpadEnabled _isHexNumpadRegistryChecked = true; } + return _isHexNumpadEnabled; } } @@ -951,23 +955,21 @@ private static bool IsHexNumpadEnabled // ScanCode of Numpad keys. internal static class NumpadScanCode { - internal static int DigitFromScanCode(int scanCode) - { - switch (scanCode) - { - case Numpad0: return 0; - case Numpad1: return 1; - case Numpad2: return 2; - case Numpad3: return 3; - case Numpad4: return 4; - case Numpad5: return 5; - case Numpad6: return 6; - case Numpad7: return 7; - case Numpad8: return 8; - case Numpad9: return 9; - } - return -1; - } + internal static int DigitFromScanCode(int scanCode) => scanCode switch + { + Numpad0 => 0, + Numpad1 => 1, + Numpad2 => 2, + Numpad3 => 3, + Numpad4 => 4, + Numpad5 => 5, + Numpad6 => 6, + Numpad7 => 7, + Numpad8 => 8, + Numpad9 => 9, + _ => -1, + }; + internal const int NumpadDot = 0x53; internal const int NumpadPlus = 0x4e; internal const int Numpad0 = 0x52; diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs index aa055401be8..febda4e81ed 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs @@ -213,7 +213,7 @@ internal enum ShellExecuteFlags public const int MB_USEGLYPHCHARS = 0x00000004; public const int MB_ERR_INVALID_CHARS = 0x00000008; [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Unicode, SetLastError=true)] - public static extern int MultiByteToWideChar(int CodePage, int dwFlags, byte[] lpMultiByteStr, int cchMultiByte, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpWideCharStr, int cchWideChar); + public static unsafe extern int MultiByteToWideChar(int CodePage, int dwFlags, byte* lpMultiByteStr, int cchMultiByte, char* lpWideCharStr, int cchWideChar); [DllImport(ExternDll.Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] public static extern int WideCharToMultiByte(int codePage, int flags, [MarshalAs(UnmanagedType.LPWStr)]string wideStr, int chars, [In,Out]byte[] pOutBytes, int bufferBytes, IntPtr defaultChar, IntPtr pDefaultUsed);