Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Core / CSharp / System / Windows / InterOp / HwndKeyboardInputProvider.cs / 1 / HwndKeyboardInputProvider.cs
using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using MS.Utility; using MS.Internal; using MS.Win32; using MS.Internal.PresentationCore; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace System.Windows.Interop { internal sealed class HwndKeyboardInputProvider : DispatcherObject, IKeyboardInputProvider, IDisposable { ////// Accesses and store critical data. This class is also critical (_site and _source) /// [SecurityCritical] internal HwndKeyboardInputProvider(HwndSource source) { (new UIPermission(PermissionState.Unrestricted)).Assert(); try //Blessed assert for InputManager.Current.RegisterInputProvider { _site = new SecurityCriticalDataClass(InputManager.Current.RegisterInputProvider(this)); } finally { UIPermission.RevertAssert(); } _source = new SecurityCriticalDataClass (source); } /// /// Critical:This class accesses critical data, _site. /// TreatAsSafe: This class does not expose the critical data /// [SecurityCritical, SecurityTreatAsSafe] public void Dispose() { if(_site != null) { _site.Value.Dispose(); _site = null; } _source = null; } public void OnRootChanged(Visual oldRoot, Visual newRoot) { if(_active && newRoot != null) { Keyboard.Focus(null); // internally we will set the focus to the root. } } ////// Critical: As this accesses critical data HwndSource /// TreatAsSafe:Information about whether a given input provider services /// a visual is safe to expose. This method does not expose the critical data either. /// [SecurityCritical,SecurityTreatAsSafe] bool IInputProvider.ProvidesInputForRootVisual(Visual v) { Debug.Assert( null != _source ); return _source.Value.RootVisual == v; } void IInputProvider.NotifyDeactivate() { _active = false; _partialActive = false; } ////// SecurityCritical: This code calls into GetWindowLong/GetFocus both of which are /// critical.It also retrieves the handle to the current /// window that has focus.Additionally it also calls into SetFocus which also /// returns a critical resouce in the form of a window handle. In this case all that is /// retrieved is the extended window style and that data is not exposed. /// It is simply used to detect if the window is set to WS_EX_NOACTIVATE. /// The risky operation here is the call to SetFocus. /// [SecurityCritical] bool IKeyboardInputProvider.AcquireFocus() { bool success = false; Debug.Assert( null != _source ); try { int windowStyle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, _source.Value.CriticalHandle), NativeMethods.GWL_EXSTYLE); if((windowStyle & NativeMethods.WS_EX_NOACTIVATE) == NativeMethods.WS_EX_NOACTIVATE) { // If this window has the WS_EX_NOACTIVATE style, then we do // not request keyboard focus. Doing so will actually activate // the window. If this window is part of a thread with other // windows, keyboard input from them can actually be directed // to elements within this window. Menus/ComboBoxes use this. } else { // Attempt to set the focus to ourselves. // // However, there are scenarios where this won't work: if a // worker thread calls this API, Win32 will ignore it. If // we are running while a window that a different thread // created has focus, then Win32 will also ignore it. // we do not want the resulting WM_SETFOCUS message // to give focus to a child window _restoreFocusWindow = IntPtr.Zero; _restoreFocus = null; UnsafeNativeMethods.SetFocus(new HandleRef(this, _source.Value.CriticalHandle)); } // We can only claim the attempt to acquire focus succeeded if // one of our windows has focus. In that case, keyboard input // will get properly forwarded to the element. if(!_active) { IntPtr focus = UnsafeNativeMethods.GetFocus(); success = IsOurWindow(focus); } else { success = true; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetFocus failed!"); } return success; } //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 ////// Critical: This code is critical since it handles all keyboard messages and could be used to spoof input /// [SecurityCritical] internal IntPtr FilterMessage(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; // It is possible to be re-entered during disposal. Just return. if(null == _source || null == _source.Value) { return result; } _msgTime = 0; try { _msgTime = SafeNativeMethods.GetMessageTime(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndKeyboardInputProvider: GetMessageTime failed!"); } switch(message) { // WM_KEYDOWN is sent when a nonsystem key is pressed. // A nonsystem key is a key that is pressed when the ALT key // is not pressed. // WM_SYSKEYDOWN is sent when a system key is pressed. case NativeMethods.WM_SYSKEYDOWN: case NativeMethods.WM_KEYDOWN: { // If we have a IKeyboardInputSite, then we should have already // called ProcessKeyDown (from TranslateAccelerator) // But there are several paths (our message pump / app's message // pump) where we do (or don't) call through IKeyboardInputSink. // So the best way is to just check here if we already did it. if(_source.Value.IsRepeatedKeyboardMessage(hwnd, message, wParam, lParam)) { break; } // We will use the current time before generating KeyDown events so we can filter // the later posted WM_CHAR. int currentTime = 0; try { currentTime = SafeNativeMethods.GetTickCount(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetTickCount failed!"); } // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // In case a nested message pump is used before we return // from processing this message, we disable processing the // next WM_CHAR message because if the code pumps messages // it should really mark the message as handled. HwndSource._eatCharMessages = true; DispatcherOperation restoreCharMessages = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(HwndSource.RestoreCharMessages), null); MSG msg = new MSG(hwnd, message, wParam, lParam, _msgTime, 0, 0); ProcessKeyAction(ref msg, ref handled); if(!handled) { // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // We did not handle the WM_KEYDOWN, so it is OK to process WM_CHAR messages. // We can also abort the pending restore operation since we don't need it. HwndSource._eatCharMessages = false; restoreCharMessages.Abort(); } // System.Console.WriteLine("KEYDOWN(message={0}, wParam={1})={2}", message, wParam, handled); } break; // WM_KEYUP is sent when a nonsystem key is released. // A nonsystem key is a key that is pressed when the ALT key // is not pressed. // WM_SYSKEYUP is sent when a system key is released. case NativeMethods.WM_SYSKEYUP: case NativeMethods.WM_KEYUP: { if(_source.Value.IsRepeatedKeyboardMessage(hwnd, message, wParam, lParam)) { break; } MSG msg = new MSG(hwnd, message, wParam, lParam, _msgTime, 0, 0); ProcessKeyAction(ref msg, ref handled); // System.Console.WriteLine("KEYUP (message={0}, wParam={1})={2}", message, wParam, handled); } break; // case NativeMethods.WM_CHAR: case NativeMethods.WM_DEADCHAR: case NativeMethods.WM_SYSCHAR: case NativeMethods.WM_SYSDEADCHAR: { if(_source.Value.IsRepeatedKeyboardMessage(hwnd, message, wParam, lParam)) { break; } // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS if(HwndSource._eatCharMessages) { break; } ProcessTextInputAction(hwnd, message, wParam, lParam, ref handled); // System.Console.WriteLine("CHAR(message={0}, wParam={1})={2}", message, wParam, handled); } break; case NativeMethods.WM_EXITMENULOOP: case NativeMethods.WM_EXITSIZEMOVE: { // MITIGATION: KEYBOARD_STATE_OUT_OF_[....] // // Avalon relies on keeping it's copy of the keyboard // state. This is for a number of reasons, including that // we need to be able to give this state to worker threads. // // There are a number of cases where Win32 eats the // keyboard messages, and this can cause our keyboard // state to become stale. Obviously this can happen when // another app is in the foreground, but we handle that // by re-synching our keyboard state when we get focus. // // Other times are when Win32 enters a nested loop. While // any one could enter a nested loop at any time for any // reason, Win32 is nice enough to let us know when it is // finished with the two common loops: menus and sizing. // We re-[....] our keyboard device in response to these. // if(_active) { _partialActive = true; ReportInput(hwnd, InputMode.Foreground, _msgTime, RawKeyboardActions.Activate, 0, false, false, 0); } } break; // WM_SETFOCUS is sent immediately after focus is granted. // This is our clue that the keyboard is active. case NativeMethods.WM_SETFOCUS: { if(!_active) { // Console.WriteLine("WM_SETFOCUS"); ReportInput(hwnd, InputMode.Foreground, _msgTime, RawKeyboardActions.Activate, 0, false, false, 0); // MITIGATION: KEYBOARD_STATE_OUT_OF_[....] // // This is how we deal with the fact that Win32 sometimes sends // us a WM_SETFOCUS message BEFORE it has updated it's internal // internal keyboard state information. When we get the // WM_SETFOCUS message, we activate the keyboard with the // keyboard state (even though it could be wrong). Then when // we get the first "real" keyboard input event, we activate // the keyboard again, since Win32 will have updated the // keyboard state correctly by then. // _partialActive = true; // Restore the keyboard focus to the child window or element that had // the focus before we last lost Win32 focus. If nothing // had focus before, set it to null. if (_restoreFocusWindow != IntPtr.Zero) { IntPtr hwndRestoreFocus = _restoreFocusWindow; _restoreFocusWindow = IntPtr.Zero; UnsafeNativeMethods.TrySetFocus(new HandleRef(this, hwndRestoreFocus), ref hwndRestoreFocus); } else { IInputElement restoreFocus = _restoreFocus; _restoreFocus = null; Keyboard.Focus(restoreFocus); } } handled = true; } break; // WM_KILLFOCUS is sent immediately before focus is removed. // This is our clue that the keyboard is inactive. case NativeMethods.WM_KILLFOCUS: { if(_active && wParam != _source.Value.CriticalHandle ) { // Console.WriteLine("WM_KILLFOCUS"); // when the window that's acquiring focus (wParam) is // a descendant of our window, remember the immediate // child so that we can restore focus to it. _restoreFocusWindow = GetImmediateChildFor((IntPtr)wParam, _source.Value.CriticalHandle); // Remember the element that currently has keyboard // focus so that we can restore focus to it when this // window gets Win32 keyboard focus again. if (_restoreFocusWindow == IntPtr.Zero) { _restoreFocus = Keyboard.FocusedElement; } PossiblyDeactivate((IntPtr)wParam); } handled = true; } break; // WM_UPDATEUISTATE is sent when the user presses ALT, expecting // the app to display accelerator keys. We don't always hear the // keystroke - another message loop may handle it. So report it // here. case NativeMethods.WM_UPDATEUISTATE: { RawUIStateInputReport report = new RawUIStateInputReport(_source.Value, InputMode.Foreground, _msgTime, (RawUIStateActions)NativeMethods.SignedLOWORD((int)wParam), (RawUIStateTargets)NativeMethods.SignedHIWORD((int)wParam)); _site.Value.ReportInput(report); handled = true; } break; } if (handled && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.HWNDMESSAGEGUID), MS.Utility.EventType.Info, Dispatcher.GetHashCode(), hwnd.ToInt64(), message, (int)wParam, (int)lParam); } return result; } ////// Critical:This can be used to spoof input /// [SecurityCritical] internal void ProcessKeyAction(ref MSG msg, ref bool handled) { // Remember the last message MSG previousMSG = ComponentDispatcher.UnsecureCurrentKeyboardMessage; ComponentDispatcher.UnsecureCurrentKeyboardMessage = msg; try { int virtualKey = GetVirtualKey(msg.wParam, msg.lParam); int scanCode = GetScanCode(msg.wParam, msg.lParam); bool isExtendedKey = IsExtendedKey(msg.lParam); bool isSystemKey = ((msg.message == NativeMethods.WM_SYSKEYDOWN) || (msg.message == NativeMethods.WM_SYSKEYUP)); RawKeyboardActions action = GetKeyUpKeyDown(msg.message); // Console.WriteLine("WM_KEYDOWN: " + virtualKey + "," + scanCode); handled = ReportInput(msg.hwnd, InputMode.Foreground, _msgTime, action, scanCode, isExtendedKey, isSystemKey, virtualKey); } finally { // Restore the last message ComponentDispatcher.UnsecureCurrentKeyboardMessage = previousMSG; } } ////// Critical - calls a critical method _source.Value. /// [SecurityCritical ] internal void ProcessTextInputAction(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { char charcode = (char)wParam; bool isDeadChar = ((msg == NativeMethods.WM_DEADCHAR) || (msg == NativeMethods.WM_SYSDEADCHAR)); bool isSystemChar = ((msg == NativeMethods.WM_SYSCHAR) || (msg == NativeMethods.WM_SYSDEADCHAR)); bool isControlChar = false; // If the control is pressed but Alt is not, the char is control char. try { if (((UnsafeNativeMethods.GetKeyState(NativeMethods.VK_CONTROL) & 0x8000) != 0) && ((UnsafeNativeMethods.GetKeyState(NativeMethods.VK_MENU) & 0x8000) == 0) && Char.IsControl(charcode)) { isControlChar = true; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetKeyState failed!"); } RawTextInputReport report = new RawTextInputReport(_source.Value , InputMode.Foreground, _msgTime, isDeadChar, isSystemChar, isControlChar, charcode); handled = _site.Value.ReportInput(report); } internal static int GetVirtualKey(IntPtr wParam, IntPtr lParam) { int virtualKey = NativeMethods.IntPtrToInt32( wParam); int scanCode = 0; int keyData = NativeMethods.IntPtrToInt32(lParam); // Find the left/right instance SHIFT keys. if(virtualKey == NativeMethods.VK_SHIFT) { scanCode = (keyData & 0xFF0000) >> 16; try { virtualKey = SafeNativeMethods.MapVirtualKey(scanCode, 3); if(virtualKey == 0) { virtualKey = NativeMethods.VK_LSHIFT; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: MapVirtualKey failed!"); virtualKey = NativeMethods.VK_LSHIFT; } } // Find the left/right instance ALT keys. if(virtualKey == NativeMethods.VK_MENU) { bool right = ((keyData & 0x1000000) >> 24) != 0; if(right) { virtualKey = NativeMethods.VK_RMENU; } else { virtualKey = NativeMethods.VK_LMENU; } } // Find the left/right instance CONTROL keys. if(virtualKey == NativeMethods.VK_CONTROL) { bool right = ((keyData & 0x1000000) >> 24) != 0; if(right) { virtualKey = NativeMethods.VK_RCONTROL; } else { virtualKey = NativeMethods.VK_LCONTROL; } } return virtualKey; } internal static int GetScanCode(IntPtr wParam, IntPtr lParam) { int keyData = NativeMethods.IntPtrToInt32(lParam); int scanCode = (keyData & 0xFF0000) >> 16; if(scanCode == 0) { try { int virtualKey = GetVirtualKey(wParam, lParam); scanCode = SafeNativeMethods.MapVirtualKey(virtualKey, 0); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: MapVirtualKey failed!"); } } return scanCode; } internal static bool IsExtendedKey(IntPtr lParam) { int keyData = NativeMethods.IntPtrToInt32(lParam); return ((keyData & 0x01000000) != 0) ? true : false; } ////// Returns the set of modifier keys currently pressed as determined by calling to Win32 /// ////// Marked as FriendAccessAllowed so HwndHost in PresentationFramework can call it /// ////// Critical: It calls an UnsafeNativeMethod (GetKeyState). /// TreatAsSafe: It's safe to return whether shift, control or alt keys are being pressed or not. /// [FriendAccessAllowed] [SecurityCritical,SecurityTreatAsSafe] internal static ModifierKeys GetSystemModifierKeys() { ModifierKeys modifierKeys = ModifierKeys.None; short keyState = UnsafeNativeMethods.GetKeyState(NativeMethods.VK_SHIFT); if((keyState & 0x8000) == 0x8000) { modifierKeys |= ModifierKeys.Shift; } keyState = UnsafeNativeMethods.GetKeyState(NativeMethods.VK_CONTROL); if((keyState & 0x8000) == 0x8000) { modifierKeys |= ModifierKeys.Control; } keyState = UnsafeNativeMethods.GetKeyState(NativeMethods.VK_MENU); if((keyState & 0x8000) == 0x8000) { modifierKeys |= ModifierKeys.Alt; } return modifierKeys; } private RawKeyboardActions GetKeyUpKeyDown(int msg) { if( msg == NativeMethods.WM_KEYDOWN || msg == NativeMethods.WM_SYSKEYDOWN ) return RawKeyboardActions.KeyDown; if( msg == NativeMethods.WM_KEYUP || msg == NativeMethods.WM_SYSKEYUP ) return RawKeyboardActions.KeyUp; throw new ArgumentException(SR.Get(SRID.OnlyAcceptsKeyMessages)); } ////// Critical: This code causes this window to loose focus not ok to expose /// It also calls into a critical code path. /// [SecurityCritical] private void PossiblyDeactivate(IntPtr hwndFocus) { Debug.Assert( null != _source ); // We are now longer active ourselves, but it is possible that the // window the keyboard is going to intereact with is in the same // Dispatcher as ourselves. If so, we don't want to deactivate the // keyboard input stream because the other window hasn't activated // it yet, and it may result in the input stream "flickering" between // active/inactive/active. This is ugly, so we try to supress the // uneccesary transitions. // bool deactivate = !IsOurWindow(hwndFocus); // This window itself should not be active anymore. _active = false; // Only deactivate the keyboard input stream if needed. if(deactivate) { ReportInput(_source.Value.CriticalHandle, InputMode.Foreground, _msgTime, RawKeyboardActions.Deactivate, 0, false, false, 0); } } ////// Critical: This code does not store any critical data, it accesses PresentationSource /// TreatAsSafe: This information is safe to expose /// [SecurityCritical,SecurityTreatAsSafe] private bool IsOurWindow(IntPtr hwnd) { bool isOurWindow = false; Debug.Assert( null != _source ); if(hwnd != IntPtr.Zero) { HwndSource hwndSource = HwndSource.CriticalFromHwnd(hwnd); if(hwndSource != null) { if(hwndSource.Dispatcher == _source.Value.Dispatcher) { // The window has the same dispatcher, must be ours. isOurWindow = true; } else { // The window has a different dispatcher, must not be ours. isOurWindow = false; } } else { // The window is non-Avalon. // Such windows are never ours. isOurWindow = false; } } else { // This is not even a window. isOurWindow = false; } return isOurWindow; } // return the immediate child (if any) of hwndRoot that governs the // given hwnd. If hwnd is not a descendant of hwndRoot, return 0. ////// Critical:This method calls critical methods /// [SecurityCritical] private IntPtr GetImmediateChildFor(IntPtr hwnd, IntPtr hwndRoot) { while (hwnd != IntPtr.Zero) { // We only care to restore focus to child windows. Notice that WS_POPUP // windows also have parents but we do not want to track those here. int windowStyle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this,hwnd), NativeMethods.GWL_STYLE); if((windowStyle & NativeMethods.WS_CHILD) == 0) { break; } IntPtr hwndParent = UnsafeNativeMethods.GetParent(new HandleRef(this, hwnd)); if (hwndParent == hwndRoot) { return hwnd; } hwnd = hwndParent; } return IntPtr.Zero; } ////// Critical:This code can cause input simulation and hence is critical. /// The current code path is only hit under RootBrowserWindow scenario for now. /// [SecurityCritical] private bool ReportInput( IntPtr hwnd, InputMode mode, int timestamp, RawKeyboardActions actions, int scanCode, bool isExtendedKey, bool isSystemKey, int virtualKey) { Debug.Assert( null != _source ); // The first event should also activate the keyboard device. if((actions & RawKeyboardActions.Deactivate) == 0) { if(!_active || _partialActive) { try { // Include the activation action. actions |= RawKeyboardActions.Activate; // Remember that we are active. _active = true; _partialActive = false; } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetKeyboardState failed!"); // We'll go ahead and report the input, but we'll try to "activate" next time. } } } // Get the extra information sent along with the message. IntPtr extraInformation = IntPtr.Zero; try { extraInformation = UnsafeNativeMethods.GetMessageExtraInfo(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetMessageExtraInfo failed!"); } RawKeyboardInputReport report = new RawKeyboardInputReport(_source.Value, mode, timestamp, actions, scanCode, isExtendedKey, isSystemKey, virtualKey, extraInformation); bool handled = _site.Value.ReportInput(report); return handled; } private int _msgTime; ////// This is got under an elevation and is hence critical. This data is not ok to expose. /// private SecurityCriticalDataClass_source; /// /// This is got under an elevation and is hence critical.This data is not ok to expose. /// private SecurityCriticalDataClass_site; private IInputElement _restoreFocus; /// /// This is got under an elevation and is hence critical.This data is not ok to expose. /// [SecurityCritical] private IntPtr _restoreFocusWindow; private bool _active; private bool _partialActive; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using MS.Utility; using MS.Internal; using MS.Win32; using MS.Internal.PresentationCore; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace System.Windows.Interop { internal sealed class HwndKeyboardInputProvider : DispatcherObject, IKeyboardInputProvider, IDisposable { ////// Accesses and store critical data. This class is also critical (_site and _source) /// [SecurityCritical] internal HwndKeyboardInputProvider(HwndSource source) { (new UIPermission(PermissionState.Unrestricted)).Assert(); try //Blessed assert for InputManager.Current.RegisterInputProvider { _site = new SecurityCriticalDataClass(InputManager.Current.RegisterInputProvider(this)); } finally { UIPermission.RevertAssert(); } _source = new SecurityCriticalDataClass (source); } /// /// Critical:This class accesses critical data, _site. /// TreatAsSafe: This class does not expose the critical data /// [SecurityCritical, SecurityTreatAsSafe] public void Dispose() { if(_site != null) { _site.Value.Dispose(); _site = null; } _source = null; } public void OnRootChanged(Visual oldRoot, Visual newRoot) { if(_active && newRoot != null) { Keyboard.Focus(null); // internally we will set the focus to the root. } } ////// Critical: As this accesses critical data HwndSource /// TreatAsSafe:Information about whether a given input provider services /// a visual is safe to expose. This method does not expose the critical data either. /// [SecurityCritical,SecurityTreatAsSafe] bool IInputProvider.ProvidesInputForRootVisual(Visual v) { Debug.Assert( null != _source ); return _source.Value.RootVisual == v; } void IInputProvider.NotifyDeactivate() { _active = false; _partialActive = false; } ////// SecurityCritical: This code calls into GetWindowLong/GetFocus both of which are /// critical.It also retrieves the handle to the current /// window that has focus.Additionally it also calls into SetFocus which also /// returns a critical resouce in the form of a window handle. In this case all that is /// retrieved is the extended window style and that data is not exposed. /// It is simply used to detect if the window is set to WS_EX_NOACTIVATE. /// The risky operation here is the call to SetFocus. /// [SecurityCritical] bool IKeyboardInputProvider.AcquireFocus() { bool success = false; Debug.Assert( null != _source ); try { int windowStyle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, _source.Value.CriticalHandle), NativeMethods.GWL_EXSTYLE); if((windowStyle & NativeMethods.WS_EX_NOACTIVATE) == NativeMethods.WS_EX_NOACTIVATE) { // If this window has the WS_EX_NOACTIVATE style, then we do // not request keyboard focus. Doing so will actually activate // the window. If this window is part of a thread with other // windows, keyboard input from them can actually be directed // to elements within this window. Menus/ComboBoxes use this. } else { // Attempt to set the focus to ourselves. // // However, there are scenarios where this won't work: if a // worker thread calls this API, Win32 will ignore it. If // we are running while a window that a different thread // created has focus, then Win32 will also ignore it. // we do not want the resulting WM_SETFOCUS message // to give focus to a child window _restoreFocusWindow = IntPtr.Zero; _restoreFocus = null; UnsafeNativeMethods.SetFocus(new HandleRef(this, _source.Value.CriticalHandle)); } // We can only claim the attempt to acquire focus succeeded if // one of our windows has focus. In that case, keyboard input // will get properly forwarded to the element. if(!_active) { IntPtr focus = UnsafeNativeMethods.GetFocus(); success = IsOurWindow(focus); } else { success = true; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetFocus failed!"); } return success; } //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 ////// Critical: This code is critical since it handles all keyboard messages and could be used to spoof input /// [SecurityCritical] internal IntPtr FilterMessage(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; // It is possible to be re-entered during disposal. Just return. if(null == _source || null == _source.Value) { return result; } _msgTime = 0; try { _msgTime = SafeNativeMethods.GetMessageTime(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndKeyboardInputProvider: GetMessageTime failed!"); } switch(message) { // WM_KEYDOWN is sent when a nonsystem key is pressed. // A nonsystem key is a key that is pressed when the ALT key // is not pressed. // WM_SYSKEYDOWN is sent when a system key is pressed. case NativeMethods.WM_SYSKEYDOWN: case NativeMethods.WM_KEYDOWN: { // If we have a IKeyboardInputSite, then we should have already // called ProcessKeyDown (from TranslateAccelerator) // But there are several paths (our message pump / app's message // pump) where we do (or don't) call through IKeyboardInputSink. // So the best way is to just check here if we already did it. if(_source.Value.IsRepeatedKeyboardMessage(hwnd, message, wParam, lParam)) { break; } // We will use the current time before generating KeyDown events so we can filter // the later posted WM_CHAR. int currentTime = 0; try { currentTime = SafeNativeMethods.GetTickCount(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetTickCount failed!"); } // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // In case a nested message pump is used before we return // from processing this message, we disable processing the // next WM_CHAR message because if the code pumps messages // it should really mark the message as handled. HwndSource._eatCharMessages = true; DispatcherOperation restoreCharMessages = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(HwndSource.RestoreCharMessages), null); MSG msg = new MSG(hwnd, message, wParam, lParam, _msgTime, 0, 0); ProcessKeyAction(ref msg, ref handled); if(!handled) { // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // We did not handle the WM_KEYDOWN, so it is OK to process WM_CHAR messages. // We can also abort the pending restore operation since we don't need it. HwndSource._eatCharMessages = false; restoreCharMessages.Abort(); } // System.Console.WriteLine("KEYDOWN(message={0}, wParam={1})={2}", message, wParam, handled); } break; // WM_KEYUP is sent when a nonsystem key is released. // A nonsystem key is a key that is pressed when the ALT key // is not pressed. // WM_SYSKEYUP is sent when a system key is released. case NativeMethods.WM_SYSKEYUP: case NativeMethods.WM_KEYUP: { if(_source.Value.IsRepeatedKeyboardMessage(hwnd, message, wParam, lParam)) { break; } MSG msg = new MSG(hwnd, message, wParam, lParam, _msgTime, 0, 0); ProcessKeyAction(ref msg, ref handled); // System.Console.WriteLine("KEYUP (message={0}, wParam={1})={2}", message, wParam, handled); } break; // case NativeMethods.WM_CHAR: case NativeMethods.WM_DEADCHAR: case NativeMethods.WM_SYSCHAR: case NativeMethods.WM_SYSDEADCHAR: { if(_source.Value.IsRepeatedKeyboardMessage(hwnd, message, wParam, lParam)) { break; } // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS if(HwndSource._eatCharMessages) { break; } ProcessTextInputAction(hwnd, message, wParam, lParam, ref handled); // System.Console.WriteLine("CHAR(message={0}, wParam={1})={2}", message, wParam, handled); } break; case NativeMethods.WM_EXITMENULOOP: case NativeMethods.WM_EXITSIZEMOVE: { // MITIGATION: KEYBOARD_STATE_OUT_OF_[....] // // Avalon relies on keeping it's copy of the keyboard // state. This is for a number of reasons, including that // we need to be able to give this state to worker threads. // // There are a number of cases where Win32 eats the // keyboard messages, and this can cause our keyboard // state to become stale. Obviously this can happen when // another app is in the foreground, but we handle that // by re-synching our keyboard state when we get focus. // // Other times are when Win32 enters a nested loop. While // any one could enter a nested loop at any time for any // reason, Win32 is nice enough to let us know when it is // finished with the two common loops: menus and sizing. // We re-[....] our keyboard device in response to these. // if(_active) { _partialActive = true; ReportInput(hwnd, InputMode.Foreground, _msgTime, RawKeyboardActions.Activate, 0, false, false, 0); } } break; // WM_SETFOCUS is sent immediately after focus is granted. // This is our clue that the keyboard is active. case NativeMethods.WM_SETFOCUS: { if(!_active) { // Console.WriteLine("WM_SETFOCUS"); ReportInput(hwnd, InputMode.Foreground, _msgTime, RawKeyboardActions.Activate, 0, false, false, 0); // MITIGATION: KEYBOARD_STATE_OUT_OF_[....] // // This is how we deal with the fact that Win32 sometimes sends // us a WM_SETFOCUS message BEFORE it has updated it's internal // internal keyboard state information. When we get the // WM_SETFOCUS message, we activate the keyboard with the // keyboard state (even though it could be wrong). Then when // we get the first "real" keyboard input event, we activate // the keyboard again, since Win32 will have updated the // keyboard state correctly by then. // _partialActive = true; // Restore the keyboard focus to the child window or element that had // the focus before we last lost Win32 focus. If nothing // had focus before, set it to null. if (_restoreFocusWindow != IntPtr.Zero) { IntPtr hwndRestoreFocus = _restoreFocusWindow; _restoreFocusWindow = IntPtr.Zero; UnsafeNativeMethods.TrySetFocus(new HandleRef(this, hwndRestoreFocus), ref hwndRestoreFocus); } else { IInputElement restoreFocus = _restoreFocus; _restoreFocus = null; Keyboard.Focus(restoreFocus); } } handled = true; } break; // WM_KILLFOCUS is sent immediately before focus is removed. // This is our clue that the keyboard is inactive. case NativeMethods.WM_KILLFOCUS: { if(_active && wParam != _source.Value.CriticalHandle ) { // Console.WriteLine("WM_KILLFOCUS"); // when the window that's acquiring focus (wParam) is // a descendant of our window, remember the immediate // child so that we can restore focus to it. _restoreFocusWindow = GetImmediateChildFor((IntPtr)wParam, _source.Value.CriticalHandle); // Remember the element that currently has keyboard // focus so that we can restore focus to it when this // window gets Win32 keyboard focus again. if (_restoreFocusWindow == IntPtr.Zero) { _restoreFocus = Keyboard.FocusedElement; } PossiblyDeactivate((IntPtr)wParam); } handled = true; } break; // WM_UPDATEUISTATE is sent when the user presses ALT, expecting // the app to display accelerator keys. We don't always hear the // keystroke - another message loop may handle it. So report it // here. case NativeMethods.WM_UPDATEUISTATE: { RawUIStateInputReport report = new RawUIStateInputReport(_source.Value, InputMode.Foreground, _msgTime, (RawUIStateActions)NativeMethods.SignedLOWORD((int)wParam), (RawUIStateTargets)NativeMethods.SignedHIWORD((int)wParam)); _site.Value.ReportInput(report); handled = true; } break; } if (handled && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.HWNDMESSAGEGUID), MS.Utility.EventType.Info, Dispatcher.GetHashCode(), hwnd.ToInt64(), message, (int)wParam, (int)lParam); } return result; } ////// Critical:This can be used to spoof input /// [SecurityCritical] internal void ProcessKeyAction(ref MSG msg, ref bool handled) { // Remember the last message MSG previousMSG = ComponentDispatcher.UnsecureCurrentKeyboardMessage; ComponentDispatcher.UnsecureCurrentKeyboardMessage = msg; try { int virtualKey = GetVirtualKey(msg.wParam, msg.lParam); int scanCode = GetScanCode(msg.wParam, msg.lParam); bool isExtendedKey = IsExtendedKey(msg.lParam); bool isSystemKey = ((msg.message == NativeMethods.WM_SYSKEYDOWN) || (msg.message == NativeMethods.WM_SYSKEYUP)); RawKeyboardActions action = GetKeyUpKeyDown(msg.message); // Console.WriteLine("WM_KEYDOWN: " + virtualKey + "," + scanCode); handled = ReportInput(msg.hwnd, InputMode.Foreground, _msgTime, action, scanCode, isExtendedKey, isSystemKey, virtualKey); } finally { // Restore the last message ComponentDispatcher.UnsecureCurrentKeyboardMessage = previousMSG; } } ////// Critical - calls a critical method _source.Value. /// [SecurityCritical ] internal void ProcessTextInputAction(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { char charcode = (char)wParam; bool isDeadChar = ((msg == NativeMethods.WM_DEADCHAR) || (msg == NativeMethods.WM_SYSDEADCHAR)); bool isSystemChar = ((msg == NativeMethods.WM_SYSCHAR) || (msg == NativeMethods.WM_SYSDEADCHAR)); bool isControlChar = false; // If the control is pressed but Alt is not, the char is control char. try { if (((UnsafeNativeMethods.GetKeyState(NativeMethods.VK_CONTROL) & 0x8000) != 0) && ((UnsafeNativeMethods.GetKeyState(NativeMethods.VK_MENU) & 0x8000) == 0) && Char.IsControl(charcode)) { isControlChar = true; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetKeyState failed!"); } RawTextInputReport report = new RawTextInputReport(_source.Value , InputMode.Foreground, _msgTime, isDeadChar, isSystemChar, isControlChar, charcode); handled = _site.Value.ReportInput(report); } internal static int GetVirtualKey(IntPtr wParam, IntPtr lParam) { int virtualKey = NativeMethods.IntPtrToInt32( wParam); int scanCode = 0; int keyData = NativeMethods.IntPtrToInt32(lParam); // Find the left/right instance SHIFT keys. if(virtualKey == NativeMethods.VK_SHIFT) { scanCode = (keyData & 0xFF0000) >> 16; try { virtualKey = SafeNativeMethods.MapVirtualKey(scanCode, 3); if(virtualKey == 0) { virtualKey = NativeMethods.VK_LSHIFT; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: MapVirtualKey failed!"); virtualKey = NativeMethods.VK_LSHIFT; } } // Find the left/right instance ALT keys. if(virtualKey == NativeMethods.VK_MENU) { bool right = ((keyData & 0x1000000) >> 24) != 0; if(right) { virtualKey = NativeMethods.VK_RMENU; } else { virtualKey = NativeMethods.VK_LMENU; } } // Find the left/right instance CONTROL keys. if(virtualKey == NativeMethods.VK_CONTROL) { bool right = ((keyData & 0x1000000) >> 24) != 0; if(right) { virtualKey = NativeMethods.VK_RCONTROL; } else { virtualKey = NativeMethods.VK_LCONTROL; } } return virtualKey; } internal static int GetScanCode(IntPtr wParam, IntPtr lParam) { int keyData = NativeMethods.IntPtrToInt32(lParam); int scanCode = (keyData & 0xFF0000) >> 16; if(scanCode == 0) { try { int virtualKey = GetVirtualKey(wParam, lParam); scanCode = SafeNativeMethods.MapVirtualKey(virtualKey, 0); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: MapVirtualKey failed!"); } } return scanCode; } internal static bool IsExtendedKey(IntPtr lParam) { int keyData = NativeMethods.IntPtrToInt32(lParam); return ((keyData & 0x01000000) != 0) ? true : false; } ////// Returns the set of modifier keys currently pressed as determined by calling to Win32 /// ////// Marked as FriendAccessAllowed so HwndHost in PresentationFramework can call it /// ////// Critical: It calls an UnsafeNativeMethod (GetKeyState). /// TreatAsSafe: It's safe to return whether shift, control or alt keys are being pressed or not. /// [FriendAccessAllowed] [SecurityCritical,SecurityTreatAsSafe] internal static ModifierKeys GetSystemModifierKeys() { ModifierKeys modifierKeys = ModifierKeys.None; short keyState = UnsafeNativeMethods.GetKeyState(NativeMethods.VK_SHIFT); if((keyState & 0x8000) == 0x8000) { modifierKeys |= ModifierKeys.Shift; } keyState = UnsafeNativeMethods.GetKeyState(NativeMethods.VK_CONTROL); if((keyState & 0x8000) == 0x8000) { modifierKeys |= ModifierKeys.Control; } keyState = UnsafeNativeMethods.GetKeyState(NativeMethods.VK_MENU); if((keyState & 0x8000) == 0x8000) { modifierKeys |= ModifierKeys.Alt; } return modifierKeys; } private RawKeyboardActions GetKeyUpKeyDown(int msg) { if( msg == NativeMethods.WM_KEYDOWN || msg == NativeMethods.WM_SYSKEYDOWN ) return RawKeyboardActions.KeyDown; if( msg == NativeMethods.WM_KEYUP || msg == NativeMethods.WM_SYSKEYUP ) return RawKeyboardActions.KeyUp; throw new ArgumentException(SR.Get(SRID.OnlyAcceptsKeyMessages)); } ////// Critical: This code causes this window to loose focus not ok to expose /// It also calls into a critical code path. /// [SecurityCritical] private void PossiblyDeactivate(IntPtr hwndFocus) { Debug.Assert( null != _source ); // We are now longer active ourselves, but it is possible that the // window the keyboard is going to intereact with is in the same // Dispatcher as ourselves. If so, we don't want to deactivate the // keyboard input stream because the other window hasn't activated // it yet, and it may result in the input stream "flickering" between // active/inactive/active. This is ugly, so we try to supress the // uneccesary transitions. // bool deactivate = !IsOurWindow(hwndFocus); // This window itself should not be active anymore. _active = false; // Only deactivate the keyboard input stream if needed. if(deactivate) { ReportInput(_source.Value.CriticalHandle, InputMode.Foreground, _msgTime, RawKeyboardActions.Deactivate, 0, false, false, 0); } } ////// Critical: This code does not store any critical data, it accesses PresentationSource /// TreatAsSafe: This information is safe to expose /// [SecurityCritical,SecurityTreatAsSafe] private bool IsOurWindow(IntPtr hwnd) { bool isOurWindow = false; Debug.Assert( null != _source ); if(hwnd != IntPtr.Zero) { HwndSource hwndSource = HwndSource.CriticalFromHwnd(hwnd); if(hwndSource != null) { if(hwndSource.Dispatcher == _source.Value.Dispatcher) { // The window has the same dispatcher, must be ours. isOurWindow = true; } else { // The window has a different dispatcher, must not be ours. isOurWindow = false; } } else { // The window is non-Avalon. // Such windows are never ours. isOurWindow = false; } } else { // This is not even a window. isOurWindow = false; } return isOurWindow; } // return the immediate child (if any) of hwndRoot that governs the // given hwnd. If hwnd is not a descendant of hwndRoot, return 0. ////// Critical:This method calls critical methods /// [SecurityCritical] private IntPtr GetImmediateChildFor(IntPtr hwnd, IntPtr hwndRoot) { while (hwnd != IntPtr.Zero) { // We only care to restore focus to child windows. Notice that WS_POPUP // windows also have parents but we do not want to track those here. int windowStyle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this,hwnd), NativeMethods.GWL_STYLE); if((windowStyle & NativeMethods.WS_CHILD) == 0) { break; } IntPtr hwndParent = UnsafeNativeMethods.GetParent(new HandleRef(this, hwnd)); if (hwndParent == hwndRoot) { return hwnd; } hwnd = hwndParent; } return IntPtr.Zero; } ////// Critical:This code can cause input simulation and hence is critical. /// The current code path is only hit under RootBrowserWindow scenario for now. /// [SecurityCritical] private bool ReportInput( IntPtr hwnd, InputMode mode, int timestamp, RawKeyboardActions actions, int scanCode, bool isExtendedKey, bool isSystemKey, int virtualKey) { Debug.Assert( null != _source ); // The first event should also activate the keyboard device. if((actions & RawKeyboardActions.Deactivate) == 0) { if(!_active || _partialActive) { try { // Include the activation action. actions |= RawKeyboardActions.Activate; // Remember that we are active. _active = true; _partialActive = false; } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetKeyboardState failed!"); // We'll go ahead and report the input, but we'll try to "activate" next time. } } } // Get the extra information sent along with the message. IntPtr extraInformation = IntPtr.Zero; try { extraInformation = UnsafeNativeMethods.GetMessageExtraInfo(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetMessageExtraInfo failed!"); } RawKeyboardInputReport report = new RawKeyboardInputReport(_source.Value, mode, timestamp, actions, scanCode, isExtendedKey, isSystemKey, virtualKey, extraInformation); bool handled = _site.Value.ReportInput(report); return handled; } private int _msgTime; ////// This is got under an elevation and is hence critical. This data is not ok to expose. /// private SecurityCriticalDataClass_source; /// /// This is got under an elevation and is hence critical.This data is not ok to expose. /// private SecurityCriticalDataClass_site; private IInputElement _restoreFocus; /// /// This is got under an elevation and is hence critical.This data is not ok to expose. /// [SecurityCritical] private IntPtr _restoreFocusWindow; private bool _active; private bool _partialActive; } } // 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
- WebPartDisplayModeEventArgs.cs
- RectangleHotSpot.cs
- Positioning.cs
- WebService.cs
- HtmlImage.cs
- columnmapfactory.cs
- UInt16.cs
- XmlSchemaElement.cs
- SynchronizationHandlesCodeDomSerializer.cs
- RemotingConfiguration.cs
- Trigger.cs
- _emptywebproxy.cs
- DataGridViewCellParsingEventArgs.cs
- EventEntry.cs
- WeakEventManager.cs
- KeyFrames.cs
- CheckBoxFlatAdapter.cs
- XmlResolver.cs
- UserControl.cs
- HTTPNotFoundHandler.cs
- templategroup.cs
- Set.cs
- StrongName.cs
- OutputCache.cs
- EventMap.cs
- OleServicesContext.cs
- ParentControlDesigner.cs
- TransactionInterop.cs
- CheckBoxRenderer.cs
- AggregateNode.cs
- PowerStatus.cs
- UnsafeNativeMethods.cs
- RemoteWebConfigurationHostServer.cs
- glyphs.cs
- BrowsableAttribute.cs
- ControlParameter.cs
- ModifierKeysConverter.cs
- DataColumnMappingCollection.cs
- ConfigXmlDocument.cs
- EnumBuilder.cs
- Context.cs
- MetadataArtifactLoaderCompositeResource.cs
- isolationinterop.cs
- SocketAddress.cs
- StrongNameKeyPair.cs
- WebControlsSection.cs
- DBPropSet.cs
- ServiceChannelProxy.cs
- RecognizedPhrase.cs
- CorrelationRequestContext.cs
- MimePart.cs
- TypeDelegator.cs
- DoubleMinMaxAggregationOperator.cs
- ZoneMembershipCondition.cs
- ConnectionPoint.cs
- ToolStripLocationCancelEventArgs.cs
- SortedList.cs
- EmptyElement.cs
- KeyBinding.cs
- StreamGeometry.cs
- CurrencyManager.cs
- ServerValidateEventArgs.cs
- SyntaxCheck.cs
- SystemWebCachingSectionGroup.cs
- JoinCqlBlock.cs
- QueryCreatedEventArgs.cs
- InlineUIContainer.cs
- SafeLocalMemHandle.cs
- EventRouteFactory.cs
- HwndSourceParameters.cs
- SchemaDeclBase.cs
- XmlNamespaceDeclarationsAttribute.cs
- DataGridViewUtilities.cs
- DelegatedStream.cs
- OdbcConnectionStringbuilder.cs
- DesignSurface.cs
- SqlConnectionPoolGroupProviderInfo.cs
- XmlBinaryReader.cs
- SymLanguageVendor.cs
- Properties.cs
- XmlUnspecifiedAttribute.cs
- StrongTypingException.cs
- SimpleLine.cs
- TypePresenter.xaml.cs
- ThemeDictionaryExtension.cs
- ResourcePart.cs
- BrowserDefinition.cs
- TypeValidationEventArgs.cs
- EntitySetBaseCollection.cs
- ToolStripItemTextRenderEventArgs.cs
- StdValidatorsAndConverters.cs
- ExtensionFile.cs
- altserialization.cs
- SignatureToken.cs
- HostingEnvironmentWrapper.cs
- CollectionBuilder.cs
- NoClickablePointException.cs
- TypedRowHandler.cs
- PersonalizationStateInfo.cs
- WsdlParser.cs