Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Core / CSharp / System / Windows / InterOp / HwndTarget.cs / 1 / HwndTarget.cs
//------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation. All rights reserved. // // File: HwndTarget.cs // //----------------------------------------------------------------------------- using System; using System.Diagnostics; using System.Windows.Threading; using System.Threading; using System.Windows; using System.Collections.Generic; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; using System.Windows.Media.Composition; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Security; using MS.Internal; using MS.Internal.Automation; using MS.Win32; using MS.Utility; using Microsoft.Internal; using MS.Internal.PresentationCore; // SecurityHelper using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; #pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace System.Windows.Interop { // This is the internal, more expressive, enum used by the SetRenderingMode method. // See the RenderMode enum and the RenderMode property for the public version. internal enum RenderingMode { Default = MILRTInitializationFlags.MIL_RT_INITIALIZE_DEFAULT, Software = MILRTInitializationFlags.MIL_RT_SOFTWARE_ONLY, Hardware = MILRTInitializationFlags.MIL_RT_HARDWARE_ONLY, HardwareReference = MILRTInitializationFlags.MIL_RT_HARDWARE_ONLY | MILRTInitializationFlags.MIL_RT_USE_REF_RAST, } // This is the public, more limited, enum exposed for use with the RenderMode property. // See the RenderingMode enum and SetRenderingMode method for the internal version. ////// Render mode preference. /// public enum RenderMode { ////// The rendering layer should use the GPU and CPU as appropriate. /// Default, ////// The rendering layer should only use the CPU. /// SoftwareOnly } ////// The HwndTarget class represents a binding to an HWND. /// ///The HwndTarget is not thread-safe. Accessing the HwndTarget from a different /// thread than it was created will throw a public class HwndTarget : CompositionTarget { ///. /// Critical - Gets to the unmanaged layer under elevation (cctor, ctor, HandleMessage). /// [SecurityCritical] private static int s_dwmRedirectionEnvironmentChanged; ////// Critical - Gets to the unmanaged layer under elevation (cctor, ctor, SetChannelNotificationWindow). /// [SecurityCritical] private static int s_channelNotifyMessage; ////// Critical - Gets to the unmanaged layer under elevation (cctor, ctor, HandleMessage). /// [SecurityCritical] private static int s_transformHintUpdateMessage; ////// Critical - Gets to the unmanaged layer (ctor, HandleMessage, UpdateWindowSettings). /// [SecurityCritical] private static int s_updateWindowSettings; private MatrixTransform _worldTransform; private double _devicePixelsPerInchX; private double _devicePixelsPerInchY; ////// Critical - We don't want partial trust code changing the rendering preference. /// private SecurityCriticalDataForSet_renderModePreference = new SecurityCriticalDataForSet (RenderMode.Default); /// /// Critical - obtained under an elevation. /// [SecurityCritical] private NativeMethods.HWND _hWnd; private NativeMethods.RECT _hwndClientRectInScreenCoords; private NativeMethods.RECT _hwndWindowRectInScreenCoords; private Color _backgroundColor = Color.FromRgb(0, 0, 0); private DUCE.MultiChannelResource _compositionTarget = new DUCE.MultiChannelResource(); private bool _isRenderTargetEnabled = true; // private Nullable_colorKey = null; // private double _opacity = 1.0; private bool _usesPerPixelOpacity = false; // It is important that this start at zero to allow an initial // UpdateWindowSettings(enable) command to enable the render target // without a preceeding UpdateWindowSettings(disable) command. private int _disableCookie = 0; // Used to deal with layered window problems. See comments where they are used. private bool _isMinimized = false; private bool _isSessionDisconnected = false; private bool _isSuspended = false; /// /// Initializes static variables for this class. /// ////// Critical - Sets the SecurityCritical static variables holding the message ids; calls RegisterWindowMessage. /// TreatAsSafe - The message ids are not exposed; no external parameters are taken in. /// [SecurityCritical, SecurityTreatAsSafe] static HwndTarget() { s_dwmRedirectionEnvironmentChanged = UnsafeNativeMethods.RegisterWindowMessage("DwmRedirectionEnvironmentChangedHint"); s_channelNotifyMessage = UnsafeNativeMethods.RegisterWindowMessage("MilChannelNotify"); s_transformHintUpdateMessage = UnsafeNativeMethods.RegisterWindowMessage("TransformHintUpdate"); s_updateWindowSettings = UnsafeNativeMethods.RegisterWindowMessage("UpdateWindowSettings"); } ////// Attaches a hwndTarget to the hWnd /// /// The HWND to which the HwndTarget will draw. ////// This API link demands for UIWindowPermission.AllWindows /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - accepts unmanaged pointer handle.Not safe to create since it /// can be used to draw to a window /// PublicOk - demands UIPermission /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Window=UIPermissionWindow.AllWindows)] public HwndTarget(IntPtr hwnd) { bool exceptionThrown = true; AttachToHwnd(hwnd); try { if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.MEDIACREATEVISUALGUID), MS.Utility.EventType.Info, Dispatcher.GetHashCode(), hwnd.ToInt64()); } _hWnd = NativeMethods.HWND.Cast(hwnd); // Get the client rectangle... GetWindowRectsInScreenCoordinates(); // Set device independent resolution to 96 DPI. // NativeMethods.HDC hdc = NativeMethods.GetDC(_hWnd); _devicePixelsPerInchX = 96.0f; _devicePixelsPerInchY = 96.0f; // // Determine whether hWnd corresponds to MIL or GDI window. // IntPtr hdcW = UnsafeNativeMethods.GetDC( new HandleRef(this, _hWnd)); if (hdcW == IntPtr.Zero) { // // If we were unable to obtain HDC for the given // window, assume the default of 96 DPI. // _devicePixelsPerInchX = 96.0f; _devicePixelsPerInchY = 96.0f; } else { // // Obtain and cache DPI values for window's HDC. // _devicePixelsPerInchX = (double)UnsafeNativeMethods.GetDeviceCaps( new HandleRef(this, hdcW), NativeMethods.LOGPIXELSX); _devicePixelsPerInchY = (double)UnsafeNativeMethods.GetDeviceCaps( new HandleRef(this, hdcW), NativeMethods.LOGPIXELSY); // // Release DC object. // UnsafeNativeMethods.ReleaseDC( new HandleRef(this, _hWnd), new HandleRef(this, hdcW)); } _worldTransform = new MatrixTransform(new Matrix( _devicePixelsPerInchX * (1.0f / 96.0f), 0, 0, _devicePixelsPerInchY * (1.0f / 96.0f), 0, 0)); // // Register CompositionTarget with MediaContext. // MediaContext.RegisterICompositionTarget(Dispatcher, this); // // Change the window message filter to allow session and transform // hint control messages to be sent from the DWM. On down-level // platforms, these calls will result in no-op. // ChangeWindowMessageFilter((uint)s_dwmRedirectionEnvironmentChanged, 1 /* MSGFLT_ADD */); ChangeWindowMessageFilter((uint)s_transformHintUpdateMessage, 1 /* MSGFLT_ADD */); // // Hint window redirection layer that Avalon content has been attached // to this window. As a result, redirection layer will create necessary // resources and manage clip and position for this window in TS and // Accessibility scenarios. If DWM is not running, this call will result // in NoOp. // HRESULT.Check(MilContent_AttachToHwnd(_hWnd)); exceptionThrown = false; } finally { // // If exception has occurred after we attached this target to // the window, we need to detach from this window. Otherwise, window // will be left in a state when no other HwndTarget can be created // for it. // if(exceptionThrown) { #pragma warning suppress 6031 // Return value ignored on purpose. VisualTarget_DetachFromHwnd(hwnd); } } } ////// AttachToHwnd /// ////// Critical as it calls a function that performs an elevation (IsWindow). /// [SecurityCritical] internal void AttachToHwnd(IntPtr hwnd) { int processId = 0; int threadId = UnsafeNativeMethods.GetWindowThreadProcessId( new HandleRef(this, hwnd), out processId ); if (!UnsafeNativeMethods.IsWindow(new HandleRef(this, hwnd))) { throw new ArgumentException( SR.Get(SRID.HwndTarget_InvalidWindowHandle), "hwnd" ); } else if (processId != SafeNativeMethods.GetCurrentProcessId()) { throw new ArgumentException( SR.Get(SRID.HwndTarget_InvalidWindowProcess), "hwnd" ); } else if (threadId != SafeNativeMethods.GetCurrentThreadId()) { throw new ArgumentException( SR.Get(SRID.HwndTarget_InvalidWindowThread), "hwnd" ); } int hr = VisualTarget_AttachToHwnd(hwnd); if (HRESULT.Failed(hr)) { if (hr == unchecked((int)0x80070005)) // E_ACCESSDENIED { throw new InvalidOperationException( SR.Get(SRID.HwndTarget_WindowAlreadyHasContent) ); } else { HRESULT.Check(hr); } } } ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical,SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore, EntryPoint = "MilVisualTarget_AttachToHwnd")] internal static extern int VisualTarget_AttachToHwnd( IntPtr hwnd ); ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical, SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore, EntryPoint = "MilVisualTarget_DetachFromHwnd")] internal static extern int VisualTarget_DetachFromHwnd( IntPtr hwnd ); ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical, SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore)] internal static extern int MilContent_AttachToHwnd( IntPtr hwnd ); ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical, SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore)] internal static extern int MilContent_DetachFromHwnd( IntPtr hwnd ); ////// Allow lower integrity applications to send specified window messages /// in case we are elevated. Failure is non-fatal and on down-level /// platforms this call will result in a no-op. /// ////// Critical -- Calls unsafe native methods GetModuleHandle and GetProcAddress. /// Manually elevates unmanaged code permissions to pinvoke through /// a function pointer. /// [SecurityCritical] private void ChangeWindowMessageFilter(uint message, uint flag) { // Find the address of ChangeWindowMessageFilter in user32.dll. IntPtr user32Module = UnsafeNativeMethods.GetModuleHandle("user32.dll"); // Get the address of the function. If this fails it means the OS // doesn't support this function, in which case we don't // need to do anything further. IntPtr functionAddress = UnsafeNativeMethods.GetProcAddressNoThrow( new HandleRef(null, user32Module), "ChangeWindowMessageFilter"); if (functionAddress != IntPtr.Zero) { // Convert the function pointer into a callable delegate and then call it ChangeWindowMessageFilterNative function = Marshal.GetDelegateForFunctionPointer( functionAddress, typeof(ChangeWindowMessageFilterNative)) as ChangeWindowMessageFilterNative; // In order to call the function we need unmanaged code access, // because the function is native code. (new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert(); try { function(message, flag); } finally { SecurityPermission.RevertAssert(); } } } ////// Prototype for user32's ChangeWindowMessageFilter function, which we load dynamically on Longhorn. /// private delegate void ChangeWindowMessageFilterNative(uint message, uint flag); internal void SetRenderingMode(RenderingMode mode) { // // If ForceLocalTransport is set then the transport is connected to a client (magnifier) that cannot // handle our transport protocol version. Therefore we force software rendering so that the rendered // content is available through NTUser redirection. If software is not allowed an exception is thrown. // if (MediaSystem.ForcedLocalTransport) // present using bitblt if a local transport was forced { if (mode == RenderingMode.Hardware || mode == RenderingMode.HardwareReference) { throw new InvalidOperationException(SR.Get(SRID.HwndTarget_HardwareNotSupportDueToProtocolMismatch)); } else { Debug.Assert(mode == RenderingMode.Software || mode == RenderingMode.Default); // If the mode is default we can chose what works. When we have a mismatched transport protocol version // we need to fallback to software rendering. mode = RenderingMode.Software; } } // Select the render target initialization flags based on the requested // rendering mode. DUCE.ChannelSet channelSet = MediaContext.From(Dispatcher).GetChannels(); DUCE.Channel channel = channelSet.Channel; DUCE.CompositionTarget.SetRenderingMode( _compositionTarget.GetHandle(channel), (MILRTInitializationFlags)mode, channel); } ////// Specifies the render mode preference for the window. /// ////// This property specifies a preference, it does not necessarily change the actual /// rendering mode. Among other things, this can be trumped by the registry settings. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to set this property. /// /// Critical: This code influences the low-level rendering code by specifying whether the /// rendering system should use the GPU or CPU. /// PublicOK: We don't want to enable this in partial trust, so we have a link demand /// on the setter. It is not privileged data, so the getter is not protected. /// public RenderMode RenderMode { get { return _renderModePreference.Value; } // Note: We think it is safe to expose this in partial trust, but doing so would suggest // we should also expose HwndSource (the only way to get to the HwndTarget instance). // We don't want to bite off that much exposure at this point in the product, so we enforce // that this is not accessible from partial trust for now. [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] set { if (value != RenderMode.Default && value != RenderMode.SoftwareOnly) { throw new System.ComponentModel.InvalidEnumArgumentException("value", (int)value, typeof(RenderMode)); } _renderModePreference.Value = value; SetRenderingMode(value == RenderMode.SoftwareOnly ? RenderingMode.Software : RenderingMode.Default); } } ////// Dispose cleans up the state associated with HwndTarget. /// ////// Critical - accesses the _hwnd that is critical, calls unmanaged code /// PublicOK - dispose is in effect stoping the contents of the target from /// rendering. Equivalent to removing all elements in window, considered safe. /// [SecurityCritical] public override void Dispose() { // Its outside the try finally block because we want the exception to be // thrown if we are on a different thread and we don't want to call Dispose // on base class in that case. VerifyAccess(); try { // According to spec: Dispose should not raise exception if called multiple times. // This test is needed because the HwndTarget is Disposed from both the media contex and // the hwndsrc. if (!IsDisposed) { RootVisual = null; HRESULT.Check(VisualTarget_DetachFromHwnd(_hWnd)); // // Unregister this CompositionTarget from the MediaSystem. // MediaContext.UnregisterICompositionTarget(Dispatcher, this); // // Hint window redirection layer that Avalon content has been detached // from this window. Redirection layer will then have a chance to clean // up the resources that it created for Avalon hosting. If DWM is not // running, this call will result in NoOp. // HRESULT.Check(MilContent_DetachFromHwnd(_hWnd)); // Unregister for Fast User Switching messages if necessary if (_usesPerPixelOpacity) { UnsafeNativeMethods.WTSUnRegisterSessionNotification(_hWnd); } } } finally { base.Dispose(); } } ////// This method is used to create all uce resources either on Startup or session connect /// ////// Critical - uses unmanaged pointer handle _hWnd /// TreatAsSafe - doesn't return or expose _hWnd /// [SecurityCritical, SecurityTreatAsSafe] internal override void CreateUCEResources(DUCE.Channel channel, DUCE.Channel outOfBandChannel) { // create visual target resources // this forces the creation of the media context if we don't already have one. base.CreateUCEResources(channel, outOfBandChannel); Debug.Assert(!_compositionTarget.IsOnChannel(channel)); Debug.Assert(!_compositionTarget.IsOnChannel(outOfBandChannel)); // // For each HwndTarget we are building some structures in the UCE. // This includes spinning up a UCE render target. We need to commit the // batch for those changes right away, since we need to be able to process // the invalidate packages that we send down on WM_PAINTs. If we don't commit // right away a WM_PAINT can get fired before we get a chance to commit // the batch. // // // First we create the composition target, composition context, and the composition root node. // Note, that composition target will be created out of band because invalidate // command is also sent out of band and that can occur before current channel is committed. // We would like to avoid commiting channel here to prevent visual artifacts. // bool resourceCreated = _compositionTarget.CreateOrAddRefOnChannel(outOfBandChannel, DUCE.ResourceType.TYPE_HWNDRENDERTARGET); Debug.Assert(resourceCreated); _compositionTarget.DuplicateHandle(outOfBandChannel, channel); outOfBandChannel.Commit(); DUCE.CompositionTarget.HwndInitialize( _compositionTarget.GetHandle(channel), _hWnd, _hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left, _hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top, MediaSystem.ForcedLocalTransport, // present using bitblt if a local transport was forced channel ); DUCE.ResourceHandle hWorldTransform = ((DUCE.IResource)_worldTransform).AddRefOnChannel(channel); DUCE.CompositionNode.SetTransform( _contentRoot.GetHandle(channel), hWorldTransform, channel); DUCE.CompositionTarget.SetClearColor( _compositionTarget.GetHandle(channel), _backgroundColor, channel); // // Set initial state on the visual target. // Rect clientRect = new Rect( 0, 0, (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left))), (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top)))); StateChangedCallback( new object[] { HostStateFlags.WorldTransform | HostStateFlags.ClipBounds, _worldTransform.Matrix, clientRect }); DUCE.CompositionTarget.SetRoot( _compositionTarget.GetHandle(channel), _contentRoot.GetHandle(channel), channel); // reset the disable cookie when creating the slave resource. This happens when creating the // managed resource and on handling a connect. _disableCookie = 0; // // Finally, update window settings to reflect the state of this object. // Because CreateUCEResources is called for each channel, only call // UpdateWindowSettings on that channel this time. // DUCE.ChannelSet channelSet; channelSet.Channel = channel; channelSet.OutOfBandChannel = outOfBandChannel; UpdateWindowSettings(_isRenderTargetEnabled, channelSet); } ////// This method is used to release all uce resources either on Shutdown or session disconnect /// ////// Critical - uses unmanaged pointer handle _hWnd /// TreatAsSafe - doesn't return or expose _hWnd /// [SecurityCritical, SecurityTreatAsSafe] internal override void ReleaseUCEResources(DUCE.Channel channel, DUCE.Channel outOfBandChannel) { if (_compositionTarget.IsOnChannel(channel)) { // // GSchneid: If we need to flush the batch we need to render first all visual targets that // are still registered with the MediaContext to avoid strutural tearing. // Set the composition target root node to null. DUCE.CompositionTarget.SetRoot( _compositionTarget.GetHandle(channel), DUCE.ResourceHandle.Null, channel); _compositionTarget.ReleaseOnChannel(channel); } if (_compositionTarget.IsOnChannel(outOfBandChannel)) { _compositionTarget.ReleaseOnChannel(outOfBandChannel); } DUCE.ResourceHandle hWorldTransform = ((DUCE.IResource)_worldTransform).GetHandle(channel); if (!hWorldTransform.IsNull) { // Release the world transform from this channel if it's currently on the channel. ((DUCE.IResource)_worldTransform).ReleaseOnChannel(channel); } // release all the visual target resources. base.ReleaseUCEResources(channel, outOfBandChannel); } ////// The HwndTarget needs to see all windows messages so that /// it can appropriately react to them. /// ////// Critical - accepts unmanaged pointer handle, also /// Elevates permissions via unsafe native methods, handles messages to /// retrieve automation provider the last one is most risky /// [SecurityCritical] internal IntPtr HandleMessage(int msg, IntPtr wparam, IntPtr lparam) { IntPtr handled = new IntPtr(0x1); // return 1 if the message is handled by this method exclusively. IntPtr unhandled = IntPtr.Zero; // return 0 if other win procs should be called. IntPtr result = unhandled; if (msg == s_dwmRedirectionEnvironmentChanged) { System.Windows.Media.MediaSystem.NotifyRedirectionEnvironmentChanged(); return handled; } else if (msg == s_channelNotifyMessage) { MediaContext.From(Dispatcher).NotifyChannelMessage(); } else if(msg == s_updateWindowSettings) { // Make sure we enable the render target if the window is visible. if(SafeNativeMethods.IsWindowVisible(_hWnd.MakeHandleRef(this))) { UpdateWindowSettings(true); } } if (IsDisposed) { return result; } switch (msg) { case NativeMethods.WM_ERASEBKGND: result = handled; // Indicates that this message is handled. break; case NativeMethods.WM_PAINT: DoPaint(); result = handled; break; case NativeMethods.WM_SIZE: // // NTRAID#Longhorn-1946030-2007/03/23-jordanpa: // When locked on downlevel, MIL stops rendering and invalidates the // window causing WM_PAINT. When the window is layered and minimized // before the lock, it'll never get the WM_PAINT on unlock and the MIL will // never get out of the "don't render" state. // // To work around this, we will invalidate ourselves on restore and not // render while minimized. // // If the Window is in minimized state, don't do layout. otherwise, in some cases, it would // pollute the measure data based on the Minized window size. if (NativeMethods.IntPtrToInt32(wparam) != NativeMethods.SIZE_MINIMIZED) { _isMinimized = false; DoPaint(); OnResize(); } else { _isMinimized = true; } break; case NativeMethods.WM_SETTINGCHANGE: if (OnSettingChange(NativeMethods.IntPtrToInt32(wparam))) { UnsafeNativeMethods.InvalidateRect(_hWnd.MakeHandleRef(this), IntPtr.Zero , true); } break; case NativeMethods.WM_GETOBJECT: result = CriticalHandleWMGetobject( wparam, lparam, RootVisual, _hWnd ); break; case NativeMethods.WM_WINDOWPOSCHANGING: OnWindowPosChanging(lparam); break; case NativeMethods.WM_WINDOWPOSCHANGED: OnWindowPosChanged(lparam); break; case NativeMethods.WM_SHOWWINDOW: OnShowWindow(wparam != IntPtr.Zero); break; case NativeMethods.WM_EXITSIZEMOVE: OnExitSizeMove(); break; case NativeMethods.WM_STYLECHANGING: unsafe { NativeMethods.STYLESTRUCT * styleStruct = (NativeMethods.STYLESTRUCT *) lparam; if ((int)wparam == NativeMethods.GWL_EXSTYLE) { if(UsesPerPixelOpacity) { // We need layered composition to accomplish per-pixel opacity. // styleStruct->styleNew |= NativeMethods.WS_EX_LAYERED; } else { // No properties that require layered composition exist. // Make sure the layered bit is off. // // Note: this prevents an external program from making // us system-layered (if we are a top-level window). // // If we are a child window, we still can't stop our // parent from being made system-layered, and we will // end up leaving visual artifacts on the screen under // WindowsXP. // styleStruct->styleNew &= (~NativeMethods.WS_EX_LAYERED); } } } break; case NativeMethods.WM_STYLECHANGED: unsafe { bool updateWindowSettings = false; NativeMethods.STYLESTRUCT * styleStruct = (NativeMethods.STYLESTRUCT *) lparam; if ((int)wparam == NativeMethods.GWL_STYLE) { bool oldIsChild = (styleStruct->styleOld & NativeMethods.WS_CHILD) == NativeMethods.WS_CHILD; bool newIsChild = (styleStruct->styleNew & NativeMethods.WS_CHILD) == NativeMethods.WS_CHILD; updateWindowSettings = (oldIsChild != newIsChild); } else { bool oldIsRTL = (styleStruct->styleOld & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL; bool newIsRTL = (styleStruct->styleNew & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL; updateWindowSettings = (oldIsRTL != newIsRTL); } if(updateWindowSettings) { UpdateWindowSettings(); } } break; // // NTRAID#Longhorn-1967619-2007/03/23-jordanpa: // When a Fast User Switch happens, MIL gets an invalid display error when trying to // render and they invalidate the window resulting in us getting a WM_PAINT. For // layered windows, we get the WM_PAINT immediately which causes us to // tell MIL to render and the cycle repeats. On Vista, this creates an infinite loop. // Downlevel there isn't a loop, but the layered window will never update again. // // To work around this problem, we'll make sure not to tell MIL to render when // we're switched out and will render on coming back. // case NativeMethods.WM_WTSSESSION_CHANGE: switch (NativeMethods.IntPtrToInt32(wparam)) { // Session is disconnected. Due to: // 1. Switched to a different user // 2. TS logoff // 3. Screen locked case NativeMethods.WTS_CONSOLE_DISCONNECT: case NativeMethods.WTS_REMOTE_DISCONNECT: case NativeMethods.WTS_SESSION_LOCK: _isSessionDisconnected = true; break; // Session is reconnected. See above case NativeMethods.WTS_CONSOLE_CONNECT: case NativeMethods.WTS_REMOTE_CONNECT: case NativeMethods.WTS_SESSION_UNLOCK: _isSessionDisconnected = false; DoPaint(); break; default: break; } break; // // NTRAID#Longhorn-1975236-2007/05/22-jordanpa: // Downlevel, if we try to present a layered window while suspended the app will crash. // This has been fixed in Vista but we still need to work around it for older versions // by not invalidating while suspended. // case NativeMethods.WM_POWERBROADCAST: switch (NativeMethods.IntPtrToInt32(wparam)) { case NativeMethods.PBT_APMSUSPEND: _isSuspended = true; break; case NativeMethods.PBT_APMRESUMESUSPEND: case NativeMethods.PBT_APMRESUMECRITICAL: case NativeMethods.PBT_APMRESUMEAUTOMATIC: _isSuspended = false; DoPaint(); break; default: break; } break; default: break; } return result; } ////// Paints a rect /// /// Note: This gets called a lot to help with layered window problems even when /// the window isn't layered, but that's okay because rcPaint will be empty. /// /// ////// Critical - Elevates permissions via unsafe native methods, calls into begin paint /// [SecurityCritical] private void DoPaint() { NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); NativeMethods.HDC hdc; HandleRef handleRef = new HandleRef(this, _hWnd); hdc.h = UnsafeNativeMethods.BeginPaint(handleRef, ref ps); int retval = UnsafeNativeMethods.GetWindowLong(handleRef, NativeMethods.GWL_EXSTYLE); NativeMethods.RECT rcPaint = new NativeMethods.RECT(ps.rcPaint_left, ps.rcPaint_top, ps.rcPaint_right, ps.rcPaint_bottom); // // If we get a BeginPaint with an empty rect then check // if this is a special layered, non-redirected window // which would mean we need to do a full paint when it // won't cause a problem. // if (rcPaint.IsEmpty && ((retval & NativeMethods.WS_EX_LAYERED) != 0) && !UnsafeNativeMethods.GetLayeredWindowAttributes(_hWnd.MakeHandleRef(this), IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) && !_isSessionDisconnected && !_isMinimized && !_isSuspended) { rcPaint = new NativeMethods.RECT( 0, 0, _hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left, _hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top); } AdjustForRightToLeft(ref rcPaint, handleRef); if (!rcPaint.IsEmpty) { InvalidateRect(rcPaint); } UnsafeNativeMethods.EndPaint(_hWnd.MakeHandleRef(this), ref ps); } ////// Critical - 1) This method exposes the automation object which can be used to /// query the system for critical information or spoof input. /// 2) it Asserts to call ReturnRawElementProvider /// [SecurityCritical] private static IntPtr CriticalHandleWMGetobject(IntPtr wparam, IntPtr lparam, Visual root, IntPtr handle) { try { if (root == null) { // Valid case, but need to handle separately. For now, return 0 to avoid exceptions // in referencing this later on. Real solution is more complex, see WindowsClient#873800. return IntPtr.Zero; } AutomationPeer peer = null; if (root.CheckFlagsAnd(VisualFlags.IsUIElement)) { UIElement uiroot = (UIElement)root; peer = UIElementAutomationPeer.CreatePeerForElement(uiroot); //there si no specific peer for this UIElement, create a generic root if(peer == null) peer = uiroot.CreateGenericRootAutomationPeer(); if(peer != null) peer.Hwnd = handle; } // This can happen if the root visual is not UIElement. In this case, // attempt to find one in the visual tree. if (peer == null) { peer = UIElementAutomationPeer.GetRootAutomationPeer(root, handle); } if (peer == null) { return IntPtr.Zero; } // get the element proxy // it's ok to pass the same peer as reference connected peer here because // it's guaranteed to be a connected one (it's initialized as root already) IRawElementProviderSimple el = ElementProxy.StaticWrap(peer, peer); peer.AddToAutomationEventList(); // The assert here is considered OK // as we're assuming the WM_GETOBJECT is coming only from a PostMessage of an Hwnd. // to do the post message - you needed to have Unmanaged code permission // PermissionSet unpackPermissionSet = new PermissionSet(PermissionState.None); // below permissions needed to unpack an object. unpackPermissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.UnmanagedCode | SecurityPermissionFlag.RemotingConfiguration)); unpackPermissionSet.AddPermission(new System.Net.DnsPermission(PermissionState.Unrestricted)); unpackPermissionSet.AddPermission(new System.Net.SocketPermission(PermissionState.Unrestricted)); unpackPermissionSet.Assert(); try { return AutomationInteropProvider.ReturnRawElementProvider(handle, wparam, lparam, el); } finally { CodeAccessPermission.RevertAll(); } } #pragma warning disable 56500 catch (Exception e) { if(CriticalExceptions.IsCriticalException(e)) { throw; } return new IntPtr(Marshal.GetHRForException(e)); } #pragma warning restore 56500 } ////// Adjusts a RECT to compensate for Win32 RTL conversion logic /// ////// When a window is marked with the WS_EX_LAYOUTRTL style, Win32 /// mirrors the coordinates during the various translation APIs. /// /// Avalon also sets up mirroring transforms so that we properly /// mirror the output since we render to DirectX, not a GDI DC. /// /// Unfortunately, this means that our coordinates are already mirrored /// by Win32, and Avalon mirrors them again. To solve this /// problem, we un-mirror the coordinates from Win32 before painting /// in Avalon. /// /// /// The RECT to be adjusted /// /// /// internal void AdjustForRightToLeft(ref NativeMethods.RECT rc, HandleRef handleRef) { int windowStyle = SafeNativeMethods.GetWindowStyle(handleRef, true); if(( windowStyle & NativeMethods.WS_EX_LAYOUTRTL ) == NativeMethods.WS_EX_LAYOUTRTL) { NativeMethods.RECT rcClient = new NativeMethods.RECT(); SafeNativeMethods.GetClientRect(handleRef, ref rcClient); int width = rc.right - rc.left; // preserve width rc.right = rcClient.right - rc.left; // set right of rect to be as far from right of window as left of rect was from left of window rc.left = rc.right - width; // restore width by adjusting left and preserving right } } ////// Force total re-rendering to handle system parameters change /// (font smoothing settings, gamma correction, etc.) /// ///true if rerendering was forced ////// Critical: This can be used to cause annoyance by causing re rendering /// [SecurityCritical] private bool OnSettingChange(Int32 firstParam) { if ( (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHING || (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHINGTYPE || (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHINGCONTRAST || (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHINGORIENTATION || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYPIXELSTRUCTURE || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYGAMMA || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYCLEARTYPELEVEL || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYTEXTCONTRASTLEVEL ) { HRESULT.Check(MILUpdateSystemParametersInfo.Update()); return true; } return false; } ////// Let the DWM know of this HWND as belonging to a MIL/WPF application. /// Force total re-rendering to handle system Desktop Window Manager change /// ////// Critical: This can be used to cause annoyance by trying to cause re rendering. /// [SecurityCritical] internal void OnDWMCompositionChanged(bool isDesktopCompositionEnabled) { // // Hint window redirection layer that Avalon content has been attached // to this window. As a result, redirection layer will create necessary // resources and manage clip and position for this window in TS and // Accessibility scenarios. // // We're going to attempt to attach to DWM every time the desktop composition // state changes to ensure that we properly handle DWM crashing/restarting/etc. // HRESULT.Check(MilContent_AttachToHwnd(_hWnd)); // HRESULT.Check(MILUpdateSystemParametersInfo.Update()); UnsafeNativeMethods.InvalidateRect(_hWnd.MakeHandleRef(this), IntPtr.Zero , true); } ////// This function should be called to paint the specified /// region of the window along with any other pending /// changes. While this function is generally called /// in response to a WM_PAINT it is up to the user to /// call BeginPaint and EndPaint or to otherwise validate /// the bitmap region. /// /// The rectangle that is dirty. private void InvalidateRect(NativeMethods.RECT rcDirty) { DUCE.ChannelSet channelSet = MediaContext.From(Dispatcher).GetChannels(); DUCE.Channel channel = channelSet.Channel; DUCE.Channel outOfBandChannel = channelSet.OutOfBandChannel; // handle InvalidateRect requests only if we have uce resources. if (_compositionTarget.IsOnChannel(channel)) { // // Send a message with the invalid region to the compositor. We create a little batch to send this // out of order. // DUCE.CompositionTarget.Invalidate( _compositionTarget.GetHandle(outOfBandChannel), ref rcDirty, outOfBandChannel); } } ////// Calling this function causes us to update state to reflect a /// size change of the underlying HWND /// ////// Critical - accesses a critical member (_hwnd) /// TreatAsSafe - uses the _hwnd to call a safeNativeMethods. Data is cached - but not considered critical. /// [SecurityCritical, SecurityTreatAsSafe ] private void OnResize() { #if DEBUG MediaTrace.HwndTarget.Trace("OnResize"); #endif // handle OnResize requests only if we have uce resources. if (_compositionTarget.IsOnAnyChannel) { MediaContext mctx = MediaContext.From(Dispatcher); // // Let the render target know that window size has changed. // UpdateWindowSettings(); // // Push client size chnage to the visual target. // Rect clientRect = new Rect( 0, 0, (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left))), (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top)))); StateChangedCallback( new object[] { HostStateFlags.ClipBounds, null, clientRect }); mctx.Resize(this); // To ensure that the client area and the non-client area resize // together, we need to wait, on resize, for the composition // engine to present the resized frame. The call to CompleteRender // blocks until that happens. mctx.CompleteRender(); } } ////// Calculates the client and window rectangle in screen coordinates. /// ////// Critical - calls critical methods (ClientToScreen) /// TreatAsSafe - no information returned, coordinates stored in member field. /// [SecurityCritical, SecurityTreatAsSafe] private void GetWindowRectsInScreenCoordinates() { NativeMethods.RECT rcClient = new NativeMethods.RECT(); // // Get the window and client rectangles // SafeNativeMethods.GetWindowRect(_hWnd.MakeHandleRef(this), ref _hwndWindowRectInScreenCoords); SafeNativeMethods.GetClientRect(_hWnd.MakeHandleRef(this), ref rcClient); NativeMethods.POINT ptClientTopLeft = new NativeMethods.POINT(rcClient.left, rcClient.top); UnsafeNativeMethods.ClientToScreen(_hWnd.MakeHandleRef(this), ptClientTopLeft); NativeMethods.POINT ptClientBottomRight = new NativeMethods.POINT(rcClient.right, rcClient.bottom); UnsafeNativeMethods.ClientToScreen(_hWnd.MakeHandleRef(this), ptClientBottomRight); if(ptClientBottomRight.x >= ptClientTopLeft.x) { _hwndClientRectInScreenCoords.left = ptClientTopLeft.x; _hwndClientRectInScreenCoords.right = ptClientBottomRight.x; } else { // RTL windows will cause the right edge to be on the left... _hwndClientRectInScreenCoords.left = ptClientBottomRight.x; _hwndClientRectInScreenCoords.right = ptClientTopLeft.x; } if(ptClientBottomRight.y >= ptClientTopLeft.y) { _hwndClientRectInScreenCoords.top = ptClientTopLeft.y; _hwndClientRectInScreenCoords.bottom = ptClientBottomRight.y; } else { // RTL windows will cause the right edge to be on the left... // This doesn't affect top/bottom, but the code should be symmetrical. _hwndClientRectInScreenCoords.top = ptClientBottomRight.y; _hwndClientRectInScreenCoords.bottom = ptClientTopLeft.y; } // } ////// Critical - accepts an unmanaged pointer to a structure /// [SecurityCritical] private void OnWindowPosChanging(IntPtr lParam) { _windowPosChanging = true; UpdateWindowPos(lParam); } ////// Critical - accepts an unmanaged pointer to a structure /// [SecurityCritical] private void OnWindowPosChanged(IntPtr lParam) { _windowPosChanging = false; UpdateWindowPos(lParam); } ////// Critical - accepts an unmanaged pointer to a structure /// [SecurityCritical] private void UpdateWindowPos(IntPtr lParam) { // // We need to update the window settings used by the render thread when // 1) The size or position of the render target needs to change // 2) The render target needs to be enabled or disabled. // // Further, we need to synchronize the render thread during sizing operations. // This is because some APIs that the render thread uses (such as // UpdateLayeredWindow) have the unintended side-effect of also changing the // window size. We can't let the render thread and the UI thread fight // over setting the window size. // // Generally, Windows sends our window to messages that bracket the size // operation: // 1) WM_WINDOWPOSCHANGING // Here we synchronize with the render thread, and ask the render thread // to not render to this window for a while. // 2) WM_WINDOWPOSCHANGED // This is after the window size has actually been changed, so we tell // the render thread that it can render to the window again. // // However, there are complications. Sometimes Windows will send a // WM_WINDOWPOSCHANGING without sending a WM_WINDOWPOSCHANGED. This happens // when the window size is not really going to change. Also note that // more than just size/position information is provided by these messages. // We'll get these messages when nothing but the z-order changes for instance. // // // The first order of business is to determine if the render target // size or position changed. If so, we need to pass this information to // the render thread. // NativeMethods.WINDOWPOS windowPos = (NativeMethods.WINDOWPOS)UnsafeNativeMethods.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS)); bool isMove = (windowPos.flags & NativeMethods.SWP_NOMOVE) == 0; bool isSize = (windowPos.flags & NativeMethods.SWP_NOSIZE) == 0; bool positionChanged = (isMove || isSize); if (positionChanged) { // // We have found that sometimes we get told that the size or position // of the window has changed, when it really hasn't. So we double // check here. This is critical because we won't be given a // WM_WINDOWPOSCHANGED unless the size or position really had changed. // if (!isMove) { // This is just to avoid any possible integer overflow problems. windowPos.x = windowPos.y = 0; } if (!isSize) { // This is just to avoid any possible integer overflow problems. windowPos.cx = windowPos.cy = 0; } // // WINDOWPOS stores the window coordinates relative to its parent. // If the parent is NULL, then these are already screen coordinates. // Otherwise, we need to convert to screen coordinates. // NativeMethods.RECT windowRectInScreenCoords = new NativeMethods.RECT(windowPos.x, windowPos.y, windowPos.x + windowPos.cx, windowPos.y + windowPos.cy); IntPtr hwndParent = UnsafeNativeMethods.GetParent(new HandleRef(null, windowPos.hwnd)); if(hwndParent != IntPtr.Zero) { SafeSecurityHelper.TransformLocalRectToScreen(new HandleRef(null, hwndParent), ref windowRectInScreenCoords); } if (!isMove) { // We weren't actually moving, so the WINDOWPOS structure // did not contain valid (x,y) information. Just use our // old values. int width = (windowRectInScreenCoords.right - windowRectInScreenCoords.left); int height = (windowRectInScreenCoords.bottom - windowRectInScreenCoords.top); windowRectInScreenCoords.left = _hwndWindowRectInScreenCoords.left; windowRectInScreenCoords.right = windowRectInScreenCoords.left + width; windowRectInScreenCoords.top = _hwndWindowRectInScreenCoords.top; windowRectInScreenCoords.bottom = windowRectInScreenCoords.top + height; } if (!isSize) { // We weren't actually sizing, so the WINDOWPOS structure // did not contain valid (cx,cy) information. Just use our // old values. int width = (_hwndWindowRectInScreenCoords.right - _hwndWindowRectInScreenCoords.left); int height = (_hwndWindowRectInScreenCoords.bottom - _hwndWindowRectInScreenCoords.top); windowRectInScreenCoords.right = windowRectInScreenCoords.left + width; windowRectInScreenCoords.bottom = windowRectInScreenCoords.top + height; } positionChanged = ( _hwndWindowRectInScreenCoords.left != windowRectInScreenCoords.left || _hwndWindowRectInScreenCoords.top != windowRectInScreenCoords.top || _hwndWindowRectInScreenCoords.right != windowRectInScreenCoords.right || _hwndWindowRectInScreenCoords.bottom != windowRectInScreenCoords.bottom); } // // The second order of business is to determine whether or not the render // target should be enabled. If we are disabling the render target, then // we need to synchronize with the render thread. Basically, // a WM_WINDOWPOSCHANGED always enables the render target it the window is // visible. And a WM_WINDOWPOSCHANGING will disable the render target // unless it is not really a size/move, in which case we will not be sent // a WM_WINDOWPOSCHANGED, so we can't disable the render target. // bool enableRenderTarget = SafeNativeMethods.IsWindowVisible(_hWnd.MakeHandleRef(this)); if(enableRenderTarget) { if(_windowPosChanging && (positionChanged)) { enableRenderTarget = false; } } if (positionChanged || (enableRenderTarget != _isRenderTargetEnabled)) { UpdateWindowSettings(enableRenderTarget); } } bool _windowPosChanging; private void OnShowWindow(bool enableRenderTarget) { if (enableRenderTarget != _isRenderTargetEnabled) { UpdateWindowSettings(enableRenderTarget); } } private void OnExitSizeMove() { if (_windowPosChanging) { _windowPosChanging = false; UpdateWindowSettings(true); } } private void UpdateWindowSettings() { UpdateWindowSettings(_isRenderTargetEnabled, null); } private void UpdateWindowSettings(bool enableRenderTarget) { UpdateWindowSettings(enableRenderTarget, null); } ////// Critical - calls critical methods (UpdateWindowSettings) /// TreatAsSafe - just updates composition information for this window, /// does not accept arbitrary input. /// [SecurityCritical, SecurityTreatAsSafe] private void UpdateWindowSettings(bool enableRenderTarget, DUCE.ChannelSet? channelSet) { MediaContext mctx = MediaContext.From(Dispatcher); if (_isRenderTargetEnabled != enableRenderTarget) { _isRenderTargetEnabled = enableRenderTarget; // Basic idea: the render thread and the UI thread have a // race condition when the UI thread wants to modify // HWND data and the render thread is using it. The render // thread can paint garbage on the screen, and it can also // cause the old data to be set again (ULW issue, hence ULWEx). // // So we tell the render thread to stop rendering and then we // wait for them to stop when disabling the render target by // issuing the UpdateWindowSettings command synchronously on // an out-of-band channel. } // if we are disconnected we are done. if (!_compositionTarget.IsOnAnyChannel) { return; } // // Calculate the client rectangle in screen coordinates. // GetWindowRectsInScreenCoordinates(); Int32 style = UnsafeNativeMethods.GetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_STYLE); Int32 exStyle = UnsafeNativeMethods.GetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_EXSTYLE); bool isLayered = (exStyle & NativeMethods.WS_EX_LAYERED) != 0; bool isChild = (style & NativeMethods.WS_CHILD) != 0; bool isRTL = (exStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0; int width = _hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left; int height = _hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top; MILTransparencyFlags flags = MILTransparencyFlags.Opaque; // if (!DoubleUtil.AreClose(_opacity, 1.0)) // { // flags |= MILTransparencyFlags.ConstantAlpha; // } // if (_colorKey.HasValue) // { // flags |= MILTransparencyFlags.ColorKey; // } if (_usesPerPixelOpacity) { flags |= MILTransparencyFlags.PerPixelAlpha; } if (!isLayered && flags != MILTransparencyFlags.Opaque) { // The window is not layered, but it should be -- set the layered flag. UnsafeNativeMethods.SetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_EXSTYLE, new IntPtr(exStyle | NativeMethods.WS_EX_LAYERED)); } else if (isLayered && flags == MILTransparencyFlags.Opaque) { // The window is layered but should not be -- unset the layered flag. UnsafeNativeMethods.SetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_EXSTYLE, new IntPtr(exStyle & ~NativeMethods.WS_EX_LAYERED)); } else if(isLayered && flags != MILTransparencyFlags.Opaque && _isRenderTargetEnabled && (width == 0 || height == 0)) { // The window is already layered, and it should be. But we are enabling a window // that is has a 0-size dimension. This may cause us to leave the last sprite // on the screen. The best way to get rid of this is to just make the entire // sprite transparent. NativeMethods.BLENDFUNCTION blend = new NativeMethods.BLENDFUNCTION(); blend.BlendOp = NativeMethods.AC_SRC_OVER; blend.SourceConstantAlpha = 0; // transparent UnsafeNativeMethods.UpdateLayeredWindow(_hWnd.h, IntPtr.Zero, null, null, IntPtr.Zero, null, 0, ref blend, NativeMethods.ULW_ALPHA); } isLayered = (flags != MILTransparencyFlags.Opaque); // Every UpdateWindowSettings command that disables the render target is // assigned a new cookie. Every UpdateWindowSettings command that enables // the render target uses the most recent cookie. This allows the // compositor to ignore UpdateWindowSettings(enable) commands that come // out of order due to us disabling out-of-band and enabling in-band. if (!_isRenderTargetEnabled) { _disableCookie++; } if (channelSet == null) { channelSet = mctx.GetChannels(); } // // When enabling the render target, stay in-band. This allows any // client-side rendering instructions to be included in the same packet. // Otherwise pass in the OutOfBand handle. // DUCE.Channel channel = channelSet.Value.Channel; DUCE.Channel outOfBandChannel = channelSet.Value.OutOfBandChannel; DUCE.CompositionTarget.UpdateWindowSettings( _isRenderTargetEnabled ? _compositionTarget.GetHandle(channel) : _compositionTarget.GetHandle(outOfBandChannel), _hwndClientRectInScreenCoords, Colors.Transparent, // _colorKey.GetValueOrDefault(Colors.Black), 1.0f, // (float)_opacity, isLayered ? (_usesPerPixelOpacity ? MILWindowLayerType.ApplicationManagedLayer : MILWindowLayerType.SystemManagedLayer) : MILWindowLayerType.NotLayered, flags, isChild, isRTL, _isRenderTargetEnabled, _disableCookie, _isRenderTargetEnabled ? channel : outOfBandChannel); if (_isRenderTargetEnabled) { // // Re-render the visual tree. // mctx.PostRender(); } else { // If we disabled the render target, we run the risk of leaving it disabled. // One such example is when a window is programatically sized, but then // GetMinMaxInfo denies the change. We do not receive any message that would // allow us to re-enable the render targer. To cover these odd cases, we // post ourselves a message to possible re-enable the render target when // we are done with the current message processing. UnsafeNativeMethods.PostMessage(new HandleRef(this, _hWnd), s_updateWindowSettings, IntPtr.Zero, IntPtr.Zero); } } ////// Gets and sets the root Visual of this HwndTarget. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This code accesses HWND which is critical and setting RootVisual /// is critical /// PublicOK: This code blocks inheritance and public callers via Inheritance (in base class) and link demands /// public override Visual RootVisual { [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] set { base.RootVisual = value; if (value != null) { // UIAutomation listens for the EventObjectUIFragmentCreate WinEvent to // understand when UI that natively implements UIAutomation comes up // UnsafeNativeMethods.NotifyWinEvent(UnsafeNativeMethods.EventObjectUIFragmentCreate, _hWnd.MakeHandleRef(this), 0, 0); } } } ////// Returns matrix that can be used to transform coordinates from this /// target to the rendering destination device. /// public override Matrix TransformToDevice { get { VerifyAPIReadOnly(); Matrix m = Matrix.Identity; m.Scale(_devicePixelsPerInchX / 96.0, _devicePixelsPerInchY / 96.0); return m; } } ////// Returns matrix that can be used to transform coordinates from /// the rendering destination device to this target. /// public override Matrix TransformFromDevice { get { VerifyAPIReadOnly(); Matrix m = Matrix.Identity; m.Scale(96.0 / _devicePixelsPerInchX, 96.0 / _devicePixelsPerInchY); return m; } } ////// This is the color that is drawn before everything else. If /// this color has an alpha component other than 1 it will be ignored. /// public Color BackgroundColor { get { VerifyAPIReadOnly(); return _backgroundColor; } set { VerifyAPIReadWrite(); if (_backgroundColor != value) { _backgroundColor = value; MediaContext mctx = MediaContext.From(Dispatcher); DUCE.ChannelSet channelSet = mctx.GetChannels(); DUCE.Channel channel = channelSet.Channel; if (channel == null) { // MediaContext is in disconnected state, so we will send // the clear color when CreateUCEResources gets called Debug.Assert(!_compositionTarget.IsOnChannel(channel)); } else { DUCE.CompositionTarget.SetClearColor( _compositionTarget.GetHandle(channel), _backgroundColor, channel); mctx.PostRender(); } } } } // ///// /// Specifies the color to display as transparent. // /// // ///// /// Use null to indicate that no color should be transparent. // /// // public NullableColorKey // { // get // { // VerifyAPIReadOnly(); // // return _colorKey; // } // // set // { // VerifyAPIReadWrite(); // // if(_colorKey != value) // { // _colorKey = value; // // UpdateWindowSettings(); // } // } // } // /// // /// 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 // { // VerifyAPIReadOnly(); // // return _opacity; // } // // set // { // VerifyAPIReadWrite(); // // if(value < 0.0) value = 0.0; // if(value > 1.0) value = 1.0; // // if(!MS.Internal.DoubleUtil.AreClose(value, _opacity)) // { // _opacity = value; // // UpdateWindowSettings(); // } // } // } ////// 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 - accesses critical _hWnd, calls unmanaged code /// PublicOK - internal property, hwnd not exposed or modified, WTSRegisterSessionNotification /// only results in the hwnd getting more messages that we use to enable/disable /// rendering /// public bool UsesPerPixelOpacity { get { VerifyAPIReadOnly(); return _usesPerPixelOpacity; } [SecurityCritical, SecurityTreatAsSafe] internal set { VerifyAPIReadWrite(); if(_usesPerPixelOpacity != value) { _usesPerPixelOpacity = value; // Register/unregister for Fast User Switching messages only if necessary because // this pinvoke loads wtsapi32.dll if (_usesPerPixelOpacity) { UnsafeNativeMethods.WTSRegisterSessionNotification(_hWnd, NativeMethods.NOTIFY_FOR_THIS_SESSION); } else { UnsafeNativeMethods.WTSUnRegisterSessionNotification(_hWnd); } UpdateWindowSettings(); } } } ////// Tells a channel to send notifications to a particular target's window. /// /// /// The channel from which we want notifications. /// /// /// The target whose window will receive the notifications. /// ////// Critical - Calls a critical channel method. /// TreatAsSafe - We are associated with the window handle that we /// are passing to the channel, so somebody already /// decided that it's OK for us to interact with that /// window. We also registered a window message so /// that we can avoid collisions with other messages. /// [SecurityCritical, SecurityTreatAsSafe] internal static void SetChannelNotificationWindow(DUCE.Channel channel, HwndTarget target) { channel.SetNotificationWindow(target._hWnd, s_channelNotifyMessage); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation. All rights reserved. // // File: HwndTarget.cs // //----------------------------------------------------------------------------- using System; using System.Diagnostics; using System.Windows.Threading; using System.Threading; using System.Windows; using System.Collections.Generic; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; using System.Windows.Media.Composition; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Security; using MS.Internal; using MS.Internal.Automation; using MS.Win32; using MS.Utility; using Microsoft.Internal; using MS.Internal.PresentationCore; // SecurityHelper using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; #pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace System.Windows.Interop { // This is the internal, more expressive, enum used by the SetRenderingMode method. // See the RenderMode enum and the RenderMode property for the public version. internal enum RenderingMode { Default = MILRTInitializationFlags.MIL_RT_INITIALIZE_DEFAULT, Software = MILRTInitializationFlags.MIL_RT_SOFTWARE_ONLY, Hardware = MILRTInitializationFlags.MIL_RT_HARDWARE_ONLY, HardwareReference = MILRTInitializationFlags.MIL_RT_HARDWARE_ONLY | MILRTInitializationFlags.MIL_RT_USE_REF_RAST, } // This is the public, more limited, enum exposed for use with the RenderMode property. // See the RenderingMode enum and SetRenderingMode method for the internal version. ////// Render mode preference. /// public enum RenderMode { ////// The rendering layer should use the GPU and CPU as appropriate. /// Default, ////// The rendering layer should only use the CPU. /// SoftwareOnly } ////// The HwndTarget class represents a binding to an HWND. /// ///The HwndTarget is not thread-safe. Accessing the HwndTarget from a different /// thread than it was created will throw a public class HwndTarget : CompositionTarget { ///. /// Critical - Gets to the unmanaged layer under elevation (cctor, ctor, HandleMessage). /// [SecurityCritical] private static int s_dwmRedirectionEnvironmentChanged; ////// Critical - Gets to the unmanaged layer under elevation (cctor, ctor, SetChannelNotificationWindow). /// [SecurityCritical] private static int s_channelNotifyMessage; ////// Critical - Gets to the unmanaged layer under elevation (cctor, ctor, HandleMessage). /// [SecurityCritical] private static int s_transformHintUpdateMessage; ////// Critical - Gets to the unmanaged layer (ctor, HandleMessage, UpdateWindowSettings). /// [SecurityCritical] private static int s_updateWindowSettings; private MatrixTransform _worldTransform; private double _devicePixelsPerInchX; private double _devicePixelsPerInchY; ////// Critical - We don't want partial trust code changing the rendering preference. /// private SecurityCriticalDataForSet_renderModePreference = new SecurityCriticalDataForSet (RenderMode.Default); /// /// Critical - obtained under an elevation. /// [SecurityCritical] private NativeMethods.HWND _hWnd; private NativeMethods.RECT _hwndClientRectInScreenCoords; private NativeMethods.RECT _hwndWindowRectInScreenCoords; private Color _backgroundColor = Color.FromRgb(0, 0, 0); private DUCE.MultiChannelResource _compositionTarget = new DUCE.MultiChannelResource(); private bool _isRenderTargetEnabled = true; // private Nullable_colorKey = null; // private double _opacity = 1.0; private bool _usesPerPixelOpacity = false; // It is important that this start at zero to allow an initial // UpdateWindowSettings(enable) command to enable the render target // without a preceeding UpdateWindowSettings(disable) command. private int _disableCookie = 0; // Used to deal with layered window problems. See comments where they are used. private bool _isMinimized = false; private bool _isSessionDisconnected = false; private bool _isSuspended = false; /// /// Initializes static variables for this class. /// ////// Critical - Sets the SecurityCritical static variables holding the message ids; calls RegisterWindowMessage. /// TreatAsSafe - The message ids are not exposed; no external parameters are taken in. /// [SecurityCritical, SecurityTreatAsSafe] static HwndTarget() { s_dwmRedirectionEnvironmentChanged = UnsafeNativeMethods.RegisterWindowMessage("DwmRedirectionEnvironmentChangedHint"); s_channelNotifyMessage = UnsafeNativeMethods.RegisterWindowMessage("MilChannelNotify"); s_transformHintUpdateMessage = UnsafeNativeMethods.RegisterWindowMessage("TransformHintUpdate"); s_updateWindowSettings = UnsafeNativeMethods.RegisterWindowMessage("UpdateWindowSettings"); } ////// Attaches a hwndTarget to the hWnd /// /// The HWND to which the HwndTarget will draw. ////// This API link demands for UIWindowPermission.AllWindows /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - accepts unmanaged pointer handle.Not safe to create since it /// can be used to draw to a window /// PublicOk - demands UIPermission /// [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand,Window=UIPermissionWindow.AllWindows)] public HwndTarget(IntPtr hwnd) { bool exceptionThrown = true; AttachToHwnd(hwnd); try { if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.MEDIACREATEVISUALGUID), MS.Utility.EventType.Info, Dispatcher.GetHashCode(), hwnd.ToInt64()); } _hWnd = NativeMethods.HWND.Cast(hwnd); // Get the client rectangle... GetWindowRectsInScreenCoordinates(); // Set device independent resolution to 96 DPI. // NativeMethods.HDC hdc = NativeMethods.GetDC(_hWnd); _devicePixelsPerInchX = 96.0f; _devicePixelsPerInchY = 96.0f; // // Determine whether hWnd corresponds to MIL or GDI window. // IntPtr hdcW = UnsafeNativeMethods.GetDC( new HandleRef(this, _hWnd)); if (hdcW == IntPtr.Zero) { // // If we were unable to obtain HDC for the given // window, assume the default of 96 DPI. // _devicePixelsPerInchX = 96.0f; _devicePixelsPerInchY = 96.0f; } else { // // Obtain and cache DPI values for window's HDC. // _devicePixelsPerInchX = (double)UnsafeNativeMethods.GetDeviceCaps( new HandleRef(this, hdcW), NativeMethods.LOGPIXELSX); _devicePixelsPerInchY = (double)UnsafeNativeMethods.GetDeviceCaps( new HandleRef(this, hdcW), NativeMethods.LOGPIXELSY); // // Release DC object. // UnsafeNativeMethods.ReleaseDC( new HandleRef(this, _hWnd), new HandleRef(this, hdcW)); } _worldTransform = new MatrixTransform(new Matrix( _devicePixelsPerInchX * (1.0f / 96.0f), 0, 0, _devicePixelsPerInchY * (1.0f / 96.0f), 0, 0)); // // Register CompositionTarget with MediaContext. // MediaContext.RegisterICompositionTarget(Dispatcher, this); // // Change the window message filter to allow session and transform // hint control messages to be sent from the DWM. On down-level // platforms, these calls will result in no-op. // ChangeWindowMessageFilter((uint)s_dwmRedirectionEnvironmentChanged, 1 /* MSGFLT_ADD */); ChangeWindowMessageFilter((uint)s_transformHintUpdateMessage, 1 /* MSGFLT_ADD */); // // Hint window redirection layer that Avalon content has been attached // to this window. As a result, redirection layer will create necessary // resources and manage clip and position for this window in TS and // Accessibility scenarios. If DWM is not running, this call will result // in NoOp. // HRESULT.Check(MilContent_AttachToHwnd(_hWnd)); exceptionThrown = false; } finally { // // If exception has occurred after we attached this target to // the window, we need to detach from this window. Otherwise, window // will be left in a state when no other HwndTarget can be created // for it. // if(exceptionThrown) { #pragma warning suppress 6031 // Return value ignored on purpose. VisualTarget_DetachFromHwnd(hwnd); } } } ////// AttachToHwnd /// ////// Critical as it calls a function that performs an elevation (IsWindow). /// [SecurityCritical] internal void AttachToHwnd(IntPtr hwnd) { int processId = 0; int threadId = UnsafeNativeMethods.GetWindowThreadProcessId( new HandleRef(this, hwnd), out processId ); if (!UnsafeNativeMethods.IsWindow(new HandleRef(this, hwnd))) { throw new ArgumentException( SR.Get(SRID.HwndTarget_InvalidWindowHandle), "hwnd" ); } else if (processId != SafeNativeMethods.GetCurrentProcessId()) { throw new ArgumentException( SR.Get(SRID.HwndTarget_InvalidWindowProcess), "hwnd" ); } else if (threadId != SafeNativeMethods.GetCurrentThreadId()) { throw new ArgumentException( SR.Get(SRID.HwndTarget_InvalidWindowThread), "hwnd" ); } int hr = VisualTarget_AttachToHwnd(hwnd); if (HRESULT.Failed(hr)) { if (hr == unchecked((int)0x80070005)) // E_ACCESSDENIED { throw new InvalidOperationException( SR.Get(SRID.HwndTarget_WindowAlreadyHasContent) ); } else { HRESULT.Check(hr); } } } ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical,SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore, EntryPoint = "MilVisualTarget_AttachToHwnd")] internal static extern int VisualTarget_AttachToHwnd( IntPtr hwnd ); ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical, SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore, EntryPoint = "MilVisualTarget_DetachFromHwnd")] internal static extern int VisualTarget_DetachFromHwnd( IntPtr hwnd ); ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical, SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore)] internal static extern int MilContent_AttachToHwnd( IntPtr hwnd ); ////// Critical: This code causes unmanaged code elevation /// [SecurityCritical, SuppressUnmanagedCodeSecurity] [DllImport(DllImport.MilCore)] internal static extern int MilContent_DetachFromHwnd( IntPtr hwnd ); ////// Allow lower integrity applications to send specified window messages /// in case we are elevated. Failure is non-fatal and on down-level /// platforms this call will result in a no-op. /// ////// Critical -- Calls unsafe native methods GetModuleHandle and GetProcAddress. /// Manually elevates unmanaged code permissions to pinvoke through /// a function pointer. /// [SecurityCritical] private void ChangeWindowMessageFilter(uint message, uint flag) { // Find the address of ChangeWindowMessageFilter in user32.dll. IntPtr user32Module = UnsafeNativeMethods.GetModuleHandle("user32.dll"); // Get the address of the function. If this fails it means the OS // doesn't support this function, in which case we don't // need to do anything further. IntPtr functionAddress = UnsafeNativeMethods.GetProcAddressNoThrow( new HandleRef(null, user32Module), "ChangeWindowMessageFilter"); if (functionAddress != IntPtr.Zero) { // Convert the function pointer into a callable delegate and then call it ChangeWindowMessageFilterNative function = Marshal.GetDelegateForFunctionPointer( functionAddress, typeof(ChangeWindowMessageFilterNative)) as ChangeWindowMessageFilterNative; // In order to call the function we need unmanaged code access, // because the function is native code. (new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert(); try { function(message, flag); } finally { SecurityPermission.RevertAssert(); } } } ////// Prototype for user32's ChangeWindowMessageFilter function, which we load dynamically on Longhorn. /// private delegate void ChangeWindowMessageFilterNative(uint message, uint flag); internal void SetRenderingMode(RenderingMode mode) { // // If ForceLocalTransport is set then the transport is connected to a client (magnifier) that cannot // handle our transport protocol version. Therefore we force software rendering so that the rendered // content is available through NTUser redirection. If software is not allowed an exception is thrown. // if (MediaSystem.ForcedLocalTransport) // present using bitblt if a local transport was forced { if (mode == RenderingMode.Hardware || mode == RenderingMode.HardwareReference) { throw new InvalidOperationException(SR.Get(SRID.HwndTarget_HardwareNotSupportDueToProtocolMismatch)); } else { Debug.Assert(mode == RenderingMode.Software || mode == RenderingMode.Default); // If the mode is default we can chose what works. When we have a mismatched transport protocol version // we need to fallback to software rendering. mode = RenderingMode.Software; } } // Select the render target initialization flags based on the requested // rendering mode. DUCE.ChannelSet channelSet = MediaContext.From(Dispatcher).GetChannels(); DUCE.Channel channel = channelSet.Channel; DUCE.CompositionTarget.SetRenderingMode( _compositionTarget.GetHandle(channel), (MILRTInitializationFlags)mode, channel); } ////// Specifies the render mode preference for the window. /// ////// This property specifies a preference, it does not necessarily change the actual /// rendering mode. Among other things, this can be trumped by the registry settings. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to set this property. /// /// Critical: This code influences the low-level rendering code by specifying whether the /// rendering system should use the GPU or CPU. /// PublicOK: We don't want to enable this in partial trust, so we have a link demand /// on the setter. It is not privileged data, so the getter is not protected. /// public RenderMode RenderMode { get { return _renderModePreference.Value; } // Note: We think it is safe to expose this in partial trust, but doing so would suggest // we should also expose HwndSource (the only way to get to the HwndTarget instance). // We don't want to bite off that much exposure at this point in the product, so we enforce // that this is not accessible from partial trust for now. [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] set { if (value != RenderMode.Default && value != RenderMode.SoftwareOnly) { throw new System.ComponentModel.InvalidEnumArgumentException("value", (int)value, typeof(RenderMode)); } _renderModePreference.Value = value; SetRenderingMode(value == RenderMode.SoftwareOnly ? RenderingMode.Software : RenderingMode.Default); } } ////// Dispose cleans up the state associated with HwndTarget. /// ////// Critical - accesses the _hwnd that is critical, calls unmanaged code /// PublicOK - dispose is in effect stoping the contents of the target from /// rendering. Equivalent to removing all elements in window, considered safe. /// [SecurityCritical] public override void Dispose() { // Its outside the try finally block because we want the exception to be // thrown if we are on a different thread and we don't want to call Dispose // on base class in that case. VerifyAccess(); try { // According to spec: Dispose should not raise exception if called multiple times. // This test is needed because the HwndTarget is Disposed from both the media contex and // the hwndsrc. if (!IsDisposed) { RootVisual = null; HRESULT.Check(VisualTarget_DetachFromHwnd(_hWnd)); // // Unregister this CompositionTarget from the MediaSystem. // MediaContext.UnregisterICompositionTarget(Dispatcher, this); // // Hint window redirection layer that Avalon content has been detached // from this window. Redirection layer will then have a chance to clean // up the resources that it created for Avalon hosting. If DWM is not // running, this call will result in NoOp. // HRESULT.Check(MilContent_DetachFromHwnd(_hWnd)); // Unregister for Fast User Switching messages if necessary if (_usesPerPixelOpacity) { UnsafeNativeMethods.WTSUnRegisterSessionNotification(_hWnd); } } } finally { base.Dispose(); } } ////// This method is used to create all uce resources either on Startup or session connect /// ////// Critical - uses unmanaged pointer handle _hWnd /// TreatAsSafe - doesn't return or expose _hWnd /// [SecurityCritical, SecurityTreatAsSafe] internal override void CreateUCEResources(DUCE.Channel channel, DUCE.Channel outOfBandChannel) { // create visual target resources // this forces the creation of the media context if we don't already have one. base.CreateUCEResources(channel, outOfBandChannel); Debug.Assert(!_compositionTarget.IsOnChannel(channel)); Debug.Assert(!_compositionTarget.IsOnChannel(outOfBandChannel)); // // For each HwndTarget we are building some structures in the UCE. // This includes spinning up a UCE render target. We need to commit the // batch for those changes right away, since we need to be able to process // the invalidate packages that we send down on WM_PAINTs. If we don't commit // right away a WM_PAINT can get fired before we get a chance to commit // the batch. // // // First we create the composition target, composition context, and the composition root node. // Note, that composition target will be created out of band because invalidate // command is also sent out of band and that can occur before current channel is committed. // We would like to avoid commiting channel here to prevent visual artifacts. // bool resourceCreated = _compositionTarget.CreateOrAddRefOnChannel(outOfBandChannel, DUCE.ResourceType.TYPE_HWNDRENDERTARGET); Debug.Assert(resourceCreated); _compositionTarget.DuplicateHandle(outOfBandChannel, channel); outOfBandChannel.Commit(); DUCE.CompositionTarget.HwndInitialize( _compositionTarget.GetHandle(channel), _hWnd, _hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left, _hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top, MediaSystem.ForcedLocalTransport, // present using bitblt if a local transport was forced channel ); DUCE.ResourceHandle hWorldTransform = ((DUCE.IResource)_worldTransform).AddRefOnChannel(channel); DUCE.CompositionNode.SetTransform( _contentRoot.GetHandle(channel), hWorldTransform, channel); DUCE.CompositionTarget.SetClearColor( _compositionTarget.GetHandle(channel), _backgroundColor, channel); // // Set initial state on the visual target. // Rect clientRect = new Rect( 0, 0, (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left))), (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top)))); StateChangedCallback( new object[] { HostStateFlags.WorldTransform | HostStateFlags.ClipBounds, _worldTransform.Matrix, clientRect }); DUCE.CompositionTarget.SetRoot( _compositionTarget.GetHandle(channel), _contentRoot.GetHandle(channel), channel); // reset the disable cookie when creating the slave resource. This happens when creating the // managed resource and on handling a connect. _disableCookie = 0; // // Finally, update window settings to reflect the state of this object. // Because CreateUCEResources is called for each channel, only call // UpdateWindowSettings on that channel this time. // DUCE.ChannelSet channelSet; channelSet.Channel = channel; channelSet.OutOfBandChannel = outOfBandChannel; UpdateWindowSettings(_isRenderTargetEnabled, channelSet); } ////// This method is used to release all uce resources either on Shutdown or session disconnect /// ////// Critical - uses unmanaged pointer handle _hWnd /// TreatAsSafe - doesn't return or expose _hWnd /// [SecurityCritical, SecurityTreatAsSafe] internal override void ReleaseUCEResources(DUCE.Channel channel, DUCE.Channel outOfBandChannel) { if (_compositionTarget.IsOnChannel(channel)) { // // GSchneid: If we need to flush the batch we need to render first all visual targets that // are still registered with the MediaContext to avoid strutural tearing. // Set the composition target root node to null. DUCE.CompositionTarget.SetRoot( _compositionTarget.GetHandle(channel), DUCE.ResourceHandle.Null, channel); _compositionTarget.ReleaseOnChannel(channel); } if (_compositionTarget.IsOnChannel(outOfBandChannel)) { _compositionTarget.ReleaseOnChannel(outOfBandChannel); } DUCE.ResourceHandle hWorldTransform = ((DUCE.IResource)_worldTransform).GetHandle(channel); if (!hWorldTransform.IsNull) { // Release the world transform from this channel if it's currently on the channel. ((DUCE.IResource)_worldTransform).ReleaseOnChannel(channel); } // release all the visual target resources. base.ReleaseUCEResources(channel, outOfBandChannel); } ////// The HwndTarget needs to see all windows messages so that /// it can appropriately react to them. /// ////// Critical - accepts unmanaged pointer handle, also /// Elevates permissions via unsafe native methods, handles messages to /// retrieve automation provider the last one is most risky /// [SecurityCritical] internal IntPtr HandleMessage(int msg, IntPtr wparam, IntPtr lparam) { IntPtr handled = new IntPtr(0x1); // return 1 if the message is handled by this method exclusively. IntPtr unhandled = IntPtr.Zero; // return 0 if other win procs should be called. IntPtr result = unhandled; if (msg == s_dwmRedirectionEnvironmentChanged) { System.Windows.Media.MediaSystem.NotifyRedirectionEnvironmentChanged(); return handled; } else if (msg == s_channelNotifyMessage) { MediaContext.From(Dispatcher).NotifyChannelMessage(); } else if(msg == s_updateWindowSettings) { // Make sure we enable the render target if the window is visible. if(SafeNativeMethods.IsWindowVisible(_hWnd.MakeHandleRef(this))) { UpdateWindowSettings(true); } } if (IsDisposed) { return result; } switch (msg) { case NativeMethods.WM_ERASEBKGND: result = handled; // Indicates that this message is handled. break; case NativeMethods.WM_PAINT: DoPaint(); result = handled; break; case NativeMethods.WM_SIZE: // // NTRAID#Longhorn-1946030-2007/03/23-jordanpa: // When locked on downlevel, MIL stops rendering and invalidates the // window causing WM_PAINT. When the window is layered and minimized // before the lock, it'll never get the WM_PAINT on unlock and the MIL will // never get out of the "don't render" state. // // To work around this, we will invalidate ourselves on restore and not // render while minimized. // // If the Window is in minimized state, don't do layout. otherwise, in some cases, it would // pollute the measure data based on the Minized window size. if (NativeMethods.IntPtrToInt32(wparam) != NativeMethods.SIZE_MINIMIZED) { _isMinimized = false; DoPaint(); OnResize(); } else { _isMinimized = true; } break; case NativeMethods.WM_SETTINGCHANGE: if (OnSettingChange(NativeMethods.IntPtrToInt32(wparam))) { UnsafeNativeMethods.InvalidateRect(_hWnd.MakeHandleRef(this), IntPtr.Zero , true); } break; case NativeMethods.WM_GETOBJECT: result = CriticalHandleWMGetobject( wparam, lparam, RootVisual, _hWnd ); break; case NativeMethods.WM_WINDOWPOSCHANGING: OnWindowPosChanging(lparam); break; case NativeMethods.WM_WINDOWPOSCHANGED: OnWindowPosChanged(lparam); break; case NativeMethods.WM_SHOWWINDOW: OnShowWindow(wparam != IntPtr.Zero); break; case NativeMethods.WM_EXITSIZEMOVE: OnExitSizeMove(); break; case NativeMethods.WM_STYLECHANGING: unsafe { NativeMethods.STYLESTRUCT * styleStruct = (NativeMethods.STYLESTRUCT *) lparam; if ((int)wparam == NativeMethods.GWL_EXSTYLE) { if(UsesPerPixelOpacity) { // We need layered composition to accomplish per-pixel opacity. // styleStruct->styleNew |= NativeMethods.WS_EX_LAYERED; } else { // No properties that require layered composition exist. // Make sure the layered bit is off. // // Note: this prevents an external program from making // us system-layered (if we are a top-level window). // // If we are a child window, we still can't stop our // parent from being made system-layered, and we will // end up leaving visual artifacts on the screen under // WindowsXP. // styleStruct->styleNew &= (~NativeMethods.WS_EX_LAYERED); } } } break; case NativeMethods.WM_STYLECHANGED: unsafe { bool updateWindowSettings = false; NativeMethods.STYLESTRUCT * styleStruct = (NativeMethods.STYLESTRUCT *) lparam; if ((int)wparam == NativeMethods.GWL_STYLE) { bool oldIsChild = (styleStruct->styleOld & NativeMethods.WS_CHILD) == NativeMethods.WS_CHILD; bool newIsChild = (styleStruct->styleNew & NativeMethods.WS_CHILD) == NativeMethods.WS_CHILD; updateWindowSettings = (oldIsChild != newIsChild); } else { bool oldIsRTL = (styleStruct->styleOld & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL; bool newIsRTL = (styleStruct->styleNew & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL; updateWindowSettings = (oldIsRTL != newIsRTL); } if(updateWindowSettings) { UpdateWindowSettings(); } } break; // // NTRAID#Longhorn-1967619-2007/03/23-jordanpa: // When a Fast User Switch happens, MIL gets an invalid display error when trying to // render and they invalidate the window resulting in us getting a WM_PAINT. For // layered windows, we get the WM_PAINT immediately which causes us to // tell MIL to render and the cycle repeats. On Vista, this creates an infinite loop. // Downlevel there isn't a loop, but the layered window will never update again. // // To work around this problem, we'll make sure not to tell MIL to render when // we're switched out and will render on coming back. // case NativeMethods.WM_WTSSESSION_CHANGE: switch (NativeMethods.IntPtrToInt32(wparam)) { // Session is disconnected. Due to: // 1. Switched to a different user // 2. TS logoff // 3. Screen locked case NativeMethods.WTS_CONSOLE_DISCONNECT: case NativeMethods.WTS_REMOTE_DISCONNECT: case NativeMethods.WTS_SESSION_LOCK: _isSessionDisconnected = true; break; // Session is reconnected. See above case NativeMethods.WTS_CONSOLE_CONNECT: case NativeMethods.WTS_REMOTE_CONNECT: case NativeMethods.WTS_SESSION_UNLOCK: _isSessionDisconnected = false; DoPaint(); break; default: break; } break; // // NTRAID#Longhorn-1975236-2007/05/22-jordanpa: // Downlevel, if we try to present a layered window while suspended the app will crash. // This has been fixed in Vista but we still need to work around it for older versions // by not invalidating while suspended. // case NativeMethods.WM_POWERBROADCAST: switch (NativeMethods.IntPtrToInt32(wparam)) { case NativeMethods.PBT_APMSUSPEND: _isSuspended = true; break; case NativeMethods.PBT_APMRESUMESUSPEND: case NativeMethods.PBT_APMRESUMECRITICAL: case NativeMethods.PBT_APMRESUMEAUTOMATIC: _isSuspended = false; DoPaint(); break; default: break; } break; default: break; } return result; } ////// Paints a rect /// /// Note: This gets called a lot to help with layered window problems even when /// the window isn't layered, but that's okay because rcPaint will be empty. /// /// ////// Critical - Elevates permissions via unsafe native methods, calls into begin paint /// [SecurityCritical] private void DoPaint() { NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); NativeMethods.HDC hdc; HandleRef handleRef = new HandleRef(this, _hWnd); hdc.h = UnsafeNativeMethods.BeginPaint(handleRef, ref ps); int retval = UnsafeNativeMethods.GetWindowLong(handleRef, NativeMethods.GWL_EXSTYLE); NativeMethods.RECT rcPaint = new NativeMethods.RECT(ps.rcPaint_left, ps.rcPaint_top, ps.rcPaint_right, ps.rcPaint_bottom); // // If we get a BeginPaint with an empty rect then check // if this is a special layered, non-redirected window // which would mean we need to do a full paint when it // won't cause a problem. // if (rcPaint.IsEmpty && ((retval & NativeMethods.WS_EX_LAYERED) != 0) && !UnsafeNativeMethods.GetLayeredWindowAttributes(_hWnd.MakeHandleRef(this), IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) && !_isSessionDisconnected && !_isMinimized && !_isSuspended) { rcPaint = new NativeMethods.RECT( 0, 0, _hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left, _hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top); } AdjustForRightToLeft(ref rcPaint, handleRef); if (!rcPaint.IsEmpty) { InvalidateRect(rcPaint); } UnsafeNativeMethods.EndPaint(_hWnd.MakeHandleRef(this), ref ps); } ////// Critical - 1) This method exposes the automation object which can be used to /// query the system for critical information or spoof input. /// 2) it Asserts to call ReturnRawElementProvider /// [SecurityCritical] private static IntPtr CriticalHandleWMGetobject(IntPtr wparam, IntPtr lparam, Visual root, IntPtr handle) { try { if (root == null) { // Valid case, but need to handle separately. For now, return 0 to avoid exceptions // in referencing this later on. Real solution is more complex, see WindowsClient#873800. return IntPtr.Zero; } AutomationPeer peer = null; if (root.CheckFlagsAnd(VisualFlags.IsUIElement)) { UIElement uiroot = (UIElement)root; peer = UIElementAutomationPeer.CreatePeerForElement(uiroot); //there si no specific peer for this UIElement, create a generic root if(peer == null) peer = uiroot.CreateGenericRootAutomationPeer(); if(peer != null) peer.Hwnd = handle; } // This can happen if the root visual is not UIElement. In this case, // attempt to find one in the visual tree. if (peer == null) { peer = UIElementAutomationPeer.GetRootAutomationPeer(root, handle); } if (peer == null) { return IntPtr.Zero; } // get the element proxy // it's ok to pass the same peer as reference connected peer here because // it's guaranteed to be a connected one (it's initialized as root already) IRawElementProviderSimple el = ElementProxy.StaticWrap(peer, peer); peer.AddToAutomationEventList(); // The assert here is considered OK // as we're assuming the WM_GETOBJECT is coming only from a PostMessage of an Hwnd. // to do the post message - you needed to have Unmanaged code permission // PermissionSet unpackPermissionSet = new PermissionSet(PermissionState.None); // below permissions needed to unpack an object. unpackPermissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.UnmanagedCode | SecurityPermissionFlag.RemotingConfiguration)); unpackPermissionSet.AddPermission(new System.Net.DnsPermission(PermissionState.Unrestricted)); unpackPermissionSet.AddPermission(new System.Net.SocketPermission(PermissionState.Unrestricted)); unpackPermissionSet.Assert(); try { return AutomationInteropProvider.ReturnRawElementProvider(handle, wparam, lparam, el); } finally { CodeAccessPermission.RevertAll(); } } #pragma warning disable 56500 catch (Exception e) { if(CriticalExceptions.IsCriticalException(e)) { throw; } return new IntPtr(Marshal.GetHRForException(e)); } #pragma warning restore 56500 } ////// Adjusts a RECT to compensate for Win32 RTL conversion logic /// ////// When a window is marked with the WS_EX_LAYOUTRTL style, Win32 /// mirrors the coordinates during the various translation APIs. /// /// Avalon also sets up mirroring transforms so that we properly /// mirror the output since we render to DirectX, not a GDI DC. /// /// Unfortunately, this means that our coordinates are already mirrored /// by Win32, and Avalon mirrors them again. To solve this /// problem, we un-mirror the coordinates from Win32 before painting /// in Avalon. /// /// /// The RECT to be adjusted /// /// /// internal void AdjustForRightToLeft(ref NativeMethods.RECT rc, HandleRef handleRef) { int windowStyle = SafeNativeMethods.GetWindowStyle(handleRef, true); if(( windowStyle & NativeMethods.WS_EX_LAYOUTRTL ) == NativeMethods.WS_EX_LAYOUTRTL) { NativeMethods.RECT rcClient = new NativeMethods.RECT(); SafeNativeMethods.GetClientRect(handleRef, ref rcClient); int width = rc.right - rc.left; // preserve width rc.right = rcClient.right - rc.left; // set right of rect to be as far from right of window as left of rect was from left of window rc.left = rc.right - width; // restore width by adjusting left and preserving right } } ////// Force total re-rendering to handle system parameters change /// (font smoothing settings, gamma correction, etc.) /// ///true if rerendering was forced ////// Critical: This can be used to cause annoyance by causing re rendering /// [SecurityCritical] private bool OnSettingChange(Int32 firstParam) { if ( (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHING || (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHINGTYPE || (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHINGCONTRAST || (int)firstParam == (int)NativeMethods.SPI_SETFONTSMOOTHINGORIENTATION || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYPIXELSTRUCTURE || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYGAMMA || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYCLEARTYPELEVEL || (int)firstParam == (int)NativeMethods.SPI_SETDISPLAYTEXTCONTRASTLEVEL ) { HRESULT.Check(MILUpdateSystemParametersInfo.Update()); return true; } return false; } ////// Let the DWM know of this HWND as belonging to a MIL/WPF application. /// Force total re-rendering to handle system Desktop Window Manager change /// ////// Critical: This can be used to cause annoyance by trying to cause re rendering. /// [SecurityCritical] internal void OnDWMCompositionChanged(bool isDesktopCompositionEnabled) { // // Hint window redirection layer that Avalon content has been attached // to this window. As a result, redirection layer will create necessary // resources and manage clip and position for this window in TS and // Accessibility scenarios. // // We're going to attempt to attach to DWM every time the desktop composition // state changes to ensure that we properly handle DWM crashing/restarting/etc. // HRESULT.Check(MilContent_AttachToHwnd(_hWnd)); // HRESULT.Check(MILUpdateSystemParametersInfo.Update()); UnsafeNativeMethods.InvalidateRect(_hWnd.MakeHandleRef(this), IntPtr.Zero , true); } ////// This function should be called to paint the specified /// region of the window along with any other pending /// changes. While this function is generally called /// in response to a WM_PAINT it is up to the user to /// call BeginPaint and EndPaint or to otherwise validate /// the bitmap region. /// /// The rectangle that is dirty. private void InvalidateRect(NativeMethods.RECT rcDirty) { DUCE.ChannelSet channelSet = MediaContext.From(Dispatcher).GetChannels(); DUCE.Channel channel = channelSet.Channel; DUCE.Channel outOfBandChannel = channelSet.OutOfBandChannel; // handle InvalidateRect requests only if we have uce resources. if (_compositionTarget.IsOnChannel(channel)) { // // Send a message with the invalid region to the compositor. We create a little batch to send this // out of order. // DUCE.CompositionTarget.Invalidate( _compositionTarget.GetHandle(outOfBandChannel), ref rcDirty, outOfBandChannel); } } ////// Calling this function causes us to update state to reflect a /// size change of the underlying HWND /// ////// Critical - accesses a critical member (_hwnd) /// TreatAsSafe - uses the _hwnd to call a safeNativeMethods. Data is cached - but not considered critical. /// [SecurityCritical, SecurityTreatAsSafe ] private void OnResize() { #if DEBUG MediaTrace.HwndTarget.Trace("OnResize"); #endif // handle OnResize requests only if we have uce resources. if (_compositionTarget.IsOnAnyChannel) { MediaContext mctx = MediaContext.From(Dispatcher); // // Let the render target know that window size has changed. // UpdateWindowSettings(); // // Push client size chnage to the visual target. // Rect clientRect = new Rect( 0, 0, (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left))), (float)(Math.Ceiling((double)(_hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top)))); StateChangedCallback( new object[] { HostStateFlags.ClipBounds, null, clientRect }); mctx.Resize(this); // To ensure that the client area and the non-client area resize // together, we need to wait, on resize, for the composition // engine to present the resized frame. The call to CompleteRender // blocks until that happens. mctx.CompleteRender(); } } ////// Calculates the client and window rectangle in screen coordinates. /// ////// Critical - calls critical methods (ClientToScreen) /// TreatAsSafe - no information returned, coordinates stored in member field. /// [SecurityCritical, SecurityTreatAsSafe] private void GetWindowRectsInScreenCoordinates() { NativeMethods.RECT rcClient = new NativeMethods.RECT(); // // Get the window and client rectangles // SafeNativeMethods.GetWindowRect(_hWnd.MakeHandleRef(this), ref _hwndWindowRectInScreenCoords); SafeNativeMethods.GetClientRect(_hWnd.MakeHandleRef(this), ref rcClient); NativeMethods.POINT ptClientTopLeft = new NativeMethods.POINT(rcClient.left, rcClient.top); UnsafeNativeMethods.ClientToScreen(_hWnd.MakeHandleRef(this), ptClientTopLeft); NativeMethods.POINT ptClientBottomRight = new NativeMethods.POINT(rcClient.right, rcClient.bottom); UnsafeNativeMethods.ClientToScreen(_hWnd.MakeHandleRef(this), ptClientBottomRight); if(ptClientBottomRight.x >= ptClientTopLeft.x) { _hwndClientRectInScreenCoords.left = ptClientTopLeft.x; _hwndClientRectInScreenCoords.right = ptClientBottomRight.x; } else { // RTL windows will cause the right edge to be on the left... _hwndClientRectInScreenCoords.left = ptClientBottomRight.x; _hwndClientRectInScreenCoords.right = ptClientTopLeft.x; } if(ptClientBottomRight.y >= ptClientTopLeft.y) { _hwndClientRectInScreenCoords.top = ptClientTopLeft.y; _hwndClientRectInScreenCoords.bottom = ptClientBottomRight.y; } else { // RTL windows will cause the right edge to be on the left... // This doesn't affect top/bottom, but the code should be symmetrical. _hwndClientRectInScreenCoords.top = ptClientBottomRight.y; _hwndClientRectInScreenCoords.bottom = ptClientTopLeft.y; } // } ////// Critical - accepts an unmanaged pointer to a structure /// [SecurityCritical] private void OnWindowPosChanging(IntPtr lParam) { _windowPosChanging = true; UpdateWindowPos(lParam); } ////// Critical - accepts an unmanaged pointer to a structure /// [SecurityCritical] private void OnWindowPosChanged(IntPtr lParam) { _windowPosChanging = false; UpdateWindowPos(lParam); } ////// Critical - accepts an unmanaged pointer to a structure /// [SecurityCritical] private void UpdateWindowPos(IntPtr lParam) { // // We need to update the window settings used by the render thread when // 1) The size or position of the render target needs to change // 2) The render target needs to be enabled or disabled. // // Further, we need to synchronize the render thread during sizing operations. // This is because some APIs that the render thread uses (such as // UpdateLayeredWindow) have the unintended side-effect of also changing the // window size. We can't let the render thread and the UI thread fight // over setting the window size. // // Generally, Windows sends our window to messages that bracket the size // operation: // 1) WM_WINDOWPOSCHANGING // Here we synchronize with the render thread, and ask the render thread // to not render to this window for a while. // 2) WM_WINDOWPOSCHANGED // This is after the window size has actually been changed, so we tell // the render thread that it can render to the window again. // // However, there are complications. Sometimes Windows will send a // WM_WINDOWPOSCHANGING without sending a WM_WINDOWPOSCHANGED. This happens // when the window size is not really going to change. Also note that // more than just size/position information is provided by these messages. // We'll get these messages when nothing but the z-order changes for instance. // // // The first order of business is to determine if the render target // size or position changed. If so, we need to pass this information to // the render thread. // NativeMethods.WINDOWPOS windowPos = (NativeMethods.WINDOWPOS)UnsafeNativeMethods.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS)); bool isMove = (windowPos.flags & NativeMethods.SWP_NOMOVE) == 0; bool isSize = (windowPos.flags & NativeMethods.SWP_NOSIZE) == 0; bool positionChanged = (isMove || isSize); if (positionChanged) { // // We have found that sometimes we get told that the size or position // of the window has changed, when it really hasn't. So we double // check here. This is critical because we won't be given a // WM_WINDOWPOSCHANGED unless the size or position really had changed. // if (!isMove) { // This is just to avoid any possible integer overflow problems. windowPos.x = windowPos.y = 0; } if (!isSize) { // This is just to avoid any possible integer overflow problems. windowPos.cx = windowPos.cy = 0; } // // WINDOWPOS stores the window coordinates relative to its parent. // If the parent is NULL, then these are already screen coordinates. // Otherwise, we need to convert to screen coordinates. // NativeMethods.RECT windowRectInScreenCoords = new NativeMethods.RECT(windowPos.x, windowPos.y, windowPos.x + windowPos.cx, windowPos.y + windowPos.cy); IntPtr hwndParent = UnsafeNativeMethods.GetParent(new HandleRef(null, windowPos.hwnd)); if(hwndParent != IntPtr.Zero) { SafeSecurityHelper.TransformLocalRectToScreen(new HandleRef(null, hwndParent), ref windowRectInScreenCoords); } if (!isMove) { // We weren't actually moving, so the WINDOWPOS structure // did not contain valid (x,y) information. Just use our // old values. int width = (windowRectInScreenCoords.right - windowRectInScreenCoords.left); int height = (windowRectInScreenCoords.bottom - windowRectInScreenCoords.top); windowRectInScreenCoords.left = _hwndWindowRectInScreenCoords.left; windowRectInScreenCoords.right = windowRectInScreenCoords.left + width; windowRectInScreenCoords.top = _hwndWindowRectInScreenCoords.top; windowRectInScreenCoords.bottom = windowRectInScreenCoords.top + height; } if (!isSize) { // We weren't actually sizing, so the WINDOWPOS structure // did not contain valid (cx,cy) information. Just use our // old values. int width = (_hwndWindowRectInScreenCoords.right - _hwndWindowRectInScreenCoords.left); int height = (_hwndWindowRectInScreenCoords.bottom - _hwndWindowRectInScreenCoords.top); windowRectInScreenCoords.right = windowRectInScreenCoords.left + width; windowRectInScreenCoords.bottom = windowRectInScreenCoords.top + height; } positionChanged = ( _hwndWindowRectInScreenCoords.left != windowRectInScreenCoords.left || _hwndWindowRectInScreenCoords.top != windowRectInScreenCoords.top || _hwndWindowRectInScreenCoords.right != windowRectInScreenCoords.right || _hwndWindowRectInScreenCoords.bottom != windowRectInScreenCoords.bottom); } // // The second order of business is to determine whether or not the render // target should be enabled. If we are disabling the render target, then // we need to synchronize with the render thread. Basically, // a WM_WINDOWPOSCHANGED always enables the render target it the window is // visible. And a WM_WINDOWPOSCHANGING will disable the render target // unless it is not really a size/move, in which case we will not be sent // a WM_WINDOWPOSCHANGED, so we can't disable the render target. // bool enableRenderTarget = SafeNativeMethods.IsWindowVisible(_hWnd.MakeHandleRef(this)); if(enableRenderTarget) { if(_windowPosChanging && (positionChanged)) { enableRenderTarget = false; } } if (positionChanged || (enableRenderTarget != _isRenderTargetEnabled)) { UpdateWindowSettings(enableRenderTarget); } } bool _windowPosChanging; private void OnShowWindow(bool enableRenderTarget) { if (enableRenderTarget != _isRenderTargetEnabled) { UpdateWindowSettings(enableRenderTarget); } } private void OnExitSizeMove() { if (_windowPosChanging) { _windowPosChanging = false; UpdateWindowSettings(true); } } private void UpdateWindowSettings() { UpdateWindowSettings(_isRenderTargetEnabled, null); } private void UpdateWindowSettings(bool enableRenderTarget) { UpdateWindowSettings(enableRenderTarget, null); } ////// Critical - calls critical methods (UpdateWindowSettings) /// TreatAsSafe - just updates composition information for this window, /// does not accept arbitrary input. /// [SecurityCritical, SecurityTreatAsSafe] private void UpdateWindowSettings(bool enableRenderTarget, DUCE.ChannelSet? channelSet) { MediaContext mctx = MediaContext.From(Dispatcher); if (_isRenderTargetEnabled != enableRenderTarget) { _isRenderTargetEnabled = enableRenderTarget; // Basic idea: the render thread and the UI thread have a // race condition when the UI thread wants to modify // HWND data and the render thread is using it. The render // thread can paint garbage on the screen, and it can also // cause the old data to be set again (ULW issue, hence ULWEx). // // So we tell the render thread to stop rendering and then we // wait for them to stop when disabling the render target by // issuing the UpdateWindowSettings command synchronously on // an out-of-band channel. } // if we are disconnected we are done. if (!_compositionTarget.IsOnAnyChannel) { return; } // // Calculate the client rectangle in screen coordinates. // GetWindowRectsInScreenCoordinates(); Int32 style = UnsafeNativeMethods.GetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_STYLE); Int32 exStyle = UnsafeNativeMethods.GetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_EXSTYLE); bool isLayered = (exStyle & NativeMethods.WS_EX_LAYERED) != 0; bool isChild = (style & NativeMethods.WS_CHILD) != 0; bool isRTL = (exStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0; int width = _hwndClientRectInScreenCoords.right - _hwndClientRectInScreenCoords.left; int height = _hwndClientRectInScreenCoords.bottom - _hwndClientRectInScreenCoords.top; MILTransparencyFlags flags = MILTransparencyFlags.Opaque; // if (!DoubleUtil.AreClose(_opacity, 1.0)) // { // flags |= MILTransparencyFlags.ConstantAlpha; // } // if (_colorKey.HasValue) // { // flags |= MILTransparencyFlags.ColorKey; // } if (_usesPerPixelOpacity) { flags |= MILTransparencyFlags.PerPixelAlpha; } if (!isLayered && flags != MILTransparencyFlags.Opaque) { // The window is not layered, but it should be -- set the layered flag. UnsafeNativeMethods.SetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_EXSTYLE, new IntPtr(exStyle | NativeMethods.WS_EX_LAYERED)); } else if (isLayered && flags == MILTransparencyFlags.Opaque) { // The window is layered but should not be -- unset the layered flag. UnsafeNativeMethods.SetWindowLong(_hWnd.MakeHandleRef(this), NativeMethods.GWL_EXSTYLE, new IntPtr(exStyle & ~NativeMethods.WS_EX_LAYERED)); } else if(isLayered && flags != MILTransparencyFlags.Opaque && _isRenderTargetEnabled && (width == 0 || height == 0)) { // The window is already layered, and it should be. But we are enabling a window // that is has a 0-size dimension. This may cause us to leave the last sprite // on the screen. The best way to get rid of this is to just make the entire // sprite transparent. NativeMethods.BLENDFUNCTION blend = new NativeMethods.BLENDFUNCTION(); blend.BlendOp = NativeMethods.AC_SRC_OVER; blend.SourceConstantAlpha = 0; // transparent UnsafeNativeMethods.UpdateLayeredWindow(_hWnd.h, IntPtr.Zero, null, null, IntPtr.Zero, null, 0, ref blend, NativeMethods.ULW_ALPHA); } isLayered = (flags != MILTransparencyFlags.Opaque); // Every UpdateWindowSettings command that disables the render target is // assigned a new cookie. Every UpdateWindowSettings command that enables // the render target uses the most recent cookie. This allows the // compositor to ignore UpdateWindowSettings(enable) commands that come // out of order due to us disabling out-of-band and enabling in-band. if (!_isRenderTargetEnabled) { _disableCookie++; } if (channelSet == null) { channelSet = mctx.GetChannels(); } // // When enabling the render target, stay in-band. This allows any // client-side rendering instructions to be included in the same packet. // Otherwise pass in the OutOfBand handle. // DUCE.Channel channel = channelSet.Value.Channel; DUCE.Channel outOfBandChannel = channelSet.Value.OutOfBandChannel; DUCE.CompositionTarget.UpdateWindowSettings( _isRenderTargetEnabled ? _compositionTarget.GetHandle(channel) : _compositionTarget.GetHandle(outOfBandChannel), _hwndClientRectInScreenCoords, Colors.Transparent, // _colorKey.GetValueOrDefault(Colors.Black), 1.0f, // (float)_opacity, isLayered ? (_usesPerPixelOpacity ? MILWindowLayerType.ApplicationManagedLayer : MILWindowLayerType.SystemManagedLayer) : MILWindowLayerType.NotLayered, flags, isChild, isRTL, _isRenderTargetEnabled, _disableCookie, _isRenderTargetEnabled ? channel : outOfBandChannel); if (_isRenderTargetEnabled) { // // Re-render the visual tree. // mctx.PostRender(); } else { // If we disabled the render target, we run the risk of leaving it disabled. // One such example is when a window is programatically sized, but then // GetMinMaxInfo denies the change. We do not receive any message that would // allow us to re-enable the render targer. To cover these odd cases, we // post ourselves a message to possible re-enable the render target when // we are done with the current message processing. UnsafeNativeMethods.PostMessage(new HandleRef(this, _hWnd), s_updateWindowSettings, IntPtr.Zero, IntPtr.Zero); } } ////// Gets and sets the root Visual of this HwndTarget. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical: This code accesses HWND which is critical and setting RootVisual /// is critical /// PublicOK: This code blocks inheritance and public callers via Inheritance (in base class) and link demands /// public override Visual RootVisual { [SecurityCritical] [UIPermissionAttribute(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] set { base.RootVisual = value; if (value != null) { // UIAutomation listens for the EventObjectUIFragmentCreate WinEvent to // understand when UI that natively implements UIAutomation comes up // UnsafeNativeMethods.NotifyWinEvent(UnsafeNativeMethods.EventObjectUIFragmentCreate, _hWnd.MakeHandleRef(this), 0, 0); } } } ////// Returns matrix that can be used to transform coordinates from this /// target to the rendering destination device. /// public override Matrix TransformToDevice { get { VerifyAPIReadOnly(); Matrix m = Matrix.Identity; m.Scale(_devicePixelsPerInchX / 96.0, _devicePixelsPerInchY / 96.0); return m; } } ////// Returns matrix that can be used to transform coordinates from /// the rendering destination device to this target. /// public override Matrix TransformFromDevice { get { VerifyAPIReadOnly(); Matrix m = Matrix.Identity; m.Scale(96.0 / _devicePixelsPerInchX, 96.0 / _devicePixelsPerInchY); return m; } } ////// This is the color that is drawn before everything else. If /// this color has an alpha component other than 1 it will be ignored. /// public Color BackgroundColor { get { VerifyAPIReadOnly(); return _backgroundColor; } set { VerifyAPIReadWrite(); if (_backgroundColor != value) { _backgroundColor = value; MediaContext mctx = MediaContext.From(Dispatcher); DUCE.ChannelSet channelSet = mctx.GetChannels(); DUCE.Channel channel = channelSet.Channel; if (channel == null) { // MediaContext is in disconnected state, so we will send // the clear color when CreateUCEResources gets called Debug.Assert(!_compositionTarget.IsOnChannel(channel)); } else { DUCE.CompositionTarget.SetClearColor( _compositionTarget.GetHandle(channel), _backgroundColor, channel); mctx.PostRender(); } } } } // ///// /// Specifies the color to display as transparent. // /// // ///// /// Use null to indicate that no color should be transparent. // /// // public NullableColorKey // { // get // { // VerifyAPIReadOnly(); // // return _colorKey; // } // // set // { // VerifyAPIReadWrite(); // // if(_colorKey != value) // { // _colorKey = value; // // UpdateWindowSettings(); // } // } // } // /// // /// 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 // { // VerifyAPIReadOnly(); // // return _opacity; // } // // set // { // VerifyAPIReadWrite(); // // if(value < 0.0) value = 0.0; // if(value > 1.0) value = 1.0; // // if(!MS.Internal.DoubleUtil.AreClose(value, _opacity)) // { // _opacity = value; // // UpdateWindowSettings(); // } // } // } ////// 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 - accesses critical _hWnd, calls unmanaged code /// PublicOK - internal property, hwnd not exposed or modified, WTSRegisterSessionNotification /// only results in the hwnd getting more messages that we use to enable/disable /// rendering /// public bool UsesPerPixelOpacity { get { VerifyAPIReadOnly(); return _usesPerPixelOpacity; } [SecurityCritical, SecurityTreatAsSafe] internal set { VerifyAPIReadWrite(); if(_usesPerPixelOpacity != value) { _usesPerPixelOpacity = value; // Register/unregister for Fast User Switching messages only if necessary because // this pinvoke loads wtsapi32.dll if (_usesPerPixelOpacity) { UnsafeNativeMethods.WTSRegisterSessionNotification(_hWnd, NativeMethods.NOTIFY_FOR_THIS_SESSION); } else { UnsafeNativeMethods.WTSUnRegisterSessionNotification(_hWnd); } UpdateWindowSettings(); } } } ////// Tells a channel to send notifications to a particular target's window. /// /// /// The channel from which we want notifications. /// /// /// The target whose window will receive the notifications. /// ////// Critical - Calls a critical channel method. /// TreatAsSafe - We are associated with the window handle that we /// are passing to the channel, so somebody already /// decided that it's OK for us to interact with that /// window. We also registered a window message so /// that we can avoid collisions with other messages. /// [SecurityCritical, SecurityTreatAsSafe] internal static void SetChannelNotificationWindow(DUCE.Channel channel, HwndTarget target) { channel.SetNotificationWindow(target._hWnd, s_channelNotifyMessage); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RawStylusInputReport.cs
- SpecularMaterial.cs
- ChildrenQuery.cs
- Part.cs
- ForeignKeyConstraint.cs
- COM2PropertyPageUITypeConverter.cs
- ContextMenu.cs
- SqlSelectStatement.cs
- LocalBuilder.cs
- GridLength.cs
- CheckBoxBaseAdapter.cs
- _Connection.cs
- Decoder.cs
- DebugManager.cs
- ArcSegment.cs
- FileCodeGroup.cs
- ValueTable.cs
- PersistenceProviderElement.cs
- SqlWebEventProvider.cs
- FontUnit.cs
- ExternalDataExchangeClient.cs
- FileDetails.cs
- SymmetricKeyWrap.cs
- DataServiceProviderWrapper.cs
- dbenumerator.cs
- TraceRecord.cs
- DefinitionBase.cs
- HtmlElementCollection.cs
- CodeChecksumPragma.cs
- HttpGetProtocolReflector.cs
- SystemInformation.cs
- ObjectHandle.cs
- Int32CAMarshaler.cs
- WebPartVerbsEventArgs.cs
- ListCollectionView.cs
- SpStreamWrapper.cs
- DataTablePropertyDescriptor.cs
- WebPartConnectionsCloseVerb.cs
- FlowLayoutSettings.cs
- WindowsGraphicsCacheManager.cs
- PageParserFilter.cs
- MenuTracker.cs
- Lease.cs
- UIPermission.cs
- DateTimeUtil.cs
- LayoutSettings.cs
- DecimalAnimationUsingKeyFrames.cs
- recordstatescratchpad.cs
- Cursors.cs
- DiscreteKeyFrames.cs
- ImpersonateTokenRef.cs
- OrderablePartitioner.cs
- CfgParser.cs
- Win32Exception.cs
- HotCommands.cs
- ImpersonateTokenRef.cs
- XsltConvert.cs
- CLSCompliantAttribute.cs
- MaskDescriptor.cs
- Baml2006SchemaContext.cs
- IriParsingElement.cs
- SimpleBitVector32.cs
- TypeBuilderInstantiation.cs
- SourceInterpreter.cs
- FunctionDescription.cs
- AncestorChangedEventArgs.cs
- ImageAutomationPeer.cs
- Expressions.cs
- MulticastOption.cs
- XmlNodeReader.cs
- CompilerHelpers.cs
- VisualBasicDesignerHelper.cs
- LicenseException.cs
- Visitors.cs
- DigestTraceRecordHelper.cs
- ConfigViewGenerator.cs
- FontTypeConverter.cs
- RowType.cs
- DirectoryRootQuery.cs
- SafeMILHandle.cs
- SpellerStatusTable.cs
- ComplexLine.cs
- XmlExpressionDumper.cs
- KeyGestureValueSerializer.cs
- UxThemeWrapper.cs
- MultilineStringConverter.cs
- UInt64Converter.cs
- ServiceNameElement.cs
- IisTraceListener.cs
- FunctionImportElement.cs
- DataContractSerializerElement.cs
- RuntimeConfigurationRecord.cs
- ItemsPanelTemplate.cs
- SmtpLoginAuthenticationModule.cs
- ClientSettings.cs
- StatusBar.cs
- VerificationAttribute.cs
- HttpWriter.cs
- TransformedBitmap.cs
- AssemblyContextControlItem.cs