using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Security; using System.Windows.Forms; using Vanara.PInvoke; using static Vanara.PInvoke.DwmApi; namespace Vanara.Windows.Forms; /// Main DWM class, provides glass sheet effect and blur behind. [SecuritySafeCritical] public static partial class DesktopWindowManager { internal static readonly ThumbnailMgr thumbnailMgr = new(); private static readonly object _lock = new(); private static readonly object colorizationColorChangedKey = new(); private static readonly object compositionChangedKey = new(); private static readonly object nonClientRenderingChangedKey = new(); private static readonly object[] keys = { compositionChangedKey, nonClientRenderingChangedKey, colorizationColorChangedKey/*, WindowMaximizedChangedKey*/ }; private static EventHandlerList? eventHandlerList; private static MessageWindow? msgWin; /// Occurs when the colorization color has changed. public static event EventHandler ColorizationColorChanged { add => AddEventHandler(colorizationColorChangedKey, value); remove => RemoveEventHandler(colorizationColorChangedKey, value); } /// Occurs when the desktop window composition has been enabled or disabled. public static event EventHandler CompositionChanged { add => AddEventHandler(compositionChangedKey, value); remove => RemoveEventHandler(compositionChangedKey, value); } /// Occurs when the non-client area rendering policy has changed. public static event EventHandler NonClientRenderingChanged { add => AddEventHandler(nonClientRenderingChangedKey, value); remove => RemoveEventHandler(nonClientRenderingChangedKey, value); } /// /// Use with GetWindowAttr and WindowAttribute.Cloaked. If the window is cloaked, provides one of the following values explaining why. /// [Flags] public enum CloakingSource { /// The window was cloaked by its owner application. App = 0x01, /// The window was cloaked by the Shell. Shell = 0x02, /// The cloak value was inherited from its owner window. Inherited = 0x04 } /// Flags used by the SetWindowAttr method to specify the Flip3D window policy. public enum Flip3DWindowPolicy { /// /// Use the window's style and visibility settings to determine whether to hide or include the window in Flip3D rendering. /// Default, /// Exclude the window from Flip3D and display it below the Flip3D rendering. ExcludeBelow, /// Exclude the window from Flip3D and display it above the Flip3D rendering. ExcludeAbove } /// Flags used by the SetWindowAttr method to specify the non-client area rendering policy. public enum NonClientRenderingPolicy { /// The non-client rendering area is rendered based on the window style. UseWindowStyle, /// The non-client area rendering is disabled; the window style is ignored. Disabled, /// The non-client area rendering is enabled; the window style is ignored. Enabled } /// /// Gets or sets the current color used for Desktop Window Manager (DWM) glass composition. This value is based on the current color /// scheme and can be modified by the user. /// /// The color of the glass composition. public static Color CompositionColor { get { if (!CompositionSupported) return Color.Transparent; var value = (int)Microsoft.Win32.Registry.CurrentUser.GetValue(@"Software\Microsoft\Windows\DWM\ColorizationColor", 0)!; return Color.FromArgb(value); } set { if (!CompositionSupported) return; DwmpGetColorizationParameters(out var p).ThrowIfFailed(); p.clrColor = (COLORREF)value; DwmpSetColorizationParameters(p, 1).ThrowIfFailed(); Microsoft.Win32.Registry.CurrentUser.SetValue(@"Software\Microsoft\Windows\DWM\ColorizationColor", value.ToArgb(), Microsoft.Win32.RegistryValueKind.DWord); } } /// Gets or sets a value indicating whether composition (Windows Aero) is enabled. /// true if composition is enabled; otherwise, false. public static bool CompositionEnabled { get => IsCompositionEnabled(); set { if (CompositionSupported) EnableComposition(value); } } /// Gets or sets a value indicating whether composition (Windows Aero) is supported. /// true if composition is supported; otherwise, false. public static bool CompositionSupported => Environment.OSVersion.Version.Major >= 6; /// /// Gets a value notifying the Desktop Window Manager (DWM) to opt in to or out of Multimedia Class Schedule Service (MMCSS) /// scheduling while the calling process is alive. /// /// /// true to instruct DWM to participate in MMCSS scheduling; otherwise, false to opt out or end participation in MMCSS scheduling. /// public static bool MultimediaClassScheduleServiceEnabled { set => DwmEnableMMCSS(value).ThrowIfFailed(); } /// Gets or sets a value that indicates whether the is transparent. /// true if transparent; otherwise, false. public static bool TransparencyEnabled { get { if (!CompositionSupported) return false; var value = (int)Microsoft.Win32.Registry.CurrentUser.GetValue(@"Software\Microsoft\Windows\DWM\ColorizationOpaqueBlend", 1)!; return value == 0; } set { if (!CompositionSupported) return; DwmpGetColorizationParameters(out var p).ThrowIfFailed(); p.fOpaque = value; DwmpSetColorizationParameters(p, 1).ThrowIfFailed(); Microsoft.Win32.Registry.CurrentUser.SetValue(@"Software\Microsoft\Windows\DWM\ColorizationOpaqueBlend", p.fOpaque, Microsoft.Win32.RegistryValueKind.DWord); } } /*/// /// Occurs when a Desktop Window Manager (DWM) composed window is maximized. public static event EventHandler WindowMaximizedChanged { add { AddEventHandler(WindowMaximizedChangedKey, value); } remove { RemoveEventHandler(WindowMaximizedChangedKey, value); } }*/ /// Enables content rendered in the non-client area to be visible on the frame drawn by DWM. /// The form. /// /// Set to true to enable content rendered in the non-client area to be visible on the frame; otherwise, false. /// public static void AllowNonClientPainting(this Form form, bool allowNCPaint) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_ALLOW_NCPAINT, allowNCPaint); /// Cloaks the window such that it is not visible to the user. The window is still composed by DWM. /// The form. /// If set to true, cloak. public static void Cloak(this Form form, bool cloak) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, cloak); /// Enables or forcibly disables DWM transitions. /// The form. /// true to disable transitions. public static void DisableTransitions(this Form form, bool forceDisabled) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, forceDisabled); /// /// Do not show peek preview for the window. The peek view shows a full-sized preview of the window when the mouse hovers over the /// window's thumbnail in the taskbar. /// /// The form. /// /// if set to true, hovering the mouse pointer over the window's thumbnail dismisses peek (in case another window in the group /// has a peek preview showing). /// public static void DisallowPeekPreview(this Form form, bool disallowPeek) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_DISALLOW_PEEK, disallowPeek); /// Enable the Aero "Blur Behind" effect on the whole client area. Background must be black. /// The window. /// true to enable blur behind for this window, false to disable it. public static void EnableBlurBehind(this IWin32Window window, bool enabled) => EnableBlurBehind(window, null, null, enabled, false); /// Enable the Aero "Blur Behind" effect on a specific region of a drawing area. Background must be black. /// The window. /// The graphics area on which the region resides. /// The region within the client area to apply the blur behind. /// true to enable blur behind for this region, false to disable it. /// /// true if the window's colorization should transition to match the maximized windows; otherwise, false. /// public static void EnableBlurBehind(this IWin32Window window, Graphics? graphics, Region? region, bool enabled, bool transitionOnMaximized) { if (window == null) throw new ArgumentNullException(nameof(window)); var bb = new DWM_BLURBEHIND(enabled); if (graphics != null && region != null) { bb.hRgnBlur = region.GetHrgn(graphics); bb.dwFlags |= DWM_BLURBEHIND_Mask.DWM_BB_BLURREGION; } if (transitionOnMaximized) bb.TransitionOnMaximized = true; DwmEnableBlurBehindWindow(window.Handle, bb); } /// Enables or disables Desktop Window Manager (DWM) composition. /// true to enable DWM composition; false to disable composition. public static void EnableComposition(bool value) => DwmEnableComposition(value); /// Excludes the specified child control from the glass effect. /// The parent control. /// The control to exclude. /// Occurs if control is null. /// Occurs if control is not a child control. public static void ExcludeChildFromGlass(this Control parent, Control control) { if (parent == null) throw new ArgumentNullException(nameof(parent)); if (control == null) throw new ArgumentNullException(nameof(control)); if (!parent.Contains(control)) throw new ArgumentException("Control must be a child control."); if (IsCompositionEnabled()) { var clientScreen = parent.RectangleToScreen(parent.ClientRectangle); var controlScreen = control.RectangleToScreen(control.ClientRectangle); var margins = new MARGINS(controlScreen.Left - clientScreen.Left, controlScreen.Top - clientScreen.Top, clientScreen.Right - controlScreen.Right, clientScreen.Bottom - controlScreen.Bottom); // Extend the Frame into client area DwmExtendFrameIntoClientArea(parent.Handle, margins); } } /// Extends the window frame beyond the client area. /// The window. /// The padding to use as the area into which the frame is extended. public static void ExtendFrameIntoClientArea(this IWin32Window window, Padding padding) { if (window == null) throw new ArgumentNullException(nameof(window)); var m = new MARGINS(padding.Left, padding.Right, padding.Top, padding.Bottom); DwmExtendFrameIntoClientArea(window.Handle, m); } /// /// Issues a flush call that blocks the caller until the next present, when all of the Microsoft DirectX surface updates that are /// currently outstanding have been made. This compensates for very complex scenes or calling processes with very low priority. /// public static void Flush() => DwmFlush().ThrowIfFailed(); /// /// Forces the window to display an iconic thumbnail or peek representation (a static bitmap), even if a live or snapshot /// representation of the window is available. This value normally is set during a window's creation and not changed throughout the /// window's lifetime. Some scenarios, however, might require the value to change over time. /// /// The form. /// true to require a iconic thumbnail or peek representation; otherwise, false. public static void ForceIconicRepresentation(this Form form, bool iconRep) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_FORCE_ICONIC_REPRESENTATION, iconRep); /// /// Freeze the window's thumbnail image with its current visuals. Do no further live updates on the thumbnail image to match the /// window's contents. /// /// The form. /// if set to true freeze thumbnail. public static void FreezeLiveThumbnail(this Form form, bool freeze) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_FREEZE_REPRESENTATION, freeze); /// Retrieves the bounds of the caption button area in the window-relative space. /// The form. /// The bounds. public static Rectangle GetCaptionButtonBounds(this Form form) => GetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_CAPTION_BUTTON_BOUNDS); /// If the window is cloaked, provides a value explaining why. /// The form. /// The reason the window is cloaked. public static CloakingSource GetCloakingSource(this Form form) => (CloakingSource)GetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED); /// Retrieves the extended frame bounds rectangle in screen space. /// The form. /// The bounds. public static Rectangle GetExtendedFrameBounds(this Form form) => GetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS); /// Gets the live client thumbnail. /// The window. /// public static LiveThumbnail GetLiveClientThumbnail(this IWin32Window window) => new(window); /// /// The window will provide a bitmap for use by DWM as an iconic thumbnail or peek representation (a static bitmap) for the window. /// DWMWA_HAS_ICONIC_BITMAP can be specified with DWMWA_FORCE_ICONIC_REPRESENTATION. DWMWA_HAS_ICONIC_BITMAP normally is set during a /// window's creation and not changed throughout the window's lifetime. Some scenarios, however, might require the value to change /// over time. /// /// The form. /// if set to true inform DWM that the window will provide an iconic thumbnail or peek representation. public static void HasIconicBitmap(this Form form, bool hasIcon) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_HAS_ICONIC_BITMAP, hasIcon); /// /// Called by an application to indicate that all previously provided iconic bitmaps from a window, both thumbnails and peek /// representations, should be refreshed. /// /// /// The window or tab whose bitmaps are being invalidated through this call. This window must belong to the calling process. /// public static void InvalidateIconicBitmaps(this IWin32Window window) => DwmInvalidateIconicBitmaps(window.Handle).ThrowIfFailed(); /// Discovers whether non-client rendering is enabled. /// The form. /// true if non client rendering is enabled; otherwise, false. public static bool IsNonClientRenderingEnabled(this Form form) => GetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_ENABLED); /// Specifies whether non-client content is right-to-left (RTL) mirrored. /// The form. /// if set to true the non-client content is right-to-left (RTL) mirrored. public static void NonClientRightToLeft(this Form form, bool rtl) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_NONCLIENT_RTL_LAYOUT, rtl); /// Prevents a window from fading to a glass sheet when peek is invoked. /// The form. /// if set to true prevent the window from fading during another window's peek. public static void PreventFadingOnPeekPreview(this Form form, bool prevent) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_EXCLUDED_FROM_PEEK, prevent); /// Sets how Flip3D treats the window. /// The form. /// The policy. public static void SetFlip3DPolicy(this Form form, Flip3DWindowPolicy policy) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_FLIP3D_POLICY, (DWMFLIP3DWINDOWPOLICY)policy); /// Sets the non-client rendering policy. /// The form. /// The policy. public static void SetNonClientRenderingPolicy(this Form form, NonClientRenderingPolicy policy) => SetWindowAttribute(form, DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, (DWMNCRENDERINGPOLICY)policy); private static void AddEventHandler(object id, EventHandler value) { lock (_lock) { msgWin ??= new MessageWindow(); eventHandlerList ??= new EventHandlerList(); eventHandlerList.AddHandler(id, value); } } /// Gets the specified window attribute from the Desktop Window Manager (DWM). /// Return type. Must match the attribute. /// The window. /// The attribute. /// Value of the windows attribute. private static T GetWindowAttribute(this IWin32Window window, DWMWINDOWATTRIBUTE attribute) where T : struct { if (window == null) throw new ArgumentNullException(nameof(window)); using var ptr = SafeCoTaskMemHandle.CreateFromStructure(); DwmGetWindowAttribute(window.Handle, attribute, (IntPtr)ptr, ptr.Size); return ptr.ToStructure(); } /// Indicates whether Desktop Window Manager (DWM) composition is enabled. /// true if is composition enabled; otherwise, false. private static bool IsCompositionEnabled() { if (!CompositionSupported || !System.IO.File.Exists(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), @"dwmapi.dll"))) return false; DwmIsCompositionEnabled(out var res); return res; } private static void RemoveEventHandler(object id, EventHandler value) { lock (_lock) { eventHandlerList?.RemoveHandler(id, value); } } /// Sets the specified window attribute through the Desktop Window Manager (DWM). /// The window. /// The attribute. /// The value. private static void SetWindowAttribute(this IWin32Window window, DWMWINDOWATTRIBUTE attribute, T value) where T : struct { if (window == null) throw new ArgumentNullException(nameof(window)); using var ptr = SafeCoTaskMemHandle.CreateFromStructure(value); DwmSetWindowAttribute(window.Handle, attribute, (IntPtr)ptr, ptr.Size); } internal class ThumbnailMgr : IDisposable { private readonly Dictionary thumbnails = new(); public ThumbnailMgr() { } public void Dispose() { foreach (var hThumb in thumbnails.Values) DwmUnregisterThumbnail(hThumb); GC.SuppressFinalize(this); } public HTHUMBNAIL Register(IWin32Window win) { if (thumbnails.TryGetValue(win.Handle, out var hThumbnail)) { DwmUnregisterThumbnail(hThumbnail); thumbnails.Remove(win.Handle); } DwmRegisterThumbnail(win.Handle, User32.FindWindow("Progman", null), out var hThumb).ThrowIfFailed(); thumbnails.Add(win.Handle, hThumb); return hThumb; } public void Unregister(HTHUMBNAIL hThumbnail) { foreach (var kv in thumbnails) { if (kv.Value == hThumbnail) { DwmUnregisterThumbnail(hThumbnail); thumbnails.Remove(kv.Key); break; } } } } [SecuritySafeCritical] private class MessageWindow : NativeWindow, IDisposable { public MessageWindow() { var cp = new CreateParams { Style = 0, ExStyle = 0, ClassStyle = 0, Parent = IntPtr.Zero, Caption = GetType().Name }; CreateHandle(cp); } public void Dispose() => DestroyHandle(); protected override void WndProc(ref Message m) { // ReSharper disable InconsistentNaming const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320; const int WM_DWMCOMPOSITIONCHANGED = 0x031E; //const int WM_DWMNCRENDERINGCHANGED = 0x031F; //const int WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321; // ReSharper restore InconsistentNaming if (m.Msg is >= WM_DWMCOMPOSITIONCHANGED and <= WM_DWMCOLORIZATIONCOLORCHANGED) ExecuteEvents(m.Msg - WM_DWMCOMPOSITIONCHANGED); base.WndProc(ref m); } private static void ExecuteEvents(int idx) { if (eventHandlerList == null) return; lock (_lock) { try { ((EventHandler)eventHandlerList[keys[idx]]!).Invoke(null, EventArgs.Empty); } catch { }; } } } }