Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / InterOp / HwndSource.cs / 1305600 / HwndSource.cs
//------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, 2004 // // File: HwndSource.cs //----------------------------------------------------------------------------- using System.Collections; using System.Collections.Generic; using System.Threading; using System.Windows.Threading; using System.Windows.Input; using System.Windows.Media; using System.Windows.Interop; using System.Runtime.InteropServices; using MS.Win32; using MS.Utility; using MS.Internal; using MS.Internal.Interop; using MS.Internal.PresentationCore; // SecurityHelper using Microsoft.Win32; using System.Diagnostics; using System.ComponentModel; using System; using System.Security; using System.Security.Permissions; using System.IO; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; #pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace System.Windows.Interop { ////// The HwndSource class presents content within a Win32 HWND. /// public class HwndSource : PresentationSource, IDisposable, IWin32Window, IKeyboardInputSink { ////// Critical: This code calls into RegisterWindowMesssage which is critical /// TreatAsSafe: This is safe to call as no external parameters are taken in /// [SecurityCritical,SecurityTreatAsSafe] static HwndSource() { _threadSlot = Thread.AllocateDataSlot(); } ////// Constructs an instance of the HwndSource class that will always resize to its content size. /// /// /// The Win32 class styles for this window. /// /// /// The Win32 styles for this window. /// /// /// The extended Win32 styles for this window. /// /// /// The position of the left edge of this window. /// /// /// The position of the upper edge of this window. /// /// /// The name of this window. /// /// /// The Win32 window that should be the parent of this window. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Added a demand - so that this API does not work in InternetZone. /// Critical: This accesses critical code Initialize /// PublicOK: This code has a demand which will ensure that it does /// not work in partial trust without the correct permissions /// [SecurityCritical] public HwndSource( int classStyle, int style, int exStyle, int x, int y, string name, IntPtr parent) { SecurityHelper.DemandUIWindowPermission(); HwndSourceParameters param = new HwndSourceParameters(name); param.WindowClassStyle = classStyle; param.WindowStyle = style; param.ExtendedWindowStyle = exStyle; param.SetPosition(x, y); param.ParentWindow = parent; Initialize(param); } ////// Constructs an instance of the HwndSource class. This version requires an /// explicit width and height be sepecified. /// /// /// The Win32 class styles for this window. /// /// /// The Win32 styles for this window. /// /// /// The extended Win32 styles for this window. /// /// /// The position of the left edge of this window. /// /// /// The position of the upper edge of this window. /// /// /// The width of this window. /// /// /// The height of this window. /// /// /// The name of this window. /// /// /// The Win32 window that should be the parent of this window. /// /// /// Indicates that HwndSource should include the non-client area /// of the hwnd when it calls the Layout Manager /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Added a demand - so that this API does not work in InternetZone. /// Critical: This acceses critical code Initialize /// PublicOK: This code has a demand which will ensure that it does /// not work in partial trust without the correct permissions /// [SecurityCritical] public HwndSource(int classStyle, int style, int exStyle, int x, int y, int width, int height, string name, IntPtr parent, bool adjustSizingForNonClientArea) { SecurityHelper.DemandUIWindowPermission(); HwndSourceParameters parameters = new HwndSourceParameters(name, width, height); parameters.WindowClassStyle = classStyle; parameters.WindowStyle = style; parameters.ExtendedWindowStyle = exStyle; parameters.SetPosition(x, y); parameters.ParentWindow = parent; parameters.AdjustSizingForNonClientArea = adjustSizingForNonClientArea; Initialize(parameters); } ////// Constructs an instance of the HwndSource class. This version requires an /// explicit width and height be sepecified. /// /// /// The Win32 class styles for this window. /// /// /// The Win32 styles for this window. /// /// /// The extended Win32 styles for this window. /// /// /// The position of the left edge of this window. /// /// /// The position of the upper edge of this window. /// /// /// The width of this window. /// /// /// The height of this window. /// /// /// The name of this window. /// /// /// The Win32 window that should be the parent of this window. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Added a demand - so that this API does not work in InternetZone. /// Critical: This accesses critical code Initialize /// PublicOK: This code has a demand which will ensure that it does /// not work in partial trust without the correct permissions /// [SecurityCritical] public HwndSource( int classStyle, int style, int exStyle, int x, int y, int width, int height, string name, IntPtr parent) { SecurityHelper.DemandUIWindowPermission(); HwndSourceParameters parameters = new HwndSourceParameters(name, width, height); parameters.WindowClassStyle = classStyle; parameters.WindowStyle = style; parameters.ExtendedWindowStyle = exStyle; parameters.SetPosition(x, y); parameters.ParentWindow = parent; Initialize(parameters); } ////// HwndSource Ctor /// /// parameter block ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This acceses critical code Initialize /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] public HwndSource(HwndSourceParameters parameters) { Initialize(parameters); } ////// HwndSource Ctor /// /// parameter block ////// Critical: This code access critical (HwndMouseInputProvider, HwndKeyboardInputProvider /// ,HwndStylusInputProvider and the various hooks)objects and creates the /// providers under elevation. /// [SecurityCritical] private void Initialize(HwndSourceParameters parameters) { _mouse = new SecurityCriticalDataClass(new HwndMouseInputProvider(this)); _keyboard = new SecurityCriticalDataClass (new HwndKeyboardInputProvider(this)); _layoutHook = new HwndWrapperHook(LayoutFilterMessage); _inputHook = new HwndWrapperHook(InputFilterMessage); _hwndTargetHook = new HwndWrapperHook(HwndTargetFilterMessage); _publicHook = new HwndWrapperHook(PublicHooksFilterMessage); // When processing WM_SIZE, LayoutFilterMessage must be invoked before // HwndTargetFilterMessage. This way layout will be updated before resizing // HwndTarget, resulting in single render per resize. This means that // layout hook should appear before HwndTarget hook in the wrapper hooks // list. If this is done the other way around, first HwndTarget resize will // force re-render, then layout will be updated according to the new size, // scheduling another render. HwndWrapperHook[] wrapperHooks = { _hwndTargetHook, _layoutHook, _inputHook, null }; if (null != parameters.HwndSourceHook) { // In case there's more than one delegate, add these to the event storage backwards // so they'll get invoked in the expected order. Delegate[] handlers = parameters.HwndSourceHook.GetInvocationList(); for (int i = handlers.Length -1; i >= 0; --i) { _hooks += (HwndSourceHook)handlers[i]; } wrapperHooks[3] = _publicHook; } _restoreFocusMode = parameters.RestoreFocusMode; _acquireHwndFocusInMenuMode = parameters.AcquireHwndFocusInMenuMode; // A window must be marked WS_EX_LAYERED if (and only if): // 1) it is not a child window // -- AND -- // 2) a color-key is specified // 3) or an opacity other than 1.0 is specified // 4) or per-pixel alpha is requested. if((parameters.WindowStyle & NativeMethods.WS_CHILD) == 0 && ( //parameters.ColorKey != null || //!MS.Internal.DoubleUtil.AreClose(parameters.Opacity, 1.0) || parameters.UsesPerPixelOpacity)) { parameters.ExtendedWindowStyle |= NativeMethods.WS_EX_LAYERED; } else { parameters.ExtendedWindowStyle &= (~NativeMethods.WS_EX_LAYERED); } _constructionParameters = parameters; _hwndWrapper = new HwndWrapper(parameters.WindowClassStyle, parameters.WindowStyle, parameters.ExtendedWindowStyle, parameters.PositionX, parameters.PositionY, parameters.Width, parameters.Height, parameters.WindowName, parameters.ParentWindow, wrapperHooks); _hwndTarget = new HwndTarget(_hwndWrapper.Handle); //_hwndTarget.ColorKey = parameters.ColorKey; //_hwndTarget.Opacity = parameters.Opacity; _hwndTarget.UsesPerPixelOpacity = parameters.UsesPerPixelOpacity; if(_hwndTarget.UsesPerPixelOpacity) { _hwndTarget.BackgroundColor = Colors.Transparent; // Prevent this window from being themed. UnsafeNativeMethods.CriticalSetWindowTheme(new HandleRef(this, _hwndWrapper.Handle), "", ""); } _constructionParameters = null; if (!parameters.HasAssignedSize) _sizeToContent = SizeToContent.WidthAndHeight; _adjustSizingForNonClientArea = parameters.AdjustSizingForNonClientArea; // Listen to the UIContext.Disposed event so we can clean up. // The HwndTarget cannot work without a MediaContext which // is disposed when the UIContext is disposed. So we need to // dispose the HwndTarget and also never use it again (to // paint or process input). The easiest way to do this is to just // dispose the HwndSource at the same time. _weakShutdownHandler = new WeakEventDispatcherShutdown(this, this.Dispatcher); // Listen to the HwndWrapper.Disposed event so we can clean up. // The HwndTarget cannot work without a live HWND, and since // the HwndSource represents an HWND, we make sure we dispose // ourselves if the HWND is destroyed out from underneath us. _hwndWrapper.Disposed += new EventHandler(OnHwndDisposed); _stylus = new SecurityCriticalDataClass (new HwndStylusInputProvider(this)); // WM_APPCOMMAND events are handled thru this. _appCommand = new SecurityCriticalDataClass (new HwndAppCommandInputProvider(this)); // Register the top level source with the ComponentDispatcher. if (parameters.TreatAsInputRoot) { _weakPreprocessMessageHandler = new WeakEventPreprocessMessage(this, false); } AddSource(); // Register dropable window. // The checking CallerHasPermissionWithAppDomainOptimization will call RegisterDropTarget // safely without the security exception in case of no unmanaged code permission. // So RegisterDropTarget will be called safely in case of having the unmanged code permission. // Otherwise, the security exception cause System.Printing to be instatiated which will // load system.drawing module. if (_hwndWrapper.Handle != IntPtr.Zero && SecurityHelper.CallerHasPermissionWithAppDomainOptimization(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode))) { // This call is safe since DragDrop.RegisterDropTarget is checking the unmanged // code permission. DragDrop.RegisterDropTarget(_hwndWrapper.Handle); _registeredDropTargetCount++; } } /// /// Disposes the object /// ////// This API is not available in Internet Zone. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ////// Adds a hook that gets called for every window message. /// /// /// The hook to add. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - uses a critical field. /// PublicOK - as there's a demand. /// [SecurityCritical ] public void AddHook(HwndSourceHook hook) { SecurityHelper.DemandUIWindowPermission(); Verify.IsNotNull(hook, "hook"); CheckDisposed(true); if(_hooks == null) { _hwndWrapper.AddHook(_publicHook); } _hooks += hook; } ////// Removes a hook that was previously added. /// /// /// The hook to remove. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - accesses a crtical field - _publicHook /// PublicOK - performs a demand. /// [SecurityCritical ] public void RemoveHook(HwndSourceHook hook) { SecurityHelper.DemandUIWindowPermission(); //this.VerifyAccess(); _hooks -= hook; if(_hooks == null) { _hwndWrapper.RemoveHook(_publicHook); } } ////// GetInputProvider - Given a InputDevice, returns corresponding Provider /// /// InputDevice for which we need InputProvider ///InputProvider, if known ////// This API is not available in Internet Zone. /// ////// Critical: This code accesses and returns critical data *providers* /// [SecurityCritical] internal override IInputProvider GetInputProvider(Type inputDevice) { if (inputDevice == typeof(MouseDevice)) return (_mouse != null ? _mouse.Value : null); if (inputDevice == typeof(KeyboardDevice)) return (_keyboard != null ? _keyboard.Value : null); if (inputDevice == typeof(StylusDevice)) return (_stylus != null ? _stylus.Value : null); return null; } ////// Announces when this source is disposed. /// public event EventHandler Disposed; ////// Announces when the SizeToContent property changes on this source. /// public event EventHandler SizeToContentChanged; ////// Whether or not the object is disposed. /// public override bool IsDisposed {get {return _isDisposed;}} ////// The Root Visual for this window. If it is a UIElement /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This code sets a rootvisual which is risky to do because /// it can destabilize assumptions made in popup code /// PublicOK: The getter is safe and the setter has a link demand to block unwarrented /// public use /// public override Visual RootVisual { [SecurityCritical] get { if (_isDisposed) return null; return (_rootVisual.Value); } [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Window=UIPermissionWindow.AllWindows)] set { CheckDisposed(true); RootVisualInternal = value; } } ////// Critical: Acceses KeyboardInputProvider which is considered as critical data /// also it can be used to set root visual which is deemed as an unsafe operation /// private Visual RootVisualInternal { [SecurityCritical] set { if (_rootVisual.Value != value) { Visual oldRoot = _rootVisual.Value; if(value != null) { _rootVisual.Value = value; if(_rootVisual.Value is UIElement) { ((UIElement)(_rootVisual.Value)).LayoutUpdated += new EventHandler(OnLayoutUpdated); } if (_hwndTarget != null && _hwndTarget.IsDisposed == false) { _hwndTarget.RootVisual = _rootVisual.Value; } UIElement.PropagateResumeLayout(null, value); } else { _rootVisual.Value = null; if (_hwndTarget != null && !_hwndTarget.IsDisposed) { _hwndTarget.RootVisual = null; } } if(oldRoot != null) { if(oldRoot is UIElement) { ((UIElement)oldRoot).LayoutUpdated -= new EventHandler(OnLayoutUpdated); } UIElement.PropagateSuspendLayout(oldRoot); } RootChanged(oldRoot, _rootVisual.Value); if (IsLayoutActive() == true) { // Call the helper method SetLayoutSize to set Layout's size SetLayoutSize(); // Post the firing of ContentRendered as Input priority work item so that ContentRendered will be // fired after render query empties. this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(FireContentRendered), this); } else { // Even though layout won't run (the root visual is either null or not // a UIElement), the hit-test results will certainly have changed. InputManager.SafeCurrentNotifyHitTestInvalidated(); } // It is possible that someone would have closed the window in one of the // previous callouts - such as during RootChanged or during the layout // we syncronously invoke. In such cases, the state of this object would // have been torn down. We just need to protect against that. if(_keyboard != null) { _keyboard.Value.OnRootChanged(oldRoot, _rootVisual.Value); } } } } ////// Returns a sequence of registered input sinks. /// public IEnumerableChildKeyboardInputSinks { get { if (_keyboardInputSinkChildren != null) { foreach (IKeyboardInputSite site in _keyboardInputSinkChildren) yield return site.Sink; } } } /// /// Returns the HwndSource that corresponds to the specified window. /// /// The window. ///The source that corresponds to the specified window. ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This information is not ok to expose since this HwndSource /// is deemed critical to expose /// PublicOK: There is a demand on this method that prevents this /// from working in partial trust unless you have the right permissions. /// [SecurityCritical] public static HwndSource FromHwnd(IntPtr hwnd) { SecurityHelper.DemandUIWindowPermission(); return CriticalFromHwnd(hwnd); } ////// Critical: This code extracts the HwndSource for an HWND /// This function is only for internal consumption /// [SecurityCritical] internal static HwndSource CriticalFromHwnd(IntPtr hwnd) { if (hwnd == IntPtr.Zero) { throw new ArgumentException(SR.Get(SRID.NullHwnd)); } HwndSource hwndSource = null; foreach (PresentationSource source in PresentationSource.CriticalCurrentSources) { HwndSource test = source as HwndSource; if (test != null && test.CriticalHandle == hwnd) { // Don't hand out a disposed source. if (!test.IsDisposed) hwndSource = test; break; } } return hwndSource; } ////// The visual manager for the visuals being presented in the source. /// Type-specific version of the CompositionTarget property for this source. /// ////// Critical: Accesses hwndTarget and returns it /// PublicOk: Protected by a LinkDemand /// public new HwndTarget CompositionTarget { [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Window=UIPermissionWindow.AllWindows)] get { if (_isDisposed) return null; // Even though we created the HwndTarget, it can get disposed out from // underneath us. if (_hwndTarget!= null && _hwndTarget.IsDisposed == true) { return null; } return _hwndTarget; } } ////// Returns visual target for this source. /// ////// Critical: calls get_CompositionTarget() and returns its value. /// [SecurityCritical] protected override CompositionTarget GetCompositionTargetCore() { return CompositionTarget; } ////// When an HwndSource enters menu mode, it subscribes to the /// ComponentDispatcher.ThreadPreprocessMessage event to get /// privileged access to the window messages. /// ////// The ThreadPreprocessMessage handler for menu mode is /// independent of the handler for the same event used for /// keyboard processing by top-level windows. /// ////// Critical: Accesses the ComponentDispatcher, which is generally /// considered critical. /// TreatAsSafe: MenuMode is approved for public access. /// [SecurityCritical, SecurityTreatAsSafe] internal override void OnEnterMenuMode() { // We opt-in this HwndSource to the new behavior for "exclusive" // menu mode only if AcquireHwndFocusInMenuMode is false. IsInExclusiveMenuMode = !_acquireHwndFocusInMenuMode; if(IsInExclusiveMenuMode) { Debug.Assert(_weakMenuModeMessageHandler == null); // Re-subscribe to the ComponentDispatcher.ThreadPreprocessMessage so we go first. _weakMenuModeMessageHandler = new WeakEventPreprocessMessage(this, true); // Hide the Win32 caret UnsafeNativeMethods.HideCaret(new HandleRef(this, IntPtr.Zero)); } } ////// When an HwndSource leaves menu mode, it unsubscribes from the /// ComponentDispatcher.ThreadPreprocessMessage event because it no /// longer needs privileged access to the window messages. /// ////// The ThreadPreprocessMessage handler for menu mode is /// independent of the handler for the same event used for /// keyboard processing by top-level windows. /// ////// Critical: Accesses the ComponentDispatcher, which is generally /// considered critical. /// TreatAsSafe: MenuMode is approved for public access. /// [SecurityCritical, SecurityTreatAsSafe] internal override void OnLeaveMenuMode() { if(IsInExclusiveMenuMode) { Debug.Assert(_weakMenuModeMessageHandler != null); // Unsubscribe the special menu-mode handler since we don't need to go first anymore. _weakMenuModeMessageHandler.Dispose(); _weakMenuModeMessageHandler = null; // Restore the Win32 caret. This does not necessarily show the caret, it // just undoes the HideCaret call in OnEnterMenuMode. UnsafeNativeMethods.ShowCaret(new HandleRef(this, IntPtr.Zero)); } IsInExclusiveMenuMode = false; } internal bool IsInExclusiveMenuMode{get; private set;} ////// Event invoked when the layout causes the HwndSource to resize automatically. /// public event AutoResizedEventHandler AutoResized; ////// Handler for LayoutUpdated event of a rootVisual. /// ////// Critical: This code causes resize of window and accesses HwndTarget /// [SecurityCritical] private void OnLayoutUpdated(object obj, EventArgs args) { UIElement root = _rootVisual.Value as UIElement; if(root != null) { Size newSize = root.RenderSize; if ( _previousSize == null || !DoubleUtil.AreClose(_previousSize.Value.Width, newSize.Width) || !DoubleUtil.AreClose(_previousSize.Value.Height, newSize.Height)) { // We should update _previousSize, even if the hwnd is not // sizing to content. This fixes the scenario where: // // 1) hwnd is sized to content to say a, b // 2) hwnd is resize to a bigger size // 3) hwnd is sized to content to a, b again _previousSize = newSize; // // Don't resize while the Window is in Minimized mode. // if (_sizeToContent != SizeToContent.Manual && !_isWindowInMinimizeState ) { Resize(newSize); } } } } ////// This is called when LayoutManager was updated and its size (the layout size of top element) changed. /// Ask LayoutManager.Size to see what the new value is. /// ////// Critical: This code causes resize of window and accesses HwndTarget /// TreatAsSafe: In RBW the resize values are clamped also one cannot construct or get to a HwndSource in partial trust /// [SecurityCritical, SecurityTreatAsSafe] private void Resize(Size newSize) { try { _myOwnUpdate = true; if (IsUsable) { NativeMethods.RECT rect = AdjustWindowSize(newSize); int newWidth = rect.right - rect.left; int newHeight = rect.bottom - rect.top; // Set the new window size UnsafeNativeMethods.SetWindowPos(new HandleRef(this,_hwndWrapper.Handle), new HandleRef(null,IntPtr.Zero), 0, 0, newWidth, newHeight, NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE); if (AutoResized != null) { AutoResized(this, new AutoResizedEventArgs(newSize)); } } } finally { _myOwnUpdate = false; } } ////// This shows the system menu for the top level window that this HwndSource is in. /// ////// Critical: Accesses GetAncestor & PostMessage. This method is deemed inherently unsafe /// because opening the system menu will eat user input. /// [SecurityCritical] internal void ShowSystemMenu() { // Find the topmost window. This will handle the case where the HwndSource // is a child window. IntPtr hwndRoot = UnsafeNativeMethods.GetAncestor(new HandleRef(this, CriticalHandle), NativeMethods.GA_ROOT); // Open the system menu. UnsafeNativeMethods.PostMessage(new HandleRef(this, hwndRoot), MS.Internal.Interop.WindowMessage.WM_SYSCOMMAND, new IntPtr(NativeMethods.SC_KEYMENU), new IntPtr(NativeMethods.VK_SPACE)); } ////// Critical: This code accesses critical member _hwndTarget. /// TreatAsSafe: It calculates the new point without changing the HWND. /// [SecurityCritical, SecurityTreatAsSafe] internal Point TransformToDevice(Point pt) { // return _hwndTarget.TransformToDevice.Transform(pt); } ////// Critical: This code accesses critical member _hwndTarget. /// TreatAsSafe: It calculates the new point without changing the HWND. /// [SecurityCritical, SecurityTreatAsSafe] internal Point TransformFromDevice(Point pt) { return _hwndTarget.TransformFromDevice.Transform(pt); } ////// Critical: This code accesses _hwndWrapper. /// TreatAsSafe: It calculates the hwnd size without changing it. /// [SecurityCritical, SecurityTreatAsSafe] private NativeMethods.RECT AdjustWindowSize(Size newSize) { // Gather the new client dimensions // The dimension WPF uses is logical unit. We need to convert to device unit first. Point pt = TransformToDevice(new Point(newSize.Width, newSize.Height)); RoundDeviceSize(ref pt); NativeMethods.RECT rect = new NativeMethods.RECT(0, 0, (int)pt.X, (int)pt.Y); // If we're here, and it is the Window case (_adjustSizingForNonClientArea == true) // we get the size which includes the outside size of the window. For browser case, // we don't support SizeToContent, so we don't take care of this condition. // For non-Window cases, we need to calculate the outside size of the window // // For windows with UsesPerPixelOpacity, we force the client to // fill the window, so we don't need to add in any frame space. // if (_adjustSizingForNonClientArea == false && !UsesPerPixelOpacity) { int style = NativeMethods.IntPtrToInt32((IntPtr)SafeNativeMethods.GetWindowStyle(new HandleRef(this, _hwndWrapper.Handle), false)); int styleEx = NativeMethods.IntPtrToInt32((IntPtr)SafeNativeMethods.GetWindowStyle(new HandleRef(this, _hwndWrapper.Handle), true)); SafeNativeMethods.AdjustWindowRectEx(ref rect, style, false, styleEx); } return rect; } // If the root element has Pixel snapping enabled, round the window size to the // nearest int. Otherwise round the size up to the next int. private void RoundDeviceSize(ref Point size) { UIElement root = _rootVisual.Value as UIElement; if (root != null && root.SnapsToDevicePixels) { size = new Point(DoubleUtil.DoubleToInt(size.X), DoubleUtil.DoubleToInt(size.Y)); } else { size = new Point(Math.Ceiling(size.X), Math.Ceiling(size.Y)); } } ////// Returns the hwnd handle to the window. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical:This is not safe to expose in internet zone, it returns a window handle /// PublicOK: There exists a demand on this code /// public IntPtr Handle { [SecurityCritical] get { SecurityHelper.DemandUIWindowPermission(); return CriticalHandle; } } ////// Critical:Internal helper to retrieve handle for security purposes only Please /// DO NOT USE THIS TO EXPOSE HANDLE TO OUTSIDE WORLD /// internal IntPtr CriticalHandle { [FriendAccessAllowed] [SecurityCritical] get { if (null != _hwndWrapper) return _hwndWrapper.Handle; return IntPtr.Zero; } } ////// Critical: returns the critical _hwndWrapper. /// internal HwndWrapper HwndWrapper { [SecurityCritical] get { return _hwndWrapper; } } // Return whether this presentation source has capture. ////// Critical: calls CriticalHandle /// TreatAsSafe: Returning whether a presentation source has capture is considered safe. /// internal bool HasCapture { [SecurityCritical, SecurityTreatAsSafe] get { IntPtr capture = SafeNativeMethods.GetCapture(); return ( capture == CriticalHandle ); } } ////// Critical - accesses _hwndWrapper. /// TreatAsSafe - checking for null is considered safe. /// internal bool IsHandleNull { [SecurityCritical, SecurityTreatAsSafe ] get { return _hwndWrapper.Handle == IntPtr.Zero ; } } ////// Returns the hwnd handle to the window. /// public HandleRef CreateHandleRef() { return new HandleRef(this,Handle); } ////// SizeToContent on HwndSource /// ////// The default value is SizeToContent.Manual /// public SizeToContent SizeToContent { get { CheckDisposed(true); return _sizeToContent; } set { CheckDisposed(true); if (IsValidSizeToContent(value) != true) { throw new InvalidEnumArgumentException("value", (int)value, typeof(SizeToContent)); } if (_sizeToContent == value) { return; } _sizeToContent = value; // we only raise SizeToContentChanged when user interaction caused the change; // if a developer goes directly to HwndSource and sets SizeToContent, we will // not notify the wrapping Window if (IsLayoutActive() == true) { // Call the helper method SetLayoutSize to set Layout's size SetLayoutSize(); } } } ////// Critical: This code elevates to access hwndtarget /// TreatAsSafe: ok to expose /// [SecurityCritical,SecurityTreatAsSafe] private bool IsLayoutActive() { if ((_rootVisual.Value is UIElement) && _hwndTarget!= null && _hwndTarget.IsDisposed == false) { return true; } return false; } ////// This is the helper method that sets Layout's size basing it on /// the current value of SizeToContent. /// ////// TreatAsSafe: This API could be public in terms of security. /// It does three calls to UnsafeNativeMethods all in a safe way /// Critical: Makes 3 calls to UnsafeNativeMethods /// [SecurityCritical, SecurityTreatAsSafe] private void SetLayoutSize() { Debug.Assert(_hwndTarget!= null, "HwndTarget is null"); Debug.Assert(_hwndTarget.IsDisposed == false, "HwndTarget is disposed"); UIElement rootUIElement = null; rootUIElement = _rootVisual.Value as UIElement; if (rootUIElement == null) return; // hamidm - 11/01/2005 // InvalidateMeasure() call is necessary in the following scenario // // Window w = new Window(); // w.Measure(new Size(x,y)); // w.Width = x; // w.Height = y; // w.Show() // // In the above scenario, the Measure call from SetLayoutSize will be opt out // and window will not receive the MeasureOverride call. As such, the hwnd min/max // restrictions will not be applied since MeasureOverride did not happen after hwnd // creation. Thus, we call InvalidatMeasure() to ensure MeasureOverride call on // Window after hwnd creation. rootUIElement.InvalidateMeasure(); const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf; bool etwEnabled = EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info); long ctxHashCode = 0; if (_sizeToContent == SizeToContent.WidthAndHeight) { //setup constraints for measure-to-content Size sz = new Size(double.PositiveInfinity, double.PositiveInfinity); if (etwEnabled) { ctxHashCode = _hwndWrapper.Handle.ToInt64(); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode, EventTrace.LayoutSource.HwndSource_SetLayoutSize); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Measure(sz); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Arrange(new Rect(new Point(), rootUIElement.DesiredSize)); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } } else { // GetSizeFromHwnd sets either the outside size or the client size of the hwnd based on // _adjustSizeingForNonClientArea flag in logical units. Size sizeFromHwndLogicalUnits = GetSizeFromHwnd(); Size sz = new Size( (_sizeToContent == SizeToContent.Width ? double.PositiveInfinity : sizeFromHwndLogicalUnits.Width), (_sizeToContent == SizeToContent.Height ? double.PositiveInfinity : sizeFromHwndLogicalUnits.Height)); if (etwEnabled) { ctxHashCode = _hwndWrapper.Handle.ToInt64(); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode, EventTrace.LayoutSource.HwndSource_SetLayoutSize); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Measure(sz); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } if (_sizeToContent == SizeToContent.Width) sz = new Size(rootUIElement.DesiredSize.Width, sizeFromHwndLogicalUnits.Height); else if(_sizeToContent == SizeToContent.Height) sz = new Size(sizeFromHwndLogicalUnits.Width, rootUIElement.DesiredSize.Height); else sz = sizeFromHwndLogicalUnits; rootUIElement.Arrange(new Rect(new Point(), sz)); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } } rootUIElement.UpdateLayout(); } // ///// /// Specifies the color to display as transparent. // /// // ///// /// Use null to indicate that no color should be transparent. // /// // public NullableColorKey // { // get // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // return _hwndTarget.ColorKey; // } // else // { // return null; // } // } // // set // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // _hwndTarget.ColorKey = value; // } // } // } // /// // /// Specifies the constant opacity to apply to the window. // /// // ///// /// The valid values range from [0..1]. Values outside of this range are clamped. // /// // public double Opacity // { // get // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // return _hwndTarget.Opacity; // } // else // { // return 1.0; // } // } // // set // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // _hwndTarget.Opacity = value; // } // } // } ////// Specifies whether or not the per-pixel opacity of the window content /// is respected. /// ////// By enabling per-pixel opacity, the system will no longer draw the non-client area. /// ////// Critical: Because it accesses _hwndTarget /// PublicOK: We don't pass it out; it is just used to query UsesPerPixelOpacity /// public bool UsesPerPixelOpacity { [SecurityCritical] get { CheckDisposed(true); HwndTarget hwndTarget = CompositionTarget; // checks for disposed if(_hwndTarget != null) { return _hwndTarget.UsesPerPixelOpacity; } else { return false; } } /* Not allowing this to change at run-time yet. [SecurityCritical] set { CheckDisposed(true); HwndTarget hwndTarget = CompositionTarget; // checks for disposed if(_hwndTarget != null) { _hwndTarget.UsesPerPixelOpacity = value; } } */ } ////// Critical: Accesses _hwndWrapper.Handle to call unmanaged code to get the client rectangle. /// TreatAsSafe: The handle is not passed out, and the client rectangle does not need to be protected. /// [SecurityCritical, SecurityTreatAsSafe] private Size GetSizeFromHwnd() { // Compute View's size and set NativeMethods.RECT rc = new NativeMethods.RECT(0, 0, 0, 0); if (_adjustSizingForNonClientArea == true) { // get correct size for avalon Window (standalone and browser case) GetSizeForWindowObject(ref rc); } else { SafeNativeMethods.GetClientRect(new HandleRef(this,_hwndWrapper.Handle), ref rc); } Point convertedPt = TransformFromDevice(new Point(rc.right - rc.left, rc.bottom - rc.top)); return new Size(convertedPt.X, convertedPt.Y); } ////// Critical: This code can be used to spoof input /// [SecurityCritical] private IntPtr HwndTargetFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; if (IsUsable) { HwndTarget hwndTarget = _hwndTarget; if (hwndTarget != null) { result = hwndTarget.HandleMessage((WindowMessage)msg, wParam, lParam); if (result != IntPtr.Zero) { handled = true; } } } return result; } ////// Critical:These hooks can all be used for input spoofing /// [SecurityCritical] private IntPtr LayoutFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; WindowMessage message = (WindowMessage)msg; // We have to revalidate everything because the Access() call // could have caused the CLR to enter a nested message pump, // during which almost anything could have happened that might // invalidate our checks. UIElement rootUIElement=null; rootUIElement = _rootVisual.Value as UIElement; if (IsUsable && rootUIElement != null) { switch (message) { // A window receives this message when the user chooses a command from // the Window menu or when the user chooses the maximize button, minimize // button, restore button, or close button. case WindowMessage.WM_SYSCOMMAND: { // The four low-order bits of the wParam parameter are used // internally by the system. Int32 sysCommand = NativeMethods.IntPtrToInt32(wParam) & 0xFFF0; // Turn off SizeToContent if user chooses to maximize or resize. if ((sysCommand == NativeMethods.SC_MAXIMIZE) || (sysCommand == NativeMethods.SC_SIZE)) { DisableSizeToContent(rootUIElement, hwnd); } } break; // We get WM_SIZING. It means that user starts resizing the window. // It is the first notification sent when user resizes (before WM_WINDOWPOSCHANGING) // and it's not sent if window is resized programmatically. // SizeToContent is turned off after user resizes. case WindowMessage.WM_SIZING: DisableSizeToContent(rootUIElement, hwnd); break; // The WM_WINDOWPOSCHANGING message is sent // 1. when the size, position, or place in the Z order is about to change as a result of a call to // the SetWindowPos function or other window-management functions. SizeToContent orverrides all in this case. // 2. when user resizes window. If it's user resize, we have turned SizeToContent off when we get WM_SIZING. // It is sent before WM_SIZE. // We can't use WM_GETMINMAXINFO, because if there is no window size change (we still need to make sure // the client size not change), that notification wouldnt be sent. case WindowMessage.WM_WINDOWPOSCHANGING: Process_WM_WINDOWPOSCHANGING(rootUIElement, hwnd, message, wParam, lParam); break; // WM_SIZE message is sent after the size has changed. // lParam has the new width and height of client area. // root element's size should be adjust based on the new width and height and SizeToContent's value. case WindowMessage.WM_SIZE: Process_WM_SIZE(rootUIElement, hwnd, message, wParam, lParam); break; } } // Certain messages need to be processed while we are in the middle // of construction - and thus an HwndTarget is not available. if(!handled && (_constructionParameters != null || IsUsable)) { // Get the usesPerPixelOpacity from either the constructor parameters or the HwndTarget. bool usesPerPixelOpacity = _constructionParameters != null ? ((HwndSourceParameters)_constructionParameters).UsesPerPixelOpacity : _hwndTarget.UsesPerPixelOpacity; switch(message) { case WindowMessage.WM_NCCALCSIZE: { // Windows that use per-pixel opacity don't get // their frames drawn by the system. Generally // this is OK, as users of per-pixel alpha tend // to be doing customized UI anyways. But we // don't render correctly if we leave a non-client // area, so here we expand the client area to // cover any non-client area. // if(usesPerPixelOpacity) { if(wParam == IntPtr.Zero) { // If wParam is FALSE, lParam points to a RECT // structure. On entry, the structure contains // the proposed window rectangle for the // window. On exit, the structure should // contain the screen coordinates of the // corresponding window client area. // // Since all we want to do is make the client // rect the same as the window rect, we don't // have to do anything. // result = IntPtr.Zero; handled = true; } else { // If wParam is TRUE, lParam points to an // NCCALCSIZE_PARAMS structure that contains // information an application can use to // calculate the new size and position of // the client rectangle. // // When Windows sends the WM_NCCALCSIZE // message, the NCCALCSIZE_PARAMS structure // is filled out like this: // // rgrc[0] = new window rectangle (in parent coordinates) // rgrc[1] = old window rectangle (in parent coordinates) // rgrc[2] = old client rectangle (in parent coordinates) // // Notice that the client rectangle is given // in parent coordinates, not in client // coordinates. // // When your window procedure returns, Windows // expects the NCCALCSIZE_PARAMS structure to // be filled out like this: // // rgrc[0] = new client rectangle (in parent coordinates) // // Furthermore, if you return anything other // than 0, Windows expects the remaining two // rectangles to be filled out like this: // // rgrc[1] = destination rectangle (in parent coordinates) // rgrc[2] = source rectangle (in parent coordinates) // // (If you return 0, then Windows assumes that // the destination rectangle equals the new // client rectangle and the source rectangle // equals the old client rectangle.) // // Since all we want to do is make the client // rect the same as the window rect, we don't // have to do anything. // result = IntPtr.Zero; handled = true; } } } break; } } return result; } ////// Critical: Because it uses _hwndTarget /// [SecurityCritical] private void Process_WM_WINDOWPOSCHANGING(UIElement rootUIElement, IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam) { // Only if SizeToContent overrides Win32 sizing change calls. // If it's coming from OnResize (_myOwnUpdate != true), it means we are adjusting // to the right size; don't need to do anything here. if ((_myOwnUpdate != true) && (SizeToContent != SizeToContent.Manual)) { // Get the current style and calculate the size to be with the new style. // If WM_WINDOWPOSCHANGING is sent because of style changes, WM_STYLECHANGED is sent // before this. The style bits we get here are updated ones, but haven't been applied // to Window yet. For example, when the window is changing to borderless, without updating the window size, // the window size will remain the same but the client area will be bigger. So when SizeToContent is on, we calculate // the window size to be with the new style using AdustWindowRectEx and adjust it to make sure client area is not affected. NativeMethods.RECT rect = AdjustWindowSize(rootUIElement.RenderSize); int newCX = rect.right - rect.left; int newCY = rect.bottom - rect.top; // Get WINDOWPOS structure data from lParam; it contains information about the window's // new size and position. NativeMethods.WINDOWPOS windowPos = (NativeMethods.WINDOWPOS)UnsafeNativeMethods.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS)); bool sizeChanged = false; // If SWP_NOSIZE is set to ignore cx, cy. It could be a style or position change. if ((windowPos.flags & NativeMethods.SWP_NOSIZE) == NativeMethods.SWP_NOSIZE) { NativeMethods.RECT windowRect = new NativeMethods.RECT(0, 0, 0, 0); // Get the current Window rect SafeNativeMethods.GetWindowRect(new HandleRef(this, _hwndWrapper.Handle), ref windowRect); // If there is no size change with the new style we don't need to do anything. if ((newCX != (windowRect.right - windowRect.left)) || (newCY != (windowRect.bottom - windowRect.top))) { // Otherwise unmark the flag to make our changes effective. windowPos.flags &= ~NativeMethods.SWP_NOSIZE; // When SWP_NOSIZE is on, the size info in cx and cy is bogus. They are ignored. // When we turn it off, we need to provide valid value for both of them. windowPos.cx = newCX; windowPos.cy = newCY; sizeChanged = true; } } else { // We have excluded SizeToContent == SizeToContent.Manual before entering this. bool sizeToWidth = (SizeToContent == SizeToContent.Height) ? false : true; bool sizeToHeight = (SizeToContent == SizeToContent.Width) ? false : true; // Update WindowPos with the size we want. if ((sizeToWidth) && (windowPos.cx != newCX)) { windowPos.cx = newCX; sizeChanged = true; } if ((sizeToHeight) && (windowPos.cy != newCY)) { windowPos.cy = newCY; sizeChanged = true; } } // Marshal the structure back only when changed if (sizeChanged) { Marshal.StructureToPtr(windowPos, lParam, true); } } } ////// Critical: Has access to the window handle and uses the parameters provided to modify the layout /// of elements within the window. /// [SecurityCritical] private void Process_WM_SIZE(UIElement rootUIElement, IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam) { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); Point pt = new Point(x, y); const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf; bool etwEnabled = EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info); long ctxHashCode = 0; // 1. If it's coming from Layout (_myOwnUpdate), it means we are adjusting // to the right size; don't need to do anything here. // 2. If SizeToContent is set to WidthAndHeight, then we maintain the current hwnd size // in WM_WINDOWPOSCHANGING, so we don't need to re-layout here. If SizeToContent // is set to Width or Height and developer calls SetWindowPos to set a new // Width/Height, we need to do a layout. // 3. We also don't need to do anything if it's minimized. // Keeps the status of whether the Window is in Minimized state or not. _isWindowInMinimizeState = (NativeMethods.IntPtrToInt32(wParam) == NativeMethods.SIZE_MINIMIZED) ? true : false; if ((!_myOwnUpdate) && (_sizeToContent != SizeToContent.WidthAndHeight) && !_isWindowInMinimizeState) { Point relevantPt = new Point(pt.X, pt.Y); // WM_SIZE has the client size of the window. // for appmodel window case, get the outside size of the hwnd. if (_adjustSizingForNonClientArea == true) { NativeMethods.RECT rect = new NativeMethods.RECT(0, 0, (int)pt.X, (int)pt.Y); GetSizeForWindowObject(ref rect); relevantPt.X = rect.right - rect.left; relevantPt.Y = rect.bottom - rect.top; } // The lParam/wParam size and the GetSizeForWindowObject size are // both in device co-ods, thus we convert to Measure co-ods here. relevantPt = TransformFromDevice(relevantPt); Size sz = new Size( (_sizeToContent == SizeToContent.Width ? double.PositiveInfinity : relevantPt.X), (_sizeToContent == SizeToContent.Height ? double.PositiveInfinity : relevantPt.Y)); // NOTE: hamidm -- 6/03/04 // 962884 Avalon content does not resize when the favorites // (or other side pane) is closed // // The issue is that when the browser shows favorites window, avalon // window is resized and we get WM_SIZE. Here, we pass the IE window's // size to Measure so that Computed[Width/Height] gets the correct // IE window dimensions. Since, IE window's size may not change, the // call to Measure is optimized out and no layout happens. Invalidating // layout here ensures that we do layout. // This can happen only in the Window case if (_adjustSizingForNonClientArea == true) { rootUIElement.InvalidateMeasure(); } if (etwEnabled) { ctxHashCode = _hwndWrapper.Handle.ToInt64(); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode, EventTrace.LayoutSource.HwndSource_WMSIZE); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode);; } rootUIElement.Measure(sz); if (_sizeToContent == SizeToContent.Width) sz = new Size(rootUIElement.DesiredSize.Width, relevantPt.Y); else if (_sizeToContent == SizeToContent.Height) sz = new Size(relevantPt.X, rootUIElement.DesiredSize.Height); else sz = new Size(relevantPt.X, relevantPt.Y); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Arrange(new Rect(new Point(), sz)); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } rootUIElement.UpdateLayout(); //finalizes layout } } ////// Critical: Because it uses _hwndTarget /// [SecurityCritical] private void DisableSizeToContent(UIElement rootUIElement, IntPtr hwnd) { if (_sizeToContent != SizeToContent.Manual) { _sizeToContent = SizeToContent.Manual; // hamidm - 10/27/2005 // 1348020 Window expereience layout issue when SizeToContent is being turned // off by user interaction // This bug was caused b/c we were giving rootUIElement.DesiredSize as input // to Measure/Arrange below. That is incorrect since rootUIElement.DesiredSize may not // cover the entire hwnd client area. // GetSizeFromHwnd returns either the outside size or the client size of the hwnd based on // _adjustSizeingForNonClientArea flag in logical units. Size sizeLogicalUnits = GetSizeFromHwnd(); rootUIElement.Measure(sizeLogicalUnits); rootUIElement.Arrange(new Rect(new Point(), sizeLogicalUnits)); rootUIElement.UpdateLayout(); //finalizes layout if (SizeToContentChanged != null) { SizeToContentChanged(this, EventArgs.Empty); } } } // This method makes sure that we get the size from the correct // hwnd for the browser case. ////// Critical - calls critical methods (HwndSourceHelper.GetHandle and GetAncestor) /// TreatAsSafe - it's ok to return size of window. it doesn't return info gotten through critical calls. /// [SecurityCritical, SecurityTreatAsSafe] private void GetSizeForWindowObject(ref NativeMethods.RECT rc) { IntPtr hwndRoot = UnsafeNativeMethods.GetAncestor(new HandleRef(this, CriticalHandle), NativeMethods.GA_ROOT); SafeNativeMethods.GetWindowRect(new HandleRef(this, hwndRoot), ref rc); } ////// Critical: This is a hook that gets called back with information about messages related to input /// Calling this from outside or causing this to be invoked could yield risky situations /// [SecurityCritical] private IntPtr InputFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; WindowMessage message = (WindowMessage)msg; // NOTE (alexz): invoke _stylus.FilterMessage before _mouse.FilterMessage // to give _stylus a chance to eat mouse message generated by stylus if (!_isDisposed && _stylus != null && !handled) { result = _stylus.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } if (!_isDisposed && _mouse != null && !handled) { result = _mouse.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } if (!_isDisposed && _keyboard != null && !handled) { result = _keyboard.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } if (!_isDisposed && _appCommand != null && !handled) { result = _appCommand.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } return result; } ////// Called from HwndWrapper on all window messages. /// // assumes Context.Access() is held. private IntPtr PublicHooksFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { // The default result for messages we handle is 0. IntPtr result = IntPtr.Zero ; WindowMessage message = (WindowMessage)msg; // Call all of the public hooks // We do this even if we are disposed because otherwise the hooks // would never see the WM_DESTROY etc. message. if (_hooks != null) { Delegate[] handlers = _hooks.GetInvocationList(); for (int i = handlers.Length -1; i >= 0; --i) { var hook = (HwndSourceHook)handlers[i]; result = hook(hwnd, msg, wParam, lParam, ref handled); if(handled) { break; } } } if (WindowMessage.WM_NCDESTROY == message) { // We delivered the message to the hooks and the message // is WM_NCDESTROY, so our commitments should be finished // we can do final teardown. (like disposing the _hooks) OnNoMoreWindowMessages(); } return result; } #region IKeyboardInputSink /// General security note on the implementation pattern of this interface. In Dev10 it was chosen /// to expose the interface implementation for overriding to customers. We did so by keeping the /// explicit interface implementations (that do have the property of being hidden from the public /// contract, which limits IntelliSense on derived types like WebBrowser) while sticking protected /// virtuals next to them. Those virtuals contain our base implementation, while the explicit /// interface implementation methods do call trivially into the virtuals. /// /// This comment outlines the security rationale applied to those methods. /// ////// The security attributes on the virtual methods within this region mirror the corresponding /// IKeyboardInputSink methods; customers can override those methods, so we insert a LinkDemand /// to encourage them to have a LinkDemand too (via FxCop). /// /// While the methods have LinkDemands on them, the bodies of the methods typically contain /// full demands through a SecurityHelper.DemandUnmanagedCode call. This might seem redundant. /// The point here is we do a full demand for stronger protection of our built-in implementation /// compared to the LinkDemand on the public interface. We really want full demands here but /// declarative Demand doesn't work on interface methods. In addition, we try to take advantage /// of the fact LinkDemands are consistently enforced between base and overridden virtual methods, /// something full Demands do not give us, even when applied declaratively. /// private class MSGDATA { public MSG msg; public bool handled; } ////// HwndSource keyboard input is sent through this delegate to check for /// Child Hwnd interop requirments. If there are no child hwnds or focus /// is on this non-child hwnd then normal Avalon processing is done. /// ////// Critical - This can be used to spoof input /// [SecurityCritical] private void OnPreprocessMessageThunk(ref MSG msg, ref bool handled) { // VerifyAccess(); if (handled) { return; } // We only do these message. switch ((WindowMessage)msg.message) { case WindowMessage.WM_KEYUP: case WindowMessage.WM_KEYDOWN: case WindowMessage.WM_SYSKEYUP: case WindowMessage.WM_SYSKEYDOWN: case WindowMessage.WM_CHAR: case WindowMessage.WM_SYSCHAR: case WindowMessage.WM_DEADCHAR: case WindowMessage.WM_SYSDEADCHAR: MSGDATA msgdata = new MSGDATA(); msgdata.msg = msg; msgdata.handled = handled; // Do this under the exception filter/handlers of the // dispatcher for this thread. // // NOTE: we lose the "perf optimization" of passing everything // around by-ref since we have to call through a delegate. object result = Dispatcher.CurrentDispatcher.Invoke( DispatcherPriority.Send, new DispatcherOperationCallback(OnPreprocessMessage), msgdata); if (result != null) { handled = (bool)result; } // the semantics dictate that the callers could change this data. msg = msgdata.msg; break; } } ////// Critical: /// Can be used to spoof input. /// Asserts UnmanagedCode permission to call IKeyboardInputSink /// methods. /// For HANDLED_KEYDOWN_STILL_GENERATES_CHARS we also cause the /// Dispatcher to defer processing the queue until after any /// currently pending messages. /// [SecurityCritical] private object OnPreprocessMessage(object param) { MSGDATA msgdata = (MSGDATA) param; // Always process messages if this window is in menu mode. // // Otherwise, only process messages if someone below us has Focus. // // Mnemonics are broadcast to all branches of the window tree; even // those that don't have focus. BUT! at least someone under this // top-level window must have focus. if (!((IKeyboardInputSink)this).HasFocusWithin() && !IsInExclusiveMenuMode) { return msgdata.handled; } ModifierKeys modifierKeys = HwndKeyboardInputProvider.GetSystemModifierKeys(); // Interop with the Interop layer // switch ((WindowMessage)msgdata.msg.message) { case WindowMessage.WM_SYSKEYDOWN: case WindowMessage.WM_KEYDOWN: // 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. _eatCharMessages = true; DispatcherOperation restoreCharMessages = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(RestoreCharMessages), null); // Force the Dispatcher to post a new message to service any // pending operations, so that the operation we just posted // is guaranteed to get dispatched after any pending WM_CHAR // messages are dispatched. Dispatcher.CriticalRequestProcessing(true); msgdata.handled = CriticalTranslateAccelerator(ref msgdata.msg, modifierKeys); if(!msgdata.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. _eatCharMessages = false; restoreCharMessages.Abort(); } // Menu mode handles all keyboard messages so that they don't // get dispatched to some random window with focus. if(IsInExclusiveMenuMode) { // However, if the WM_KEYDOWN message was not explicitly // handled, then we need to generate WM_CHAR messages. WPF // expects this, but when we return handled, the outer // message pump will skip the TranslateMessage and // DispatchMessage calls. We mitigate this by calling // TranslateMessage directly. This is the same trick that // Win32 does in its menu loop. if(!msgdata.handled) { UnsafeNativeMethods.TranslateMessage(ref msgdata.msg); } msgdata.handled = true; } break; case WindowMessage.WM_SYSKEYUP: case WindowMessage.WM_KEYUP: msgdata.handled = CriticalTranslateAccelerator(ref msgdata.msg, modifierKeys); // Menu mode handles all keyboard messages so that they don't // get dispatched to some random window with focus. if(IsInExclusiveMenuMode) { msgdata.handled = true; } break; case WindowMessage.WM_CHAR: case WindowMessage.WM_SYSCHAR: case WindowMessage.WM_DEADCHAR: case WindowMessage.WM_SYSDEADCHAR: // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS if(!_eatCharMessages) { // IKIS implementation does a demand. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { msgdata.handled = ((IKeyboardInputSink)this).TranslateChar(ref msgdata.msg, modifierKeys); if (!msgdata.handled) { msgdata.handled = ((IKeyboardInputSink)this).OnMnemonic(ref msgdata.msg, modifierKeys); } } finally { SecurityPermission.RevertAssert(); } if (!msgdata.handled) { _keyboard.Value.ProcessTextInputAction(msgdata.msg.hwnd, (WindowMessage)msgdata.msg.message, msgdata.msg.wParam, msgdata.msg.lParam, ref msgdata.handled); } } // Menu mode handles all keyboard messages so that they don't // get dispatched to some random window with focus. if(IsInExclusiveMenuMode) { // If the WM_CHAR message is not explicitly handled, the // standard behavior is to beep. if(!msgdata.handled) { SafeNativeMethods.MessageBeep(0); } msgdata.handled = true; } break; } return msgdata.handled; } ////// Registers a child KeyboardInputSink with this sink. A site /// is returned. /// ////// This API requires unrestricted UI Window permission. /// We explicitly don't make this method overridable as we want to keep the /// precise implementation fixed and make sure the _keyboardInputSinkChildren /// state is kep consistent. By making the method protected, implementors can /// still call into it when required. Notice as calls are made through the /// IKIS interface, there's still a way for ---- developers to override /// the behavior by re-implementing the interface. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.Demand, Unrestricted=true)] protected IKeyboardInputSite RegisterKeyboardInputSinkCore(IKeyboardInputSink sink) { CheckDisposed(true); if (sink == null) { throw new ArgumentNullException("sink"); } if (sink.KeyboardInputSite != null) { throw new ArgumentException(SR.Get(SRID.KeyboardSinkAlreadyOwned)); } HwndSourceKeyboardInputSite site = new HwndSourceKeyboardInputSite(this, sink); if (_keyboardInputSinkChildren == null) _keyboardInputSinkChildren = new List(); _keyboardInputSinkChildren.Add(site); return site; } /// /// Critical: This method can be used to intercept and potentially tamper with raw input. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] IKeyboardInputSite IKeyboardInputSink.RegisterKeyboardInputSink(IKeyboardInputSink sink) { return RegisterKeyboardInputSinkCore(sink); } ////// Gives the component a chance to process keyboard input. /// Return value is true if handled, false if not. Components /// will generally call a child component's TranslateAccelerator /// if they can't handle the input themselves. The message must /// either be WM_KEYDOWN or WM_SYSKEYDOWN. It is illegal to /// modify the MSG structure, it's passed by reference only as /// a performance optimization. /// ////// This API is not available in Internet Zone. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] protected virtual bool TranslateAcceleratorCore(ref MSG msg, ModifierKeys modifiers) { SecurityHelper.DemandUnmanagedCode(); // VerifyAccess(); return CriticalTranslateAccelerator(ref msg, modifiers); } ////// Critical: Calls a method with a LinkDemand on it. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical] bool IKeyboardInputSink.TranslateAccelerator(ref MSG msg, ModifierKeys modifiers) { return TranslateAcceleratorCore(ref msg, modifiers); } ////// Set focus to the first or last tab stop (according to the /// TraversalRequest). If it can't, because it has no tab stops, /// the return value is false. /// protected virtual bool TabIntoCore(TraversalRequest request) { bool traversed = false; if(request == null) { throw new ArgumentNullException("request"); } UIElement root =_rootVisual.Value as UIElement; if(root != null) { // atanask: // request.Mode == FocusNavigationDirection.First will navigate to the fist tabstop including root // request.Mode == FocusNavigationDirection.Last will navigate to the last tabstop including root traversed = root.MoveFocus(request); } return traversed; } bool IKeyboardInputSink.TabInto(TraversalRequest request) { if(request == null) { throw new ArgumentNullException("request"); } return TabIntoCore(request); } ////// The property should start with a null value. The component's /// container will set this property to a non-null value before /// any other methods are called. It may be set multiple times, /// and should be set to null before disposal. /// ////// Setting KeyboardInputSite is not available in Internet Zone. /// We explicitly don't make this property overridable as we want to keep the /// precise implementation as a smart field for _keyboardInputSite fixed. /// By making the property protected, implementors can still call into it /// when required. Notice as calls are made through the IKIS interface, /// there's still a way for ---- developers to override the behavior by /// re-implementing the interface. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This property has demands on its accessors. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// protected IKeyboardInputSite KeyboardInputSiteCore { [SecurityCritical] get { SecurityHelper.DemandUnmanagedCode(); return _keyboardInputSite; } [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] set { SecurityHelper.DemandUnmanagedCode(); _keyboardInputSite = value; } } ////// Critical: Calls a property with a LinkDemand on it. /// PublicOK: The interface declaration for this property has a demand on it. /// IKeyboardInputSite IKeyboardInputSink.KeyboardInputSite { get { return KeyboardInputSiteCore; } [SecurityCritical] set { KeyboardInputSiteCore = value; } } ////// This method is called whenever one of the component's /// mnemonics is invoked. The message must either be WM_KEYDOWN /// or WM_SYSKEYDOWN. It's illegal to modify the MSG structrure, /// it's passed by reference only as a performance optimization. /// If this component contains child components, the container /// OnMnemonic will need to call the child's OnMnemonic method. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] protected virtual bool OnMnemonicCore(ref MSG msg, ModifierKeys modifiers) { // VerifyAccess(); SecurityHelper.DemandUnmanagedCode(); switch((WindowMessage)msg.message) { case WindowMessage.WM_SYSCHAR: case WindowMessage.WM_SYSDEADCHAR: string text = new string((char)msg.wParam, 1); if ((text != null) && (text.Length > 0)) { // We have to work around an ordering issue with mnemonic processing. // // Imagine you have a top level menu with _File & _Project and under _File you have _Print. // If the user pressses Alt+F,P you would expect _Print to be triggered // however if the top level window processes the mnemonic first // it will trigger _Project instead. // // One way to work around this would be for the top level window to notice that // keyboard focus is in another root window & not handle the mnemonics. The popup // window would then get a chance to process the mnemonic and _Print would be triggered. // // This doesn't work out becasue the popup window is no-activate, so it doesn't have Win32 focus // and it will bail out of OnPreprocessMessage before it handles the mnemonic. // // Instead the top level window should delegate OnMnemonic directly to the mnemonic scope window // instead of processing it here. This will let the Popup handle mnemonics instead of the top- // level window. // // The mnemonic scope window is defined as the window with WPF keyboard focus. // // This is a behavioral breaking change, so we've decided to only do it when IsInExclusiveMenuMode // is true to force the user to opt-in. DependencyObject focusObject = Keyboard.FocusedElement as DependencyObject; HwndSource mnemonicScope = (focusObject == null ? null : PresentationSource.CriticalFromVisual(focusObject) as HwndSource); if (mnemonicScope != null && mnemonicScope != this && IsInExclusiveMenuMode) { return ((IKeyboardInputSink)mnemonicScope).OnMnemonic(ref msg, modifiers); } if (AccessKeyManager.IsKeyRegistered(this, text)) { AccessKeyManager.ProcessKey(this, text, false); // return true; } } // these are OK break; case WindowMessage.WM_CHAR: case WindowMessage.WM_DEADCHAR: // these are OK break; default: throw new ArgumentException(SR.Get(SRID.OnlyAcceptsKeyMessages)); } // We record the last message that was processed by us. // This is also checked in WndProc processing to prevent double processing. _lastKeyboardMessage = msg; // The bubble will take care of access key processing for this HWND. Call // the IKIS children unless we are in menu mode. if (_keyboardInputSinkChildren != null && !IsInExclusiveMenuMode) { foreach ( HwndSourceKeyboardInputSite childSite in _keyboardInputSinkChildren ) { if (((IKeyboardInputSite)childSite).Sink.OnMnemonic(ref msg, modifiers)) return true; } } return false; } ////// Critical: Calls a method with a LinkDemand on it. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical] bool IKeyboardInputSink.OnMnemonic(ref MSG msg, ModifierKeys modifiers) { return OnMnemonicCore(ref msg, modifiers); } ////// Gives the component a chance to process keyboard input messages /// WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or WM_SYSDEADCHAR before calling OnMnemonic. /// Will return true if "handled" meaning don't pass it to OnMnemonic. /// The message must be WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or WM_SYSDEADCHAR. /// It is illegal to modify the MSG structure, it's passed by reference /// only as a performance optimization. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] protected virtual bool TranslateCharCore(ref MSG msg, ModifierKeys modifiers) { SecurityHelper.DemandUnmanagedCode(); if(HasFocus || IsInExclusiveMenuMode) return false; IKeyboardInputSink focusSink = this.ChildSinkWithFocus; if(null != focusSink) { return focusSink.TranslateChar(ref msg, modifiers); } return false; } ////// Critical: Calls a method with a LinkDemand on it. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical] bool IKeyboardInputSink.TranslateChar(ref MSG msg, ModifierKeys modifiers) { return TranslateCharCore(ref msg, modifiers); } ////// /// protected virtual bool HasFocusWithinCore() { if(HasFocus) { return true; } else { if (null == _keyboardInputSinkChildren) return false; foreach (HwndSourceKeyboardInputSite site in _keyboardInputSinkChildren) { if (((IKeyboardInputSite)site).Sink.HasFocusWithin()) { return true; } } return false; } } bool IKeyboardInputSink.HasFocusWithin() { return HasFocusWithinCore(); } ////// The RestoreFocusMode for the window. /// ////// This property can only be set at construction time via the /// HwndSourceParameters.RestoreFocusMode property. /// public RestoreFocusMode RestoreFocusMode { get { return _restoreFocusMode; } } ////// The default value for the AcquireHwndFocusInMenuMode setting. /// public static bool DefaultAcquireHwndFocusInMenuMode { get { if(!_defaultAcquireHwndFocusInMenuMode.HasValue) { // The default value is true, for compat. _defaultAcquireHwndFocusInMenuMode = true; } return _defaultAcquireHwndFocusInMenuMode.Value; } set { _defaultAcquireHwndFocusInMenuMode = value; } } /// /// The AcquireHwndFocusInMenuMode setting for the window. /// ////// This property can only be set at construction time via the /// HwndSourceParameters.AcquireHwndFocusInMenuMode property. /// public bool AcquireHwndFocusInMenuMode { get { return _acquireHwndFocusInMenuMode; } } ////// The method is not part of the interface (IKeyboardInputSink). /// /// The Site that containes the sink to unregister ////// Critical - calls critical methods. /// [ SecurityCritical ] internal void CriticalUnregisterKeyboardInputSink(HwndSourceKeyboardInputSite site) { if(_isDisposed) return; if (null != _keyboardInputSinkChildren) { if (!_keyboardInputSinkChildren.Remove(site)) { throw new InvalidOperationException(SR.Get(SRID.KeyboardSinkNotAChild)); } } } IKeyboardInputSink ChildSinkWithFocus { get { IKeyboardInputSink ikis=null; if(null == _keyboardInputSinkChildren) return null; foreach (HwndSourceKeyboardInputSite site in _keyboardInputSinkChildren) { IKeyboardInputSite isite = (IKeyboardInputSite)site; if (isite.Sink.HasFocusWithin()) { ikis = isite.Sink; break; } } // This private property should only be called correctly. Debug.Assert(null!=ikis, "ChildSinkWithFocus called when none had focus"); return ikis; } } ////// Critical: This API could be vulnerable to input spoofing. /// [SecurityCritical, FriendAccessAllowed] internal bool CriticalTranslateAccelerator(ref MSG msg, ModifierKeys modifiers) { switch ((WindowMessage)msg.message) { case WindowMessage.WM_KEYUP: case WindowMessage.WM_KEYDOWN: case WindowMessage.WM_SYSKEYUP: case WindowMessage.WM_SYSKEYDOWN: // these are OK break; default: throw new ArgumentException(SR.Get(SRID.OnlyAcceptsKeyMessages)); } if (_keyboard == null) return false; bool handled = false; // TranslateAccelerator is called recursively on child Hwnds (Source & Host) // If this is the first Avalon TranslateAccelerator processing then we send the // key to be processed to the standard Avalon Input Filters and stuff. if (PerThreadData.TranslateAcceleratorCallDepth == 0) { // We record the last message that was processed by us. // This is also checked in WndProc processing to prevent double processing. // TranslateAcclerator is called from the pump before DispatchMessage // and the WndProc is called from DispatchMessage. We have processing // in both places. If we run the pump we process keyboard message here. // If we don't own the pump we process them in HwndKeyboardInputProvider.WndProc. _lastKeyboardMessage = msg; // NORMAL AVALON KEYBOARD INPUT CASE // If this is the top most Avalon window (it might be a child Hwnd // but no Avalon windows above it). And focus is on this window then // do the Normal Avalon Keyboard input Processing. if (HasFocus || IsInExclusiveMenuMode) { _keyboard.Value.ProcessKeyAction(ref msg, ref handled); } // ELSE the focus is probably in but not on this HwndSource. // Beware: It is possible that someone calls IKIS.TranslateAccelerator() while the focus is // somewhere entirely outside. // Do the once only message input filters etc and Tunnel/Bubble down // to the element that contains the child window with focus. // The Child HwndHost object will hook OnPreviewKeyDown() etc // to make the transition to its TranslateAccelerator() between the // tunnel and the bubble. else { IKeyboardInputSink focusSink = ChildSinkWithFocus; // can be null! IInputElement focusElement = (IInputElement)focusSink; try { PerThreadData.TranslateAcceleratorCallDepth += 1; Keyboard.PrimaryDevice.ForceTarget = focusElement; _keyboard.Value.ProcessKeyAction(ref msg, ref handled); } finally { Keyboard.PrimaryDevice.ForceTarget = null; PerThreadData.TranslateAcceleratorCallDepth -= 1; } } } // ELSE we have seen this MSG before, we are HwndSource decendant of an // HwndSource (that ancestor presumably did the processing above). // Here we raise the tunnel/bubble events without the once only keyboard // input filtering. else { int virtualKey = HwndKeyboardInputProvider.GetVirtualKey(msg.wParam, msg.lParam); int scanCode = HwndKeyboardInputProvider.GetScanCode(msg.wParam, msg.lParam); bool isExtendedKey = HwndKeyboardInputProvider.IsExtendedKey(msg.lParam); Key key = KeyInterop.KeyFromVirtualKey(virtualKey); RoutedEvent keyPreviewEvent=null; RoutedEvent keyEvent=null; switch ((WindowMessage)msg.message) { case WindowMessage.WM_KEYUP: case WindowMessage.WM_SYSKEYUP: keyPreviewEvent = Keyboard.PreviewKeyUpEvent; keyEvent = Keyboard.KeyUpEvent; break; case WindowMessage.WM_KEYDOWN: case WindowMessage.WM_SYSKEYDOWN: keyPreviewEvent = Keyboard.PreviewKeyDownEvent; keyEvent = Keyboard.KeyDownEvent; break; } bool hasFocus = HasFocus; IKeyboardInputSink focusSink = (hasFocus || IsInExclusiveMenuMode) ? null : ChildSinkWithFocus; IInputElement focusElement = focusSink as IInputElement; // focusElement may be null, in which case Target is just "focus", but we use it only if it's an // element within this HwndSource. It is possible that someone calls IKIS.TranslateAccelerator() // on a nested HwndSource while the focus is somewhere entirely outside. // HasFocus implies the focused element should be within this HwndSource, but unfortunately // we allow 'split focus', at least for popup windows; that's why check explicitly. // Note that KeyboardDevice.Target will likely be the ForceTarget corresponding to the // container of this HwndSource. That's why we look at the real FocusedElement. if (focusElement == null && hasFocus) { focusElement = Keyboard.PrimaryDevice.FocusedElement; if (focusElement != null && PresentationSource.CriticalFromVisual((DependencyObject)focusElement) != this) { focusElement = null; } } try { Keyboard.PrimaryDevice.ForceTarget = focusSink as IInputElement; if (focusElement != null) { KeyEventArgs tunnelArgs = new KeyEventArgs(Keyboard.PrimaryDevice, this, msg.time, key); tunnelArgs.ScanCode = scanCode; tunnelArgs.IsExtendedKey = isExtendedKey; tunnelArgs.RoutedEvent = keyPreviewEvent; focusElement.RaiseEvent(tunnelArgs); handled = tunnelArgs.Handled; } if (!handled) { KeyEventArgs bubbleArgs = new KeyEventArgs(Keyboard.PrimaryDevice, this, msg.time, key); bubbleArgs.ScanCode = scanCode; bubbleArgs.IsExtendedKey = isExtendedKey; bubbleArgs.RoutedEvent=keyEvent; if(focusElement != null) { focusElement.RaiseEvent(bubbleArgs); handled = bubbleArgs.Handled; } if (!handled) { // Raise the TranslateAccelerator event on the // InputManager to allow keyboard navigation to // happen on a descendent HwndSource InputManager.UnsecureCurrent.RaiseTranslateAccelerator(bubbleArgs); handled = bubbleArgs.Handled; } } } finally { Keyboard.PrimaryDevice.ForceTarget = null; } } return handled; } // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // Go back to accepting character messages. This method is posted // to the dispatcher when char messages are disable. internal static object RestoreCharMessages(object unused) { _eatCharMessages = false; return null; } #endregion IKeyboardInputSink internal bool IsRepeatedKeyboardMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam) { if (msg != _lastKeyboardMessage.message) return false; if (hwnd != _lastKeyboardMessage.hwnd) return false; if (wParam != _lastKeyboardMessage.wParam) return false; if (lParam != _lastKeyboardMessage.lParam) return false; return true; } ////// This event handler is called from HwndWrapper when it is Disposing. /// // This could happen if someone calls his Dispose before (or instead // of) our dispose. Or, more likely, the real window was killed by // something like the user clicking the close box. private void OnHwndDisposed(object sender, EventArgs args) { // This method is called from the HwndWrapper.Dispose(). // So make sure we don't call HwndWrapper.Dispose(). _inRealHwndDispose = true; Dispose(); } ////// Called after the last window message is processed. /// // HwndSource is required to continue certain operations while, // and even after, Dispose runs. HwndSource is resposible for // calling WndProcHooks with every message the window sees. // Including: WM_CLOSE, WM_DESTROY, WM_NCDESTROY. The doc says // WM_NCDESTROY is the very last message, so we can release the // Hooks after that. // This assumes the Context.Access() is held. private void OnNoMoreWindowMessages() { _hooks = null; } private void OnShutdownFinished(object sender, EventArgs args) { // Note: We are already in the context being disposed. Dispose(); } // // NOTE: shutdown order is very important. Review any changes // carefully. // ////// Critical: This accesses the various sites and providers. /// Asserting UnmanagedCode to access site, this is consistent with the requirements of the interface declaration /// as it can potentially be used for spoofing. /// Safe: Disposing the object is a safe operation. /// Not exposing IKIS, just using it to unregister. /// [SecurityCritical, SecurityTreatAsSafe] private void Dispose(bool disposing) { if(disposing) { // Make sure all access is synchronized. // this.VerifyAccess(); if (!_isDisposing) { // _isDisposing is a guard against re-entery into this // routine. We fire Dispose and SourceChanged (RootVisual // change) events which could cause re-entery. _isDisposing = true; // Notify listeners that we are being disposed. We do this // before we dispose our internal stuff, in case the event // listener needs to access something. if(Disposed != null) { try { Disposed(this, EventArgs.Empty); } #pragma warning disable 56500 // We can't tolerate an exception thrown by third-party code to // abort our Dispose half-way through. So we just eat it. catch { } #pragma warning restore 56500 Disposed = null; } // Remove any listeners of the ContentRendered event ClearContentRenderedListeners(); // Clear the root visual. This will raise a SourceChanged // event to registered listeners. RootVisualInternal = null; RemoveSource(); new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { // Unregister ourselves if we are a registered KeyboardInputSink. // Use the property instead of the backing field in case a subclass has overridden it. IKeyboardInputSite keyboardInputSite = ((IKeyboardInputSink)this).KeyboardInputSite; if (keyboardInputSite != null) { keyboardInputSite.Unregister(); ((IKeyboardInputSink)this).KeyboardInputSite = null; } _keyboardInputSinkChildren = null; } finally { SecurityPermission.RevertAssert(); } // Dispose the HwndStylusInputProvider BEFORE we destroy the HWND. // This us because the stylus provider has an async channel and // they don't want to process data after the HWND is destroyed. if (_stylus != null) { _stylus.Value.Dispose(); _stylus = null; } // Our general shut-down principle is to destroy the window // and let the individual HwndXXX components respons to WM_DESTROY. // // (see comment above about disposing the HwndStylusInputProvider) // { if (_hwndTarget != null) { _hwndTarget.Dispose(); _hwndTarget = null; } if (_hwndWrapper != null) { // Revoke the drop target. if (_hwndWrapper.Handle != IntPtr.Zero && _registeredDropTargetCount > 0) { // This call is safe since DragDrop.RevokeDropTarget is checking the unmanged // code permission. DragDrop.RevokeDropTarget(_hwndWrapper.Handle); _registeredDropTargetCount--; } // Remove our HwndWrapper.Dispose() hander. _hwndWrapper.Disposed -= new EventHandler(OnHwndDisposed); if (!_inRealHwndDispose) _hwndWrapper.Dispose(); // Don't null out _hwndWrapper after the Dispose(). // Dispose() will start destroying the Window but we // still need to talk to it during that process while // the WM_ msgs arrive. } } if(_mouse != null) { _mouse.Value.Dispose(); _mouse = null; } if(_keyboard != null) { _keyboard.Value.Dispose(); _keyboard = null; } if (_appCommand != null) { _appCommand.Value.Dispose(); _appCommand = null; } if(null != _weakShutdownHandler) { _weakShutdownHandler.Dispose(); _weakShutdownHandler = null; } if(null != _weakPreprocessMessageHandler) { _weakPreprocessMessageHandler.Dispose(); _weakPreprocessMessageHandler = null; } // We wait to set the "_isDisposed" flag until after the // Disposed, SourceChange (RootVisual=null), etc. events // have fired. We want to remain functional should their // handlers call methods on us. // // Note: as the HwndWrapper shuts down, the final few messages // will continue to pass through our WndProc hook. _isDisposed = true; } } } private void CheckDisposed(bool verifyAccess) { if(verifyAccess) { // this.VerifyAccess(); } if(_isDisposed) { throw new ObjectDisposedException(null, SR.Get(SRID.HwndSourceDisposed)); } } ////// Critical: This code accesses hwndtarget /// TreatAsSafe: Information is ok to expose /// private bool IsUsable { [SecurityCritical,SecurityTreatAsSafe] get { return _isDisposed == false && _hwndTarget != null && _hwndTarget.IsDisposed == false; } } ////// Critical - calls a method with an elevation ( GetFocus ) /// TreatAsSafe - determining whether you have focus within the window is considered safe. /// Worst case you can know whether keyboard/keypress events will go to the current window. /// private bool HasFocus { [SecurityCritical, SecurityTreatAsSafe] get { return UnsafeNativeMethods.GetFocus() == CriticalHandle; } } private static bool IsValidSizeToContent(SizeToContent value) { return value == SizeToContent.Manual || value == SizeToContent.Width || value == SizeToContent.Height || value == SizeToContent.WidthAndHeight; } class ThreadDataBlob { public int TranslateAcceleratorCallDepth; } private static ThreadDataBlob PerThreadData { get { ThreadDataBlob data; object obj = Thread.GetData(_threadSlot); if(null == obj) { data = new ThreadDataBlob(); Thread.SetData(_threadSlot, data); } else { data = (ThreadDataBlob) obj; } return data; } } #region WeakEventHandlers private class WeakEventDispatcherShutdown: WeakReference { public WeakEventDispatcherShutdown(HwndSource source, Dispatcher that): base(source) { _that = that; _that.ShutdownFinished += new EventHandler(this.OnShutdownFinished); } public void OnShutdownFinished(object sender, EventArgs e) { HwndSource source = this.Target as HwndSource; if(null != source) { source.OnShutdownFinished(sender, e); } else { Dispose(); } } public void Dispose() { if(null != _that) { _that.ShutdownFinished-= new EventHandler(this.OnShutdownFinished); } } private Dispatcher _that; } private class WeakEventPreprocessMessage: WeakReference { ////// Critical: This code calls attaches an arbitrary window /// to the call path for the component dispatcher call back /// [SecurityCritical] public WeakEventPreprocessMessage(HwndSource source, bool addToFront): base(source) { _addToFront = addToFront; _handler = new ThreadMessageEventHandler(this.OnPreprocessMessage); if(addToFront) { ComponentDispatcher.CriticalAddThreadPreprocessMessageHandlerFirst(_handler); } else { ComponentDispatcher.ThreadPreprocessMessage += _handler; } } ////// Critical: This can be used to spoof and change input /// [SecurityCritical] public void OnPreprocessMessage(ref MSG msg, ref bool handled) { HwndSource source = this.Target as HwndSource; if(null != source) { source.OnPreprocessMessageThunk(ref msg, ref handled); } else { Dispose(); } } ////// Critical:This code calls into ComponentDispatcher /// to disconnect a listener /// TreatAsSafe: This code is ok to call /// [SecurityCritical,SecurityTreatAsSafe] public void Dispose() { if(_addToFront) { ComponentDispatcher.CriticalRemoveThreadPreprocessMessageHandlerFirst(_handler); } else { ComponentDispatcher.ThreadPreprocessMessage -= _handler; } _handler = null; } private bool _addToFront; private ThreadMessageEventHandler _handler; } #endregion WeakEventHandlers private object _constructionParameters; // boxed HwndSourceParameters private bool _isDisposed = false; private bool _isDisposing = false; private bool _inRealHwndDispose = false; private bool _adjustSizingForNonClientArea; private bool _myOwnUpdate; private bool _isWindowInMinimizeState = false; private int _registeredDropTargetCount; private SizeToContent _sizeToContent = SizeToContent.Manual; private Size? _previousSize; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// [SecurityCritical] private HwndWrapper _hwndWrapper; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// [SecurityCritical] private HwndTarget _hwndTarget; private SecurityCriticalDataForSet_rootVisual; private event HwndSourceHook _hooks; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_mouse; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_keyboard; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_stylus; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_appCommand; WeakEventDispatcherShutdown _weakShutdownHandler; WeakEventPreprocessMessage _weakPreprocessMessageHandler; WeakEventPreprocessMessage _weakMenuModeMessageHandler; private static System.LocalDataStoreSlot _threadSlot; private RestoreFocusMode _restoreFocusMode; [ThreadStatic] private static bool? _defaultAcquireHwndFocusInMenuMode; private bool _acquireHwndFocusInMenuMode; private MSG _lastKeyboardMessage; private List _keyboardInputSinkChildren; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// // Be careful about accessing this field directly. // It's bound to IKeyboardInputSink.KeyboardInputSite, so if a derived class overrides // that property then this field will be incorrect. [SecurityCritical] private IKeyboardInputSite _keyboardInputSite = null; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _layoutHook; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _inputHook; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _hwndTargetHook; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _publicHook; // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // // Avalon relies on the policy that if you handle the KeyDown // event, you will not get the TextInput events caused by // pressing the key. This is generally implemented because the // message pump calls ComponentDispatcher.RaiseThreadMessage and // we return whether or not the WM_KEYDOWN message was handled, // and the message pump will only call TranslateMessage() if the // WM_KEYDOWN was not handled. However, naive message pumps don't // call ComponentDispatcher.RaiseThreadMessage, and always call // TranslateMessage, so the WM_CHAR is generated no matter what. // The best work around we could think of was to eat the WM_CHAR // messages and not report them to Avalon. // [ThreadStatic] internal static bool _eatCharMessages; // used from HwndKeyboardInputProvider } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, 2004 // // File: HwndSource.cs //----------------------------------------------------------------------------- using System.Collections; using System.Collections.Generic; using System.Threading; using System.Windows.Threading; using System.Windows.Input; using System.Windows.Media; using System.Windows.Interop; using System.Runtime.InteropServices; using MS.Win32; using MS.Utility; using MS.Internal; using MS.Internal.Interop; using MS.Internal.PresentationCore; // SecurityHelper using Microsoft.Win32; using System.Diagnostics; using System.ComponentModel; using System; using System.Security; using System.Security.Permissions; using System.IO; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; #pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace System.Windows.Interop { ////// The HwndSource class presents content within a Win32 HWND. /// public class HwndSource : PresentationSource, IDisposable, IWin32Window, IKeyboardInputSink { ////// Critical: This code calls into RegisterWindowMesssage which is critical /// TreatAsSafe: This is safe to call as no external parameters are taken in /// [SecurityCritical,SecurityTreatAsSafe] static HwndSource() { _threadSlot = Thread.AllocateDataSlot(); } ////// Constructs an instance of the HwndSource class that will always resize to its content size. /// /// /// The Win32 class styles for this window. /// /// /// The Win32 styles for this window. /// /// /// The extended Win32 styles for this window. /// /// /// The position of the left edge of this window. /// /// /// The position of the upper edge of this window. /// /// /// The name of this window. /// /// /// The Win32 window that should be the parent of this window. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Added a demand - so that this API does not work in InternetZone. /// Critical: This accesses critical code Initialize /// PublicOK: This code has a demand which will ensure that it does /// not work in partial trust without the correct permissions /// [SecurityCritical] public HwndSource( int classStyle, int style, int exStyle, int x, int y, string name, IntPtr parent) { SecurityHelper.DemandUIWindowPermission(); HwndSourceParameters param = new HwndSourceParameters(name); param.WindowClassStyle = classStyle; param.WindowStyle = style; param.ExtendedWindowStyle = exStyle; param.SetPosition(x, y); param.ParentWindow = parent; Initialize(param); } ////// Constructs an instance of the HwndSource class. This version requires an /// explicit width and height be sepecified. /// /// /// The Win32 class styles for this window. /// /// /// The Win32 styles for this window. /// /// /// The extended Win32 styles for this window. /// /// /// The position of the left edge of this window. /// /// /// The position of the upper edge of this window. /// /// /// The width of this window. /// /// /// The height of this window. /// /// /// The name of this window. /// /// /// The Win32 window that should be the parent of this window. /// /// /// Indicates that HwndSource should include the non-client area /// of the hwnd when it calls the Layout Manager /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Added a demand - so that this API does not work in InternetZone. /// Critical: This acceses critical code Initialize /// PublicOK: This code has a demand which will ensure that it does /// not work in partial trust without the correct permissions /// [SecurityCritical] public HwndSource(int classStyle, int style, int exStyle, int x, int y, int width, int height, string name, IntPtr parent, bool adjustSizingForNonClientArea) { SecurityHelper.DemandUIWindowPermission(); HwndSourceParameters parameters = new HwndSourceParameters(name, width, height); parameters.WindowClassStyle = classStyle; parameters.WindowStyle = style; parameters.ExtendedWindowStyle = exStyle; parameters.SetPosition(x, y); parameters.ParentWindow = parent; parameters.AdjustSizingForNonClientArea = adjustSizingForNonClientArea; Initialize(parameters); } ////// Constructs an instance of the HwndSource class. This version requires an /// explicit width and height be sepecified. /// /// /// The Win32 class styles for this window. /// /// /// The Win32 styles for this window. /// /// /// The extended Win32 styles for this window. /// /// /// The position of the left edge of this window. /// /// /// The position of the upper edge of this window. /// /// /// The width of this window. /// /// /// The height of this window. /// /// /// The name of this window. /// /// /// The Win32 window that should be the parent of this window. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Added a demand - so that this API does not work in InternetZone. /// Critical: This accesses critical code Initialize /// PublicOK: This code has a demand which will ensure that it does /// not work in partial trust without the correct permissions /// [SecurityCritical] public HwndSource( int classStyle, int style, int exStyle, int x, int y, int width, int height, string name, IntPtr parent) { SecurityHelper.DemandUIWindowPermission(); HwndSourceParameters parameters = new HwndSourceParameters(name, width, height); parameters.WindowClassStyle = classStyle; parameters.WindowStyle = style; parameters.ExtendedWindowStyle = exStyle; parameters.SetPosition(x, y); parameters.ParentWindow = parent; Initialize(parameters); } ////// HwndSource Ctor /// /// parameter block ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This acceses critical code Initialize /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] public HwndSource(HwndSourceParameters parameters) { Initialize(parameters); } ////// HwndSource Ctor /// /// parameter block ////// Critical: This code access critical (HwndMouseInputProvider, HwndKeyboardInputProvider /// ,HwndStylusInputProvider and the various hooks)objects and creates the /// providers under elevation. /// [SecurityCritical] private void Initialize(HwndSourceParameters parameters) { _mouse = new SecurityCriticalDataClass(new HwndMouseInputProvider(this)); _keyboard = new SecurityCriticalDataClass (new HwndKeyboardInputProvider(this)); _layoutHook = new HwndWrapperHook(LayoutFilterMessage); _inputHook = new HwndWrapperHook(InputFilterMessage); _hwndTargetHook = new HwndWrapperHook(HwndTargetFilterMessage); _publicHook = new HwndWrapperHook(PublicHooksFilterMessage); // When processing WM_SIZE, LayoutFilterMessage must be invoked before // HwndTargetFilterMessage. This way layout will be updated before resizing // HwndTarget, resulting in single render per resize. This means that // layout hook should appear before HwndTarget hook in the wrapper hooks // list. If this is done the other way around, first HwndTarget resize will // force re-render, then layout will be updated according to the new size, // scheduling another render. HwndWrapperHook[] wrapperHooks = { _hwndTargetHook, _layoutHook, _inputHook, null }; if (null != parameters.HwndSourceHook) { // In case there's more than one delegate, add these to the event storage backwards // so they'll get invoked in the expected order. Delegate[] handlers = parameters.HwndSourceHook.GetInvocationList(); for (int i = handlers.Length -1; i >= 0; --i) { _hooks += (HwndSourceHook)handlers[i]; } wrapperHooks[3] = _publicHook; } _restoreFocusMode = parameters.RestoreFocusMode; _acquireHwndFocusInMenuMode = parameters.AcquireHwndFocusInMenuMode; // A window must be marked WS_EX_LAYERED if (and only if): // 1) it is not a child window // -- AND -- // 2) a color-key is specified // 3) or an opacity other than 1.0 is specified // 4) or per-pixel alpha is requested. if((parameters.WindowStyle & NativeMethods.WS_CHILD) == 0 && ( //parameters.ColorKey != null || //!MS.Internal.DoubleUtil.AreClose(parameters.Opacity, 1.0) || parameters.UsesPerPixelOpacity)) { parameters.ExtendedWindowStyle |= NativeMethods.WS_EX_LAYERED; } else { parameters.ExtendedWindowStyle &= (~NativeMethods.WS_EX_LAYERED); } _constructionParameters = parameters; _hwndWrapper = new HwndWrapper(parameters.WindowClassStyle, parameters.WindowStyle, parameters.ExtendedWindowStyle, parameters.PositionX, parameters.PositionY, parameters.Width, parameters.Height, parameters.WindowName, parameters.ParentWindow, wrapperHooks); _hwndTarget = new HwndTarget(_hwndWrapper.Handle); //_hwndTarget.ColorKey = parameters.ColorKey; //_hwndTarget.Opacity = parameters.Opacity; _hwndTarget.UsesPerPixelOpacity = parameters.UsesPerPixelOpacity; if(_hwndTarget.UsesPerPixelOpacity) { _hwndTarget.BackgroundColor = Colors.Transparent; // Prevent this window from being themed. UnsafeNativeMethods.CriticalSetWindowTheme(new HandleRef(this, _hwndWrapper.Handle), "", ""); } _constructionParameters = null; if (!parameters.HasAssignedSize) _sizeToContent = SizeToContent.WidthAndHeight; _adjustSizingForNonClientArea = parameters.AdjustSizingForNonClientArea; // Listen to the UIContext.Disposed event so we can clean up. // The HwndTarget cannot work without a MediaContext which // is disposed when the UIContext is disposed. So we need to // dispose the HwndTarget and also never use it again (to // paint or process input). The easiest way to do this is to just // dispose the HwndSource at the same time. _weakShutdownHandler = new WeakEventDispatcherShutdown(this, this.Dispatcher); // Listen to the HwndWrapper.Disposed event so we can clean up. // The HwndTarget cannot work without a live HWND, and since // the HwndSource represents an HWND, we make sure we dispose // ourselves if the HWND is destroyed out from underneath us. _hwndWrapper.Disposed += new EventHandler(OnHwndDisposed); _stylus = new SecurityCriticalDataClass (new HwndStylusInputProvider(this)); // WM_APPCOMMAND events are handled thru this. _appCommand = new SecurityCriticalDataClass (new HwndAppCommandInputProvider(this)); // Register the top level source with the ComponentDispatcher. if (parameters.TreatAsInputRoot) { _weakPreprocessMessageHandler = new WeakEventPreprocessMessage(this, false); } AddSource(); // Register dropable window. // The checking CallerHasPermissionWithAppDomainOptimization will call RegisterDropTarget // safely without the security exception in case of no unmanaged code permission. // So RegisterDropTarget will be called safely in case of having the unmanged code permission. // Otherwise, the security exception cause System.Printing to be instatiated which will // load system.drawing module. if (_hwndWrapper.Handle != IntPtr.Zero && SecurityHelper.CallerHasPermissionWithAppDomainOptimization(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode))) { // This call is safe since DragDrop.RegisterDropTarget is checking the unmanged // code permission. DragDrop.RegisterDropTarget(_hwndWrapper.Handle); _registeredDropTargetCount++; } } /// /// Disposes the object /// ////// This API is not available in Internet Zone. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ////// Adds a hook that gets called for every window message. /// /// /// The hook to add. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - uses a critical field. /// PublicOK - as there's a demand. /// [SecurityCritical ] public void AddHook(HwndSourceHook hook) { SecurityHelper.DemandUIWindowPermission(); Verify.IsNotNull(hook, "hook"); CheckDisposed(true); if(_hooks == null) { _hwndWrapper.AddHook(_publicHook); } _hooks += hook; } ////// Removes a hook that was previously added. /// /// /// The hook to remove. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - accesses a crtical field - _publicHook /// PublicOK - performs a demand. /// [SecurityCritical ] public void RemoveHook(HwndSourceHook hook) { SecurityHelper.DemandUIWindowPermission(); //this.VerifyAccess(); _hooks -= hook; if(_hooks == null) { _hwndWrapper.RemoveHook(_publicHook); } } ////// GetInputProvider - Given a InputDevice, returns corresponding Provider /// /// InputDevice for which we need InputProvider ///InputProvider, if known ////// This API is not available in Internet Zone. /// ////// Critical: This code accesses and returns critical data *providers* /// [SecurityCritical] internal override IInputProvider GetInputProvider(Type inputDevice) { if (inputDevice == typeof(MouseDevice)) return (_mouse != null ? _mouse.Value : null); if (inputDevice == typeof(KeyboardDevice)) return (_keyboard != null ? _keyboard.Value : null); if (inputDevice == typeof(StylusDevice)) return (_stylus != null ? _stylus.Value : null); return null; } ////// Announces when this source is disposed. /// public event EventHandler Disposed; ////// Announces when the SizeToContent property changes on this source. /// public event EventHandler SizeToContentChanged; ////// Whether or not the object is disposed. /// public override bool IsDisposed {get {return _isDisposed;}} ////// The Root Visual for this window. If it is a UIElement /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This code sets a rootvisual which is risky to do because /// it can destabilize assumptions made in popup code /// PublicOK: The getter is safe and the setter has a link demand to block unwarrented /// public use /// public override Visual RootVisual { [SecurityCritical] get { if (_isDisposed) return null; return (_rootVisual.Value); } [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Window=UIPermissionWindow.AllWindows)] set { CheckDisposed(true); RootVisualInternal = value; } } ////// Critical: Acceses KeyboardInputProvider which is considered as critical data /// also it can be used to set root visual which is deemed as an unsafe operation /// private Visual RootVisualInternal { [SecurityCritical] set { if (_rootVisual.Value != value) { Visual oldRoot = _rootVisual.Value; if(value != null) { _rootVisual.Value = value; if(_rootVisual.Value is UIElement) { ((UIElement)(_rootVisual.Value)).LayoutUpdated += new EventHandler(OnLayoutUpdated); } if (_hwndTarget != null && _hwndTarget.IsDisposed == false) { _hwndTarget.RootVisual = _rootVisual.Value; } UIElement.PropagateResumeLayout(null, value); } else { _rootVisual.Value = null; if (_hwndTarget != null && !_hwndTarget.IsDisposed) { _hwndTarget.RootVisual = null; } } if(oldRoot != null) { if(oldRoot is UIElement) { ((UIElement)oldRoot).LayoutUpdated -= new EventHandler(OnLayoutUpdated); } UIElement.PropagateSuspendLayout(oldRoot); } RootChanged(oldRoot, _rootVisual.Value); if (IsLayoutActive() == true) { // Call the helper method SetLayoutSize to set Layout's size SetLayoutSize(); // Post the firing of ContentRendered as Input priority work item so that ContentRendered will be // fired after render query empties. this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(FireContentRendered), this); } else { // Even though layout won't run (the root visual is either null or not // a UIElement), the hit-test results will certainly have changed. InputManager.SafeCurrentNotifyHitTestInvalidated(); } // It is possible that someone would have closed the window in one of the // previous callouts - such as during RootChanged or during the layout // we syncronously invoke. In such cases, the state of this object would // have been torn down. We just need to protect against that. if(_keyboard != null) { _keyboard.Value.OnRootChanged(oldRoot, _rootVisual.Value); } } } } ////// Returns a sequence of registered input sinks. /// public IEnumerableChildKeyboardInputSinks { get { if (_keyboardInputSinkChildren != null) { foreach (IKeyboardInputSite site in _keyboardInputSinkChildren) yield return site.Sink; } } } /// /// Returns the HwndSource that corresponds to the specified window. /// /// The window. ///The source that corresponds to the specified window. ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This information is not ok to expose since this HwndSource /// is deemed critical to expose /// PublicOK: There is a demand on this method that prevents this /// from working in partial trust unless you have the right permissions. /// [SecurityCritical] public static HwndSource FromHwnd(IntPtr hwnd) { SecurityHelper.DemandUIWindowPermission(); return CriticalFromHwnd(hwnd); } ////// Critical: This code extracts the HwndSource for an HWND /// This function is only for internal consumption /// [SecurityCritical] internal static HwndSource CriticalFromHwnd(IntPtr hwnd) { if (hwnd == IntPtr.Zero) { throw new ArgumentException(SR.Get(SRID.NullHwnd)); } HwndSource hwndSource = null; foreach (PresentationSource source in PresentationSource.CriticalCurrentSources) { HwndSource test = source as HwndSource; if (test != null && test.CriticalHandle == hwnd) { // Don't hand out a disposed source. if (!test.IsDisposed) hwndSource = test; break; } } return hwndSource; } ////// The visual manager for the visuals being presented in the source. /// Type-specific version of the CompositionTarget property for this source. /// ////// Critical: Accesses hwndTarget and returns it /// PublicOk: Protected by a LinkDemand /// public new HwndTarget CompositionTarget { [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Window=UIPermissionWindow.AllWindows)] get { if (_isDisposed) return null; // Even though we created the HwndTarget, it can get disposed out from // underneath us. if (_hwndTarget!= null && _hwndTarget.IsDisposed == true) { return null; } return _hwndTarget; } } ////// Returns visual target for this source. /// ////// Critical: calls get_CompositionTarget() and returns its value. /// [SecurityCritical] protected override CompositionTarget GetCompositionTargetCore() { return CompositionTarget; } ////// When an HwndSource enters menu mode, it subscribes to the /// ComponentDispatcher.ThreadPreprocessMessage event to get /// privileged access to the window messages. /// ////// The ThreadPreprocessMessage handler for menu mode is /// independent of the handler for the same event used for /// keyboard processing by top-level windows. /// ////// Critical: Accesses the ComponentDispatcher, which is generally /// considered critical. /// TreatAsSafe: MenuMode is approved for public access. /// [SecurityCritical, SecurityTreatAsSafe] internal override void OnEnterMenuMode() { // We opt-in this HwndSource to the new behavior for "exclusive" // menu mode only if AcquireHwndFocusInMenuMode is false. IsInExclusiveMenuMode = !_acquireHwndFocusInMenuMode; if(IsInExclusiveMenuMode) { Debug.Assert(_weakMenuModeMessageHandler == null); // Re-subscribe to the ComponentDispatcher.ThreadPreprocessMessage so we go first. _weakMenuModeMessageHandler = new WeakEventPreprocessMessage(this, true); // Hide the Win32 caret UnsafeNativeMethods.HideCaret(new HandleRef(this, IntPtr.Zero)); } } ////// When an HwndSource leaves menu mode, it unsubscribes from the /// ComponentDispatcher.ThreadPreprocessMessage event because it no /// longer needs privileged access to the window messages. /// ////// The ThreadPreprocessMessage handler for menu mode is /// independent of the handler for the same event used for /// keyboard processing by top-level windows. /// ////// Critical: Accesses the ComponentDispatcher, which is generally /// considered critical. /// TreatAsSafe: MenuMode is approved for public access. /// [SecurityCritical, SecurityTreatAsSafe] internal override void OnLeaveMenuMode() { if(IsInExclusiveMenuMode) { Debug.Assert(_weakMenuModeMessageHandler != null); // Unsubscribe the special menu-mode handler since we don't need to go first anymore. _weakMenuModeMessageHandler.Dispose(); _weakMenuModeMessageHandler = null; // Restore the Win32 caret. This does not necessarily show the caret, it // just undoes the HideCaret call in OnEnterMenuMode. UnsafeNativeMethods.ShowCaret(new HandleRef(this, IntPtr.Zero)); } IsInExclusiveMenuMode = false; } internal bool IsInExclusiveMenuMode{get; private set;} ////// Event invoked when the layout causes the HwndSource to resize automatically. /// public event AutoResizedEventHandler AutoResized; ////// Handler for LayoutUpdated event of a rootVisual. /// ////// Critical: This code causes resize of window and accesses HwndTarget /// [SecurityCritical] private void OnLayoutUpdated(object obj, EventArgs args) { UIElement root = _rootVisual.Value as UIElement; if(root != null) { Size newSize = root.RenderSize; if ( _previousSize == null || !DoubleUtil.AreClose(_previousSize.Value.Width, newSize.Width) || !DoubleUtil.AreClose(_previousSize.Value.Height, newSize.Height)) { // We should update _previousSize, even if the hwnd is not // sizing to content. This fixes the scenario where: // // 1) hwnd is sized to content to say a, b // 2) hwnd is resize to a bigger size // 3) hwnd is sized to content to a, b again _previousSize = newSize; // // Don't resize while the Window is in Minimized mode. // if (_sizeToContent != SizeToContent.Manual && !_isWindowInMinimizeState ) { Resize(newSize); } } } } ////// This is called when LayoutManager was updated and its size (the layout size of top element) changed. /// Ask LayoutManager.Size to see what the new value is. /// ////// Critical: This code causes resize of window and accesses HwndTarget /// TreatAsSafe: In RBW the resize values are clamped also one cannot construct or get to a HwndSource in partial trust /// [SecurityCritical, SecurityTreatAsSafe] private void Resize(Size newSize) { try { _myOwnUpdate = true; if (IsUsable) { NativeMethods.RECT rect = AdjustWindowSize(newSize); int newWidth = rect.right - rect.left; int newHeight = rect.bottom - rect.top; // Set the new window size UnsafeNativeMethods.SetWindowPos(new HandleRef(this,_hwndWrapper.Handle), new HandleRef(null,IntPtr.Zero), 0, 0, newWidth, newHeight, NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE); if (AutoResized != null) { AutoResized(this, new AutoResizedEventArgs(newSize)); } } } finally { _myOwnUpdate = false; } } ////// This shows the system menu for the top level window that this HwndSource is in. /// ////// Critical: Accesses GetAncestor & PostMessage. This method is deemed inherently unsafe /// because opening the system menu will eat user input. /// [SecurityCritical] internal void ShowSystemMenu() { // Find the topmost window. This will handle the case where the HwndSource // is a child window. IntPtr hwndRoot = UnsafeNativeMethods.GetAncestor(new HandleRef(this, CriticalHandle), NativeMethods.GA_ROOT); // Open the system menu. UnsafeNativeMethods.PostMessage(new HandleRef(this, hwndRoot), MS.Internal.Interop.WindowMessage.WM_SYSCOMMAND, new IntPtr(NativeMethods.SC_KEYMENU), new IntPtr(NativeMethods.VK_SPACE)); } ////// Critical: This code accesses critical member _hwndTarget. /// TreatAsSafe: It calculates the new point without changing the HWND. /// [SecurityCritical, SecurityTreatAsSafe] internal Point TransformToDevice(Point pt) { // return _hwndTarget.TransformToDevice.Transform(pt); } ////// Critical: This code accesses critical member _hwndTarget. /// TreatAsSafe: It calculates the new point without changing the HWND. /// [SecurityCritical, SecurityTreatAsSafe] internal Point TransformFromDevice(Point pt) { return _hwndTarget.TransformFromDevice.Transform(pt); } ////// Critical: This code accesses _hwndWrapper. /// TreatAsSafe: It calculates the hwnd size without changing it. /// [SecurityCritical, SecurityTreatAsSafe] private NativeMethods.RECT AdjustWindowSize(Size newSize) { // Gather the new client dimensions // The dimension WPF uses is logical unit. We need to convert to device unit first. Point pt = TransformToDevice(new Point(newSize.Width, newSize.Height)); RoundDeviceSize(ref pt); NativeMethods.RECT rect = new NativeMethods.RECT(0, 0, (int)pt.X, (int)pt.Y); // If we're here, and it is the Window case (_adjustSizingForNonClientArea == true) // we get the size which includes the outside size of the window. For browser case, // we don't support SizeToContent, so we don't take care of this condition. // For non-Window cases, we need to calculate the outside size of the window // // For windows with UsesPerPixelOpacity, we force the client to // fill the window, so we don't need to add in any frame space. // if (_adjustSizingForNonClientArea == false && !UsesPerPixelOpacity) { int style = NativeMethods.IntPtrToInt32((IntPtr)SafeNativeMethods.GetWindowStyle(new HandleRef(this, _hwndWrapper.Handle), false)); int styleEx = NativeMethods.IntPtrToInt32((IntPtr)SafeNativeMethods.GetWindowStyle(new HandleRef(this, _hwndWrapper.Handle), true)); SafeNativeMethods.AdjustWindowRectEx(ref rect, style, false, styleEx); } return rect; } // If the root element has Pixel snapping enabled, round the window size to the // nearest int. Otherwise round the size up to the next int. private void RoundDeviceSize(ref Point size) { UIElement root = _rootVisual.Value as UIElement; if (root != null && root.SnapsToDevicePixels) { size = new Point(DoubleUtil.DoubleToInt(size.X), DoubleUtil.DoubleToInt(size.Y)); } else { size = new Point(Math.Ceiling(size.X), Math.Ceiling(size.Y)); } } ////// Returns the hwnd handle to the window. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical:This is not safe to expose in internet zone, it returns a window handle /// PublicOK: There exists a demand on this code /// public IntPtr Handle { [SecurityCritical] get { SecurityHelper.DemandUIWindowPermission(); return CriticalHandle; } } ////// Critical:Internal helper to retrieve handle for security purposes only Please /// DO NOT USE THIS TO EXPOSE HANDLE TO OUTSIDE WORLD /// internal IntPtr CriticalHandle { [FriendAccessAllowed] [SecurityCritical] get { if (null != _hwndWrapper) return _hwndWrapper.Handle; return IntPtr.Zero; } } ////// Critical: returns the critical _hwndWrapper. /// internal HwndWrapper HwndWrapper { [SecurityCritical] get { return _hwndWrapper; } } // Return whether this presentation source has capture. ////// Critical: calls CriticalHandle /// TreatAsSafe: Returning whether a presentation source has capture is considered safe. /// internal bool HasCapture { [SecurityCritical, SecurityTreatAsSafe] get { IntPtr capture = SafeNativeMethods.GetCapture(); return ( capture == CriticalHandle ); } } ////// Critical - accesses _hwndWrapper. /// TreatAsSafe - checking for null is considered safe. /// internal bool IsHandleNull { [SecurityCritical, SecurityTreatAsSafe ] get { return _hwndWrapper.Handle == IntPtr.Zero ; } } ////// Returns the hwnd handle to the window. /// public HandleRef CreateHandleRef() { return new HandleRef(this,Handle); } ////// SizeToContent on HwndSource /// ////// The default value is SizeToContent.Manual /// public SizeToContent SizeToContent { get { CheckDisposed(true); return _sizeToContent; } set { CheckDisposed(true); if (IsValidSizeToContent(value) != true) { throw new InvalidEnumArgumentException("value", (int)value, typeof(SizeToContent)); } if (_sizeToContent == value) { return; } _sizeToContent = value; // we only raise SizeToContentChanged when user interaction caused the change; // if a developer goes directly to HwndSource and sets SizeToContent, we will // not notify the wrapping Window if (IsLayoutActive() == true) { // Call the helper method SetLayoutSize to set Layout's size SetLayoutSize(); } } } ////// Critical: This code elevates to access hwndtarget /// TreatAsSafe: ok to expose /// [SecurityCritical,SecurityTreatAsSafe] private bool IsLayoutActive() { if ((_rootVisual.Value is UIElement) && _hwndTarget!= null && _hwndTarget.IsDisposed == false) { return true; } return false; } ////// This is the helper method that sets Layout's size basing it on /// the current value of SizeToContent. /// ////// TreatAsSafe: This API could be public in terms of security. /// It does three calls to UnsafeNativeMethods all in a safe way /// Critical: Makes 3 calls to UnsafeNativeMethods /// [SecurityCritical, SecurityTreatAsSafe] private void SetLayoutSize() { Debug.Assert(_hwndTarget!= null, "HwndTarget is null"); Debug.Assert(_hwndTarget.IsDisposed == false, "HwndTarget is disposed"); UIElement rootUIElement = null; rootUIElement = _rootVisual.Value as UIElement; if (rootUIElement == null) return; // hamidm - 11/01/2005 // InvalidateMeasure() call is necessary in the following scenario // // Window w = new Window(); // w.Measure(new Size(x,y)); // w.Width = x; // w.Height = y; // w.Show() // // In the above scenario, the Measure call from SetLayoutSize will be opt out // and window will not receive the MeasureOverride call. As such, the hwnd min/max // restrictions will not be applied since MeasureOverride did not happen after hwnd // creation. Thus, we call InvalidatMeasure() to ensure MeasureOverride call on // Window after hwnd creation. rootUIElement.InvalidateMeasure(); const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf; bool etwEnabled = EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info); long ctxHashCode = 0; if (_sizeToContent == SizeToContent.WidthAndHeight) { //setup constraints for measure-to-content Size sz = new Size(double.PositiveInfinity, double.PositiveInfinity); if (etwEnabled) { ctxHashCode = _hwndWrapper.Handle.ToInt64(); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode, EventTrace.LayoutSource.HwndSource_SetLayoutSize); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Measure(sz); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Arrange(new Rect(new Point(), rootUIElement.DesiredSize)); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } } else { // GetSizeFromHwnd sets either the outside size or the client size of the hwnd based on // _adjustSizeingForNonClientArea flag in logical units. Size sizeFromHwndLogicalUnits = GetSizeFromHwnd(); Size sz = new Size( (_sizeToContent == SizeToContent.Width ? double.PositiveInfinity : sizeFromHwndLogicalUnits.Width), (_sizeToContent == SizeToContent.Height ? double.PositiveInfinity : sizeFromHwndLogicalUnits.Height)); if (etwEnabled) { ctxHashCode = _hwndWrapper.Handle.ToInt64(); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode, EventTrace.LayoutSource.HwndSource_SetLayoutSize); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Measure(sz); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } if (_sizeToContent == SizeToContent.Width) sz = new Size(rootUIElement.DesiredSize.Width, sizeFromHwndLogicalUnits.Height); else if(_sizeToContent == SizeToContent.Height) sz = new Size(sizeFromHwndLogicalUnits.Width, rootUIElement.DesiredSize.Height); else sz = sizeFromHwndLogicalUnits; rootUIElement.Arrange(new Rect(new Point(), sz)); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } } rootUIElement.UpdateLayout(); } // ///// /// Specifies the color to display as transparent. // /// // ///// /// Use null to indicate that no color should be transparent. // /// // public NullableColorKey // { // get // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // return _hwndTarget.ColorKey; // } // else // { // return null; // } // } // // set // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // _hwndTarget.ColorKey = value; // } // } // } // /// // /// Specifies the constant opacity to apply to the window. // /// // ///// /// The valid values range from [0..1]. Values outside of this range are clamped. // /// // public double Opacity // { // get // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // return _hwndTarget.Opacity; // } // else // { // return 1.0; // } // } // // set // { // CheckDisposed(true); // // HwndTarget hwndTarget = CompositionTarget; // checks for disposed // if(_hwndTarget != null) // { // _hwndTarget.Opacity = value; // } // } // } ////// Specifies whether or not the per-pixel opacity of the window content /// is respected. /// ////// By enabling per-pixel opacity, the system will no longer draw the non-client area. /// ////// Critical: Because it accesses _hwndTarget /// PublicOK: We don't pass it out; it is just used to query UsesPerPixelOpacity /// public bool UsesPerPixelOpacity { [SecurityCritical] get { CheckDisposed(true); HwndTarget hwndTarget = CompositionTarget; // checks for disposed if(_hwndTarget != null) { return _hwndTarget.UsesPerPixelOpacity; } else { return false; } } /* Not allowing this to change at run-time yet. [SecurityCritical] set { CheckDisposed(true); HwndTarget hwndTarget = CompositionTarget; // checks for disposed if(_hwndTarget != null) { _hwndTarget.UsesPerPixelOpacity = value; } } */ } ////// Critical: Accesses _hwndWrapper.Handle to call unmanaged code to get the client rectangle. /// TreatAsSafe: The handle is not passed out, and the client rectangle does not need to be protected. /// [SecurityCritical, SecurityTreatAsSafe] private Size GetSizeFromHwnd() { // Compute View's size and set NativeMethods.RECT rc = new NativeMethods.RECT(0, 0, 0, 0); if (_adjustSizingForNonClientArea == true) { // get correct size for avalon Window (standalone and browser case) GetSizeForWindowObject(ref rc); } else { SafeNativeMethods.GetClientRect(new HandleRef(this,_hwndWrapper.Handle), ref rc); } Point convertedPt = TransformFromDevice(new Point(rc.right - rc.left, rc.bottom - rc.top)); return new Size(convertedPt.X, convertedPt.Y); } ////// Critical: This code can be used to spoof input /// [SecurityCritical] private IntPtr HwndTargetFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; if (IsUsable) { HwndTarget hwndTarget = _hwndTarget; if (hwndTarget != null) { result = hwndTarget.HandleMessage((WindowMessage)msg, wParam, lParam); if (result != IntPtr.Zero) { handled = true; } } } return result; } ////// Critical:These hooks can all be used for input spoofing /// [SecurityCritical] private IntPtr LayoutFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; WindowMessage message = (WindowMessage)msg; // We have to revalidate everything because the Access() call // could have caused the CLR to enter a nested message pump, // during which almost anything could have happened that might // invalidate our checks. UIElement rootUIElement=null; rootUIElement = _rootVisual.Value as UIElement; if (IsUsable && rootUIElement != null) { switch (message) { // A window receives this message when the user chooses a command from // the Window menu or when the user chooses the maximize button, minimize // button, restore button, or close button. case WindowMessage.WM_SYSCOMMAND: { // The four low-order bits of the wParam parameter are used // internally by the system. Int32 sysCommand = NativeMethods.IntPtrToInt32(wParam) & 0xFFF0; // Turn off SizeToContent if user chooses to maximize or resize. if ((sysCommand == NativeMethods.SC_MAXIMIZE) || (sysCommand == NativeMethods.SC_SIZE)) { DisableSizeToContent(rootUIElement, hwnd); } } break; // We get WM_SIZING. It means that user starts resizing the window. // It is the first notification sent when user resizes (before WM_WINDOWPOSCHANGING) // and it's not sent if window is resized programmatically. // SizeToContent is turned off after user resizes. case WindowMessage.WM_SIZING: DisableSizeToContent(rootUIElement, hwnd); break; // The WM_WINDOWPOSCHANGING message is sent // 1. when the size, position, or place in the Z order is about to change as a result of a call to // the SetWindowPos function or other window-management functions. SizeToContent orverrides all in this case. // 2. when user resizes window. If it's user resize, we have turned SizeToContent off when we get WM_SIZING. // It is sent before WM_SIZE. // We can't use WM_GETMINMAXINFO, because if there is no window size change (we still need to make sure // the client size not change), that notification wouldnt be sent. case WindowMessage.WM_WINDOWPOSCHANGING: Process_WM_WINDOWPOSCHANGING(rootUIElement, hwnd, message, wParam, lParam); break; // WM_SIZE message is sent after the size has changed. // lParam has the new width and height of client area. // root element's size should be adjust based on the new width and height and SizeToContent's value. case WindowMessage.WM_SIZE: Process_WM_SIZE(rootUIElement, hwnd, message, wParam, lParam); break; } } // Certain messages need to be processed while we are in the middle // of construction - and thus an HwndTarget is not available. if(!handled && (_constructionParameters != null || IsUsable)) { // Get the usesPerPixelOpacity from either the constructor parameters or the HwndTarget. bool usesPerPixelOpacity = _constructionParameters != null ? ((HwndSourceParameters)_constructionParameters).UsesPerPixelOpacity : _hwndTarget.UsesPerPixelOpacity; switch(message) { case WindowMessage.WM_NCCALCSIZE: { // Windows that use per-pixel opacity don't get // their frames drawn by the system. Generally // this is OK, as users of per-pixel alpha tend // to be doing customized UI anyways. But we // don't render correctly if we leave a non-client // area, so here we expand the client area to // cover any non-client area. // if(usesPerPixelOpacity) { if(wParam == IntPtr.Zero) { // If wParam is FALSE, lParam points to a RECT // structure. On entry, the structure contains // the proposed window rectangle for the // window. On exit, the structure should // contain the screen coordinates of the // corresponding window client area. // // Since all we want to do is make the client // rect the same as the window rect, we don't // have to do anything. // result = IntPtr.Zero; handled = true; } else { // If wParam is TRUE, lParam points to an // NCCALCSIZE_PARAMS structure that contains // information an application can use to // calculate the new size and position of // the client rectangle. // // When Windows sends the WM_NCCALCSIZE // message, the NCCALCSIZE_PARAMS structure // is filled out like this: // // rgrc[0] = new window rectangle (in parent coordinates) // rgrc[1] = old window rectangle (in parent coordinates) // rgrc[2] = old client rectangle (in parent coordinates) // // Notice that the client rectangle is given // in parent coordinates, not in client // coordinates. // // When your window procedure returns, Windows // expects the NCCALCSIZE_PARAMS structure to // be filled out like this: // // rgrc[0] = new client rectangle (in parent coordinates) // // Furthermore, if you return anything other // than 0, Windows expects the remaining two // rectangles to be filled out like this: // // rgrc[1] = destination rectangle (in parent coordinates) // rgrc[2] = source rectangle (in parent coordinates) // // (If you return 0, then Windows assumes that // the destination rectangle equals the new // client rectangle and the source rectangle // equals the old client rectangle.) // // Since all we want to do is make the client // rect the same as the window rect, we don't // have to do anything. // result = IntPtr.Zero; handled = true; } } } break; } } return result; } ////// Critical: Because it uses _hwndTarget /// [SecurityCritical] private void Process_WM_WINDOWPOSCHANGING(UIElement rootUIElement, IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam) { // Only if SizeToContent overrides Win32 sizing change calls. // If it's coming from OnResize (_myOwnUpdate != true), it means we are adjusting // to the right size; don't need to do anything here. if ((_myOwnUpdate != true) && (SizeToContent != SizeToContent.Manual)) { // Get the current style and calculate the size to be with the new style. // If WM_WINDOWPOSCHANGING is sent because of style changes, WM_STYLECHANGED is sent // before this. The style bits we get here are updated ones, but haven't been applied // to Window yet. For example, when the window is changing to borderless, without updating the window size, // the window size will remain the same but the client area will be bigger. So when SizeToContent is on, we calculate // the window size to be with the new style using AdustWindowRectEx and adjust it to make sure client area is not affected. NativeMethods.RECT rect = AdjustWindowSize(rootUIElement.RenderSize); int newCX = rect.right - rect.left; int newCY = rect.bottom - rect.top; // Get WINDOWPOS structure data from lParam; it contains information about the window's // new size and position. NativeMethods.WINDOWPOS windowPos = (NativeMethods.WINDOWPOS)UnsafeNativeMethods.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS)); bool sizeChanged = false; // If SWP_NOSIZE is set to ignore cx, cy. It could be a style or position change. if ((windowPos.flags & NativeMethods.SWP_NOSIZE) == NativeMethods.SWP_NOSIZE) { NativeMethods.RECT windowRect = new NativeMethods.RECT(0, 0, 0, 0); // Get the current Window rect SafeNativeMethods.GetWindowRect(new HandleRef(this, _hwndWrapper.Handle), ref windowRect); // If there is no size change with the new style we don't need to do anything. if ((newCX != (windowRect.right - windowRect.left)) || (newCY != (windowRect.bottom - windowRect.top))) { // Otherwise unmark the flag to make our changes effective. windowPos.flags &= ~NativeMethods.SWP_NOSIZE; // When SWP_NOSIZE is on, the size info in cx and cy is bogus. They are ignored. // When we turn it off, we need to provide valid value for both of them. windowPos.cx = newCX; windowPos.cy = newCY; sizeChanged = true; } } else { // We have excluded SizeToContent == SizeToContent.Manual before entering this. bool sizeToWidth = (SizeToContent == SizeToContent.Height) ? false : true; bool sizeToHeight = (SizeToContent == SizeToContent.Width) ? false : true; // Update WindowPos with the size we want. if ((sizeToWidth) && (windowPos.cx != newCX)) { windowPos.cx = newCX; sizeChanged = true; } if ((sizeToHeight) && (windowPos.cy != newCY)) { windowPos.cy = newCY; sizeChanged = true; } } // Marshal the structure back only when changed if (sizeChanged) { Marshal.StructureToPtr(windowPos, lParam, true); } } } ////// Critical: Has access to the window handle and uses the parameters provided to modify the layout /// of elements within the window. /// [SecurityCritical] private void Process_WM_SIZE(UIElement rootUIElement, IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam) { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); Point pt = new Point(x, y); const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf; bool etwEnabled = EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info); long ctxHashCode = 0; // 1. If it's coming from Layout (_myOwnUpdate), it means we are adjusting // to the right size; don't need to do anything here. // 2. If SizeToContent is set to WidthAndHeight, then we maintain the current hwnd size // in WM_WINDOWPOSCHANGING, so we don't need to re-layout here. If SizeToContent // is set to Width or Height and developer calls SetWindowPos to set a new // Width/Height, we need to do a layout. // 3. We also don't need to do anything if it's minimized. // Keeps the status of whether the Window is in Minimized state or not. _isWindowInMinimizeState = (NativeMethods.IntPtrToInt32(wParam) == NativeMethods.SIZE_MINIMIZED) ? true : false; if ((!_myOwnUpdate) && (_sizeToContent != SizeToContent.WidthAndHeight) && !_isWindowInMinimizeState) { Point relevantPt = new Point(pt.X, pt.Y); // WM_SIZE has the client size of the window. // for appmodel window case, get the outside size of the hwnd. if (_adjustSizingForNonClientArea == true) { NativeMethods.RECT rect = new NativeMethods.RECT(0, 0, (int)pt.X, (int)pt.Y); GetSizeForWindowObject(ref rect); relevantPt.X = rect.right - rect.left; relevantPt.Y = rect.bottom - rect.top; } // The lParam/wParam size and the GetSizeForWindowObject size are // both in device co-ods, thus we convert to Measure co-ods here. relevantPt = TransformFromDevice(relevantPt); Size sz = new Size( (_sizeToContent == SizeToContent.Width ? double.PositiveInfinity : relevantPt.X), (_sizeToContent == SizeToContent.Height ? double.PositiveInfinity : relevantPt.Y)); // NOTE: hamidm -- 6/03/04 // 962884 Avalon content does not resize when the favorites // (or other side pane) is closed // // The issue is that when the browser shows favorites window, avalon // window is resized and we get WM_SIZE. Here, we pass the IE window's // size to Measure so that Computed[Width/Height] gets the correct // IE window dimensions. Since, IE window's size may not change, the // call to Measure is optimized out and no layout happens. Invalidating // layout here ensures that we do layout. // This can happen only in the Window case if (_adjustSizingForNonClientArea == true) { rootUIElement.InvalidateMeasure(); } if (etwEnabled) { ctxHashCode = _hwndWrapper.Handle.ToInt64(); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode, EventTrace.LayoutSource.HwndSource_WMSIZE); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode);; } rootUIElement.Measure(sz); if (_sizeToContent == SizeToContent.Width) sz = new Size(rootUIElement.DesiredSize.Width, relevantPt.Y); else if (_sizeToContent == SizeToContent.Height) sz = new Size(relevantPt.X, rootUIElement.DesiredSize.Height); else sz = new Size(relevantPt.X, relevantPt.Y); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, ctxHashCode); } rootUIElement.Arrange(new Rect(new Point(), sz)); if (etwEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, 1); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } rootUIElement.UpdateLayout(); //finalizes layout } } ////// Critical: Because it uses _hwndTarget /// [SecurityCritical] private void DisableSizeToContent(UIElement rootUIElement, IntPtr hwnd) { if (_sizeToContent != SizeToContent.Manual) { _sizeToContent = SizeToContent.Manual; // hamidm - 10/27/2005 // 1348020 Window expereience layout issue when SizeToContent is being turned // off by user interaction // This bug was caused b/c we were giving rootUIElement.DesiredSize as input // to Measure/Arrange below. That is incorrect since rootUIElement.DesiredSize may not // cover the entire hwnd client area. // GetSizeFromHwnd returns either the outside size or the client size of the hwnd based on // _adjustSizeingForNonClientArea flag in logical units. Size sizeLogicalUnits = GetSizeFromHwnd(); rootUIElement.Measure(sizeLogicalUnits); rootUIElement.Arrange(new Rect(new Point(), sizeLogicalUnits)); rootUIElement.UpdateLayout(); //finalizes layout if (SizeToContentChanged != null) { SizeToContentChanged(this, EventArgs.Empty); } } } // This method makes sure that we get the size from the correct // hwnd for the browser case. ////// Critical - calls critical methods (HwndSourceHelper.GetHandle and GetAncestor) /// TreatAsSafe - it's ok to return size of window. it doesn't return info gotten through critical calls. /// [SecurityCritical, SecurityTreatAsSafe] private void GetSizeForWindowObject(ref NativeMethods.RECT rc) { IntPtr hwndRoot = UnsafeNativeMethods.GetAncestor(new HandleRef(this, CriticalHandle), NativeMethods.GA_ROOT); SafeNativeMethods.GetWindowRect(new HandleRef(this, hwndRoot), ref rc); } ////// Critical: This is a hook that gets called back with information about messages related to input /// Calling this from outside or causing this to be invoked could yield risky situations /// [SecurityCritical] private IntPtr InputFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; WindowMessage message = (WindowMessage)msg; // NOTE (alexz): invoke _stylus.FilterMessage before _mouse.FilterMessage // to give _stylus a chance to eat mouse message generated by stylus if (!_isDisposed && _stylus != null && !handled) { result = _stylus.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } if (!_isDisposed && _mouse != null && !handled) { result = _mouse.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } if (!_isDisposed && _keyboard != null && !handled) { result = _keyboard.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } if (!_isDisposed && _appCommand != null && !handled) { result = _appCommand.Value.FilterMessage(hwnd, message, wParam, lParam, ref handled); } return result; } ////// Called from HwndWrapper on all window messages. /// // assumes Context.Access() is held. private IntPtr PublicHooksFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { // The default result for messages we handle is 0. IntPtr result = IntPtr.Zero ; WindowMessage message = (WindowMessage)msg; // Call all of the public hooks // We do this even if we are disposed because otherwise the hooks // would never see the WM_DESTROY etc. message. if (_hooks != null) { Delegate[] handlers = _hooks.GetInvocationList(); for (int i = handlers.Length -1; i >= 0; --i) { var hook = (HwndSourceHook)handlers[i]; result = hook(hwnd, msg, wParam, lParam, ref handled); if(handled) { break; } } } if (WindowMessage.WM_NCDESTROY == message) { // We delivered the message to the hooks and the message // is WM_NCDESTROY, so our commitments should be finished // we can do final teardown. (like disposing the _hooks) OnNoMoreWindowMessages(); } return result; } #region IKeyboardInputSink /// General security note on the implementation pattern of this interface. In Dev10 it was chosen /// to expose the interface implementation for overriding to customers. We did so by keeping the /// explicit interface implementations (that do have the property of being hidden from the public /// contract, which limits IntelliSense on derived types like WebBrowser) while sticking protected /// virtuals next to them. Those virtuals contain our base implementation, while the explicit /// interface implementation methods do call trivially into the virtuals. /// /// This comment outlines the security rationale applied to those methods. /// ////// The security attributes on the virtual methods within this region mirror the corresponding /// IKeyboardInputSink methods; customers can override those methods, so we insert a LinkDemand /// to encourage them to have a LinkDemand too (via FxCop). /// /// While the methods have LinkDemands on them, the bodies of the methods typically contain /// full demands through a SecurityHelper.DemandUnmanagedCode call. This might seem redundant. /// The point here is we do a full demand for stronger protection of our built-in implementation /// compared to the LinkDemand on the public interface. We really want full demands here but /// declarative Demand doesn't work on interface methods. In addition, we try to take advantage /// of the fact LinkDemands are consistently enforced between base and overridden virtual methods, /// something full Demands do not give us, even when applied declaratively. /// private class MSGDATA { public MSG msg; public bool handled; } ////// HwndSource keyboard input is sent through this delegate to check for /// Child Hwnd interop requirments. If there are no child hwnds or focus /// is on this non-child hwnd then normal Avalon processing is done. /// ////// Critical - This can be used to spoof input /// [SecurityCritical] private void OnPreprocessMessageThunk(ref MSG msg, ref bool handled) { // VerifyAccess(); if (handled) { return; } // We only do these message. switch ((WindowMessage)msg.message) { case WindowMessage.WM_KEYUP: case WindowMessage.WM_KEYDOWN: case WindowMessage.WM_SYSKEYUP: case WindowMessage.WM_SYSKEYDOWN: case WindowMessage.WM_CHAR: case WindowMessage.WM_SYSCHAR: case WindowMessage.WM_DEADCHAR: case WindowMessage.WM_SYSDEADCHAR: MSGDATA msgdata = new MSGDATA(); msgdata.msg = msg; msgdata.handled = handled; // Do this under the exception filter/handlers of the // dispatcher for this thread. // // NOTE: we lose the "perf optimization" of passing everything // around by-ref since we have to call through a delegate. object result = Dispatcher.CurrentDispatcher.Invoke( DispatcherPriority.Send, new DispatcherOperationCallback(OnPreprocessMessage), msgdata); if (result != null) { handled = (bool)result; } // the semantics dictate that the callers could change this data. msg = msgdata.msg; break; } } ////// Critical: /// Can be used to spoof input. /// Asserts UnmanagedCode permission to call IKeyboardInputSink /// methods. /// For HANDLED_KEYDOWN_STILL_GENERATES_CHARS we also cause the /// Dispatcher to defer processing the queue until after any /// currently pending messages. /// [SecurityCritical] private object OnPreprocessMessage(object param) { MSGDATA msgdata = (MSGDATA) param; // Always process messages if this window is in menu mode. // // Otherwise, only process messages if someone below us has Focus. // // Mnemonics are broadcast to all branches of the window tree; even // those that don't have focus. BUT! at least someone under this // top-level window must have focus. if (!((IKeyboardInputSink)this).HasFocusWithin() && !IsInExclusiveMenuMode) { return msgdata.handled; } ModifierKeys modifierKeys = HwndKeyboardInputProvider.GetSystemModifierKeys(); // Interop with the Interop layer // switch ((WindowMessage)msgdata.msg.message) { case WindowMessage.WM_SYSKEYDOWN: case WindowMessage.WM_KEYDOWN: // 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. _eatCharMessages = true; DispatcherOperation restoreCharMessages = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(RestoreCharMessages), null); // Force the Dispatcher to post a new message to service any // pending operations, so that the operation we just posted // is guaranteed to get dispatched after any pending WM_CHAR // messages are dispatched. Dispatcher.CriticalRequestProcessing(true); msgdata.handled = CriticalTranslateAccelerator(ref msgdata.msg, modifierKeys); if(!msgdata.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. _eatCharMessages = false; restoreCharMessages.Abort(); } // Menu mode handles all keyboard messages so that they don't // get dispatched to some random window with focus. if(IsInExclusiveMenuMode) { // However, if the WM_KEYDOWN message was not explicitly // handled, then we need to generate WM_CHAR messages. WPF // expects this, but when we return handled, the outer // message pump will skip the TranslateMessage and // DispatchMessage calls. We mitigate this by calling // TranslateMessage directly. This is the same trick that // Win32 does in its menu loop. if(!msgdata.handled) { UnsafeNativeMethods.TranslateMessage(ref msgdata.msg); } msgdata.handled = true; } break; case WindowMessage.WM_SYSKEYUP: case WindowMessage.WM_KEYUP: msgdata.handled = CriticalTranslateAccelerator(ref msgdata.msg, modifierKeys); // Menu mode handles all keyboard messages so that they don't // get dispatched to some random window with focus. if(IsInExclusiveMenuMode) { msgdata.handled = true; } break; case WindowMessage.WM_CHAR: case WindowMessage.WM_SYSCHAR: case WindowMessage.WM_DEADCHAR: case WindowMessage.WM_SYSDEADCHAR: // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS if(!_eatCharMessages) { // IKIS implementation does a demand. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { msgdata.handled = ((IKeyboardInputSink)this).TranslateChar(ref msgdata.msg, modifierKeys); if (!msgdata.handled) { msgdata.handled = ((IKeyboardInputSink)this).OnMnemonic(ref msgdata.msg, modifierKeys); } } finally { SecurityPermission.RevertAssert(); } if (!msgdata.handled) { _keyboard.Value.ProcessTextInputAction(msgdata.msg.hwnd, (WindowMessage)msgdata.msg.message, msgdata.msg.wParam, msgdata.msg.lParam, ref msgdata.handled); } } // Menu mode handles all keyboard messages so that they don't // get dispatched to some random window with focus. if(IsInExclusiveMenuMode) { // If the WM_CHAR message is not explicitly handled, the // standard behavior is to beep. if(!msgdata.handled) { SafeNativeMethods.MessageBeep(0); } msgdata.handled = true; } break; } return msgdata.handled; } ////// Registers a child KeyboardInputSink with this sink. A site /// is returned. /// ////// This API requires unrestricted UI Window permission. /// We explicitly don't make this method overridable as we want to keep the /// precise implementation fixed and make sure the _keyboardInputSinkChildren /// state is kep consistent. By making the method protected, implementors can /// still call into it when required. Notice as calls are made through the /// IKIS interface, there's still a way for ---- developers to override /// the behavior by re-implementing the interface. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.Demand, Unrestricted=true)] protected IKeyboardInputSite RegisterKeyboardInputSinkCore(IKeyboardInputSink sink) { CheckDisposed(true); if (sink == null) { throw new ArgumentNullException("sink"); } if (sink.KeyboardInputSite != null) { throw new ArgumentException(SR.Get(SRID.KeyboardSinkAlreadyOwned)); } HwndSourceKeyboardInputSite site = new HwndSourceKeyboardInputSite(this, sink); if (_keyboardInputSinkChildren == null) _keyboardInputSinkChildren = new List(); _keyboardInputSinkChildren.Add(site); return site; } /// /// Critical: This method can be used to intercept and potentially tamper with raw input. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] IKeyboardInputSite IKeyboardInputSink.RegisterKeyboardInputSink(IKeyboardInputSink sink) { return RegisterKeyboardInputSinkCore(sink); } ////// Gives the component a chance to process keyboard input. /// Return value is true if handled, false if not. Components /// will generally call a child component's TranslateAccelerator /// if they can't handle the input themselves. The message must /// either be WM_KEYDOWN or WM_SYSKEYDOWN. It is illegal to /// modify the MSG structure, it's passed by reference only as /// a performance optimization. /// ////// This API is not available in Internet Zone. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] protected virtual bool TranslateAcceleratorCore(ref MSG msg, ModifierKeys modifiers) { SecurityHelper.DemandUnmanagedCode(); // VerifyAccess(); return CriticalTranslateAccelerator(ref msg, modifiers); } ////// Critical: Calls a method with a LinkDemand on it. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical] bool IKeyboardInputSink.TranslateAccelerator(ref MSG msg, ModifierKeys modifiers) { return TranslateAcceleratorCore(ref msg, modifiers); } ////// Set focus to the first or last tab stop (according to the /// TraversalRequest). If it can't, because it has no tab stops, /// the return value is false. /// protected virtual bool TabIntoCore(TraversalRequest request) { bool traversed = false; if(request == null) { throw new ArgumentNullException("request"); } UIElement root =_rootVisual.Value as UIElement; if(root != null) { // atanask: // request.Mode == FocusNavigationDirection.First will navigate to the fist tabstop including root // request.Mode == FocusNavigationDirection.Last will navigate to the last tabstop including root traversed = root.MoveFocus(request); } return traversed; } bool IKeyboardInputSink.TabInto(TraversalRequest request) { if(request == null) { throw new ArgumentNullException("request"); } return TabIntoCore(request); } ////// The property should start with a null value. The component's /// container will set this property to a non-null value before /// any other methods are called. It may be set multiple times, /// and should be set to null before disposal. /// ////// Setting KeyboardInputSite is not available in Internet Zone. /// We explicitly don't make this property overridable as we want to keep the /// precise implementation as a smart field for _keyboardInputSite fixed. /// By making the property protected, implementors can still call into it /// when required. Notice as calls are made through the IKIS interface, /// there's still a way for ---- developers to override the behavior by /// re-implementing the interface. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This property has demands on its accessors. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// protected IKeyboardInputSite KeyboardInputSiteCore { [SecurityCritical] get { SecurityHelper.DemandUnmanagedCode(); return _keyboardInputSite; } [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] set { SecurityHelper.DemandUnmanagedCode(); _keyboardInputSite = value; } } ////// Critical: Calls a property with a LinkDemand on it. /// PublicOK: The interface declaration for this property has a demand on it. /// IKeyboardInputSite IKeyboardInputSink.KeyboardInputSite { get { return KeyboardInputSiteCore; } [SecurityCritical] set { KeyboardInputSiteCore = value; } } ////// This method is called whenever one of the component's /// mnemonics is invoked. The message must either be WM_KEYDOWN /// or WM_SYSKEYDOWN. It's illegal to modify the MSG structrure, /// it's passed by reference only as a performance optimization. /// If this component contains child components, the container /// OnMnemonic will need to call the child's OnMnemonic method. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] protected virtual bool OnMnemonicCore(ref MSG msg, ModifierKeys modifiers) { // VerifyAccess(); SecurityHelper.DemandUnmanagedCode(); switch((WindowMessage)msg.message) { case WindowMessage.WM_SYSCHAR: case WindowMessage.WM_SYSDEADCHAR: string text = new string((char)msg.wParam, 1); if ((text != null) && (text.Length > 0)) { // We have to work around an ordering issue with mnemonic processing. // // Imagine you have a top level menu with _File & _Project and under _File you have _Print. // If the user pressses Alt+F,P you would expect _Print to be triggered // however if the top level window processes the mnemonic first // it will trigger _Project instead. // // One way to work around this would be for the top level window to notice that // keyboard focus is in another root window & not handle the mnemonics. The popup // window would then get a chance to process the mnemonic and _Print would be triggered. // // This doesn't work out becasue the popup window is no-activate, so it doesn't have Win32 focus // and it will bail out of OnPreprocessMessage before it handles the mnemonic. // // Instead the top level window should delegate OnMnemonic directly to the mnemonic scope window // instead of processing it here. This will let the Popup handle mnemonics instead of the top- // level window. // // The mnemonic scope window is defined as the window with WPF keyboard focus. // // This is a behavioral breaking change, so we've decided to only do it when IsInExclusiveMenuMode // is true to force the user to opt-in. DependencyObject focusObject = Keyboard.FocusedElement as DependencyObject; HwndSource mnemonicScope = (focusObject == null ? null : PresentationSource.CriticalFromVisual(focusObject) as HwndSource); if (mnemonicScope != null && mnemonicScope != this && IsInExclusiveMenuMode) { return ((IKeyboardInputSink)mnemonicScope).OnMnemonic(ref msg, modifiers); } if (AccessKeyManager.IsKeyRegistered(this, text)) { AccessKeyManager.ProcessKey(this, text, false); // return true; } } // these are OK break; case WindowMessage.WM_CHAR: case WindowMessage.WM_DEADCHAR: // these are OK break; default: throw new ArgumentException(SR.Get(SRID.OnlyAcceptsKeyMessages)); } // We record the last message that was processed by us. // This is also checked in WndProc processing to prevent double processing. _lastKeyboardMessage = msg; // The bubble will take care of access key processing for this HWND. Call // the IKIS children unless we are in menu mode. if (_keyboardInputSinkChildren != null && !IsInExclusiveMenuMode) { foreach ( HwndSourceKeyboardInputSite childSite in _keyboardInputSinkChildren ) { if (((IKeyboardInputSite)childSite).Sink.OnMnemonic(ref msg, modifiers)) return true; } } return false; } ////// Critical: Calls a method with a LinkDemand on it. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical] bool IKeyboardInputSink.OnMnemonic(ref MSG msg, ModifierKeys modifiers) { return OnMnemonicCore(ref msg, modifiers); } ////// Gives the component a chance to process keyboard input messages /// WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or WM_SYSDEADCHAR before calling OnMnemonic. /// Will return true if "handled" meaning don't pass it to OnMnemonic. /// The message must be WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or WM_SYSDEADCHAR. /// It is illegal to modify the MSG structure, it's passed by reference /// only as a performance optimization. /// ////// Critical: This API can be used for input spoofing. /// PublicOK: This method has a demand on it. /// /// Also see SecurityNote named IKeyboardInputSink_Implementation higher up. /// [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)] protected virtual bool TranslateCharCore(ref MSG msg, ModifierKeys modifiers) { SecurityHelper.DemandUnmanagedCode(); if(HasFocus || IsInExclusiveMenuMode) return false; IKeyboardInputSink focusSink = this.ChildSinkWithFocus; if(null != focusSink) { return focusSink.TranslateChar(ref msg, modifiers); } return false; } ////// Critical: Calls a method with a LinkDemand on it. /// PublicOK: The interface declaration for this method has a demand on it. /// [SecurityCritical] bool IKeyboardInputSink.TranslateChar(ref MSG msg, ModifierKeys modifiers) { return TranslateCharCore(ref msg, modifiers); } ////// /// protected virtual bool HasFocusWithinCore() { if(HasFocus) { return true; } else { if (null == _keyboardInputSinkChildren) return false; foreach (HwndSourceKeyboardInputSite site in _keyboardInputSinkChildren) { if (((IKeyboardInputSite)site).Sink.HasFocusWithin()) { return true; } } return false; } } bool IKeyboardInputSink.HasFocusWithin() { return HasFocusWithinCore(); } ////// The RestoreFocusMode for the window. /// ////// This property can only be set at construction time via the /// HwndSourceParameters.RestoreFocusMode property. /// public RestoreFocusMode RestoreFocusMode { get { return _restoreFocusMode; } } ////// The default value for the AcquireHwndFocusInMenuMode setting. /// public static bool DefaultAcquireHwndFocusInMenuMode { get { if(!_defaultAcquireHwndFocusInMenuMode.HasValue) { // The default value is true, for compat. _defaultAcquireHwndFocusInMenuMode = true; } return _defaultAcquireHwndFocusInMenuMode.Value; } set { _defaultAcquireHwndFocusInMenuMode = value; } } /// /// The AcquireHwndFocusInMenuMode setting for the window. /// ////// This property can only be set at construction time via the /// HwndSourceParameters.AcquireHwndFocusInMenuMode property. /// public bool AcquireHwndFocusInMenuMode { get { return _acquireHwndFocusInMenuMode; } } ////// The method is not part of the interface (IKeyboardInputSink). /// /// The Site that containes the sink to unregister ////// Critical - calls critical methods. /// [ SecurityCritical ] internal void CriticalUnregisterKeyboardInputSink(HwndSourceKeyboardInputSite site) { if(_isDisposed) return; if (null != _keyboardInputSinkChildren) { if (!_keyboardInputSinkChildren.Remove(site)) { throw new InvalidOperationException(SR.Get(SRID.KeyboardSinkNotAChild)); } } } IKeyboardInputSink ChildSinkWithFocus { get { IKeyboardInputSink ikis=null; if(null == _keyboardInputSinkChildren) return null; foreach (HwndSourceKeyboardInputSite site in _keyboardInputSinkChildren) { IKeyboardInputSite isite = (IKeyboardInputSite)site; if (isite.Sink.HasFocusWithin()) { ikis = isite.Sink; break; } } // This private property should only be called correctly. Debug.Assert(null!=ikis, "ChildSinkWithFocus called when none had focus"); return ikis; } } ////// Critical: This API could be vulnerable to input spoofing. /// [SecurityCritical, FriendAccessAllowed] internal bool CriticalTranslateAccelerator(ref MSG msg, ModifierKeys modifiers) { switch ((WindowMessage)msg.message) { case WindowMessage.WM_KEYUP: case WindowMessage.WM_KEYDOWN: case WindowMessage.WM_SYSKEYUP: case WindowMessage.WM_SYSKEYDOWN: // these are OK break; default: throw new ArgumentException(SR.Get(SRID.OnlyAcceptsKeyMessages)); } if (_keyboard == null) return false; bool handled = false; // TranslateAccelerator is called recursively on child Hwnds (Source & Host) // If this is the first Avalon TranslateAccelerator processing then we send the // key to be processed to the standard Avalon Input Filters and stuff. if (PerThreadData.TranslateAcceleratorCallDepth == 0) { // We record the last message that was processed by us. // This is also checked in WndProc processing to prevent double processing. // TranslateAcclerator is called from the pump before DispatchMessage // and the WndProc is called from DispatchMessage. We have processing // in both places. If we run the pump we process keyboard message here. // If we don't own the pump we process them in HwndKeyboardInputProvider.WndProc. _lastKeyboardMessage = msg; // NORMAL AVALON KEYBOARD INPUT CASE // If this is the top most Avalon window (it might be a child Hwnd // but no Avalon windows above it). And focus is on this window then // do the Normal Avalon Keyboard input Processing. if (HasFocus || IsInExclusiveMenuMode) { _keyboard.Value.ProcessKeyAction(ref msg, ref handled); } // ELSE the focus is probably in but not on this HwndSource. // Beware: It is possible that someone calls IKIS.TranslateAccelerator() while the focus is // somewhere entirely outside. // Do the once only message input filters etc and Tunnel/Bubble down // to the element that contains the child window with focus. // The Child HwndHost object will hook OnPreviewKeyDown() etc // to make the transition to its TranslateAccelerator() between the // tunnel and the bubble. else { IKeyboardInputSink focusSink = ChildSinkWithFocus; // can be null! IInputElement focusElement = (IInputElement)focusSink; try { PerThreadData.TranslateAcceleratorCallDepth += 1; Keyboard.PrimaryDevice.ForceTarget = focusElement; _keyboard.Value.ProcessKeyAction(ref msg, ref handled); } finally { Keyboard.PrimaryDevice.ForceTarget = null; PerThreadData.TranslateAcceleratorCallDepth -= 1; } } } // ELSE we have seen this MSG before, we are HwndSource decendant of an // HwndSource (that ancestor presumably did the processing above). // Here we raise the tunnel/bubble events without the once only keyboard // input filtering. else { int virtualKey = HwndKeyboardInputProvider.GetVirtualKey(msg.wParam, msg.lParam); int scanCode = HwndKeyboardInputProvider.GetScanCode(msg.wParam, msg.lParam); bool isExtendedKey = HwndKeyboardInputProvider.IsExtendedKey(msg.lParam); Key key = KeyInterop.KeyFromVirtualKey(virtualKey); RoutedEvent keyPreviewEvent=null; RoutedEvent keyEvent=null; switch ((WindowMessage)msg.message) { case WindowMessage.WM_KEYUP: case WindowMessage.WM_SYSKEYUP: keyPreviewEvent = Keyboard.PreviewKeyUpEvent; keyEvent = Keyboard.KeyUpEvent; break; case WindowMessage.WM_KEYDOWN: case WindowMessage.WM_SYSKEYDOWN: keyPreviewEvent = Keyboard.PreviewKeyDownEvent; keyEvent = Keyboard.KeyDownEvent; break; } bool hasFocus = HasFocus; IKeyboardInputSink focusSink = (hasFocus || IsInExclusiveMenuMode) ? null : ChildSinkWithFocus; IInputElement focusElement = focusSink as IInputElement; // focusElement may be null, in which case Target is just "focus", but we use it only if it's an // element within this HwndSource. It is possible that someone calls IKIS.TranslateAccelerator() // on a nested HwndSource while the focus is somewhere entirely outside. // HasFocus implies the focused element should be within this HwndSource, but unfortunately // we allow 'split focus', at least for popup windows; that's why check explicitly. // Note that KeyboardDevice.Target will likely be the ForceTarget corresponding to the // container of this HwndSource. That's why we look at the real FocusedElement. if (focusElement == null && hasFocus) { focusElement = Keyboard.PrimaryDevice.FocusedElement; if (focusElement != null && PresentationSource.CriticalFromVisual((DependencyObject)focusElement) != this) { focusElement = null; } } try { Keyboard.PrimaryDevice.ForceTarget = focusSink as IInputElement; if (focusElement != null) { KeyEventArgs tunnelArgs = new KeyEventArgs(Keyboard.PrimaryDevice, this, msg.time, key); tunnelArgs.ScanCode = scanCode; tunnelArgs.IsExtendedKey = isExtendedKey; tunnelArgs.RoutedEvent = keyPreviewEvent; focusElement.RaiseEvent(tunnelArgs); handled = tunnelArgs.Handled; } if (!handled) { KeyEventArgs bubbleArgs = new KeyEventArgs(Keyboard.PrimaryDevice, this, msg.time, key); bubbleArgs.ScanCode = scanCode; bubbleArgs.IsExtendedKey = isExtendedKey; bubbleArgs.RoutedEvent=keyEvent; if(focusElement != null) { focusElement.RaiseEvent(bubbleArgs); handled = bubbleArgs.Handled; } if (!handled) { // Raise the TranslateAccelerator event on the // InputManager to allow keyboard navigation to // happen on a descendent HwndSource InputManager.UnsecureCurrent.RaiseTranslateAccelerator(bubbleArgs); handled = bubbleArgs.Handled; } } } finally { Keyboard.PrimaryDevice.ForceTarget = null; } } return handled; } // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // Go back to accepting character messages. This method is posted // to the dispatcher when char messages are disable. internal static object RestoreCharMessages(object unused) { _eatCharMessages = false; return null; } #endregion IKeyboardInputSink internal bool IsRepeatedKeyboardMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam) { if (msg != _lastKeyboardMessage.message) return false; if (hwnd != _lastKeyboardMessage.hwnd) return false; if (wParam != _lastKeyboardMessage.wParam) return false; if (lParam != _lastKeyboardMessage.lParam) return false; return true; } ////// This event handler is called from HwndWrapper when it is Disposing. /// // This could happen if someone calls his Dispose before (or instead // of) our dispose. Or, more likely, the real window was killed by // something like the user clicking the close box. private void OnHwndDisposed(object sender, EventArgs args) { // This method is called from the HwndWrapper.Dispose(). // So make sure we don't call HwndWrapper.Dispose(). _inRealHwndDispose = true; Dispose(); } ////// Called after the last window message is processed. /// // HwndSource is required to continue certain operations while, // and even after, Dispose runs. HwndSource is resposible for // calling WndProcHooks with every message the window sees. // Including: WM_CLOSE, WM_DESTROY, WM_NCDESTROY. The doc says // WM_NCDESTROY is the very last message, so we can release the // Hooks after that. // This assumes the Context.Access() is held. private void OnNoMoreWindowMessages() { _hooks = null; } private void OnShutdownFinished(object sender, EventArgs args) { // Note: We are already in the context being disposed. Dispose(); } // // NOTE: shutdown order is very important. Review any changes // carefully. // ////// Critical: This accesses the various sites and providers. /// Asserting UnmanagedCode to access site, this is consistent with the requirements of the interface declaration /// as it can potentially be used for spoofing. /// Safe: Disposing the object is a safe operation. /// Not exposing IKIS, just using it to unregister. /// [SecurityCritical, SecurityTreatAsSafe] private void Dispose(bool disposing) { if(disposing) { // Make sure all access is synchronized. // this.VerifyAccess(); if (!_isDisposing) { // _isDisposing is a guard against re-entery into this // routine. We fire Dispose and SourceChanged (RootVisual // change) events which could cause re-entery. _isDisposing = true; // Notify listeners that we are being disposed. We do this // before we dispose our internal stuff, in case the event // listener needs to access something. if(Disposed != null) { try { Disposed(this, EventArgs.Empty); } #pragma warning disable 56500 // We can't tolerate an exception thrown by third-party code to // abort our Dispose half-way through. So we just eat it. catch { } #pragma warning restore 56500 Disposed = null; } // Remove any listeners of the ContentRendered event ClearContentRenderedListeners(); // Clear the root visual. This will raise a SourceChanged // event to registered listeners. RootVisualInternal = null; RemoveSource(); new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { // Unregister ourselves if we are a registered KeyboardInputSink. // Use the property instead of the backing field in case a subclass has overridden it. IKeyboardInputSite keyboardInputSite = ((IKeyboardInputSink)this).KeyboardInputSite; if (keyboardInputSite != null) { keyboardInputSite.Unregister(); ((IKeyboardInputSink)this).KeyboardInputSite = null; } _keyboardInputSinkChildren = null; } finally { SecurityPermission.RevertAssert(); } // Dispose the HwndStylusInputProvider BEFORE we destroy the HWND. // This us because the stylus provider has an async channel and // they don't want to process data after the HWND is destroyed. if (_stylus != null) { _stylus.Value.Dispose(); _stylus = null; } // Our general shut-down principle is to destroy the window // and let the individual HwndXXX components respons to WM_DESTROY. // // (see comment above about disposing the HwndStylusInputProvider) // { if (_hwndTarget != null) { _hwndTarget.Dispose(); _hwndTarget = null; } if (_hwndWrapper != null) { // Revoke the drop target. if (_hwndWrapper.Handle != IntPtr.Zero && _registeredDropTargetCount > 0) { // This call is safe since DragDrop.RevokeDropTarget is checking the unmanged // code permission. DragDrop.RevokeDropTarget(_hwndWrapper.Handle); _registeredDropTargetCount--; } // Remove our HwndWrapper.Dispose() hander. _hwndWrapper.Disposed -= new EventHandler(OnHwndDisposed); if (!_inRealHwndDispose) _hwndWrapper.Dispose(); // Don't null out _hwndWrapper after the Dispose(). // Dispose() will start destroying the Window but we // still need to talk to it during that process while // the WM_ msgs arrive. } } if(_mouse != null) { _mouse.Value.Dispose(); _mouse = null; } if(_keyboard != null) { _keyboard.Value.Dispose(); _keyboard = null; } if (_appCommand != null) { _appCommand.Value.Dispose(); _appCommand = null; } if(null != _weakShutdownHandler) { _weakShutdownHandler.Dispose(); _weakShutdownHandler = null; } if(null != _weakPreprocessMessageHandler) { _weakPreprocessMessageHandler.Dispose(); _weakPreprocessMessageHandler = null; } // We wait to set the "_isDisposed" flag until after the // Disposed, SourceChange (RootVisual=null), etc. events // have fired. We want to remain functional should their // handlers call methods on us. // // Note: as the HwndWrapper shuts down, the final few messages // will continue to pass through our WndProc hook. _isDisposed = true; } } } private void CheckDisposed(bool verifyAccess) { if(verifyAccess) { // this.VerifyAccess(); } if(_isDisposed) { throw new ObjectDisposedException(null, SR.Get(SRID.HwndSourceDisposed)); } } ////// Critical: This code accesses hwndtarget /// TreatAsSafe: Information is ok to expose /// private bool IsUsable { [SecurityCritical,SecurityTreatAsSafe] get { return _isDisposed == false && _hwndTarget != null && _hwndTarget.IsDisposed == false; } } ////// Critical - calls a method with an elevation ( GetFocus ) /// TreatAsSafe - determining whether you have focus within the window is considered safe. /// Worst case you can know whether keyboard/keypress events will go to the current window. /// private bool HasFocus { [SecurityCritical, SecurityTreatAsSafe] get { return UnsafeNativeMethods.GetFocus() == CriticalHandle; } } private static bool IsValidSizeToContent(SizeToContent value) { return value == SizeToContent.Manual || value == SizeToContent.Width || value == SizeToContent.Height || value == SizeToContent.WidthAndHeight; } class ThreadDataBlob { public int TranslateAcceleratorCallDepth; } private static ThreadDataBlob PerThreadData { get { ThreadDataBlob data; object obj = Thread.GetData(_threadSlot); if(null == obj) { data = new ThreadDataBlob(); Thread.SetData(_threadSlot, data); } else { data = (ThreadDataBlob) obj; } return data; } } #region WeakEventHandlers private class WeakEventDispatcherShutdown: WeakReference { public WeakEventDispatcherShutdown(HwndSource source, Dispatcher that): base(source) { _that = that; _that.ShutdownFinished += new EventHandler(this.OnShutdownFinished); } public void OnShutdownFinished(object sender, EventArgs e) { HwndSource source = this.Target as HwndSource; if(null != source) { source.OnShutdownFinished(sender, e); } else { Dispose(); } } public void Dispose() { if(null != _that) { _that.ShutdownFinished-= new EventHandler(this.OnShutdownFinished); } } private Dispatcher _that; } private class WeakEventPreprocessMessage: WeakReference { ////// Critical: This code calls attaches an arbitrary window /// to the call path for the component dispatcher call back /// [SecurityCritical] public WeakEventPreprocessMessage(HwndSource source, bool addToFront): base(source) { _addToFront = addToFront; _handler = new ThreadMessageEventHandler(this.OnPreprocessMessage); if(addToFront) { ComponentDispatcher.CriticalAddThreadPreprocessMessageHandlerFirst(_handler); } else { ComponentDispatcher.ThreadPreprocessMessage += _handler; } } ////// Critical: This can be used to spoof and change input /// [SecurityCritical] public void OnPreprocessMessage(ref MSG msg, ref bool handled) { HwndSource source = this.Target as HwndSource; if(null != source) { source.OnPreprocessMessageThunk(ref msg, ref handled); } else { Dispose(); } } ////// Critical:This code calls into ComponentDispatcher /// to disconnect a listener /// TreatAsSafe: This code is ok to call /// [SecurityCritical,SecurityTreatAsSafe] public void Dispose() { if(_addToFront) { ComponentDispatcher.CriticalRemoveThreadPreprocessMessageHandlerFirst(_handler); } else { ComponentDispatcher.ThreadPreprocessMessage -= _handler; } _handler = null; } private bool _addToFront; private ThreadMessageEventHandler _handler; } #endregion WeakEventHandlers private object _constructionParameters; // boxed HwndSourceParameters private bool _isDisposed = false; private bool _isDisposing = false; private bool _inRealHwndDispose = false; private bool _adjustSizingForNonClientArea; private bool _myOwnUpdate; private bool _isWindowInMinimizeState = false; private int _registeredDropTargetCount; private SizeToContent _sizeToContent = SizeToContent.Manual; private Size? _previousSize; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// [SecurityCritical] private HwndWrapper _hwndWrapper; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// [SecurityCritical] private HwndTarget _hwndTarget; private SecurityCriticalDataForSet_rootVisual; private event HwndSourceHook _hooks; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_mouse; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_keyboard; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_stylus; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data is considered critical. /// private SecurityCriticalDataClass_appCommand; WeakEventDispatcherShutdown _weakShutdownHandler; WeakEventPreprocessMessage _weakPreprocessMessageHandler; WeakEventPreprocessMessage _weakMenuModeMessageHandler; private static System.LocalDataStoreSlot _threadSlot; private RestoreFocusMode _restoreFocusMode; [ThreadStatic] private static bool? _defaultAcquireHwndFocusInMenuMode; private bool _acquireHwndFocusInMenuMode; private MSG _lastKeyboardMessage; private List _keyboardInputSinkChildren; /// /// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// // Be careful about accessing this field directly. // It's bound to IKeyboardInputSink.KeyboardInputSite, so if a derived class overrides // that property then this field will be incorrect. [SecurityCritical] private IKeyboardInputSite _keyboardInputSite = null; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _layoutHook; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _inputHook; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _hwndTargetHook; ////// Critical:This reference cannot be given out or assigned to outside of a verified /// elevation. This data can be used to spoof input /// [SecurityCritical] private HwndWrapperHook _publicHook; // MITIGATION: HANDLED_KEYDOWN_STILL_GENERATES_CHARS // // Avalon relies on the policy that if you handle the KeyDown // event, you will not get the TextInput events caused by // pressing the key. This is generally implemented because the // message pump calls ComponentDispatcher.RaiseThreadMessage and // we return whether or not the WM_KEYDOWN message was handled, // and the message pump will only call TranslateMessage() if the // WM_KEYDOWN was not handled. However, naive message pumps don't // call ComponentDispatcher.RaiseThreadMessage, and always call // TranslateMessage, so the WM_CHAR is generated no matter what. // The best work around we could think of was to eat the WM_CHAR // messages and not report them to Avalon. // [ThreadStatic] internal static bool _eatCharMessages; // used from HwndKeyboardInputProvider } } // 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
- Parser.cs
- DataTrigger.cs
- RectAnimationBase.cs
- documentsequencetextcontainer.cs
- HttpStaticObjectsCollectionBase.cs
- EntityDataSourceDataSelectionPanel.cs
- TextTabProperties.cs
- BaseTemplateParser.cs
- DesigntimeLicenseContext.cs
- smtpconnection.cs
- TextBox.cs
- Block.cs
- TransactionFlowElement.cs
- SimpleBitVector32.cs
- PeerServiceMessageContracts.cs
- RepeaterCommandEventArgs.cs
- BaseServiceProvider.cs
- ConcurrentBag.cs
- TrackingConditionCollection.cs
- XmlNamespaceMapping.cs
- OSFeature.cs
- ReflectTypeDescriptionProvider.cs
- XsltArgumentList.cs
- ConfigurationErrorsException.cs
- BamlRecordReader.cs
- PersistChildrenAttribute.cs
- ILGenerator.cs
- DiagnosticEventProvider.cs
- DelegatingConfigHost.cs
- SafeNativeMethods.cs
- MaskedTextBox.cs
- CodeConditionStatement.cs
- ServiceMetadataBehavior.cs
- ReaderWriterLockWrapper.cs
- FontResourceCache.cs
- CmsInterop.cs
- StylusPoint.cs
- PropertyInformationCollection.cs
- ErrorFormatterPage.cs
- AuthorizationBehavior.cs
- XmlNamespaceManager.cs
- LiteralControl.cs
- WsrmMessageInfo.cs
- AlphabetConverter.cs
- BindingParameterCollection.cs
- CryptoProvider.cs
- SafeProcessHandle.cs
- TemplateXamlTreeBuilder.cs
- FigureParagraph.cs
- DependencyPropertyChangedEventArgs.cs
- WebServiceErrorEvent.cs
- BinHexEncoder.cs
- Missing.cs
- WebPartExportVerb.cs
- MouseActionValueSerializer.cs
- StateItem.cs
- FileAuthorizationModule.cs
- ObjectMaterializedEventArgs.cs
- ReflectionServiceProvider.cs
- DesignTimeTemplateParser.cs
- RemoteWebConfigurationHostStream.cs
- GrammarBuilderRuleRef.cs
- VirtualizedCellInfoCollection.cs
- IISUnsafeMethods.cs
- StateRuntime.cs
- DataGridViewDataErrorEventArgs.cs
- FontWeightConverter.cs
- FormViewInsertEventArgs.cs
- QilReplaceVisitor.cs
- MultiView.cs
- ExecutionContext.cs
- ContextStaticAttribute.cs
- ActionNotSupportedException.cs
- OletxTransactionHeader.cs
- SQLMembershipProvider.cs
- DynamicFilterExpression.cs
- Exception.cs
- WindowsFormsSectionHandler.cs
- Config.cs
- ByteStreamGeometryContext.cs
- RegistrationServices.cs
- XmlWhitespace.cs
- BitStack.cs
- ProvidersHelper.cs
- CodeAssignStatement.cs
- InstanceDescriptor.cs
- XamlToRtfParser.cs
- UIElement3D.cs
- WSHttpBinding.cs
- IMembershipProvider.cs
- Version.cs
- ServiceObjectContainer.cs
- XmlAtomicValue.cs
- DiscoveryDocumentReference.cs
- TableRowCollection.cs
- Walker.cs
- ZipIOExtraFieldElement.cs
- BaseDataList.cs
- TypeSemantics.cs
- LabelDesigner.cs