Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Core / CSharp / System / Windows / Input / TextCompositionManager.cs / 2 / TextCompositionManager.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: the CompositionManager class // // History: // 11/18/2003 : yutakas created // //--------------------------------------------------------------------------- using System; using System.Diagnostics; using System.Globalization; using System.Security; using System.Security.Permissions; using System.Text; using System.Windows.Threading; using System.Windows; using System.Runtime.InteropServices; using MS.Win32; using Microsoft.Win32; // for RegistryKey class using MS.Internal ; using MS.Internal.PresentationCore; // SecurityHelper using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace System.Windows.Input { // // Modes of AltNumpad. // 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 } ////// the CompositionManager class provides input-text/composition event promotion /// public sealed class TextCompositionManager : DispatcherObject { //----------------------------------------------------- // // static RoutedEvent // //----------------------------------------------------- ////// Preview Composition Start /// public static readonly RoutedEvent PreviewTextInputStartEvent = EventManager.RegisterRoutedEvent("PreviewTextInputStart", RoutingStrategy.Tunnel, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the PreviewTextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddPreviewTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, PreviewTextInputStartEvent, handler); } ////// Removes a handler for the PreviewTextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemovePreviewTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, PreviewTextInputStartEvent, handler); } ////// Composition Start /// public static readonly RoutedEvent TextInputStartEvent = EventManager.RegisterRoutedEvent("TextInputStart", RoutingStrategy.Bubble, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the TextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, TextInputStartEvent, handler); } ////// Removes a handler for the TextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemoveTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, TextInputStartEvent, handler); } ////// Preview Composition Updated /// public static readonly RoutedEvent PreviewTextInputUpdateEvent = EventManager.RegisterRoutedEvent("PreviewTextInputUpdate", RoutingStrategy.Tunnel, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the PreviewTextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddPreviewTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, PreviewTextInputUpdateEvent, handler); } ////// Removes a handler for the PreviewTextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemovePreviewTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, PreviewTextInputUpdateEvent, handler); } ////// Composition Updated /// public static readonly RoutedEvent TextInputUpdateEvent = EventManager.RegisterRoutedEvent("TextInputUpdate", RoutingStrategy.Bubble, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the TextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, TextInputUpdateEvent, handler); } ////// Removes a handler for the TextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemoveTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, TextInputUpdateEvent, handler); } ////// Preview Composition End /// public static readonly RoutedEvent PreviewTextInputEvent = EventManager.RegisterRoutedEvent("PreviewTextInput", RoutingStrategy.Tunnel, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the PreviewTextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddPreviewTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, PreviewTextInputEvent, handler); } ////// Removes a handler for the PreviewTextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemovePreviewTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, PreviewTextInputEvent, handler); } ////// Composition End /// public static readonly RoutedEvent TextInputEvent = EventManager.RegisterRoutedEvent("TextInput", RoutingStrategy.Bubble, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the TextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, TextInputEvent, handler); } ////// Removes a handler for the TextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemoveTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, TextInputEvent, handler); } //------------------------------------------------------ // // Constructors // //----------------------------------------------------- #region Constructors ////// Critical - Calls a critical method - PreProcessInput /// TreatAsSafe - Ok for us to register an event handler. Handler itself is critical. /// [SecurityCritical, SecurityTreatAsSafe ] internal TextCompositionManager(InputManager inputManager) { _inputManager = inputManager; _inputManager.PreProcessInput += new PreProcessInputEventHandler(PreProcessInput); _inputManager.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); } #endregion Constructors //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ ////// Start the composition. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical calls UnsafeStartComposition. /// PublicOk: Linkdemand blocks external callers /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] public static bool StartComposition(TextComposition composition) { return UnsafeStartComposition(composition); } ////// Update the composition. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical calls UnsafeUpdataComposition. /// PublicOk: Linkdemand blocks external callers /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)] public static bool UpdateComposition(TextComposition composition) { return UnsafeUpdateComposition(composition); } ////// Complete the composition. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical calls UnsafeCompleteComposition. /// PublicOk: Linkdemand blocks external callers /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)] public static bool CompleteComposition(TextComposition composition) { return UnsafeCompleteComposition(composition); } //----------------------------------------------------- // // Public Properties // //------------------------------------------------------ //----------------------------------------------------- // // Public Events // //----------------------------------------------------- //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ //------------------------------------------------------ // // Internal Properties // //----------------------------------------------------- //------------------------------------------------------ // // Internal Events // //----------------------------------------------------- //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- ////// Critical - as this refers to _InputManager and calls Critical function /// InputManager.ProcessInput. This can be used to spoof text input. /// [SecurityCritical] private static bool UnsafeStartComposition(TextComposition composition) { if (composition == null) { throw new ArgumentNullException("composition"); } if (composition._InputManager == null) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_NoInputManager, "composition")); } if (composition.Stage != TextCompositionStage.None) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionHasStarted, "composition")); } composition.Stage = TextCompositionStage.Started; TextCompositionEventArgs textargs = new TextCompositionEventArgs(composition._InputDevice, composition); textargs.RoutedEvent=TextCompositionManager.PreviewTextInputStartEvent; textargs.Source= composition.Source; return composition._InputManager.ProcessInput(textargs); } ////// Critical - as this refers to _InputManager and calls Critical function /// InputManager.ProcessInput. This can be used to spoof text input. /// [SecurityCritical] private static bool UnsafeUpdateComposition(TextComposition composition) { if (composition == null) { throw new ArgumentNullException("composition"); } if (composition._InputManager == null) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_NoInputManager, "composition")); } if (composition.Stage == TextCompositionStage.None) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionNotStarted, "composition")); } if (composition.Stage == TextCompositionStage.Done) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionHasDone, "composition")); } TextCompositionEventArgs textargs = new TextCompositionEventArgs(composition._InputDevice, composition); textargs.RoutedEvent=TextCompositionManager.PreviewTextInputUpdateEvent; textargs.Source= composition.Source; return composition._InputManager.ProcessInput(textargs); } ////// Critical - as this accesses InputManager and calls Critical method ProcessInput. /// [SecurityCritical] private static bool UnsafeCompleteComposition(TextComposition composition) { if (composition == null) { throw new ArgumentNullException("composition"); } if (composition._InputManager == null) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_NoInputManager, "composition")); } if (composition.Stage == TextCompositionStage.None) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionNotStarted, "composition")); } if (composition.Stage == TextCompositionStage.Done) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionHasDone, "composition")); } composition.Stage = TextCompositionStage.Done; TextCompositionEventArgs textargs = new TextCompositionEventArgs(composition._InputDevice, composition); textargs.RoutedEvent=TextCompositionManager.PreviewTextInputEvent; textargs.Source= composition.Source; return composition._InputManager.ProcessInput(textargs); } ////// Critical - calls unmanaged code. /// TreatAsSafe - This calls GetOWMCP() but it does not expose the return value (current oem cp). /// [SecurityCritical, SecurityTreatAsSafe] private static string GetCurrentOEMCPEncoding(int code) { SecurityPermission sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); sp.Assert();//Blessed Assert try { int cp = UnsafeNativeMethods.GetOEMCP(); return CharacterEncoding(cp, code); } finally { SecurityPermission.RevertAssert(); } } // Convert code to the string based on the code page. ////// Critical - calls unmanaged code UnsafeNativeMethods.MultiByteToWideChar which might be exploitable if there /// were BO based exploits /// TreatAsSafe - The code calls into functions that convert an int to a byte array and also the encoding length /// is a contant. /// [SecurityCritical, SecurityTreatAsSafe] private static string CharacterEncoding(int cp, int code) { Byte[] bytes = ConvertCodeToByteArray(code); StringBuilder sbuilder = new StringBuilder(EncodingBufferLen); // Win32K uses MB_PRECOMPOSED | MB_USEGLYPHCHARS. int nret = UnsafeNativeMethods.MultiByteToWideChar(cp, UnsafeNativeMethods.MB_PRECOMPOSED | UnsafeNativeMethods.MB_USEGLYPHCHARS, bytes, bytes.Length, sbuilder, EncodingBufferLen); if (nret == 0) { int win32Err = Marshal.GetLastWin32Error(); throw new System.ComponentModel.Win32Exception(win32Err); } // set the length as MultiByteToWideChar returns. sbuilder.Length = nret; return sbuilder.ToString(); } // PreProcessInput event handler ////// Critical - calls a critical method - to create a TextComposition and calls /// Critical methods UnsafeStartComposition, UnsafeUpdateComposition /// and UnsafeCompleteComposition. /// [SecurityCritical] private void PreProcessInput(object sender, PreProcessInputEventArgs e) { // KeyDown --> Alt Numpad // // We eat Alt-NumPat keys and handle them by ourselves. Avalon has its own acceralator handler // and it may have a corrision with Win32k's AltNumPad handling. As a result, the AltNumPad cache // in Win32k's ToUnicodeEx() could be broken. // if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent) { KeyEventArgs keyArgs = (KeyEventArgs) e.StagingItem.Input; if (!keyArgs.Handled) { if (!_altNumpadEntryMode) { EnterAltNumpadEntryMode(keyArgs.RealKey); } else { if (HandleAltNumpadEntry(keyArgs.RealKey, keyArgs.ScanCode, keyArgs.IsExtendedKey)) { if (_altNumpadcomposition == null) { _altNumpadcomposition = new TextComposition(_inputManager, (IInputElement)keyArgs.Source, "", TextCompositionAutoComplete.Off, keyArgs.Device); keyArgs.Handled = UnsafeStartComposition(_altNumpadcomposition); } else { _altNumpadcomposition.ClearTexts(); keyArgs.Handled = UnsafeUpdateComposition(_altNumpadcomposition); } // We ate this key for AltNumPad entry. None will be able to handle this. e.Cancel(); } else { // alt numpad entry was reset so composition needs to be finalized. if (_altNumpadcomposition != null) { _altNumpadcomposition.ClearTexts(); _altNumpadcomposition.Complete(); ClearAltnumpadComposition(); } } } } } if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent) { KeyEventArgs keyArgs = (KeyEventArgs)e.StagingItem.Input; //This makes sure that deadChar's do not get handled in commands //As a result they are unhandled KeyDown events that are sent to translate input. // if (!keyArgs.Handled && (_deadCharTextComposition != null) && (_deadCharTextComposition.Stage == TextCompositionStage.Started)) { keyArgs.MarkDeadCharProcessed(); } } } // PostProcessInput event handler //////Critical- calls critical functions pushInput, UnsafeStartComposition and /// UnsafeUpdateComposition. /// [SecurityCritical] private void PostProcessInput(object sender, ProcessInputEventArgs e) { // KeyUp if(e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent) { KeyEventArgs keyArgs = (KeyEventArgs) e.StagingItem.Input; if(!keyArgs.Handled) { if(keyArgs.RealKey == Key.LeftAlt || keyArgs.RealKey == Key.RightAlt) { // Make sure both Alt keys are up. ModifierKeys modifiers = keyArgs.KeyboardDevice.Modifiers; if((modifiers & ModifierKeys.Alt) == 0) { if(_altNumpadEntryMode) { _altNumpadEntryMode = false; // Generate the Unicode equivalent if we // actually entered a number via the numpad. if(_altNumpadEntry != 0) { _altNumpadcomposition.ClearTexts(); if (_altNumpadConversionMode == AltNumpadConversionMode.OEMCodePage) { _altNumpadcomposition.SetText(GetCurrentOEMCPEncoding(_altNumpadEntry)); } else if ((_altNumpadConversionMode == AltNumpadConversionMode.DefaultCodePage) || (_altNumpadConversionMode == AltNumpadConversionMode.HexDefaultCodePage)) { _altNumpadcomposition.SetText(CharacterEncoding(InputLanguageManager.Current.CurrentInputLanguage.TextInfo.ANSICodePage, _altNumpadEntry)); } else if (_altNumpadConversionMode == AltNumpadConversionMode.HexUnicode) { Char[] chars = new Char[1]; chars[0] = (Char) _altNumpadEntry; _altNumpadcomposition.SetText(new string(chars)); } } } } } } else { // Someone handled Alt key up event, we cancel Alt-Numpad handling. _altNumpadEntryMode = false; _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; } } // PreviewTextInputBegin --> TextInputStart else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputStartEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent=TextCompositionManager.TextInputStartEvent; text.Source= textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // PreviewTextInputUpdate --> TextInputUpdate else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputUpdateEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent=TextCompositionManager.TextInputUpdateEvent; text.Source= textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // PreviewTextInput --> TextInput else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent=TextCompositionManager.TextInputEvent; text.Source= textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // TextCompositioniBegin --> TextInput if this is AutomaticComplete. else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputStartEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { if (textArgs.TextComposition.AutoComplete == TextCompositionAutoComplete.On) { textArgs.Handled = UnsafeCompleteComposition(textArgs.TextComposition); } } } // TextCompositionUpdate --> TextInput if this is AutomaticComplete. else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputUpdateEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { if ((textArgs.TextComposition == _deadCharTextComposition) && (_deadCharTextComposition.Composed)) { textArgs.Handled = UnsafeCompleteComposition(_deadCharTextComposition); _deadCharTextComposition = null; } } } // Raw to StartComposition. InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if(input != null) { if(input.Report.Type == InputType.Text && input.RoutedEvent == InputManager.InputReportEvent) { RawTextInputReport textInput; textInput = (RawTextInputReport)input.Report; // // string inputText = new string(textInput.CharacterCode, 1); bool fDoneAltNumpadComposition = false; if (_altNumpadcomposition != null) { // Generate TextInput event from WM_CHAR handler. if (inputText.Equals(_altNumpadcomposition.Text)) { fDoneAltNumpadComposition = true; } else { // The generated text from InputReport does not matched with _altNumpadcomposition. // Cancel this composition and process the char from InputReport. _altNumpadcomposition.ClearTexts(); } _altNumpadcomposition.Complete(); ClearAltnumpadComposition(); } if (!fDoneAltNumpadComposition) { if (textInput.IsDeadCharacter) { _deadCharTextComposition = new DeadCharTextComposition(_inputManager, (IInputElement)null, inputText , TextCompositionAutoComplete.Off, InputManager.Current.PrimaryKeyboardDevice); if (textInput.IsSystemCharacter) { _deadCharTextComposition.MakeSystem(); } else if (textInput.IsControlCharacter) { _deadCharTextComposition.MakeControl(); } input.Handled = UnsafeStartComposition(_deadCharTextComposition); } else { if(inputText != null) { if (_deadCharTextComposition != null) { _deadCharTextComposition.ClearTexts(); _deadCharTextComposition.SetText(inputText); _deadCharTextComposition.Composed = true; if (textInput.IsSystemCharacter) { _deadCharTextComposition.MakeSystem(); } else if (textInput.IsControlCharacter) { _deadCharTextComposition.MakeControl(); } input.Handled = UnsafeUpdateComposition(_deadCharTextComposition); } else { TextComposition composition = new TextComposition(_inputManager, (IInputElement)e.StagingItem.Input.Source, inputText, TextCompositionAutoComplete.On, InputManager.Current.PrimaryKeyboardDevice); if (textInput.IsSystemCharacter) { composition.MakeSystem(); } else if (textInput.IsControlCharacter) { composition.MakeControl(); } input.Handled = UnsafeStartComposition(composition); } } } } } } } // AltNumpad key handler private bool EnterAltNumpadEntryMode(Key key) { bool handled = false; if(key == Key.LeftAlt || key == Key.RightAlt) { if(!_altNumpadEntryMode) { _altNumpadEntryMode = true; _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; handled = true; } } return handled; } // AltNumpad key handler private bool HandleAltNumpadEntry(Key key, int scanCode, bool isExtendedKey) { bool handled = false; Debug.Assert(_altNumpadEntryMode); // All Numpad keys (either numlock or not) are not an extended key. // We're interested in only NumPad key so we can filter them first. if (isExtendedKey) { return handled; } // If Alt key is up, we will quit AltNumpadEntryMode. if (!Keyboard.IsKeyDown(Key.LeftAlt) && !Keyboard.IsKeyDown(Key.RightAlt)) { return false; } // // Windows has historically offered a back-door for entering // characters that are not available on the keyboard. The // user can hold down one of the Alt keys, type in the numerical // value of the desried character using the num-pad keys, and // the release the Alt key. The numeric value will be converted // into Unicode, and a text event will // be raised. // if (scanCode == NumpadScanCode.NumpadDot) { if (IsHexNumpadEnabled) { _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.HexDefaultCodePage; handled = true; } else { // reset alt numpad entry and mode. _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; handled = false; } } else if (scanCode == NumpadScanCode.NumpadPlus) { if (IsHexNumpadEnabled) { _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.HexUnicode; handled = true; } else { // reset alt numpad entry and mode. _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; handled = false; } } else { int newEntry = GetNewEntry(key, scanCode); if (newEntry == -1) { _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; // we don't handle this case to cancel TextComposition. handled = false; } else { // If the first key is NumPad0, it is the default codepage. if ((_altNumpadEntry == 0) && (newEntry == 0)) _altNumpadConversionMode = AltNumpadConversionMode.DefaultCodePage; if (HexConversionMode) _altNumpadEntry = (_altNumpadEntry * 0x10) + newEntry; else _altNumpadEntry = (_altNumpadEntry * 10) + newEntry; handled = true; } } return handled; } // Convert Key and ScanCode to new entry number. private int GetNewEntry(Key key, int scanCode) { if (HexConversionMode) { switch (key) { // We accept digit keys and A-F in HexConversionMode. case Key.D0: return 0x00; case Key.D1: return 0x01; case Key.D2: return 0x02; case Key.D3: return 0x03; case Key.D4: return 0x04; case Key.D5: return 0x05; case Key.D6: return 0x06; case Key.D7: return 0x07; case Key.D8: return 0x08; case Key.D9: return 0x09; case Key.A: return 0x0A; case Key.B: return 0x0B; case Key.C: return 0x0C; case Key.D: return 0x0D; case Key.E: return 0x0E; case Key.F: return 0x0F; } } return NumpadScanCode.DigitFromScanCode(scanCode); } // Convert the code to byte array for DBCS/SBCS. ////// Critical - returns a byte array that is passed into a call under an elevation /// TreatAsSafe - The code calls is safe in terms of what it does the reason it is critical is because /// if there was logic error here in the future it could be used to exploit the elevated call. /// [SecurityCritical, SecurityTreatAsSafe] private static Byte[] ConvertCodeToByteArray(int codeEntry) { Byte[] bytes; if (codeEntry > 0xff) { bytes = new Byte[2]; bytes[0] = (Byte)(codeEntry >> 8); bytes[1] = (Byte)codeEntry; } else { bytes = new Byte[1]; bytes[0] = (Byte)codeEntry; } return bytes; } // clear the altnumpad composition object and reset entry. // the existence of the altnumpad composition object needs to be consistent with altnumpad entry. private void ClearAltnumpadComposition() { _altNumpadcomposition = null; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; _altNumpadEntry = 0; } //------------------------------------------------------ // // Private Properties // //----------------------------------------------------- // Return true if we're in hex conversion mode. private bool HexConversionMode { get { if ((_altNumpadConversionMode == AltNumpadConversionMode.HexDefaultCodePage) || (_altNumpadConversionMode == AltNumpadConversionMode.HexUnicode)) return true; return false; } } ////// Return true if HexNumPad is enabled. /// ////// Critical - asserts registry permissions to read from HKEY_CURRENT_USER. /// Treat as safe - we only read a value from HKEY_CURENT_USER. And we don't expose the value. /// private static bool IsHexNumpadEnabled { [SecurityCritical, SecurityTreatAsSafe ] get { if (!_isHexNumpadRegistryChecked) { // Acquire permissions to read the one key we care about from the registry RegistryPermission permission = new RegistryPermission( RegistryPermissionAccess.Read, "HKEY_CURRENT_USER\\Control Panel\\Input Method"); permission.Assert(); try { object obj; RegistryKey key; key = Registry.CurrentUser.OpenSubKey("Control Panel\\Input Method"); if (key != null) { obj = key.GetValue("EnableHexNumpad"); if ((obj is string) && ((string)obj != "0")) { _isHexNumpadEnabled = true; } } } finally { RegistryPermission.RevertAssert(); } _isHexNumpadRegistryChecked = true; } return _isHexNumpadEnabled; } } //------------------------------------------------------ // // Private Fields // //------------------------------------------------------ // InputManager for this TextCompositionManager ////// Critical - InputManager is critical. /// [SecurityCritical] private readonly InputManager _inputManager; // The current dead char composition. private DeadCharTextComposition _deadCharTextComposition; // The state of AltNumpad mode. private bool _altNumpadEntryMode; // The current value entered from NumPad. private int _altNumpadEntry; // The current AltNumpad conversion mode. private AltNumpadConversionMode _altNumpadConversionMode; // TextComposition for AltNumpad. private TextComposition _altNumpadcomposition; // True if EnableHexNumpad registry has been checked. private static bool _isHexNumpadRegistryChecked = false; // True if EnableHexNumpad registry is set. private static bool _isHexNumpadEnabled = false; // Character encoding length. ////// Critical - Is used to detereming length in a call to unmanaged code which happens under an elevation /// [SecurityCritical] private const int EncodingBufferLen = 4; // 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 const int NumpadDot = 0x53; internal const int NumpadPlus = 0x4e; internal const int Numpad0 = 0x52; internal const int Numpad1 = 0x4f; internal const int Numpad2 = 0x50; internal const int Numpad3 = 0x51; internal const int Numpad4 = 0x4b; internal const int Numpad5 = 0x4c; internal const int Numpad6 = 0x4d; internal const int Numpad7 = 0x47; internal const int Numpad8 = 0x48; internal const int Numpad9 = 0x49; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: the CompositionManager class // // History: // 11/18/2003 : yutakas created // //--------------------------------------------------------------------------- using System; using System.Diagnostics; using System.Globalization; using System.Security; using System.Security.Permissions; using System.Text; using System.Windows.Threading; using System.Windows; using System.Runtime.InteropServices; using MS.Win32; using Microsoft.Win32; // for RegistryKey class using MS.Internal ; using MS.Internal.PresentationCore; // SecurityHelper using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace System.Windows.Input { // // Modes of AltNumpad. // 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 } ////// the CompositionManager class provides input-text/composition event promotion /// public sealed class TextCompositionManager : DispatcherObject { //----------------------------------------------------- // // static RoutedEvent // //----------------------------------------------------- ////// Preview Composition Start /// public static readonly RoutedEvent PreviewTextInputStartEvent = EventManager.RegisterRoutedEvent("PreviewTextInputStart", RoutingStrategy.Tunnel, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the PreviewTextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddPreviewTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, PreviewTextInputStartEvent, handler); } ////// Removes a handler for the PreviewTextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemovePreviewTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, PreviewTextInputStartEvent, handler); } ////// Composition Start /// public static readonly RoutedEvent TextInputStartEvent = EventManager.RegisterRoutedEvent("TextInputStart", RoutingStrategy.Bubble, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the TextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, TextInputStartEvent, handler); } ////// Removes a handler for the TextInputStart attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemoveTextInputStartHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, TextInputStartEvent, handler); } ////// Preview Composition Updated /// public static readonly RoutedEvent PreviewTextInputUpdateEvent = EventManager.RegisterRoutedEvent("PreviewTextInputUpdate", RoutingStrategy.Tunnel, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the PreviewTextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddPreviewTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, PreviewTextInputUpdateEvent, handler); } ////// Removes a handler for the PreviewTextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemovePreviewTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, PreviewTextInputUpdateEvent, handler); } ////// Composition Updated /// public static readonly RoutedEvent TextInputUpdateEvent = EventManager.RegisterRoutedEvent("TextInputUpdate", RoutingStrategy.Bubble, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the TextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, TextInputUpdateEvent, handler); } ////// Removes a handler for the TextInputUpdate attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemoveTextInputUpdateHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, TextInputUpdateEvent, handler); } ////// Preview Composition End /// public static readonly RoutedEvent PreviewTextInputEvent = EventManager.RegisterRoutedEvent("PreviewTextInput", RoutingStrategy.Tunnel, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the PreviewTextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddPreviewTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, PreviewTextInputEvent, handler); } ////// Removes a handler for the PreviewTextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemovePreviewTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, PreviewTextInputEvent, handler); } ////// Composition End /// public static readonly RoutedEvent TextInputEvent = EventManager.RegisterRoutedEvent("TextInput", RoutingStrategy.Bubble, typeof(TextCompositionEventHandler), typeof(TextCompositionManager)); ////// Adds a handler for the TextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be added public static void AddTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.AddHandler(element, TextInputEvent, handler); } ////// Removes a handler for the TextInput attached event /// /// UIElement or ContentElement that listens to this event /// Event Handler to be removed public static void RemoveTextInputHandler(DependencyObject element, TextCompositionEventHandler handler) { if (element == null) { throw new ArgumentNullException("element"); } UIElement.RemoveHandler(element, TextInputEvent, handler); } //------------------------------------------------------ // // Constructors // //----------------------------------------------------- #region Constructors ////// Critical - Calls a critical method - PreProcessInput /// TreatAsSafe - Ok for us to register an event handler. Handler itself is critical. /// [SecurityCritical, SecurityTreatAsSafe ] internal TextCompositionManager(InputManager inputManager) { _inputManager = inputManager; _inputManager.PreProcessInput += new PreProcessInputEventHandler(PreProcessInput); _inputManager.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); } #endregion Constructors //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ ////// Start the composition. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical calls UnsafeStartComposition. /// PublicOk: Linkdemand blocks external callers /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] public static bool StartComposition(TextComposition composition) { return UnsafeStartComposition(composition); } ////// Update the composition. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical calls UnsafeUpdataComposition. /// PublicOk: Linkdemand blocks external callers /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)] public static bool UpdateComposition(TextComposition composition) { return UnsafeUpdateComposition(composition); } ////// Complete the composition. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical calls UnsafeCompleteComposition. /// PublicOk: Linkdemand blocks external callers /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)] public static bool CompleteComposition(TextComposition composition) { return UnsafeCompleteComposition(composition); } //----------------------------------------------------- // // Public Properties // //------------------------------------------------------ //----------------------------------------------------- // // Public Events // //----------------------------------------------------- //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ //------------------------------------------------------ // // Internal Properties // //----------------------------------------------------- //------------------------------------------------------ // // Internal Events // //----------------------------------------------------- //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- ////// Critical - as this refers to _InputManager and calls Critical function /// InputManager.ProcessInput. This can be used to spoof text input. /// [SecurityCritical] private static bool UnsafeStartComposition(TextComposition composition) { if (composition == null) { throw new ArgumentNullException("composition"); } if (composition._InputManager == null) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_NoInputManager, "composition")); } if (composition.Stage != TextCompositionStage.None) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionHasStarted, "composition")); } composition.Stage = TextCompositionStage.Started; TextCompositionEventArgs textargs = new TextCompositionEventArgs(composition._InputDevice, composition); textargs.RoutedEvent=TextCompositionManager.PreviewTextInputStartEvent; textargs.Source= composition.Source; return composition._InputManager.ProcessInput(textargs); } ////// Critical - as this refers to _InputManager and calls Critical function /// InputManager.ProcessInput. This can be used to spoof text input. /// [SecurityCritical] private static bool UnsafeUpdateComposition(TextComposition composition) { if (composition == null) { throw new ArgumentNullException("composition"); } if (composition._InputManager == null) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_NoInputManager, "composition")); } if (composition.Stage == TextCompositionStage.None) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionNotStarted, "composition")); } if (composition.Stage == TextCompositionStage.Done) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionHasDone, "composition")); } TextCompositionEventArgs textargs = new TextCompositionEventArgs(composition._InputDevice, composition); textargs.RoutedEvent=TextCompositionManager.PreviewTextInputUpdateEvent; textargs.Source= composition.Source; return composition._InputManager.ProcessInput(textargs); } ////// Critical - as this accesses InputManager and calls Critical method ProcessInput. /// [SecurityCritical] private static bool UnsafeCompleteComposition(TextComposition composition) { if (composition == null) { throw new ArgumentNullException("composition"); } if (composition._InputManager == null) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_NoInputManager, "composition")); } if (composition.Stage == TextCompositionStage.None) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionNotStarted, "composition")); } if (composition.Stage == TextCompositionStage.Done) { throw new ArgumentException(SR.Get(SRID.TextCompositionManager_TextCompositionHasDone, "composition")); } composition.Stage = TextCompositionStage.Done; TextCompositionEventArgs textargs = new TextCompositionEventArgs(composition._InputDevice, composition); textargs.RoutedEvent=TextCompositionManager.PreviewTextInputEvent; textargs.Source= composition.Source; return composition._InputManager.ProcessInput(textargs); } ////// Critical - calls unmanaged code. /// TreatAsSafe - This calls GetOWMCP() but it does not expose the return value (current oem cp). /// [SecurityCritical, SecurityTreatAsSafe] private static string GetCurrentOEMCPEncoding(int code) { SecurityPermission sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); sp.Assert();//Blessed Assert try { int cp = UnsafeNativeMethods.GetOEMCP(); return CharacterEncoding(cp, code); } finally { SecurityPermission.RevertAssert(); } } // Convert code to the string based on the code page. ////// Critical - calls unmanaged code UnsafeNativeMethods.MultiByteToWideChar which might be exploitable if there /// were BO based exploits /// TreatAsSafe - The code calls into functions that convert an int to a byte array and also the encoding length /// is a contant. /// [SecurityCritical, SecurityTreatAsSafe] private static string CharacterEncoding(int cp, int code) { Byte[] bytes = ConvertCodeToByteArray(code); StringBuilder sbuilder = new StringBuilder(EncodingBufferLen); // Win32K uses MB_PRECOMPOSED | MB_USEGLYPHCHARS. int nret = UnsafeNativeMethods.MultiByteToWideChar(cp, UnsafeNativeMethods.MB_PRECOMPOSED | UnsafeNativeMethods.MB_USEGLYPHCHARS, bytes, bytes.Length, sbuilder, EncodingBufferLen); if (nret == 0) { int win32Err = Marshal.GetLastWin32Error(); throw new System.ComponentModel.Win32Exception(win32Err); } // set the length as MultiByteToWideChar returns. sbuilder.Length = nret; return sbuilder.ToString(); } // PreProcessInput event handler ////// Critical - calls a critical method - to create a TextComposition and calls /// Critical methods UnsafeStartComposition, UnsafeUpdateComposition /// and UnsafeCompleteComposition. /// [SecurityCritical] private void PreProcessInput(object sender, PreProcessInputEventArgs e) { // KeyDown --> Alt Numpad // // We eat Alt-NumPat keys and handle them by ourselves. Avalon has its own acceralator handler // and it may have a corrision with Win32k's AltNumPad handling. As a result, the AltNumPad cache // in Win32k's ToUnicodeEx() could be broken. // if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent) { KeyEventArgs keyArgs = (KeyEventArgs) e.StagingItem.Input; if (!keyArgs.Handled) { if (!_altNumpadEntryMode) { EnterAltNumpadEntryMode(keyArgs.RealKey); } else { if (HandleAltNumpadEntry(keyArgs.RealKey, keyArgs.ScanCode, keyArgs.IsExtendedKey)) { if (_altNumpadcomposition == null) { _altNumpadcomposition = new TextComposition(_inputManager, (IInputElement)keyArgs.Source, "", TextCompositionAutoComplete.Off, keyArgs.Device); keyArgs.Handled = UnsafeStartComposition(_altNumpadcomposition); } else { _altNumpadcomposition.ClearTexts(); keyArgs.Handled = UnsafeUpdateComposition(_altNumpadcomposition); } // We ate this key for AltNumPad entry. None will be able to handle this. e.Cancel(); } else { // alt numpad entry was reset so composition needs to be finalized. if (_altNumpadcomposition != null) { _altNumpadcomposition.ClearTexts(); _altNumpadcomposition.Complete(); ClearAltnumpadComposition(); } } } } } if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent) { KeyEventArgs keyArgs = (KeyEventArgs)e.StagingItem.Input; //This makes sure that deadChar's do not get handled in commands //As a result they are unhandled KeyDown events that are sent to translate input. // if (!keyArgs.Handled && (_deadCharTextComposition != null) && (_deadCharTextComposition.Stage == TextCompositionStage.Started)) { keyArgs.MarkDeadCharProcessed(); } } } // PostProcessInput event handler //////Critical- calls critical functions pushInput, UnsafeStartComposition and /// UnsafeUpdateComposition. /// [SecurityCritical] private void PostProcessInput(object sender, ProcessInputEventArgs e) { // KeyUp if(e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent) { KeyEventArgs keyArgs = (KeyEventArgs) e.StagingItem.Input; if(!keyArgs.Handled) { if(keyArgs.RealKey == Key.LeftAlt || keyArgs.RealKey == Key.RightAlt) { // Make sure both Alt keys are up. ModifierKeys modifiers = keyArgs.KeyboardDevice.Modifiers; if((modifiers & ModifierKeys.Alt) == 0) { if(_altNumpadEntryMode) { _altNumpadEntryMode = false; // Generate the Unicode equivalent if we // actually entered a number via the numpad. if(_altNumpadEntry != 0) { _altNumpadcomposition.ClearTexts(); if (_altNumpadConversionMode == AltNumpadConversionMode.OEMCodePage) { _altNumpadcomposition.SetText(GetCurrentOEMCPEncoding(_altNumpadEntry)); } else if ((_altNumpadConversionMode == AltNumpadConversionMode.DefaultCodePage) || (_altNumpadConversionMode == AltNumpadConversionMode.HexDefaultCodePage)) { _altNumpadcomposition.SetText(CharacterEncoding(InputLanguageManager.Current.CurrentInputLanguage.TextInfo.ANSICodePage, _altNumpadEntry)); } else if (_altNumpadConversionMode == AltNumpadConversionMode.HexUnicode) { Char[] chars = new Char[1]; chars[0] = (Char) _altNumpadEntry; _altNumpadcomposition.SetText(new string(chars)); } } } } } } else { // Someone handled Alt key up event, we cancel Alt-Numpad handling. _altNumpadEntryMode = false; _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; } } // PreviewTextInputBegin --> TextInputStart else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputStartEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent=TextCompositionManager.TextInputStartEvent; text.Source= textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // PreviewTextInputUpdate --> TextInputUpdate else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputUpdateEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent=TextCompositionManager.TextInputUpdateEvent; text.Source= textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // PreviewTextInput --> TextInput else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent=TextCompositionManager.TextInputEvent; text.Source= textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // TextCompositioniBegin --> TextInput if this is AutomaticComplete. else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputStartEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { if (textArgs.TextComposition.AutoComplete == TextCompositionAutoComplete.On) { textArgs.Handled = UnsafeCompleteComposition(textArgs.TextComposition); } } } // TextCompositionUpdate --> TextInput if this is AutomaticComplete. else if(e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputUpdateEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs) e.StagingItem.Input; if(!textArgs.Handled) { if ((textArgs.TextComposition == _deadCharTextComposition) && (_deadCharTextComposition.Composed)) { textArgs.Handled = UnsafeCompleteComposition(_deadCharTextComposition); _deadCharTextComposition = null; } } } // Raw to StartComposition. InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if(input != null) { if(input.Report.Type == InputType.Text && input.RoutedEvent == InputManager.InputReportEvent) { RawTextInputReport textInput; textInput = (RawTextInputReport)input.Report; // // string inputText = new string(textInput.CharacterCode, 1); bool fDoneAltNumpadComposition = false; if (_altNumpadcomposition != null) { // Generate TextInput event from WM_CHAR handler. if (inputText.Equals(_altNumpadcomposition.Text)) { fDoneAltNumpadComposition = true; } else { // The generated text from InputReport does not matched with _altNumpadcomposition. // Cancel this composition and process the char from InputReport. _altNumpadcomposition.ClearTexts(); } _altNumpadcomposition.Complete(); ClearAltnumpadComposition(); } if (!fDoneAltNumpadComposition) { if (textInput.IsDeadCharacter) { _deadCharTextComposition = new DeadCharTextComposition(_inputManager, (IInputElement)null, inputText , TextCompositionAutoComplete.Off, InputManager.Current.PrimaryKeyboardDevice); if (textInput.IsSystemCharacter) { _deadCharTextComposition.MakeSystem(); } else if (textInput.IsControlCharacter) { _deadCharTextComposition.MakeControl(); } input.Handled = UnsafeStartComposition(_deadCharTextComposition); } else { if(inputText != null) { if (_deadCharTextComposition != null) { _deadCharTextComposition.ClearTexts(); _deadCharTextComposition.SetText(inputText); _deadCharTextComposition.Composed = true; if (textInput.IsSystemCharacter) { _deadCharTextComposition.MakeSystem(); } else if (textInput.IsControlCharacter) { _deadCharTextComposition.MakeControl(); } input.Handled = UnsafeUpdateComposition(_deadCharTextComposition); } else { TextComposition composition = new TextComposition(_inputManager, (IInputElement)e.StagingItem.Input.Source, inputText, TextCompositionAutoComplete.On, InputManager.Current.PrimaryKeyboardDevice); if (textInput.IsSystemCharacter) { composition.MakeSystem(); } else if (textInput.IsControlCharacter) { composition.MakeControl(); } input.Handled = UnsafeStartComposition(composition); } } } } } } } // AltNumpad key handler private bool EnterAltNumpadEntryMode(Key key) { bool handled = false; if(key == Key.LeftAlt || key == Key.RightAlt) { if(!_altNumpadEntryMode) { _altNumpadEntryMode = true; _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; handled = true; } } return handled; } // AltNumpad key handler private bool HandleAltNumpadEntry(Key key, int scanCode, bool isExtendedKey) { bool handled = false; Debug.Assert(_altNumpadEntryMode); // All Numpad keys (either numlock or not) are not an extended key. // We're interested in only NumPad key so we can filter them first. if (isExtendedKey) { return handled; } // If Alt key is up, we will quit AltNumpadEntryMode. if (!Keyboard.IsKeyDown(Key.LeftAlt) && !Keyboard.IsKeyDown(Key.RightAlt)) { return false; } // // Windows has historically offered a back-door for entering // characters that are not available on the keyboard. The // user can hold down one of the Alt keys, type in the numerical // value of the desried character using the num-pad keys, and // the release the Alt key. The numeric value will be converted // into Unicode, and a text event will // be raised. // if (scanCode == NumpadScanCode.NumpadDot) { if (IsHexNumpadEnabled) { _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.HexDefaultCodePage; handled = true; } else { // reset alt numpad entry and mode. _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; handled = false; } } else if (scanCode == NumpadScanCode.NumpadPlus) { if (IsHexNumpadEnabled) { _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.HexUnicode; handled = true; } else { // reset alt numpad entry and mode. _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; handled = false; } } else { int newEntry = GetNewEntry(key, scanCode); if (newEntry == -1) { _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; // we don't handle this case to cancel TextComposition. handled = false; } else { // If the first key is NumPad0, it is the default codepage. if ((_altNumpadEntry == 0) && (newEntry == 0)) _altNumpadConversionMode = AltNumpadConversionMode.DefaultCodePage; if (HexConversionMode) _altNumpadEntry = (_altNumpadEntry * 0x10) + newEntry; else _altNumpadEntry = (_altNumpadEntry * 10) + newEntry; handled = true; } } return handled; } // Convert Key and ScanCode to new entry number. private int GetNewEntry(Key key, int scanCode) { if (HexConversionMode) { switch (key) { // We accept digit keys and A-F in HexConversionMode. case Key.D0: return 0x00; case Key.D1: return 0x01; case Key.D2: return 0x02; case Key.D3: return 0x03; case Key.D4: return 0x04; case Key.D5: return 0x05; case Key.D6: return 0x06; case Key.D7: return 0x07; case Key.D8: return 0x08; case Key.D9: return 0x09; case Key.A: return 0x0A; case Key.B: return 0x0B; case Key.C: return 0x0C; case Key.D: return 0x0D; case Key.E: return 0x0E; case Key.F: return 0x0F; } } return NumpadScanCode.DigitFromScanCode(scanCode); } // Convert the code to byte array for DBCS/SBCS. ////// Critical - returns a byte array that is passed into a call under an elevation /// TreatAsSafe - The code calls is safe in terms of what it does the reason it is critical is because /// if there was logic error here in the future it could be used to exploit the elevated call. /// [SecurityCritical, SecurityTreatAsSafe] private static Byte[] ConvertCodeToByteArray(int codeEntry) { Byte[] bytes; if (codeEntry > 0xff) { bytes = new Byte[2]; bytes[0] = (Byte)(codeEntry >> 8); bytes[1] = (Byte)codeEntry; } else { bytes = new Byte[1]; bytes[0] = (Byte)codeEntry; } return bytes; } // clear the altnumpad composition object and reset entry. // the existence of the altnumpad composition object needs to be consistent with altnumpad entry. private void ClearAltnumpadComposition() { _altNumpadcomposition = null; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; _altNumpadEntry = 0; } //------------------------------------------------------ // // Private Properties // //----------------------------------------------------- // Return true if we're in hex conversion mode. private bool HexConversionMode { get { if ((_altNumpadConversionMode == AltNumpadConversionMode.HexDefaultCodePage) || (_altNumpadConversionMode == AltNumpadConversionMode.HexUnicode)) return true; return false; } } ////// Return true if HexNumPad is enabled. /// ////// Critical - asserts registry permissions to read from HKEY_CURRENT_USER. /// Treat as safe - we only read a value from HKEY_CURENT_USER. And we don't expose the value. /// private static bool IsHexNumpadEnabled { [SecurityCritical, SecurityTreatAsSafe ] get { if (!_isHexNumpadRegistryChecked) { // Acquire permissions to read the one key we care about from the registry RegistryPermission permission = new RegistryPermission( RegistryPermissionAccess.Read, "HKEY_CURRENT_USER\\Control Panel\\Input Method"); permission.Assert(); try { object obj; RegistryKey key; key = Registry.CurrentUser.OpenSubKey("Control Panel\\Input Method"); if (key != null) { obj = key.GetValue("EnableHexNumpad"); if ((obj is string) && ((string)obj != "0")) { _isHexNumpadEnabled = true; } } } finally { RegistryPermission.RevertAssert(); } _isHexNumpadRegistryChecked = true; } return _isHexNumpadEnabled; } } //------------------------------------------------------ // // Private Fields // //------------------------------------------------------ // InputManager for this TextCompositionManager ////// Critical - InputManager is critical. /// [SecurityCritical] private readonly InputManager _inputManager; // The current dead char composition. private DeadCharTextComposition _deadCharTextComposition; // The state of AltNumpad mode. private bool _altNumpadEntryMode; // The current value entered from NumPad. private int _altNumpadEntry; // The current AltNumpad conversion mode. private AltNumpadConversionMode _altNumpadConversionMode; // TextComposition for AltNumpad. private TextComposition _altNumpadcomposition; // True if EnableHexNumpad registry has been checked. private static bool _isHexNumpadRegistryChecked = false; // True if EnableHexNumpad registry is set. private static bool _isHexNumpadEnabled = false; // Character encoding length. ////// Critical - Is used to detereming length in a call to unmanaged code which happens under an elevation /// [SecurityCritical] private const int EncodingBufferLen = 4; // 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 const int NumpadDot = 0x53; internal const int NumpadPlus = 0x4e; internal const int Numpad0 = 0x52; internal const int Numpad1 = 0x4f; internal const int Numpad2 = 0x50; internal const int Numpad3 = 0x51; internal const int Numpad4 = 0x4b; internal const int Numpad5 = 0x4c; internal const int Numpad6 = 0x4d; internal const int Numpad7 = 0x47; internal const int Numpad8 = 0x48; internal const int Numpad9 = 0x49; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CharacterBuffer.cs
- IntegrationExceptionEventArgs.cs
- WebPartEditorOkVerb.cs
- CounterNameConverter.cs
- ListViewDataItem.cs
- XPathNodeIterator.cs
- TextWriterEngine.cs
- ToolBarButtonClickEvent.cs
- SplineQuaternionKeyFrame.cs
- EmptyEnumerable.cs
- XmlSchemaAnyAttribute.cs
- SqlRowUpdatingEvent.cs
- TabPageDesigner.cs
- ComponentSerializationService.cs
- SerializerWriterEventHandlers.cs
- SQLDoubleStorage.cs
- HtmlForm.cs
- GridViewSortEventArgs.cs
- SiteMapNodeItem.cs
- ValidatedMobileControlConverter.cs
- CustomCategoryAttribute.cs
- CustomBindingElement.cs
- GeometryModel3D.cs
- _NegotiateClient.cs
- QilStrConcat.cs
- UnsafeNativeMethods.cs
- DeclarativeCatalogPart.cs
- SmtpClient.cs
- UseLicense.cs
- RightsManagementEncryptedStream.cs
- ControlEvent.cs
- WinEventHandler.cs
- XPathDescendantIterator.cs
- SchemaNotation.cs
- HttpApplication.cs
- DataGridCommandEventArgs.cs
- JournalEntryStack.cs
- OptimisticConcurrencyException.cs
- TemplateKeyConverter.cs
- AspNetCacheProfileAttribute.cs
- TerminatorSinks.cs
- IteratorFilter.cs
- CommonRemoteMemoryBlock.cs
- IsolatedStorageFile.cs
- MetadataItemCollectionFactory.cs
- CellTreeNode.cs
- SparseMemoryStream.cs
- SmtpFailedRecipientsException.cs
- HttpApplicationStateBase.cs
- Peer.cs
- WsiProfilesElementCollection.cs
- BatchServiceHost.cs
- ControlPropertyNameConverter.cs
- TextEditorCopyPaste.cs
- MULTI_QI.cs
- TextRunProperties.cs
- PolicyConversionContext.cs
- TextBoxBase.cs
- TraceContextEventArgs.cs
- DataGridViewRowStateChangedEventArgs.cs
- FactoryGenerator.cs
- SqlAggregateChecker.cs
- TrailingSpaceComparer.cs
- ConnectAlgorithms.cs
- TextDecorationCollection.cs
- FileNotFoundException.cs
- ConnectionStringsSection.cs
- PageThemeBuildProvider.cs
- FlowDecision.cs
- ConnectionPointConverter.cs
- HttpRawResponse.cs
- GeneralTransformGroup.cs
- LogicalExpr.cs
- TabItemAutomationPeer.cs
- InvocationExpression.cs
- TransferMode.cs
- BindableTemplateBuilder.cs
- XmlNamedNodeMap.cs
- SecurityChannelFaultConverter.cs
- XNodeSchemaApplier.cs
- RSAPKCS1SignatureDeformatter.cs
- BaseAsyncResult.cs
- BaseCAMarshaler.cs
- GenericParameterDataContract.cs
- XmlSigningNodeWriter.cs
- Assign.cs
- GlyphCache.cs
- WindowsIdentity.cs
- SqlWebEventProvider.cs
- HttpServerVarsCollection.cs
- XmlSchemaFacet.cs
- WindowsAuthenticationEventArgs.cs
- CodeBlockBuilder.cs
- ButtonRenderer.cs
- AssociationType.cs
- HtmlButton.cs
- XmlMtomWriter.cs
- ItemCollection.cs
- PeerNameResolver.cs
- OdbcFactory.cs