Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Input / InputMethod.cs / 1305600 / InputMethod.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: Manage Input Methods (EA-IME, TextServicesFramework). // // History: // 07/30/2003 : yutakas - Ported from .net tree. // //--------------------------------------------------------------------------- using System.Runtime.InteropServices; using System.Collections; using System.Diagnostics; using System.Globalization; using System.Security.Permissions; using System.Threading; using System.Windows.Threading; using System.Windows.Interop; using System.Windows.Media; using System.Security; using MS.Utility; using MS.Win32; using MS.Internal; using MS.Internal.PresentationCore; // SecurityHelper using System; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace System.Windows.Input { //----------------------------------------------------- // // InputMethodState enum // //----------------------------------------------------- ////// State of Ime /// public enum InputMethodState { ////// InputMethod state is on. /// Off = 0, ////// InputMethod state is on. /// On = 1, ////// InputMethod state is not set. It does not care. /// DoNotCare = 2, } //------------------------------------------------------ // // SpeechMode enum // //----------------------------------------------------- ////// Mode of speech /// public enum SpeechMode { ////// Speech is in dictation mode. /// Dictation, ////// Speech is in command mode. /// Command, ////// Speech mode is indeterminate. /// Indeterminate, } //------------------------------------------------------ // // ImeConversionModeValues enum // //------------------------------------------------------ ////// ImeConversionModeValues /// [Flags] public enum ImeConversionModeValues { ////// Native Mode (Hiragana, Hangul, Chinese) /// Native = 0x00000001, ////// Japanese Katakana Mode /// Katakana = 0x00000002, ////// Full Shape mode /// FullShape = 0x00000004, ////// Roman Input Mode /// Roman = 0x00000008, ////// Roman Input Mode /// CharCode = 0x00000010, ////// No conversion /// NoConversion = 0x00000020, ////// EUDC symbol(bopomofo) Mode /// Eudc = 0x00000040, ////// Symbol Input Mode /// Symbol = 0x00000080, ////// Fixed Input Mode /// Fixed = 0x00000100, ////// Alphanumeric mode (Alphanumeric mode was 0x0 in Win32 IMM/Cicero). /// Alphanumeric = 0x00000200, ////// Mode is not set. It does not care. /// DoNotCare = unchecked((int)0x80000000), } //----------------------------------------------------- // // ImeSentenceModeValues enum // //------------------------------------------------------ ////// ImeSentenceModeValues /// [Flags] public enum ImeSentenceModeValues { ////// Non Sentence conversion /// None = 0x00000000, ////// PluralClause conversion /// PluralClause = 0x00000001, ////// Single Kanji/Hanja conversion /// SingleConversion = 0x00000002, ////// automatic conversion mode /// Automatic = 0x00000004, ////// phrase prediction mode /// PhrasePrediction = 0x00000008, ////// conversation style conversion mode /// Conversation = 0x00000010, ////// Mode is not set. It does not care. /// DoNotCare = unchecked((int)0x80000000), } //----------------------------------------------------- // // InputMethod class // //----------------------------------------------------- ////// The InputMethod class is a place holder for Cicero API, which are /// communicating or accessing TIP's properties. /// public class InputMethod : DispatcherObject { //----------------------------------------------------- // // Constructors // //------------------------------------------------------ internal InputMethod() { } //----------------------------------------------------- // // Static Initialization // //------------------------------------------------------ //------------------------------------------------------ // // Static Public Properties // //----------------------------------------------------- ////// A dependency property that enables alternative text inputs. /// public static readonly DependencyProperty IsInputMethodEnabledProperty = DependencyProperty.RegisterAttached( "IsInputMethodEnabled", typeof(bool), typeof(InputMethod), new PropertyMetadata( true, new PropertyChangedCallback(IsInputMethodEnabled_Changed))); ////// Setter for IsInputMethodEnabled DependencyProperty /// public static void SetIsInputMethodEnabled(DependencyObject target, bool value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(IsInputMethodEnabledProperty, value); } ////// Getter for IsInputMethodEnabled DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetIsInputMethodEnabled(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (bool)(target.GetValue(IsInputMethodEnabledProperty)); } ////// A dependency property that suspends alternative text inputs. /// If this property is true, the document focus remains in the previous focus element /// and the key events won't be dispatched into Cicero/IMEs. /// public static readonly DependencyProperty IsInputMethodSuspendedProperty = DependencyProperty.RegisterAttached( "IsInputMethodSuspended", typeof(bool), typeof(InputMethod), new PropertyMetadata(false)); ////// Setter for IsInputMethodSuspended DependencyProperty /// public static void SetIsInputMethodSuspended(DependencyObject target, bool value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(IsInputMethodSuspendedProperty, value); } ////// Getter for IsInputMethodSuspended DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetIsInputMethodSuspended(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (bool)(target.GetValue(IsInputMethodSuspendedProperty)); } ////// This is a property for UIElements such as TextBox. /// When the element gets the focus, the IME status is changed to /// the preferred state (open or close) /// public static readonly DependencyProperty PreferredImeStateProperty = DependencyProperty.RegisterAttached( "PreferredImeState", typeof(InputMethodState), typeof(InputMethod), new PropertyMetadata(InputMethodState.DoNotCare)); ////// Setter for PreferredImeState DependencyProperty /// public static void SetPreferredImeState(DependencyObject target, InputMethodState value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(PreferredImeStateProperty, value); } ////// Getter for PreferredImeState DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static InputMethodState GetPreferredImeState(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (InputMethodState)(target.GetValue(PreferredImeStateProperty)); } ////// This is a property for UIElements such as TextBox. /// When the element gets the focus, the IME conversion mode is changed to /// the preferred mode /// public static readonly DependencyProperty PreferredImeConversionModeProperty = DependencyProperty.RegisterAttached( "PreferredImeConversionMode", typeof(ImeConversionModeValues), typeof(InputMethod), new PropertyMetadata(ImeConversionModeValues.DoNotCare)); ////// Setter for PreferredImeConversionMode DependencyProperty /// public static void SetPreferredImeConversionMode(DependencyObject target, ImeConversionModeValues value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(PreferredImeConversionModeProperty, value); } ////// Getter for PreferredImeConversionMode DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static ImeConversionModeValues GetPreferredImeConversionMode(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (ImeConversionModeValues)(target.GetValue(PreferredImeConversionModeProperty)); } ////// This is a property for UIElements such as TextBox. /// When the element gets the focus, the IME sentence mode is changed to /// the preferred mode /// public static readonly DependencyProperty PreferredImeSentenceModeProperty = DependencyProperty.RegisterAttached( "PreferredImeSentenceMode", typeof(ImeSentenceModeValues), typeof(InputMethod), new PropertyMetadata(ImeSentenceModeValues.DoNotCare)); ////// Setter for PreferredImeSentenceMode DependencyProperty /// public static void SetPreferredImeSentenceMode(DependencyObject target, ImeSentenceModeValues value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(PreferredImeSentenceModeProperty, value); } ////// Getter for PreferredImeSentenceMode DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static ImeSentenceModeValues GetPreferredImeSentenceMode(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (ImeSentenceModeValues)(target.GetValue(PreferredImeSentenceModeProperty)); } ////// InputScope is the specified document context for UIElement. /// This is a property for UIElements such as TextBox. /// public static readonly DependencyProperty InputScopeProperty = DependencyProperty.RegisterAttached( "InputScope", typeof(InputScope), typeof(InputMethod), new PropertyMetadata((InputScope) null)); ////// Setter for InputScope DependencyProperty /// public static void SetInputScope(DependencyObject target, InputScope value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(InputScopeProperty, value); } ////// Getter for InputScope DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static InputScope GetInputScope(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (InputScope)(target.GetValue(InputScopeProperty)); } ////// Return the input language manager associated /// with the current context. /// public static InputMethod Current { get { InputMethod inputMethod = null; // Do not auto-create the dispatcher. Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread); if(dispatcher != null) { inputMethod = dispatcher.InputMethod as InputMethod; if (inputMethod == null) { inputMethod = new InputMethod(); dispatcher.InputMethod = inputMethod; } } return inputMethod; } } //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods ////// Show the configure UI of the current active keyboard text service. /// public void ShowConfigureUI() { ShowConfigureUI(null); } ////// Show the configure UI of the current active keyboard text service. /// /// /// Specify UIElement which frame window becomes the parent of the configure UI. /// This param can be null. /// public void ShowConfigureUI(UIElement element) { _ShowConfigureUI(element, true); } ////// Show the register word UI of the current active keyboard text service. /// public void ShowRegisterWordUI() { ShowRegisterWordUI(""); } ////// Show the register word UI of the current active keyboard text service. /// /// /// Specify default string to be registered. This is usually shown in the /// text field of the register word UI. /// public void ShowRegisterWordUI(string registeredText) { ShowRegisterWordUI(null, registeredText); } ////// Show the register word UI of the current active keyboard text service. /// /// /// Specify UIElement which frame window becomes the parent of the configure UI. /// This param can be null. /// /// /// Specify default string to be registered. This is usually shown in the /// text field of the register word UI. /// public void ShowRegisterWordUI(UIElement element, string registeredText) { _ShowRegisterWordUI(element, true, registeredText); } #endregion Public Methods //----------------------------------------------------- // // Public Operators // //----------------------------------------------------- //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- ////// Access the current keyboard on/off (open/close) status. /// ////// Critical - calls unmanaged code to access the IME /// PublicOK - adjusts the IME state, safe to do at anytime (only DOS possible) /// public InputMethodState ImeState { [SecurityCritical ] get { if (!IsImm32ImeCurrent()) { // // If the current hkl is not the real IMM32-IME, we get the open status from Cicero. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeState); if (compartment != null) { return compartment.BooleanValue ? InputMethodState.On : InputMethodState.Off; } } else { // // If the current hkl is the real IMM32-IME, we call IMM32 API to get the open status. // IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); bool fOpen = UnsafeNativeMethods.ImmGetOpenStatus(new HandleRef(this, himc)); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); return fOpen ? InputMethodState.On : InputMethodState.Off; } } return InputMethodState.Off; } [SecurityCritical ] set { Debug.Assert(value != InputMethodState.DoNotCare); // // Update Cicero's keyboard Open/Close status. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeState); if (compartment != null) { // we don't have to set compartment unless the value is changed. if (compartment.BooleanValue != (value == InputMethodState.On)) { compartment.BooleanValue = (value == InputMethodState.On); } } // // Under IMM32 enabled system, we call IMM32 API to update open status as well as Cicero // if (_immEnabled) { IntPtr hwnd = IntPtr.Zero; hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); bool fOpen = UnsafeNativeMethods.ImmGetOpenStatus(new HandleRef(this, himc)); // we don't have to call IMM unless the value is changed. if (fOpen != (value == InputMethodState.On)) { UnsafeNativeMethods.ImmSetOpenStatus(new HandleRef(this, himc), (value == InputMethodState.On)); } UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } } } } ////// Access the current microphone on/off status. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical: MicrophoneState may update the global compartment that may effects all threads in /// the desktop. /// It should be shown only with unrestricted UI permission. /// PublicOK: There is a link demand. (safe to get it.) /// public InputMethodState MicrophoneState { [SecurityCritical] get { TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.MicrophoneState); if (compartment != null) { return compartment.BooleanValue ? InputMethodState.On : InputMethodState.Off; } return InputMethodState.Off; } [SecurityCritical] set { SecurityHelper.DemandUnrestrictedUIPermission(); Debug.Assert(value != InputMethodState.DoNotCare); TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.MicrophoneState); if (compartment != null) { // we don't have to set compartment unless the value is changed. if (compartment.BooleanValue != (value == InputMethodState.On)) { compartment.BooleanValue = (value == InputMethodState.On); } } } } ////// Access the current handwriting on/off status. /// ////// Critical - calls unmanaged code to access the IME /// PublicOK - adjusts the HW state, safe to do at anytime (only DOS possible) /// public InputMethodState HandwritingState { [SecurityCritical] get { TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.HandwritingState); if (compartment != null) { return compartment.BooleanValue ? InputMethodState.On : InputMethodState.Off; } return InputMethodState.Off; } [SecurityCritical] set { Debug.Assert(value != InputMethodState.DoNotCare); TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.HandwritingState); if (compartment != null) { // we don't have to set compartment unless the value is changed. if (compartment.BooleanValue != (value == InputMethodState.On)) { compartment.BooleanValue = (value == InputMethodState.On); } } } } ////// Access the current speech mode /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical: SpeechMode may update the global compartment that may effects all threads in /// the desktop. /// It should be shown only with unrestricted UI permission. /// PublicOK: There is a link demand. (safe to get it.) /// public SpeechMode SpeechMode { [SecurityCritical] get { TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.SpeechMode); if (compartment != null) { int nValue = compartment.IntValue; if ((nValue & UnsafeNativeMethods.TF_DICTATION_ON) != 0) return SpeechMode.Dictation; if ((nValue & UnsafeNativeMethods.TF_COMMANDING_ON) != 0) return SpeechMode.Command; } return SpeechMode.Indeterminate; } [SecurityCritical] set { SecurityHelper.DemandUnrestrictedUIPermission(); TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.SpeechMode); if (compartment != null) { int nValue = compartment.IntValue; if (value == SpeechMode.Dictation) { nValue &= ~UnsafeNativeMethods.TF_COMMANDING_ON; nValue |= UnsafeNativeMethods.TF_DICTATION_ON; // we don't have to set compartment unless the value is changed. if (compartment.IntValue != nValue) { compartment.IntValue = nValue; } } else if (value == SpeechMode.Command) { nValue &= ~UnsafeNativeMethods.TF_DICTATION_ON; nValue |= UnsafeNativeMethods.TF_COMMANDING_ON; // we don't have to set compartment unless the value is changed. if (compartment.IntValue != nValue) { compartment.IntValue = nValue; } } else { Debug.Assert(false, "Unknown Speech Mode"); } } } } ////// Access the current ime conversion mode /// ////// Critical - calls unmanaged code (direct query of IME) /// PublicOK - current conversion mode is safe to expose /// public ImeConversionModeValues ImeConversionMode { [SecurityCritical ] get { if (!IsImm32ImeCurrent()) { // // If the current hkl is not the real IMM32-IME, we get the conversion status from Cicero. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeConversionModeValues); if (compartment != null) { UnsafeNativeMethods.ConversionModeFlags convmode = (UnsafeNativeMethods.ConversionModeFlags)compartment.IntValue; ImeConversionModeValues ret = 0; if ((convmode & (UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA)) == 0) ret |= ImeConversionModeValues.Alphanumeric; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE) != 0) ret |= ImeConversionModeValues.Native; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA) != 0) ret |= ImeConversionModeValues.Katakana; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE) != 0) ret |= ImeConversionModeValues.FullShape; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ROMAN) != 0) ret |= ImeConversionModeValues.Roman; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_CHARCODE) != 0) ret |= ImeConversionModeValues.CharCode; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NOCONVERSION) != 0) ret |= ImeConversionModeValues.NoConversion; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_EUDC) != 0) ret |= ImeConversionModeValues.Eudc; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_SYMBOL) != 0) ret |= ImeConversionModeValues.Symbol; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FIXED) != 0) ret |= ImeConversionModeValues.Fixed; return ret; } } else { // // If the current hkl is the real IMM32-IME, we call IMM32 API to get the conversion status. // IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); ImeConversionModeValues ret = 0; if ((convmode & (NativeMethods.IME_CMODE_NATIVE | NativeMethods.IME_CMODE_KATAKANA)) == 0) ret |= ImeConversionModeValues.Alphanumeric; if ((convmode & NativeMethods.IME_CMODE_NATIVE) != 0) ret |= ImeConversionModeValues.Native; if ((convmode & NativeMethods.IME_CMODE_KATAKANA) != 0) ret |= ImeConversionModeValues.Katakana; if ((convmode & NativeMethods.IME_CMODE_FULLSHAPE) != 0) ret |= ImeConversionModeValues.FullShape; if ((convmode & NativeMethods.IME_CMODE_ROMAN) != 0) ret |= ImeConversionModeValues.Roman; if ((convmode & NativeMethods.IME_CMODE_CHARCODE) != 0) ret |= ImeConversionModeValues.CharCode; if ((convmode & NativeMethods.IME_CMODE_NOCONVERSION) != 0) ret |= ImeConversionModeValues.NoConversion; if ((convmode & NativeMethods.IME_CMODE_EUDC) != 0) ret |= ImeConversionModeValues.Eudc; if ((convmode & NativeMethods.IME_CMODE_SYMBOL) != 0) ret |= ImeConversionModeValues.Symbol; if ((convmode & NativeMethods.IME_CMODE_FIXED) != 0) ret |= ImeConversionModeValues.Fixed; return ret; } } return ImeConversionModeValues.Alphanumeric; } [SecurityCritical ] set { if (!IsValidConversionMode(value)) { throw new ArgumentException(SR.Get(SRID.InputMethod_InvalidConversionMode, value)); } Debug.Assert((value & ImeConversionModeValues.DoNotCare) == 0); IntPtr hwnd = IntPtr.Zero; if (_immEnabled) { hwnd = HwndFromInputElement(Keyboard.FocusedElement); } // // Update Cicero's conversion mode. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeConversionModeValues); if (compartment != null) { UnsafeNativeMethods.ConversionModeFlags currentConvMode; if (_immEnabled) { currentConvMode = Imm32ConversionModeToTSFConversionMode(hwnd); } else { currentConvMode = (UnsafeNativeMethods.ConversionModeFlags)compartment.IntValue; } UnsafeNativeMethods.ConversionModeFlags convmode = 0; // TF_CONVERSIONMODE_ALPHANUMERIC is 0. // if ((value & ImeConversionModeValues.Alphanumeric) != 0) // convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ALPHANUMERIC; if ((value & ImeConversionModeValues.Native) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; if ((value & ImeConversionModeValues.Katakana) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA; if ((value & ImeConversionModeValues.FullShape) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; if ((value & ImeConversionModeValues.Roman) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ROMAN; if ((value & ImeConversionModeValues.CharCode) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_CHARCODE; if ((value & ImeConversionModeValues.NoConversion) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NOCONVERSION; if ((value & ImeConversionModeValues.Eudc) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_EUDC; if ((value & ImeConversionModeValues.Symbol) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_SYMBOL; if ((value & ImeConversionModeValues.Fixed) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FIXED; // We don't have to set the value unless the value is changed. if (currentConvMode != convmode) { UnsafeNativeMethods.ConversionModeFlags conversionModeClearBit = 0; if (convmode == (UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE)) { // Chinese, Hiragana or Korean so clear Katakana conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA; } else if (convmode == (UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE)) { // Katakana Half conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; } else if (convmode == UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE) { // Alpha Full conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; } else if (convmode == UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ALPHANUMERIC) { // Alpha Half conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; } else if (convmode == UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE) { // Hangul conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; } // Set the new conversion mode bit and apply the clear bit convmode |= currentConvMode; convmode &= ~conversionModeClearBit; compartment.IntValue = (int)convmode; } } // // Under IMM32 enabled system, we call IMM32 API to update conversion status as well as Cicero // if (_immEnabled) { if (hwnd != IntPtr.Zero) { int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); int convmodeNew = 0; // IME_CMODE_ALPHANUMERIC is 0. // if ((value & ImeConversionModeValues.Alphanumeric) != 0) // convmodeNew |= NativeMethods.IME_CMODE_ALPHANUMERIC; if ((value & ImeConversionModeValues.Native) != 0) convmodeNew |= NativeMethods.IME_CMODE_NATIVE; if ((value & ImeConversionModeValues.Katakana) != 0) convmodeNew |= NativeMethods.IME_CMODE_KATAKANA; if ((value & ImeConversionModeValues.FullShape) != 0) convmodeNew |= NativeMethods.IME_CMODE_FULLSHAPE; if ((value & ImeConversionModeValues.Roman) != 0) convmodeNew |= NativeMethods.IME_CMODE_ROMAN; if ((value & ImeConversionModeValues.CharCode) != 0) convmodeNew |= NativeMethods.IME_CMODE_CHARCODE; if ((value & ImeConversionModeValues.NoConversion) != 0) convmodeNew |= NativeMethods.IME_CMODE_NOCONVERSION; if ((value & ImeConversionModeValues.Eudc) != 0) convmodeNew |= NativeMethods.IME_CMODE_EUDC; if ((value & ImeConversionModeValues.Symbol) != 0) convmodeNew |= NativeMethods.IME_CMODE_SYMBOL; if ((value & ImeConversionModeValues.Fixed) != 0) convmodeNew |= NativeMethods.IME_CMODE_FIXED; // We don't have to call IMM unless the value is changed. if (convmode != convmodeNew) { int conversionModeClearBit = 0; if (convmodeNew == (NativeMethods.IME_CMODE_NATIVE | NativeMethods.IME_CMODE_FULLSHAPE)) { // Chinese, Hiragana or Korean so clear Katakana conversionModeClearBit = NativeMethods.IME_CMODE_KATAKANA; } else if (convmodeNew == (NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE)) { // Katakana Half conversionModeClearBit = NativeMethods.IME_CMODE_FULLSHAPE; } else if (convmodeNew == NativeMethods.IME_CMODE_FULLSHAPE) { // Alpha Full conversionModeClearBit = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; } else if (convmodeNew == NativeMethods.IME_CMODE_ALPHANUMERIC) { // Alpha Half conversionModeClearBit = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; } else if (convmodeNew == NativeMethods.IME_CMODE_NATIVE) { // Hangul conversionModeClearBit = NativeMethods.IME_CMODE_FULLSHAPE; } // Set the new conversion mode bit and apply the clear bit convmodeNew |= convmode; convmodeNew &= ~conversionModeClearBit; UnsafeNativeMethods.ImmSetConversionStatus(new HandleRef(this, himc), convmodeNew, sentence); } UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } } } } ////// Access the current ime sentence mode /// ////// Critical - calls unmanaged code to access the IME /// PublicOK - only allows setting the sentence mode, which is safe /// public ImeSentenceModeValues ImeSentenceMode { [SecurityCritical ] get { if (!IsImm32ImeCurrent()) { // // If the current hkl is not the real IMM32-IME, we get the sentence status from Cicero. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeSentenceModeValues); if (compartment != null) { UnsafeNativeMethods.SentenceModeFlags convmode = (UnsafeNativeMethods.SentenceModeFlags)compartment.IntValue; ImeSentenceModeValues ret = 0; // TF_SENTENCEMODE_ALPHANUMERIC is 0. if (convmode == UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_NONE) return ImeSentenceModeValues.None; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PLAURALCLAUSE) != 0) ret |= ImeSentenceModeValues.PluralClause; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_SINGLECONVERT) != 0) ret |= ImeSentenceModeValues.SingleConversion; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_AUTOMATIC) != 0) ret |= ImeSentenceModeValues.Automatic; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PHRASEPREDICT) != 0) ret |= ImeSentenceModeValues.PhrasePrediction; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_CONVERSATION) != 0) ret |= ImeSentenceModeValues.Conversation; return ret; } } else { // // If the current hkl is the real IMM32-IME, we call IMM32 API to get the sentence status. // IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { ImeSentenceModeValues ret = 0; int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); // TF_SENTENCEMODE_ALPHANUMERIC is 0. if (sentence == NativeMethods.IME_SMODE_NONE) return ImeSentenceModeValues.None; if ((sentence & NativeMethods.IME_SMODE_PLAURALCLAUSE) != 0) ret |= ImeSentenceModeValues.PluralClause; if ((sentence & NativeMethods.IME_SMODE_SINGLECONVERT) != 0) ret |= ImeSentenceModeValues.SingleConversion; if ((sentence & NativeMethods.IME_SMODE_AUTOMATIC) != 0) ret |= ImeSentenceModeValues.Automatic; if ((sentence & NativeMethods.IME_SMODE_PHRASEPREDICT) != 0) ret |= ImeSentenceModeValues.PhrasePrediction; if ((sentence & NativeMethods.IME_SMODE_CONVERSATION) != 0) ret |= ImeSentenceModeValues.Conversation; return ret; } } return ImeSentenceModeValues.None; } [SecurityCritical ] set { if (!IsValidSentenceMode(value)) { throw new ArgumentException(SR.Get(SRID.InputMethod_InvalidSentenceMode, value)); } Debug.Assert((value & ImeSentenceModeValues.DoNotCare) == 0); // // Update Cicero's sentence mode. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeSentenceModeValues); if (compartment != null) { UnsafeNativeMethods.SentenceModeFlags convmode = 0; if ((value & ImeSentenceModeValues.PluralClause) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PLAURALCLAUSE; if ((value & ImeSentenceModeValues.SingleConversion) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_SINGLECONVERT; if ((value & ImeSentenceModeValues.Automatic) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_AUTOMATIC; if ((value & ImeSentenceModeValues.PhrasePrediction) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PHRASEPREDICT; if ((value & ImeSentenceModeValues.Conversation) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_CONVERSATION; // We don't have to set the value unless the value is changed. if (compartment.IntValue != (int)convmode) { compartment.IntValue = (int)convmode; } } // // Under IMM32 enabled system, we call IMM32 API to update sentence status as well as Cicero // if (_immEnabled) { IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); int sentenceNew = 0; if ((value & ImeSentenceModeValues.PluralClause) != 0) sentenceNew |= NativeMethods.IME_SMODE_PLAURALCLAUSE; if ((value & ImeSentenceModeValues.SingleConversion) != 0) sentenceNew |= NativeMethods.IME_SMODE_SINGLECONVERT; if ((value & ImeSentenceModeValues.Automatic) != 0) sentenceNew |= NativeMethods.IME_SMODE_AUTOMATIC; if ((value & ImeSentenceModeValues.PhrasePrediction) != 0) sentenceNew |= NativeMethods.IME_SMODE_PHRASEPREDICT; if ((value & ImeSentenceModeValues.Conversation) != 0) sentenceNew |= NativeMethods.IME_SMODE_CONVERSATION; // We don't have to call IMM unless the value is changed. if (sentence != sentenceNew) { UnsafeNativeMethods.ImmSetConversionStatus(new HandleRef(this, himc), convmode, sentenceNew); } UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } } } } ////// This is a property that indicates if the current keybaord text services /// can show the configure UI. /// public bool CanShowConfigurationUI { get { return _ShowConfigureUI(null, false); } } ////// This is a property that indicates if the current keybaord text services /// can show the register word UI. /// public bool CanShowRegisterWordUI { get { return _ShowRegisterWordUI(null, false, ""); } } //------------------------------------------------------ // // Public Events // //------------------------------------------------------ ////// An event for input method state changed. /// public event InputMethodStateChangedEventHandler StateChanged { add { if (value == null) { throw new ArgumentNullException("value"); } // Advise compartment event sink to Win32 Cicero only when someone // has StateChanged event handler. if ((_StateChanged == null) && TextServicesLoader.ServicesInstalled) { InitializeCompartmentEventSink(); } _StateChanged += value; } remove { if (value == null) { throw new ArgumentNullException("value"); } _StateChanged -= value; if ((_StateChanged == null) && TextServicesLoader.ServicesInstalled) { // Unadvise compartment event sink to Win32 Cicero if none has StateChanged event handler. UninitializeCompartmentEventSink(); } } } //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods ////// When keyboard device gets focus, this is called. /// We will check the preferred input statues of focus element. /// The preferred input methods should be applied after Cicero TIP gots SetFocus callback. /// internal void GotKeyboardFocus(DependencyObject focus) { object value; if (focus == null) return; // // Check the InputLanguageProperty of the focus element. // value = focus.GetValue(PreferredImeStateProperty); if ((value != null) && ((InputMethodState)value != InputMethodState.DoNotCare)) { ImeState = (InputMethodState)value; } value = focus.GetValue(PreferredImeConversionModeProperty); if ((value != null) && (((ImeConversionModeValues)value & ImeConversionModeValues.DoNotCare) == 0)) { ImeConversionMode = (ImeConversionModeValues)value; } value = focus.GetValue(PreferredImeSentenceModeProperty); if ((value != null) && (((ImeSentenceModeValues)value & ImeSentenceModeValues.DoNotCare) == 0)) { ImeSentenceMode = (ImeSentenceModeValues)value; } } ////// TextServicesCompartmentEventSink forwards OnChange evennt here. /// internal void OnChange(ref Guid rguid) { if (_StateChanged != null) { InputMethodStateType imtype = InputMethodEventTypeInfo.ToType(ref rguid); // Stability Review: Task#32415 // - No state to be restored even exception happens while this callback. _StateChanged(this, new InputMethodStateChangedEventArgs(imtype)); } } ////// return true if the current keyboard layout is a real IMM32-IME. /// internal static bool IsImm32ImeCurrent() { if (!_immEnabled) { return false; } IntPtr hkl = SafeNativeMethods.GetKeyboardLayout(0); return IsImm32Ime(hkl); } ////// return true if the keyboard layout is a real IMM32-IME. /// internal static bool IsImm32Ime(IntPtr hkl) { if (hkl == IntPtr.Zero) { return false; } return ((NativeMethods.IntPtrToInt32(hkl) & 0xf0000000) == 0xe0000000); } ////// This is call back for the IsInputMethodEnable property. /// private static void IsInputMethodEnabled_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { IInputElement inputElement = (IInputElement)d; if (inputElement == Keyboard.FocusedElement) { InputMethod.Current.EnableOrDisableInputMethod((bool) e.NewValue); } } ////// InputMethod enabling/disabling function. /// This takes care of both Cicero and IMM32. /// ////// Critical: This code calls into DefaultIMC which returns data (the hImc) retrieved under /// an elevation of privilige. /// TreatAsSafe: This method is safe to expose. /// [SecurityCritical,SecurityTreatAsSafe] internal void EnableOrDisableInputMethod(bool bEnabled) { // InputMethod enable/disabled status was changed on the current focus Element. if (TextServicesLoader.ServicesInstalled && TextServicesContext.DispatcherCurrent != null) { if (bEnabled) { // Enabled. SetFocus to the default text store. TextServicesContext.DispatcherCurrent.SetFocusOnDefaultTextStore(); } else { // Disabled. SetFocus to the empty dim. TextServicesContext.DispatcherCurrent.SetFocusOnEmptyDim(); } } // // Under IMM32 enabled system, we associate default hIMC or null hIMC. // if (_immEnabled) { IntPtr hwnd; hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (bEnabled) { // // Enabled. Use the default hIMC. // if (DefaultImc != IntPtr.Zero) { UnsafeNativeMethods.ImmAssociateContext(new HandleRef(this, hwnd), new HandleRef(this, _defaultImc.Value)); } } else { // // Disable. Use null hIMC. // UnsafeNativeMethods.ImmAssociateContext(new HandleRef(this, hwnd), new HandleRef(this, IntPtr.Zero)); } } } #endregion Internal methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties // Set and get the referrence of TextServicesCompartmentContext for // the Dispatcher's dispatcher thread. internal TextServicesContext TextServicesContext { get {return _textservicesContext;} set {_textservicesContext = value;} } // Set and get the per Dispatcher cache of TextServicesCompartmentContext internal TextServicesCompartmentContext TextServicesCompartmentContext { get {return _textservicesCompartmentContext;} set {_textservicesCompartmentContext = value;} } // Set and get the per Dispatcher cache of InputLanguageManager internal InputLanguageManager InputLanguageManager { get {return _inputlanguagemanager;} set {_inputlanguagemanager = value;} } // Set and get the per Dispatcher cache of DefaultTextStore internal DefaultTextStore DefaultTextStore { get {return _defaulttextstore;} set {_defaulttextstore = value;} } #endregion Internal Properties //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ ////// Converts Imm32 conversion mode values into TSF conversion mode values. /// ////// /// Critical - calls unmanaged Windowing and IME APIs. /// [SecurityCritical] private UnsafeNativeMethods.ConversionModeFlags Imm32ConversionModeToTSFConversionMode(IntPtr hwnd) { UnsafeNativeMethods.ConversionModeFlags convMode = 0; if (hwnd != IntPtr.Zero) { int immConvMode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref immConvMode, ref sentence); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); if ((immConvMode & NativeMethods.IME_CMODE_NATIVE) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; if ((immConvMode & NativeMethods.IME_CMODE_KATAKANA) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA; if ((immConvMode & NativeMethods.IME_CMODE_FULLSHAPE) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; if ((immConvMode & NativeMethods.IME_CMODE_ROMAN) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ROMAN; if ((immConvMode & NativeMethods.IME_CMODE_CHARCODE) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_CHARCODE; if ((immConvMode & NativeMethods.IME_CMODE_NOCONVERSION) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NOCONVERSION; if ((immConvMode & NativeMethods.IME_CMODE_EUDC) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_EUDC; if ((immConvMode & NativeMethods.IME_CMODE_SYMBOL) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_SYMBOL; if ((immConvMode & NativeMethods.IME_CMODE_FIXED) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FIXED; } return convMode; } ////// Initialize the sink for compartments /// Advice event sink to Cicero's compartment so we can get the notification /// of the compartment change. /// ////// Critical - accesses message pump/input manager directly /// TreatAsSafe - safe to uninitialize/initialize event sink (worst case is breaking input for the app) /// [SecurityCritical, SecurityTreatAsSafe] private void InitializeCompartmentEventSink() { for (int i = 0; i < InputMethodEventTypeInfo.InfoList.Length; i++) { InputMethodEventTypeInfo iminfo = InputMethodEventTypeInfo.InfoList[i]; TextServicesCompartment compartment = null; if (iminfo.Scope == CompartmentScope.Thread) compartment = TextServicesCompartmentContext.Current.GetThreadCompartment(iminfo.Guid); else if (iminfo.Scope == CompartmentScope.Global) compartment = TextServicesCompartmentContext.Current.GetGlobalCompartment(iminfo.Guid); if (compartment != null) { if (_sink == null) _sink = new TextServicesCompartmentEventSink(this); compartment.AdviseNotifySink(_sink); } } } ////// Uninitialize the sink for compartments /// Unadvise the cicero's compartment event sink. /// ////// Critical - accesses message pump/input manager directly /// TreatAsSafe - safe to uninitialize/initialize event sink (worst case is breaking input for the app) /// [SecurityCritical, SecurityTreatAsSafe] private void UninitializeCompartmentEventSink() { for (int i = 0; i < InputMethodEventTypeInfo.InfoList.Length; i++) { InputMethodEventTypeInfo iminfo = InputMethodEventTypeInfo.InfoList[i]; TextServicesCompartment compartment = null; if (iminfo.Scope == CompartmentScope.Thread) compartment = TextServicesCompartmentContext.Current.GetThreadCompartment(iminfo.Guid); else if (iminfo.Scope == CompartmentScope.Global) compartment = TextServicesCompartmentContext.Current.GetGlobalCompartment(iminfo.Guid); if (compartment != null) compartment.UnadviseNotifySink(); } } ////// Get ITfFnConfigure interface and call Show() method. /// If there is no function provider in the current keyboard TIP or the keyboard TIP does /// not have ITfFnConfigure, this returns false. /// ////// This code calls IME/TIP to show its configuration UI. /// Critical: The configuration UI usually have a capability to change session wide state. /// It should be shown only with unrestricted UI permission. /// TreatAsSafe: There is Demand. /// [SecurityCritical, SecurityTreatAsSafe] private bool _ShowConfigureUI(UIElement element, bool fShow) { SecurityHelper.DemandUnrestrictedUIPermission(); bool bCanShown = false; IntPtr hkl = SafeNativeMethods.GetKeyboardLayout(0); if (!IsImm32Ime(hkl)) { UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile; UnsafeNativeMethods.ITfFunctionProvider funcPrv = GetFunctionPrvForCurrentKeyboardTIP(out tf_profile); if (funcPrv != null) { UnsafeNativeMethods.ITfFnConfigure fnConfigure; // Readonly fields can not be passed ref to the interface methods. // Create pads for them. Guid iidFn = UnsafeNativeMethods.IID_ITfFnConfigure; Guid guidNull = UnsafeNativeMethods.Guid_Null; object obj; funcPrv.GetFunction(ref guidNull, ref iidFn, out obj); fnConfigure = obj as UnsafeNativeMethods.ITfFnConfigure; if (fnConfigure != null) { // We could get ITfFnConfigure, we can say the configure UI can be shown. bCanShown = true; if (fShow) { fnConfigure.Show(HwndFromInputElement(element), tf_profile.langid, ref tf_profile.guidProfile); } Marshal.ReleaseComObject(fnConfigure); } Marshal.ReleaseComObject(funcPrv); } } else { // There is no API to test if IMM32-IME can show the configure UI. We assume they can do. bCanShown = true; if (fShow) { UnsafeNativeMethods.ImmConfigureIME(new HandleRef(this, hkl), new HandleRef(this, HwndFromInputElement(element)), NativeMethods.IME_CONFIG_GENERAL, IntPtr.Zero); } } return bCanShown; } ////// Get ITfFnConfigureRegisterWord interface and call Show() method. /// If there is no function provider in the current keyboard TIP or the keyboard TIP does /// not have ITfFnConfigureRegisterWord, this returns false. /// ////// This code calls IME/TIP to show its register word UI. /// Critical: The word registration usually update the uesr custom dictionary. And the change /// of this custom dictionary may affect the conversion of whole desktop. /// It should be shown only with unrestricted UI permission. /// TreatAsSafe: There is Demand. /// [SecurityCritical, SecurityTreatAsSafe] private bool _ShowRegisterWordUI(UIElement element, bool fShow, string strRegister) { SecurityHelper.DemandUnrestrictedUIPermission(); bool bCanShown = false; IntPtr hkl = SafeNativeMethods.GetKeyboardLayout(0); if (!IsImm32Ime(hkl)) { UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile; UnsafeNativeMethods.ITfFunctionProvider funcPrv = GetFunctionPrvForCurrentKeyboardTIP(out tf_profile); if (funcPrv != null) { UnsafeNativeMethods.ITfFnConfigureRegisterWord fnConfigure; // Readonly fields can not be passed ref to the interface methods. // Create pads for them. Guid iidFn = UnsafeNativeMethods.IID_ITfFnConfigureRegisterWord; Guid guidNull = UnsafeNativeMethods.Guid_Null; object obj; funcPrv.GetFunction(ref guidNull, ref iidFn, out obj); fnConfigure = obj as UnsafeNativeMethods.ITfFnConfigureRegisterWord; if (fnConfigure != null) { // We could get ITfFnConfigureRegisterWord, we can say the configure UI can be shown. bCanShown = true; if (fShow) { fnConfigure.Show(HwndFromInputElement(element), tf_profile.langid, ref tf_profile.guidProfile, strRegister); } Marshal.ReleaseComObject(fnConfigure); } Marshal.ReleaseComObject(funcPrv); } } else { // There is no API to test if IMM32-IME can show the configure UI. We assume they can do. bCanShown = true; if (fShow) { NativeMethods.REGISTERWORD regWord = new NativeMethods.REGISTERWORD(); regWord.lpReading = null; regWord.lpWord = strRegister; UnsafeNativeMethods.ImmConfigureIME(new HandleRef(this, hkl), new HandleRef(this, HwndFromInputElement(element)), NativeMethods.IME_CONFIG_REGISTERWORD, ref regWord); } } return bCanShown; } ////// Get hwnd handle value as IntPtr from UIElement. /// ////// Critical: This code calls into CriticalFromVisual and also elevates.It exposes the handle. /// [SecurityCritical] private static IntPtr HwndFromInputElement(IInputElement element) { IntPtr hwnd = (IntPtr)0; // We allow null element. if (element != null) { DependencyObject o = element as DependencyObject; if (o != null) { DependencyObject containingVisual = InputElement.GetContainingVisual(o); if(containingVisual != null) { IWin32Window win32Window = null; PresentationSource source = PresentationSource.CriticalFromVisual(containingVisual); if (source != null) { win32Window = source as IWin32Window; if (win32Window != null) { (new UIPermission(UIPermissionWindow.AllWindows)).Assert();//Blessed Assert try { hwnd = win32Window.Handle; } finally { UIPermission.RevertAssert(); } } } } } } return hwnd; } ////// Get ITfFunctionProvider of the current active keyboard TIP. /// ////// Critical: This code calls into COM interop pointers (ITfFunctionProvider) /// TreatAsSafe: This code has a demand for unmanaged code /// [SecurityCritical,SecurityTreatAsSafe] private UnsafeNativeMethods.ITfFunctionProvider GetFunctionPrvForCurrentKeyboardTIP(out UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile) { SecurityHelper.DemandUnmanagedCode(); // Get the profile info structre of the current active keyboard TIP. tf_profile = GetCurrentKeybordTipProfile(); // Is tf_profile.clsid is Guid_Null, this is not Cicero TIP. No Function Provider. if (tf_profile.clsid.Equals(UnsafeNativeMethods.Guid_Null)) { return null; } UnsafeNativeMethods.ITfFunctionProvider functionPrv; // ThreadMgr Method call will be marshalled to the dispatcher thread since Ciecro is STA. // We release Dispatcher while sink call back. // This thread doesn't have to acceess UIContex until it returns. TextServicesContext textservicesContext = TextServicesContext.DispatcherCurrent; textservicesContext.ThreadManager.GetFunctionProvider(ref tf_profile.clsid, out functionPrv); return functionPrv; } ////// Return the profile info structre of the current active keyboard TIP. /// This enumelates all TIP's profiles and find the active keyboard category TIP. /// ////// Critical - calls unmanaged code to get the keyboard information /// TreatAsSafe - discovery of the input language is safe /// [SecurityCritical, SecurityTreatAsSafe] private UnsafeNativeMethods.TF_LANGUAGEPROFILE GetCurrentKeybordTipProfile() { UnsafeNativeMethods.ITfInputProcessorProfiles ipp = InputProcessorProfilesLoader.Load(); UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile = new UnsafeNativeMethods.TF_LANGUAGEPROFILE(); if (ipp != null) { CultureInfo inputLang = InputLanguageManager.Current.CurrentInputLanguage; UnsafeNativeMethods.IEnumTfLanguageProfiles enumIpp; ipp.EnumLanguageProfiles((short)(inputLang.LCID), out enumIpp); UnsafeNativeMethods.TF_LANGUAGEPROFILE[] tf_profiles = new UnsafeNativeMethods.TF_LANGUAGEPROFILE[1]; int fetched; while(enumIpp.Next(1, tf_profiles, out fetched) == NativeMethods.S_OK) { // Check if this profile is active. if (tf_profiles[0].fActive == true) { // Check if this profile is keyboard category.. if (tf_profiles[0].catid.Equals(UnsafeNativeMethods.GUID_TFCAT_TIP_KEYBOARD)) { tf_profile = tf_profiles[0]; break; } } } Marshal.ReleaseComObject(enumIpp); } return tf_profile; } // This validates the ImeConversionMode value. private bool IsValidConversionMode(ImeConversionModeValues mode) { int mask = (int)(ImeConversionModeValues.Alphanumeric | ImeConversionModeValues.Native | ImeConversionModeValues.Katakana | ImeConversionModeValues.FullShape | ImeConversionModeValues.Roman | ImeConversionModeValues.CharCode | ImeConversionModeValues.NoConversion | ImeConversionModeValues.Eudc | ImeConversionModeValues.Symbol | ImeConversionModeValues.Fixed | ImeConversionModeValues.DoNotCare); if (((int)mode & ~mask) != 0) return false; return true; } // This validates the ImeSentenceMode value. private bool IsValidSentenceMode(ImeSentenceModeValues mode) { int mask = (int)(ImeSentenceModeValues.None | ImeSentenceModeValues.PluralClause | ImeSentenceModeValues.SingleConversion | ImeSentenceModeValues.Automatic | ImeSentenceModeValues.PhrasePrediction | ImeSentenceModeValues.Conversation | ImeSentenceModeValues.DoNotCare); if (((int)mode & ~mask) != 0) return false; return true; } //------------------------------------------------------ // // Private Event // //----------------------------------------------------- private event InputMethodStateChangedEventHandler _StateChanged; //------------------------------------------------------ // // Static Private Properties // //----------------------------------------------------- ////// Critical: This code returns the input method context which is not safe to expose /// especially so because it can be used to get to Cicero (any input methods) /// and IME. It also retrieves the window handle for the default IME Window /// although this is not exposed. /// private IntPtr DefaultImc { [SecurityCritical] get { if (_defaultImc==null) { //this code causes elevation of privilige to unmanaged code permsission SecurityPermission sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); sp.Assert();//Blessed Assert try { // // Get the default HIMC from default IME window. // IntPtr hwnd = UnsafeNativeMethods.ImmGetDefaultIMEWnd(new HandleRef(this, IntPtr.Zero)); IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); // Store the default imc to _defaultImc. _defaultImc = new SecurityCriticalDataClass(himc); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } finally { SecurityPermission.RevertAssert(); } } return _defaultImc.Value; } } //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields // This is a slot to keep the implementaion of ITfCompartmentEventSink. private TextServicesCompartmentEventSink _sink; // Per Dispatcher Cache for TextServicesContext // The instance of TextServiesContext is per dispather thread. // But we put a reference here so that the current Dispatcher can access TextServicesContext. private TextServicesContext _textservicesContext; // Per Dispatcher Cache for TextServicesCompartmentContext private TextServicesCompartmentContext _textservicesCompartmentContext; // Per Dispatcher Cache for InputLanguageManager private InputLanguageManager _inputlanguagemanager; // Per Dispatcher Cache for DefaultTextStore private DefaultTextStore _defaulttextstore; // If the system is IMM enabled, this is true. private static bool _immEnabled = SafeSystemMetrics.IsImmEnabled ; // the default imc. The default imc is per thread and we cache it in ThreadStatic. [ThreadStatic] private static SecurityCriticalDataClass _defaultImc; #endregion Private Fields } //------------------------------------------------------ // // InputMethodStateChangedEventHandler delegate // //----------------------------------------------------- /// /// The delegate to use for handlers that receive /// input method state changed event. /// public delegate void InputMethodStateChangedEventHandler(Object sender, InputMethodStateChangedEventArgs e); } // 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: Manage Input Methods (EA-IME, TextServicesFramework). // // History: // 07/30/2003 : yutakas - Ported from .net tree. // //--------------------------------------------------------------------------- using System.Runtime.InteropServices; using System.Collections; using System.Diagnostics; using System.Globalization; using System.Security.Permissions; using System.Threading; using System.Windows.Threading; using System.Windows.Interop; using System.Windows.Media; using System.Security; using MS.Utility; using MS.Win32; using MS.Internal; using MS.Internal.PresentationCore; // SecurityHelper using System; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace System.Windows.Input { //----------------------------------------------------- // // InputMethodState enum // //----------------------------------------------------- ////// State of Ime /// public enum InputMethodState { ////// InputMethod state is on. /// Off = 0, ////// InputMethod state is on. /// On = 1, ////// InputMethod state is not set. It does not care. /// DoNotCare = 2, } //------------------------------------------------------ // // SpeechMode enum // //----------------------------------------------------- ////// Mode of speech /// public enum SpeechMode { ////// Speech is in dictation mode. /// Dictation, ////// Speech is in command mode. /// Command, ////// Speech mode is indeterminate. /// Indeterminate, } //------------------------------------------------------ // // ImeConversionModeValues enum // //------------------------------------------------------ ////// ImeConversionModeValues /// [Flags] public enum ImeConversionModeValues { ////// Native Mode (Hiragana, Hangul, Chinese) /// Native = 0x00000001, ////// Japanese Katakana Mode /// Katakana = 0x00000002, ////// Full Shape mode /// FullShape = 0x00000004, ////// Roman Input Mode /// Roman = 0x00000008, ////// Roman Input Mode /// CharCode = 0x00000010, ////// No conversion /// NoConversion = 0x00000020, ////// EUDC symbol(bopomofo) Mode /// Eudc = 0x00000040, ////// Symbol Input Mode /// Symbol = 0x00000080, ////// Fixed Input Mode /// Fixed = 0x00000100, ////// Alphanumeric mode (Alphanumeric mode was 0x0 in Win32 IMM/Cicero). /// Alphanumeric = 0x00000200, ////// Mode is not set. It does not care. /// DoNotCare = unchecked((int)0x80000000), } //----------------------------------------------------- // // ImeSentenceModeValues enum // //------------------------------------------------------ ////// ImeSentenceModeValues /// [Flags] public enum ImeSentenceModeValues { ////// Non Sentence conversion /// None = 0x00000000, ////// PluralClause conversion /// PluralClause = 0x00000001, ////// Single Kanji/Hanja conversion /// SingleConversion = 0x00000002, ////// automatic conversion mode /// Automatic = 0x00000004, ////// phrase prediction mode /// PhrasePrediction = 0x00000008, ////// conversation style conversion mode /// Conversation = 0x00000010, ////// Mode is not set. It does not care. /// DoNotCare = unchecked((int)0x80000000), } //----------------------------------------------------- // // InputMethod class // //----------------------------------------------------- ////// The InputMethod class is a place holder for Cicero API, which are /// communicating or accessing TIP's properties. /// public class InputMethod : DispatcherObject { //----------------------------------------------------- // // Constructors // //------------------------------------------------------ internal InputMethod() { } //----------------------------------------------------- // // Static Initialization // //------------------------------------------------------ //------------------------------------------------------ // // Static Public Properties // //----------------------------------------------------- ////// A dependency property that enables alternative text inputs. /// public static readonly DependencyProperty IsInputMethodEnabledProperty = DependencyProperty.RegisterAttached( "IsInputMethodEnabled", typeof(bool), typeof(InputMethod), new PropertyMetadata( true, new PropertyChangedCallback(IsInputMethodEnabled_Changed))); ////// Setter for IsInputMethodEnabled DependencyProperty /// public static void SetIsInputMethodEnabled(DependencyObject target, bool value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(IsInputMethodEnabledProperty, value); } ////// Getter for IsInputMethodEnabled DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetIsInputMethodEnabled(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (bool)(target.GetValue(IsInputMethodEnabledProperty)); } ////// A dependency property that suspends alternative text inputs. /// If this property is true, the document focus remains in the previous focus element /// and the key events won't be dispatched into Cicero/IMEs. /// public static readonly DependencyProperty IsInputMethodSuspendedProperty = DependencyProperty.RegisterAttached( "IsInputMethodSuspended", typeof(bool), typeof(InputMethod), new PropertyMetadata(false)); ////// Setter for IsInputMethodSuspended DependencyProperty /// public static void SetIsInputMethodSuspended(DependencyObject target, bool value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(IsInputMethodSuspendedProperty, value); } ////// Getter for IsInputMethodSuspended DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetIsInputMethodSuspended(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (bool)(target.GetValue(IsInputMethodSuspendedProperty)); } ////// This is a property for UIElements such as TextBox. /// When the element gets the focus, the IME status is changed to /// the preferred state (open or close) /// public static readonly DependencyProperty PreferredImeStateProperty = DependencyProperty.RegisterAttached( "PreferredImeState", typeof(InputMethodState), typeof(InputMethod), new PropertyMetadata(InputMethodState.DoNotCare)); ////// Setter for PreferredImeState DependencyProperty /// public static void SetPreferredImeState(DependencyObject target, InputMethodState value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(PreferredImeStateProperty, value); } ////// Getter for PreferredImeState DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static InputMethodState GetPreferredImeState(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (InputMethodState)(target.GetValue(PreferredImeStateProperty)); } ////// This is a property for UIElements such as TextBox. /// When the element gets the focus, the IME conversion mode is changed to /// the preferred mode /// public static readonly DependencyProperty PreferredImeConversionModeProperty = DependencyProperty.RegisterAttached( "PreferredImeConversionMode", typeof(ImeConversionModeValues), typeof(InputMethod), new PropertyMetadata(ImeConversionModeValues.DoNotCare)); ////// Setter for PreferredImeConversionMode DependencyProperty /// public static void SetPreferredImeConversionMode(DependencyObject target, ImeConversionModeValues value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(PreferredImeConversionModeProperty, value); } ////// Getter for PreferredImeConversionMode DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static ImeConversionModeValues GetPreferredImeConversionMode(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (ImeConversionModeValues)(target.GetValue(PreferredImeConversionModeProperty)); } ////// This is a property for UIElements such as TextBox. /// When the element gets the focus, the IME sentence mode is changed to /// the preferred mode /// public static readonly DependencyProperty PreferredImeSentenceModeProperty = DependencyProperty.RegisterAttached( "PreferredImeSentenceMode", typeof(ImeSentenceModeValues), typeof(InputMethod), new PropertyMetadata(ImeSentenceModeValues.DoNotCare)); ////// Setter for PreferredImeSentenceMode DependencyProperty /// public static void SetPreferredImeSentenceMode(DependencyObject target, ImeSentenceModeValues value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(PreferredImeSentenceModeProperty, value); } ////// Getter for PreferredImeSentenceMode DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static ImeSentenceModeValues GetPreferredImeSentenceMode(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (ImeSentenceModeValues)(target.GetValue(PreferredImeSentenceModeProperty)); } ////// InputScope is the specified document context for UIElement. /// This is a property for UIElements such as TextBox. /// public static readonly DependencyProperty InputScopeProperty = DependencyProperty.RegisterAttached( "InputScope", typeof(InputScope), typeof(InputMethod), new PropertyMetadata((InputScope) null)); ////// Setter for InputScope DependencyProperty /// public static void SetInputScope(DependencyObject target, InputScope value) { if (target == null) { throw new ArgumentNullException("target"); } target.SetValue(InputScopeProperty, value); } ////// Getter for InputScope DependencyProperty /// [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static InputScope GetInputScope(DependencyObject target) { if (target == null) { throw new ArgumentNullException("target"); } return (InputScope)(target.GetValue(InputScopeProperty)); } ////// Return the input language manager associated /// with the current context. /// public static InputMethod Current { get { InputMethod inputMethod = null; // Do not auto-create the dispatcher. Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread); if(dispatcher != null) { inputMethod = dispatcher.InputMethod as InputMethod; if (inputMethod == null) { inputMethod = new InputMethod(); dispatcher.InputMethod = inputMethod; } } return inputMethod; } } //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods ////// Show the configure UI of the current active keyboard text service. /// public void ShowConfigureUI() { ShowConfigureUI(null); } ////// Show the configure UI of the current active keyboard text service. /// /// /// Specify UIElement which frame window becomes the parent of the configure UI. /// This param can be null. /// public void ShowConfigureUI(UIElement element) { _ShowConfigureUI(element, true); } ////// Show the register word UI of the current active keyboard text service. /// public void ShowRegisterWordUI() { ShowRegisterWordUI(""); } ////// Show the register word UI of the current active keyboard text service. /// /// /// Specify default string to be registered. This is usually shown in the /// text field of the register word UI. /// public void ShowRegisterWordUI(string registeredText) { ShowRegisterWordUI(null, registeredText); } ////// Show the register word UI of the current active keyboard text service. /// /// /// Specify UIElement which frame window becomes the parent of the configure UI. /// This param can be null. /// /// /// Specify default string to be registered. This is usually shown in the /// text field of the register word UI. /// public void ShowRegisterWordUI(UIElement element, string registeredText) { _ShowRegisterWordUI(element, true, registeredText); } #endregion Public Methods //----------------------------------------------------- // // Public Operators // //----------------------------------------------------- //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- ////// Access the current keyboard on/off (open/close) status. /// ////// Critical - calls unmanaged code to access the IME /// PublicOK - adjusts the IME state, safe to do at anytime (only DOS possible) /// public InputMethodState ImeState { [SecurityCritical ] get { if (!IsImm32ImeCurrent()) { // // If the current hkl is not the real IMM32-IME, we get the open status from Cicero. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeState); if (compartment != null) { return compartment.BooleanValue ? InputMethodState.On : InputMethodState.Off; } } else { // // If the current hkl is the real IMM32-IME, we call IMM32 API to get the open status. // IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); bool fOpen = UnsafeNativeMethods.ImmGetOpenStatus(new HandleRef(this, himc)); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); return fOpen ? InputMethodState.On : InputMethodState.Off; } } return InputMethodState.Off; } [SecurityCritical ] set { Debug.Assert(value != InputMethodState.DoNotCare); // // Update Cicero's keyboard Open/Close status. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeState); if (compartment != null) { // we don't have to set compartment unless the value is changed. if (compartment.BooleanValue != (value == InputMethodState.On)) { compartment.BooleanValue = (value == InputMethodState.On); } } // // Under IMM32 enabled system, we call IMM32 API to update open status as well as Cicero // if (_immEnabled) { IntPtr hwnd = IntPtr.Zero; hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); bool fOpen = UnsafeNativeMethods.ImmGetOpenStatus(new HandleRef(this, himc)); // we don't have to call IMM unless the value is changed. if (fOpen != (value == InputMethodState.On)) { UnsafeNativeMethods.ImmSetOpenStatus(new HandleRef(this, himc), (value == InputMethodState.On)); } UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } } } } ////// Access the current microphone on/off status. /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical: MicrophoneState may update the global compartment that may effects all threads in /// the desktop. /// It should be shown only with unrestricted UI permission. /// PublicOK: There is a link demand. (safe to get it.) /// public InputMethodState MicrophoneState { [SecurityCritical] get { TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.MicrophoneState); if (compartment != null) { return compartment.BooleanValue ? InputMethodState.On : InputMethodState.Off; } return InputMethodState.Off; } [SecurityCritical] set { SecurityHelper.DemandUnrestrictedUIPermission(); Debug.Assert(value != InputMethodState.DoNotCare); TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.MicrophoneState); if (compartment != null) { // we don't have to set compartment unless the value is changed. if (compartment.BooleanValue != (value == InputMethodState.On)) { compartment.BooleanValue = (value == InputMethodState.On); } } } } ////// Access the current handwriting on/off status. /// ////// Critical - calls unmanaged code to access the IME /// PublicOK - adjusts the HW state, safe to do at anytime (only DOS possible) /// public InputMethodState HandwritingState { [SecurityCritical] get { TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.HandwritingState); if (compartment != null) { return compartment.BooleanValue ? InputMethodState.On : InputMethodState.Off; } return InputMethodState.Off; } [SecurityCritical] set { Debug.Assert(value != InputMethodState.DoNotCare); TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.HandwritingState); if (compartment != null) { // we don't have to set compartment unless the value is changed. if (compartment.BooleanValue != (value == InputMethodState.On)) { compartment.BooleanValue = (value == InputMethodState.On); } } } } ////// Access the current speech mode /// ////// Callers must have UIPermission(PermissionState.Unrestricted) to call this API. /// ////// Critical: SpeechMode may update the global compartment that may effects all threads in /// the desktop. /// It should be shown only with unrestricted UI permission. /// PublicOK: There is a link demand. (safe to get it.) /// public SpeechMode SpeechMode { [SecurityCritical] get { TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.SpeechMode); if (compartment != null) { int nValue = compartment.IntValue; if ((nValue & UnsafeNativeMethods.TF_DICTATION_ON) != 0) return SpeechMode.Dictation; if ((nValue & UnsafeNativeMethods.TF_COMMANDING_ON) != 0) return SpeechMode.Command; } return SpeechMode.Indeterminate; } [SecurityCritical] set { SecurityHelper.DemandUnrestrictedUIPermission(); TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.SpeechMode); if (compartment != null) { int nValue = compartment.IntValue; if (value == SpeechMode.Dictation) { nValue &= ~UnsafeNativeMethods.TF_COMMANDING_ON; nValue |= UnsafeNativeMethods.TF_DICTATION_ON; // we don't have to set compartment unless the value is changed. if (compartment.IntValue != nValue) { compartment.IntValue = nValue; } } else if (value == SpeechMode.Command) { nValue &= ~UnsafeNativeMethods.TF_DICTATION_ON; nValue |= UnsafeNativeMethods.TF_COMMANDING_ON; // we don't have to set compartment unless the value is changed. if (compartment.IntValue != nValue) { compartment.IntValue = nValue; } } else { Debug.Assert(false, "Unknown Speech Mode"); } } } } ////// Access the current ime conversion mode /// ////// Critical - calls unmanaged code (direct query of IME) /// PublicOK - current conversion mode is safe to expose /// public ImeConversionModeValues ImeConversionMode { [SecurityCritical ] get { if (!IsImm32ImeCurrent()) { // // If the current hkl is not the real IMM32-IME, we get the conversion status from Cicero. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeConversionModeValues); if (compartment != null) { UnsafeNativeMethods.ConversionModeFlags convmode = (UnsafeNativeMethods.ConversionModeFlags)compartment.IntValue; ImeConversionModeValues ret = 0; if ((convmode & (UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA)) == 0) ret |= ImeConversionModeValues.Alphanumeric; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE) != 0) ret |= ImeConversionModeValues.Native; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA) != 0) ret |= ImeConversionModeValues.Katakana; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE) != 0) ret |= ImeConversionModeValues.FullShape; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ROMAN) != 0) ret |= ImeConversionModeValues.Roman; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_CHARCODE) != 0) ret |= ImeConversionModeValues.CharCode; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NOCONVERSION) != 0) ret |= ImeConversionModeValues.NoConversion; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_EUDC) != 0) ret |= ImeConversionModeValues.Eudc; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_SYMBOL) != 0) ret |= ImeConversionModeValues.Symbol; if ((convmode & UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FIXED) != 0) ret |= ImeConversionModeValues.Fixed; return ret; } } else { // // If the current hkl is the real IMM32-IME, we call IMM32 API to get the conversion status. // IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); ImeConversionModeValues ret = 0; if ((convmode & (NativeMethods.IME_CMODE_NATIVE | NativeMethods.IME_CMODE_KATAKANA)) == 0) ret |= ImeConversionModeValues.Alphanumeric; if ((convmode & NativeMethods.IME_CMODE_NATIVE) != 0) ret |= ImeConversionModeValues.Native; if ((convmode & NativeMethods.IME_CMODE_KATAKANA) != 0) ret |= ImeConversionModeValues.Katakana; if ((convmode & NativeMethods.IME_CMODE_FULLSHAPE) != 0) ret |= ImeConversionModeValues.FullShape; if ((convmode & NativeMethods.IME_CMODE_ROMAN) != 0) ret |= ImeConversionModeValues.Roman; if ((convmode & NativeMethods.IME_CMODE_CHARCODE) != 0) ret |= ImeConversionModeValues.CharCode; if ((convmode & NativeMethods.IME_CMODE_NOCONVERSION) != 0) ret |= ImeConversionModeValues.NoConversion; if ((convmode & NativeMethods.IME_CMODE_EUDC) != 0) ret |= ImeConversionModeValues.Eudc; if ((convmode & NativeMethods.IME_CMODE_SYMBOL) != 0) ret |= ImeConversionModeValues.Symbol; if ((convmode & NativeMethods.IME_CMODE_FIXED) != 0) ret |= ImeConversionModeValues.Fixed; return ret; } } return ImeConversionModeValues.Alphanumeric; } [SecurityCritical ] set { if (!IsValidConversionMode(value)) { throw new ArgumentException(SR.Get(SRID.InputMethod_InvalidConversionMode, value)); } Debug.Assert((value & ImeConversionModeValues.DoNotCare) == 0); IntPtr hwnd = IntPtr.Zero; if (_immEnabled) { hwnd = HwndFromInputElement(Keyboard.FocusedElement); } // // Update Cicero's conversion mode. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeConversionModeValues); if (compartment != null) { UnsafeNativeMethods.ConversionModeFlags currentConvMode; if (_immEnabled) { currentConvMode = Imm32ConversionModeToTSFConversionMode(hwnd); } else { currentConvMode = (UnsafeNativeMethods.ConversionModeFlags)compartment.IntValue; } UnsafeNativeMethods.ConversionModeFlags convmode = 0; // TF_CONVERSIONMODE_ALPHANUMERIC is 0. // if ((value & ImeConversionModeValues.Alphanumeric) != 0) // convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ALPHANUMERIC; if ((value & ImeConversionModeValues.Native) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; if ((value & ImeConversionModeValues.Katakana) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA; if ((value & ImeConversionModeValues.FullShape) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; if ((value & ImeConversionModeValues.Roman) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ROMAN; if ((value & ImeConversionModeValues.CharCode) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_CHARCODE; if ((value & ImeConversionModeValues.NoConversion) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NOCONVERSION; if ((value & ImeConversionModeValues.Eudc) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_EUDC; if ((value & ImeConversionModeValues.Symbol) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_SYMBOL; if ((value & ImeConversionModeValues.Fixed) != 0) convmode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FIXED; // We don't have to set the value unless the value is changed. if (currentConvMode != convmode) { UnsafeNativeMethods.ConversionModeFlags conversionModeClearBit = 0; if (convmode == (UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE)) { // Chinese, Hiragana or Korean so clear Katakana conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA; } else if (convmode == (UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE)) { // Katakana Half conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; } else if (convmode == UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE) { // Alpha Full conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; } else if (convmode == UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ALPHANUMERIC) { // Alpha Half conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA | UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; } else if (convmode == UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE) { // Hangul conversionModeClearBit = UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; } // Set the new conversion mode bit and apply the clear bit convmode |= currentConvMode; convmode &= ~conversionModeClearBit; compartment.IntValue = (int)convmode; } } // // Under IMM32 enabled system, we call IMM32 API to update conversion status as well as Cicero // if (_immEnabled) { if (hwnd != IntPtr.Zero) { int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); int convmodeNew = 0; // IME_CMODE_ALPHANUMERIC is 0. // if ((value & ImeConversionModeValues.Alphanumeric) != 0) // convmodeNew |= NativeMethods.IME_CMODE_ALPHANUMERIC; if ((value & ImeConversionModeValues.Native) != 0) convmodeNew |= NativeMethods.IME_CMODE_NATIVE; if ((value & ImeConversionModeValues.Katakana) != 0) convmodeNew |= NativeMethods.IME_CMODE_KATAKANA; if ((value & ImeConversionModeValues.FullShape) != 0) convmodeNew |= NativeMethods.IME_CMODE_FULLSHAPE; if ((value & ImeConversionModeValues.Roman) != 0) convmodeNew |= NativeMethods.IME_CMODE_ROMAN; if ((value & ImeConversionModeValues.CharCode) != 0) convmodeNew |= NativeMethods.IME_CMODE_CHARCODE; if ((value & ImeConversionModeValues.NoConversion) != 0) convmodeNew |= NativeMethods.IME_CMODE_NOCONVERSION; if ((value & ImeConversionModeValues.Eudc) != 0) convmodeNew |= NativeMethods.IME_CMODE_EUDC; if ((value & ImeConversionModeValues.Symbol) != 0) convmodeNew |= NativeMethods.IME_CMODE_SYMBOL; if ((value & ImeConversionModeValues.Fixed) != 0) convmodeNew |= NativeMethods.IME_CMODE_FIXED; // We don't have to call IMM unless the value is changed. if (convmode != convmodeNew) { int conversionModeClearBit = 0; if (convmodeNew == (NativeMethods.IME_CMODE_NATIVE | NativeMethods.IME_CMODE_FULLSHAPE)) { // Chinese, Hiragana or Korean so clear Katakana conversionModeClearBit = NativeMethods.IME_CMODE_KATAKANA; } else if (convmodeNew == (NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE)) { // Katakana Half conversionModeClearBit = NativeMethods.IME_CMODE_FULLSHAPE; } else if (convmodeNew == NativeMethods.IME_CMODE_FULLSHAPE) { // Alpha Full conversionModeClearBit = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; } else if (convmodeNew == NativeMethods.IME_CMODE_ALPHANUMERIC) { // Alpha Half conversionModeClearBit = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; } else if (convmodeNew == NativeMethods.IME_CMODE_NATIVE) { // Hangul conversionModeClearBit = NativeMethods.IME_CMODE_FULLSHAPE; } // Set the new conversion mode bit and apply the clear bit convmodeNew |= convmode; convmodeNew &= ~conversionModeClearBit; UnsafeNativeMethods.ImmSetConversionStatus(new HandleRef(this, himc), convmodeNew, sentence); } UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } } } } ////// Access the current ime sentence mode /// ////// Critical - calls unmanaged code to access the IME /// PublicOK - only allows setting the sentence mode, which is safe /// public ImeSentenceModeValues ImeSentenceMode { [SecurityCritical ] get { if (!IsImm32ImeCurrent()) { // // If the current hkl is not the real IMM32-IME, we get the sentence status from Cicero. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeSentenceModeValues); if (compartment != null) { UnsafeNativeMethods.SentenceModeFlags convmode = (UnsafeNativeMethods.SentenceModeFlags)compartment.IntValue; ImeSentenceModeValues ret = 0; // TF_SENTENCEMODE_ALPHANUMERIC is 0. if (convmode == UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_NONE) return ImeSentenceModeValues.None; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PLAURALCLAUSE) != 0) ret |= ImeSentenceModeValues.PluralClause; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_SINGLECONVERT) != 0) ret |= ImeSentenceModeValues.SingleConversion; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_AUTOMATIC) != 0) ret |= ImeSentenceModeValues.Automatic; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PHRASEPREDICT) != 0) ret |= ImeSentenceModeValues.PhrasePrediction; if ((convmode & UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_CONVERSATION) != 0) ret |= ImeSentenceModeValues.Conversation; return ret; } } else { // // If the current hkl is the real IMM32-IME, we call IMM32 API to get the sentence status. // IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { ImeSentenceModeValues ret = 0; int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); // TF_SENTENCEMODE_ALPHANUMERIC is 0. if (sentence == NativeMethods.IME_SMODE_NONE) return ImeSentenceModeValues.None; if ((sentence & NativeMethods.IME_SMODE_PLAURALCLAUSE) != 0) ret |= ImeSentenceModeValues.PluralClause; if ((sentence & NativeMethods.IME_SMODE_SINGLECONVERT) != 0) ret |= ImeSentenceModeValues.SingleConversion; if ((sentence & NativeMethods.IME_SMODE_AUTOMATIC) != 0) ret |= ImeSentenceModeValues.Automatic; if ((sentence & NativeMethods.IME_SMODE_PHRASEPREDICT) != 0) ret |= ImeSentenceModeValues.PhrasePrediction; if ((sentence & NativeMethods.IME_SMODE_CONVERSATION) != 0) ret |= ImeSentenceModeValues.Conversation; return ret; } } return ImeSentenceModeValues.None; } [SecurityCritical ] set { if (!IsValidSentenceMode(value)) { throw new ArgumentException(SR.Get(SRID.InputMethod_InvalidSentenceMode, value)); } Debug.Assert((value & ImeSentenceModeValues.DoNotCare) == 0); // // Update Cicero's sentence mode. // TextServicesCompartment compartment; compartment = TextServicesCompartmentContext.Current.GetCompartment(InputMethodStateType.ImeSentenceModeValues); if (compartment != null) { UnsafeNativeMethods.SentenceModeFlags convmode = 0; if ((value & ImeSentenceModeValues.PluralClause) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PLAURALCLAUSE; if ((value & ImeSentenceModeValues.SingleConversion) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_SINGLECONVERT; if ((value & ImeSentenceModeValues.Automatic) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_AUTOMATIC; if ((value & ImeSentenceModeValues.PhrasePrediction) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_PHRASEPREDICT; if ((value & ImeSentenceModeValues.Conversation) != 0) convmode |= UnsafeNativeMethods.SentenceModeFlags.TF_SENTENCEMODE_CONVERSATION; // We don't have to set the value unless the value is changed. if (compartment.IntValue != (int)convmode) { compartment.IntValue = (int)convmode; } } // // Under IMM32 enabled system, we call IMM32 API to update sentence status as well as Cicero // if (_immEnabled) { IntPtr hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (hwnd != IntPtr.Zero) { int convmode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence); int sentenceNew = 0; if ((value & ImeSentenceModeValues.PluralClause) != 0) sentenceNew |= NativeMethods.IME_SMODE_PLAURALCLAUSE; if ((value & ImeSentenceModeValues.SingleConversion) != 0) sentenceNew |= NativeMethods.IME_SMODE_SINGLECONVERT; if ((value & ImeSentenceModeValues.Automatic) != 0) sentenceNew |= NativeMethods.IME_SMODE_AUTOMATIC; if ((value & ImeSentenceModeValues.PhrasePrediction) != 0) sentenceNew |= NativeMethods.IME_SMODE_PHRASEPREDICT; if ((value & ImeSentenceModeValues.Conversation) != 0) sentenceNew |= NativeMethods.IME_SMODE_CONVERSATION; // We don't have to call IMM unless the value is changed. if (sentence != sentenceNew) { UnsafeNativeMethods.ImmSetConversionStatus(new HandleRef(this, himc), convmode, sentenceNew); } UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } } } } ////// This is a property that indicates if the current keybaord text services /// can show the configure UI. /// public bool CanShowConfigurationUI { get { return _ShowConfigureUI(null, false); } } ////// This is a property that indicates if the current keybaord text services /// can show the register word UI. /// public bool CanShowRegisterWordUI { get { return _ShowRegisterWordUI(null, false, ""); } } //------------------------------------------------------ // // Public Events // //------------------------------------------------------ ////// An event for input method state changed. /// public event InputMethodStateChangedEventHandler StateChanged { add { if (value == null) { throw new ArgumentNullException("value"); } // Advise compartment event sink to Win32 Cicero only when someone // has StateChanged event handler. if ((_StateChanged == null) && TextServicesLoader.ServicesInstalled) { InitializeCompartmentEventSink(); } _StateChanged += value; } remove { if (value == null) { throw new ArgumentNullException("value"); } _StateChanged -= value; if ((_StateChanged == null) && TextServicesLoader.ServicesInstalled) { // Unadvise compartment event sink to Win32 Cicero if none has StateChanged event handler. UninitializeCompartmentEventSink(); } } } //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods ////// When keyboard device gets focus, this is called. /// We will check the preferred input statues of focus element. /// The preferred input methods should be applied after Cicero TIP gots SetFocus callback. /// internal void GotKeyboardFocus(DependencyObject focus) { object value; if (focus == null) return; // // Check the InputLanguageProperty of the focus element. // value = focus.GetValue(PreferredImeStateProperty); if ((value != null) && ((InputMethodState)value != InputMethodState.DoNotCare)) { ImeState = (InputMethodState)value; } value = focus.GetValue(PreferredImeConversionModeProperty); if ((value != null) && (((ImeConversionModeValues)value & ImeConversionModeValues.DoNotCare) == 0)) { ImeConversionMode = (ImeConversionModeValues)value; } value = focus.GetValue(PreferredImeSentenceModeProperty); if ((value != null) && (((ImeSentenceModeValues)value & ImeSentenceModeValues.DoNotCare) == 0)) { ImeSentenceMode = (ImeSentenceModeValues)value; } } ////// TextServicesCompartmentEventSink forwards OnChange evennt here. /// internal void OnChange(ref Guid rguid) { if (_StateChanged != null) { InputMethodStateType imtype = InputMethodEventTypeInfo.ToType(ref rguid); // Stability Review: Task#32415 // - No state to be restored even exception happens while this callback. _StateChanged(this, new InputMethodStateChangedEventArgs(imtype)); } } ////// return true if the current keyboard layout is a real IMM32-IME. /// internal static bool IsImm32ImeCurrent() { if (!_immEnabled) { return false; } IntPtr hkl = SafeNativeMethods.GetKeyboardLayout(0); return IsImm32Ime(hkl); } ////// return true if the keyboard layout is a real IMM32-IME. /// internal static bool IsImm32Ime(IntPtr hkl) { if (hkl == IntPtr.Zero) { return false; } return ((NativeMethods.IntPtrToInt32(hkl) & 0xf0000000) == 0xe0000000); } ////// This is call back for the IsInputMethodEnable property. /// private static void IsInputMethodEnabled_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { IInputElement inputElement = (IInputElement)d; if (inputElement == Keyboard.FocusedElement) { InputMethod.Current.EnableOrDisableInputMethod((bool) e.NewValue); } } ////// InputMethod enabling/disabling function. /// This takes care of both Cicero and IMM32. /// ////// Critical: This code calls into DefaultIMC which returns data (the hImc) retrieved under /// an elevation of privilige. /// TreatAsSafe: This method is safe to expose. /// [SecurityCritical,SecurityTreatAsSafe] internal void EnableOrDisableInputMethod(bool bEnabled) { // InputMethod enable/disabled status was changed on the current focus Element. if (TextServicesLoader.ServicesInstalled && TextServicesContext.DispatcherCurrent != null) { if (bEnabled) { // Enabled. SetFocus to the default text store. TextServicesContext.DispatcherCurrent.SetFocusOnDefaultTextStore(); } else { // Disabled. SetFocus to the empty dim. TextServicesContext.DispatcherCurrent.SetFocusOnEmptyDim(); } } // // Under IMM32 enabled system, we associate default hIMC or null hIMC. // if (_immEnabled) { IntPtr hwnd; hwnd = HwndFromInputElement(Keyboard.FocusedElement); if (bEnabled) { // // Enabled. Use the default hIMC. // if (DefaultImc != IntPtr.Zero) { UnsafeNativeMethods.ImmAssociateContext(new HandleRef(this, hwnd), new HandleRef(this, _defaultImc.Value)); } } else { // // Disable. Use null hIMC. // UnsafeNativeMethods.ImmAssociateContext(new HandleRef(this, hwnd), new HandleRef(this, IntPtr.Zero)); } } } #endregion Internal methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties // Set and get the referrence of TextServicesCompartmentContext for // the Dispatcher's dispatcher thread. internal TextServicesContext TextServicesContext { get {return _textservicesContext;} set {_textservicesContext = value;} } // Set and get the per Dispatcher cache of TextServicesCompartmentContext internal TextServicesCompartmentContext TextServicesCompartmentContext { get {return _textservicesCompartmentContext;} set {_textservicesCompartmentContext = value;} } // Set and get the per Dispatcher cache of InputLanguageManager internal InputLanguageManager InputLanguageManager { get {return _inputlanguagemanager;} set {_inputlanguagemanager = value;} } // Set and get the per Dispatcher cache of DefaultTextStore internal DefaultTextStore DefaultTextStore { get {return _defaulttextstore;} set {_defaulttextstore = value;} } #endregion Internal Properties //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ ////// Converts Imm32 conversion mode values into TSF conversion mode values. /// ////// /// Critical - calls unmanaged Windowing and IME APIs. /// [SecurityCritical] private UnsafeNativeMethods.ConversionModeFlags Imm32ConversionModeToTSFConversionMode(IntPtr hwnd) { UnsafeNativeMethods.ConversionModeFlags convMode = 0; if (hwnd != IntPtr.Zero) { int immConvMode = 0; int sentence = 0; IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref immConvMode, ref sentence); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); if ((immConvMode & NativeMethods.IME_CMODE_NATIVE) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NATIVE; if ((immConvMode & NativeMethods.IME_CMODE_KATAKANA) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_KATAKANA; if ((immConvMode & NativeMethods.IME_CMODE_FULLSHAPE) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FULLSHAPE; if ((immConvMode & NativeMethods.IME_CMODE_ROMAN) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_ROMAN; if ((immConvMode & NativeMethods.IME_CMODE_CHARCODE) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_CHARCODE; if ((immConvMode & NativeMethods.IME_CMODE_NOCONVERSION) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_NOCONVERSION; if ((immConvMode & NativeMethods.IME_CMODE_EUDC) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_EUDC; if ((immConvMode & NativeMethods.IME_CMODE_SYMBOL) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_SYMBOL; if ((immConvMode & NativeMethods.IME_CMODE_FIXED) != 0) convMode |= UnsafeNativeMethods.ConversionModeFlags.TF_CONVERSIONMODE_FIXED; } return convMode; } ////// Initialize the sink for compartments /// Advice event sink to Cicero's compartment so we can get the notification /// of the compartment change. /// ////// Critical - accesses message pump/input manager directly /// TreatAsSafe - safe to uninitialize/initialize event sink (worst case is breaking input for the app) /// [SecurityCritical, SecurityTreatAsSafe] private void InitializeCompartmentEventSink() { for (int i = 0; i < InputMethodEventTypeInfo.InfoList.Length; i++) { InputMethodEventTypeInfo iminfo = InputMethodEventTypeInfo.InfoList[i]; TextServicesCompartment compartment = null; if (iminfo.Scope == CompartmentScope.Thread) compartment = TextServicesCompartmentContext.Current.GetThreadCompartment(iminfo.Guid); else if (iminfo.Scope == CompartmentScope.Global) compartment = TextServicesCompartmentContext.Current.GetGlobalCompartment(iminfo.Guid); if (compartment != null) { if (_sink == null) _sink = new TextServicesCompartmentEventSink(this); compartment.AdviseNotifySink(_sink); } } } ////// Uninitialize the sink for compartments /// Unadvise the cicero's compartment event sink. /// ////// Critical - accesses message pump/input manager directly /// TreatAsSafe - safe to uninitialize/initialize event sink (worst case is breaking input for the app) /// [SecurityCritical, SecurityTreatAsSafe] private void UninitializeCompartmentEventSink() { for (int i = 0; i < InputMethodEventTypeInfo.InfoList.Length; i++) { InputMethodEventTypeInfo iminfo = InputMethodEventTypeInfo.InfoList[i]; TextServicesCompartment compartment = null; if (iminfo.Scope == CompartmentScope.Thread) compartment = TextServicesCompartmentContext.Current.GetThreadCompartment(iminfo.Guid); else if (iminfo.Scope == CompartmentScope.Global) compartment = TextServicesCompartmentContext.Current.GetGlobalCompartment(iminfo.Guid); if (compartment != null) compartment.UnadviseNotifySink(); } } ////// Get ITfFnConfigure interface and call Show() method. /// If there is no function provider in the current keyboard TIP or the keyboard TIP does /// not have ITfFnConfigure, this returns false. /// ////// This code calls IME/TIP to show its configuration UI. /// Critical: The configuration UI usually have a capability to change session wide state. /// It should be shown only with unrestricted UI permission. /// TreatAsSafe: There is Demand. /// [SecurityCritical, SecurityTreatAsSafe] private bool _ShowConfigureUI(UIElement element, bool fShow) { SecurityHelper.DemandUnrestrictedUIPermission(); bool bCanShown = false; IntPtr hkl = SafeNativeMethods.GetKeyboardLayout(0); if (!IsImm32Ime(hkl)) { UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile; UnsafeNativeMethods.ITfFunctionProvider funcPrv = GetFunctionPrvForCurrentKeyboardTIP(out tf_profile); if (funcPrv != null) { UnsafeNativeMethods.ITfFnConfigure fnConfigure; // Readonly fields can not be passed ref to the interface methods. // Create pads for them. Guid iidFn = UnsafeNativeMethods.IID_ITfFnConfigure; Guid guidNull = UnsafeNativeMethods.Guid_Null; object obj; funcPrv.GetFunction(ref guidNull, ref iidFn, out obj); fnConfigure = obj as UnsafeNativeMethods.ITfFnConfigure; if (fnConfigure != null) { // We could get ITfFnConfigure, we can say the configure UI can be shown. bCanShown = true; if (fShow) { fnConfigure.Show(HwndFromInputElement(element), tf_profile.langid, ref tf_profile.guidProfile); } Marshal.ReleaseComObject(fnConfigure); } Marshal.ReleaseComObject(funcPrv); } } else { // There is no API to test if IMM32-IME can show the configure UI. We assume they can do. bCanShown = true; if (fShow) { UnsafeNativeMethods.ImmConfigureIME(new HandleRef(this, hkl), new HandleRef(this, HwndFromInputElement(element)), NativeMethods.IME_CONFIG_GENERAL, IntPtr.Zero); } } return bCanShown; } ////// Get ITfFnConfigureRegisterWord interface and call Show() method. /// If there is no function provider in the current keyboard TIP or the keyboard TIP does /// not have ITfFnConfigureRegisterWord, this returns false. /// ////// This code calls IME/TIP to show its register word UI. /// Critical: The word registration usually update the uesr custom dictionary. And the change /// of this custom dictionary may affect the conversion of whole desktop. /// It should be shown only with unrestricted UI permission. /// TreatAsSafe: There is Demand. /// [SecurityCritical, SecurityTreatAsSafe] private bool _ShowRegisterWordUI(UIElement element, bool fShow, string strRegister) { SecurityHelper.DemandUnrestrictedUIPermission(); bool bCanShown = false; IntPtr hkl = SafeNativeMethods.GetKeyboardLayout(0); if (!IsImm32Ime(hkl)) { UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile; UnsafeNativeMethods.ITfFunctionProvider funcPrv = GetFunctionPrvForCurrentKeyboardTIP(out tf_profile); if (funcPrv != null) { UnsafeNativeMethods.ITfFnConfigureRegisterWord fnConfigure; // Readonly fields can not be passed ref to the interface methods. // Create pads for them. Guid iidFn = UnsafeNativeMethods.IID_ITfFnConfigureRegisterWord; Guid guidNull = UnsafeNativeMethods.Guid_Null; object obj; funcPrv.GetFunction(ref guidNull, ref iidFn, out obj); fnConfigure = obj as UnsafeNativeMethods.ITfFnConfigureRegisterWord; if (fnConfigure != null) { // We could get ITfFnConfigureRegisterWord, we can say the configure UI can be shown. bCanShown = true; if (fShow) { fnConfigure.Show(HwndFromInputElement(element), tf_profile.langid, ref tf_profile.guidProfile, strRegister); } Marshal.ReleaseComObject(fnConfigure); } Marshal.ReleaseComObject(funcPrv); } } else { // There is no API to test if IMM32-IME can show the configure UI. We assume they can do. bCanShown = true; if (fShow) { NativeMethods.REGISTERWORD regWord = new NativeMethods.REGISTERWORD(); regWord.lpReading = null; regWord.lpWord = strRegister; UnsafeNativeMethods.ImmConfigureIME(new HandleRef(this, hkl), new HandleRef(this, HwndFromInputElement(element)), NativeMethods.IME_CONFIG_REGISTERWORD, ref regWord); } } return bCanShown; } ////// Get hwnd handle value as IntPtr from UIElement. /// ////// Critical: This code calls into CriticalFromVisual and also elevates.It exposes the handle. /// [SecurityCritical] private static IntPtr HwndFromInputElement(IInputElement element) { IntPtr hwnd = (IntPtr)0; // We allow null element. if (element != null) { DependencyObject o = element as DependencyObject; if (o != null) { DependencyObject containingVisual = InputElement.GetContainingVisual(o); if(containingVisual != null) { IWin32Window win32Window = null; PresentationSource source = PresentationSource.CriticalFromVisual(containingVisual); if (source != null) { win32Window = source as IWin32Window; if (win32Window != null) { (new UIPermission(UIPermissionWindow.AllWindows)).Assert();//Blessed Assert try { hwnd = win32Window.Handle; } finally { UIPermission.RevertAssert(); } } } } } } return hwnd; } ////// Get ITfFunctionProvider of the current active keyboard TIP. /// ////// Critical: This code calls into COM interop pointers (ITfFunctionProvider) /// TreatAsSafe: This code has a demand for unmanaged code /// [SecurityCritical,SecurityTreatAsSafe] private UnsafeNativeMethods.ITfFunctionProvider GetFunctionPrvForCurrentKeyboardTIP(out UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile) { SecurityHelper.DemandUnmanagedCode(); // Get the profile info structre of the current active keyboard TIP. tf_profile = GetCurrentKeybordTipProfile(); // Is tf_profile.clsid is Guid_Null, this is not Cicero TIP. No Function Provider. if (tf_profile.clsid.Equals(UnsafeNativeMethods.Guid_Null)) { return null; } UnsafeNativeMethods.ITfFunctionProvider functionPrv; // ThreadMgr Method call will be marshalled to the dispatcher thread since Ciecro is STA. // We release Dispatcher while sink call back. // This thread doesn't have to acceess UIContex until it returns. TextServicesContext textservicesContext = TextServicesContext.DispatcherCurrent; textservicesContext.ThreadManager.GetFunctionProvider(ref tf_profile.clsid, out functionPrv); return functionPrv; } ////// Return the profile info structre of the current active keyboard TIP. /// This enumelates all TIP's profiles and find the active keyboard category TIP. /// ////// Critical - calls unmanaged code to get the keyboard information /// TreatAsSafe - discovery of the input language is safe /// [SecurityCritical, SecurityTreatAsSafe] private UnsafeNativeMethods.TF_LANGUAGEPROFILE GetCurrentKeybordTipProfile() { UnsafeNativeMethods.ITfInputProcessorProfiles ipp = InputProcessorProfilesLoader.Load(); UnsafeNativeMethods.TF_LANGUAGEPROFILE tf_profile = new UnsafeNativeMethods.TF_LANGUAGEPROFILE(); if (ipp != null) { CultureInfo inputLang = InputLanguageManager.Current.CurrentInputLanguage; UnsafeNativeMethods.IEnumTfLanguageProfiles enumIpp; ipp.EnumLanguageProfiles((short)(inputLang.LCID), out enumIpp); UnsafeNativeMethods.TF_LANGUAGEPROFILE[] tf_profiles = new UnsafeNativeMethods.TF_LANGUAGEPROFILE[1]; int fetched; while(enumIpp.Next(1, tf_profiles, out fetched) == NativeMethods.S_OK) { // Check if this profile is active. if (tf_profiles[0].fActive == true) { // Check if this profile is keyboard category.. if (tf_profiles[0].catid.Equals(UnsafeNativeMethods.GUID_TFCAT_TIP_KEYBOARD)) { tf_profile = tf_profiles[0]; break; } } } Marshal.ReleaseComObject(enumIpp); } return tf_profile; } // This validates the ImeConversionMode value. private bool IsValidConversionMode(ImeConversionModeValues mode) { int mask = (int)(ImeConversionModeValues.Alphanumeric | ImeConversionModeValues.Native | ImeConversionModeValues.Katakana | ImeConversionModeValues.FullShape | ImeConversionModeValues.Roman | ImeConversionModeValues.CharCode | ImeConversionModeValues.NoConversion | ImeConversionModeValues.Eudc | ImeConversionModeValues.Symbol | ImeConversionModeValues.Fixed | ImeConversionModeValues.DoNotCare); if (((int)mode & ~mask) != 0) return false; return true; } // This validates the ImeSentenceMode value. private bool IsValidSentenceMode(ImeSentenceModeValues mode) { int mask = (int)(ImeSentenceModeValues.None | ImeSentenceModeValues.PluralClause | ImeSentenceModeValues.SingleConversion | ImeSentenceModeValues.Automatic | ImeSentenceModeValues.PhrasePrediction | ImeSentenceModeValues.Conversation | ImeSentenceModeValues.DoNotCare); if (((int)mode & ~mask) != 0) return false; return true; } //------------------------------------------------------ // // Private Event // //----------------------------------------------------- private event InputMethodStateChangedEventHandler _StateChanged; //------------------------------------------------------ // // Static Private Properties // //----------------------------------------------------- ////// Critical: This code returns the input method context which is not safe to expose /// especially so because it can be used to get to Cicero (any input methods) /// and IME. It also retrieves the window handle for the default IME Window /// although this is not exposed. /// private IntPtr DefaultImc { [SecurityCritical] get { if (_defaultImc==null) { //this code causes elevation of privilige to unmanaged code permsission SecurityPermission sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); sp.Assert();//Blessed Assert try { // // Get the default HIMC from default IME window. // IntPtr hwnd = UnsafeNativeMethods.ImmGetDefaultIMEWnd(new HandleRef(this, IntPtr.Zero)); IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); // Store the default imc to _defaultImc. _defaultImc = new SecurityCriticalDataClass(himc); UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); } finally { SecurityPermission.RevertAssert(); } } return _defaultImc.Value; } } //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields // This is a slot to keep the implementaion of ITfCompartmentEventSink. private TextServicesCompartmentEventSink _sink; // Per Dispatcher Cache for TextServicesContext // The instance of TextServiesContext is per dispather thread. // But we put a reference here so that the current Dispatcher can access TextServicesContext. private TextServicesContext _textservicesContext; // Per Dispatcher Cache for TextServicesCompartmentContext private TextServicesCompartmentContext _textservicesCompartmentContext; // Per Dispatcher Cache for InputLanguageManager private InputLanguageManager _inputlanguagemanager; // Per Dispatcher Cache for DefaultTextStore private DefaultTextStore _defaulttextstore; // If the system is IMM enabled, this is true. private static bool _immEnabled = SafeSystemMetrics.IsImmEnabled ; // the default imc. The default imc is per thread and we cache it in ThreadStatic. [ThreadStatic] private static SecurityCriticalDataClass _defaultImc; #endregion Private Fields } //------------------------------------------------------ // // InputMethodStateChangedEventHandler delegate // //----------------------------------------------------- /// /// The delegate to use for handlers that receive /// input method state changed event. /// public delegate void InputMethodStateChangedEventHandler(Object sender, InputMethodStateChangedEventArgs e); } // 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
- FormsAuthentication.cs
- HtmlToClrEventProxy.cs
- WebPartConnectionsConfigureVerb.cs
- SecurityPermission.cs
- TextServicesCompartmentEventSink.cs
- RtfFormatStack.cs
- HttpRuntime.cs
- WorkflowViewService.cs
- ClassicBorderDecorator.cs
- TextTreeUndo.cs
- HttpWriter.cs
- NullableLongSumAggregationOperator.cs
- JournalEntry.cs
- BezierSegment.cs
- DataBoundControl.cs
- Tokenizer.cs
- OrderedDictionary.cs
- ObjectViewListener.cs
- WebPartConnectVerb.cs
- HttpRequestCacheValidator.cs
- DBSqlParser.cs
- Cursor.cs
- DebugView.cs
- ColorInterpolationModeValidation.cs
- ACL.cs
- RelationHandler.cs
- ExpressionBinding.cs
- HttpCapabilitiesEvaluator.cs
- CanonicalFormWriter.cs
- PkcsMisc.cs
- ListBoxDesigner.cs
- HTMLTagNameToTypeMapper.cs
- HtmlMeta.cs
- PageEventArgs.cs
- AdRotatorDesigner.cs
- PartialTrustVisibleAssembliesSection.cs
- _SingleItemRequestCache.cs
- GetTokenRequest.cs
- SweepDirectionValidation.cs
- TypeKeyValue.cs
- FunctionMappingTranslator.cs
- JsonMessageEncoderFactory.cs
- InfocardExtendedInformationEntry.cs
- FacetValues.cs
- MultiSelectRootGridEntry.cs
- ByteStreamGeometryContext.cs
- DocumentSchemaValidator.cs
- EntityViewContainer.cs
- OptionalColumn.cs
- TextStore.cs
- documentation.cs
- CreateParams.cs
- WebPartVerbCollection.cs
- updateconfighost.cs
- CookieProtection.cs
- FormatVersion.cs
- CompositeTypefaceMetrics.cs
- ObjectDataSourceChooseMethodsPanel.cs
- XmlDataImplementation.cs
- CrossContextChannel.cs
- SmtpMail.cs
- SQLBinaryStorage.cs
- Geometry3D.cs
- KernelTypeValidation.cs
- ComponentCollection.cs
- CodeRemoveEventStatement.cs
- RegisteredExpandoAttribute.cs
- WpfPayload.cs
- BaseTransportHeaders.cs
- SelfIssuedAuthProofToken.cs
- StringConverter.cs
- DecoderReplacementFallback.cs
- DataColumnMapping.cs
- ToolStripComboBox.cs
- TableCellsCollectionEditor.cs
- SqlFileStream.cs
- SmtpException.cs
- XmlDocument.cs
- TypedReference.cs
- DataContext.cs
- VScrollProperties.cs
- XmlnsDefinitionAttribute.cs
- DataServices.cs
- ListView.cs
- VirtualDirectoryMappingCollection.cs
- EventLogEntryCollection.cs
- HtmlImageAdapter.cs
- CustomCategoryAttribute.cs
- SqlCacheDependencyDatabase.cs
- RSAPKCS1KeyExchangeDeformatter.cs
- GridView.cs
- CaseStatementSlot.cs
- TypographyProperties.cs
- StringResourceManager.cs
- Package.cs
- COM2ColorConverter.cs
- GridViewUpdatedEventArgs.cs
- HttpPostedFileBase.cs
- ServiceModelActivity.cs
- Int64.cs