using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Security;
using System.Windows.Forms;
using Vanara.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.DwmApi;
namespace Vanara.Windows.Forms
{
/// Main DWM class, provides glass sheet effect and blur behind.
[global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
[global::System.Security.Permissions.PermissionSet(global::System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
[SecuritySafeCritical]
public static partial class DesktopWindowManager
{
internal static readonly ThumbnailMgr thumbnailMgr = new ThumbnailMgr();
private static readonly object _lock = new object();
private static readonly object colorizationColorChangedKey = new object();
private static readonly object compositionChangedKey = new object();
//static object windowMaximizedChangedKey = new object();
private static readonly object[] keys = { compositionChangedKey, nonClientRenderingChangedKey, colorizationColorChangedKey/*, WindowMaximizedChangedKey*/ };
private static readonly object nonClientRenderingChangedKey = new object();
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.
[global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames")]
[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 = 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.SetRegion(graphics, region);
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.
/// if set to true [visible].
///
///
public static LiveThumbnail GetLiveClientThumbnail(this IWin32Window window) => new LiveThumbnail(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)
{
if (msgWin == null)
msgWin = new MessageWindow();
if (eventHandlerList == null)
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 || !global::System.IO.File.Exists(global::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 Dictionary thumbnails = new Dictionary();
public ThumbnailMgr() { }
public void Dispose()
{
foreach (var hThumb in thumbnails.Values)
DwmUnregisterThumbnail(hThumb);
thumbnails = null;
}
public HTHUMBNAIL Register(IWin32Window win)
{
if (thumbnails.TryGetValue(win.Handle, out var hThumbnail))
{
DwmUnregisterThumbnail(hThumbnail);
thumbnails.Remove(win.Handle);
}
DwmRegisterThumbnail(win.Handle, User32_Gdi.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;
}
}
}
}
[global::System.Security.Permissions.PermissionSet(global::System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
[global::System.Security.SecuritySafeCritical]
private class MessageWindow : NativeWindow, IDisposable
{
[global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MessageWindow()
{
var cp = new CreateParams { Style = 0, ExStyle = 0, ClassStyle = 0, Parent = IntPtr.Zero, Caption = GetType().Name };
CreateHandle(cp);
}
public void Dispose() => DestroyHandle();
[global::System.Security.Permissions.PermissionSet(global::System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
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 >= WM_DWMCOMPOSITIONCHANGED && m.Msg <= 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 { };
}
}
}
}
}