Files
ShrlAlgoToolkit/WPFluent/Interop/UnsafeNativeMethods.cs

618 lines
19 KiB
C#
Raw Normal View History

// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
/* This Source Code is partially based on reverse engineering of the Windows Operating System,
and is intended for use on Windows systems only.
This Source Code is partially based on the source code provided by the .NET Foundation. */
using Microsoft.Win32;
using WPFluent.Controls;
using WPFluent.Hardware;
using System.Runtime.InteropServices;
namespace WPFluent.Interop;
/// <summary>
/// A set of dangerous methods to modify the appearance.
/// </summary>
public static class UnsafeNativeMethods
{
/// <summary>
/// Tries to set the <see cref="Window"/> corner preference.
/// </summary>
/// <param name="window">Selected window.</param>
/// <param name="cornerPreference">Window corner preference.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowCornerPreference(Window window, WindowCornerPreference cornerPreference) =>
GetHandle(window, out var windowHandle)
&& ApplyWindowCornerPreference(windowHandle, cornerPreference);
/// <summary>
/// Tries to set the corner preference of the selected window.
/// </summary>
/// <param name="handle">Selected window handle.</param>
/// <param name="cornerPreference">Window corner preference.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowCornerPreference(IntPtr handle, WindowCornerPreference cornerPreference)
{
if (handle == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(handle))
{
return false;
}
var pvAttribute = (int)UnsafeReflection.Cast(cornerPreference);
// TODO: Validate HRESULT
_ = Dwmapi.DwmSetWindowAttribute(
handle,
Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE,
ref pvAttribute,
Marshal.SizeOf(typeof(int))
);
return true;
}
/// <summary>
/// Tries to remove ImmersiveDarkMode effect from the <see cref="Window"/>.
/// </summary>
/// <param name="window">The window to which the effect is to be applied.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool RemoveWindowDarkMode(Window? window) =>
GetHandle(window, out var windowHandle) && RemoveWindowDarkMode(windowHandle);
/// <summary>
/// Tries to remove ImmersiveDarkMode effect from the window handle.
/// </summary>
/// <param name="handle">Window handle.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool RemoveWindowDarkMode(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(handle))
{
return false;
}
var pvAttribute = 0x0; // Disable
var dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE;
if (!Win32.Utilities.IsOSWindows11Insider1OrNewer)
{
dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD;
}
// TODO: Validate HRESULT
_ = Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int)));
return true;
}
/// <summary>
/// Tries to apply ImmersiveDarkMode effect for the <see cref="Window"/>.
/// </summary>
/// <param name="window">The window to which the effect is to be applied.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowDarkMode(Window? window) =>
GetHandle(window, out var windowHandle) && ApplyWindowDarkMode(windowHandle);
/// <summary>
/// Tries to apply ImmersiveDarkMode effect for the window handle.
/// </summary>
/// <param name="handle">Window handle.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowDarkMode(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(handle))
{
return false;
}
var pvAttribute = 0x1; // Enable
var dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE;
if (!Win32.Utilities.IsOSWindows11Insider1OrNewer)
{
dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD;
}
// TODO: Validate HRESULT
_ = Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int)));
return true;
}
/// <summary>
/// Tries to remove titlebar from selected <see cref="Window"/>.
/// </summary>
/// <param name="window">The window to which the effect is to be applied.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool RemoveWindowTitlebarContents(Window? window)
{
if (window == null)
{
return false;
}
if (window.IsLoaded)
{
return GetHandle(window, out var windowHandle) && RemoveWindowTitlebarContents(windowHandle);
}
window.Loaded += (sender, _1) =>
{
_ = GetHandle(sender as Window, out var windowHandle);
_ = RemoveWindowTitlebarContents(windowHandle);
};
return true;
}
/// <summary>
/// Tries to remove titlebar from selected window handle.
/// </summary>
/// <param name="handle">Window handle.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool RemoveWindowTitlebarContents(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(handle))
{
return false;
}
var windowStyleLong = User32.GetWindowLong(handle, User32.GWL.GWL_STYLE);
windowStyleLong &= ~(int)User32.WS.SYSMENU;
var result = SetWindowLong(handle, User32.GWL.GWL_STYLE, windowStyleLong);
return result.ToInt64() > 0x0;
}
/// <summary>
/// Tries to apply selected backdrop type for window handle.
/// </summary>
/// <param name="handle">Selected window handle.</param>
/// <param name="backgroundType">Backdrop type.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowBackdrop(IntPtr handle, WindowBackdropType backgroundType)
{
if (handle == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(handle))
{
return false;
}
var backdropPvAttribute = (int)UnsafeReflection.Cast(backgroundType);
if (backdropPvAttribute == (int)Dwmapi.DWMSBT.DWMSBT_DISABLE)
{
return false;
}
// TODO: Validate HRESULT
_ = Dwmapi.DwmSetWindowAttribute(
handle,
Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
ref backdropPvAttribute,
Marshal.SizeOf(typeof(int))
);
return true;
}
/// <summary>
/// Tries to determine whether the provided <see cref="Window"/> has applied legacy backdrop effect.
/// </summary>
/// <param name="handle">Window handle.</param>
/// <param name="backdropType">Background backdrop type.</param>
public static bool IsWindowHasBackdrop(IntPtr handle, WindowBackdropType backdropType)
{
if (!User32.IsWindow(handle))
{
return false;
}
var pvAttribute = 0x0;
_ = Dwmapi.DwmGetWindowAttribute(
handle,
Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
ref pvAttribute,
Marshal.SizeOf(typeof(int))
);
return pvAttribute == (int)UnsafeReflection.Cast(backdropType);
}
/// <summary>
/// Tries to determine whether the provided <see cref="Window"/> has applied legacy Mica effect.
/// </summary>
/// <param name="window">Window to check.</param>
public static bool IsWindowHasLegacyMica(Window? window) =>
GetHandle(window, out var windowHandle) && IsWindowHasLegacyMica(windowHandle);
/// <summary>
/// Tries to determine whether the provided handle has applied legacy Mica effect.
/// </summary>
/// <param name="handle">Window handle.</param>
public static bool IsWindowHasLegacyMica(IntPtr handle)
{
if (!User32.IsWindow(handle))
{
return false;
}
var pvAttribute = 0x0;
_ = Dwmapi.DwmGetWindowAttribute(
handle,
Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT,
ref pvAttribute,
Marshal.SizeOf(typeof(int))
);
return pvAttribute == 0x1;
}
/// <summary>
/// Tries to apply legacy Mica effect for the selected <see cref="Window"/>.
/// </summary>
/// <param name="window">The window to which the effect is to be applied.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowLegacyMicaEffect(Window? window) =>
GetHandle(window, out var windowHandle) && ApplyWindowLegacyMicaEffect(windowHandle);
/// <summary>
/// Tries to apply legacy Mica effect for the selected <see cref="Window"/>.
/// </summary>
/// <param name="handle">Window handle.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowLegacyMicaEffect(IntPtr handle)
{
var backdropPvAttribute = 0x1; // Enable
// TODO: Validate HRESULT
_ = Dwmapi.DwmSetWindowAttribute(
handle,
Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT,
ref backdropPvAttribute,
Marshal.SizeOf(typeof(int))
);
return true;
}
/// <summary>
/// Tries to apply legacy Acrylic effect for the selected <see cref="Window"/>.
/// </summary>
/// <param name="window">The window to which the effect is to be applied.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowLegacyAcrylicEffect(Window? window) =>
GetHandle(window, out var windowHandle) && ApplyWindowLegacyAcrylicEffect(windowHandle);
/// <summary>
/// Tries to apply legacy Acrylic effect for the selected <see cref="Window"/>.
/// </summary>
/// <param name="handle">Window handle</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyWindowLegacyAcrylicEffect(IntPtr handle)
{
var accentPolicy = new User32.ACCENT_POLICY
{
nAccentState = User32.ACCENT_STATE.ACCENT_ENABLE_ACRYLICBLURBEHIND,
nColor = 0x990000 & 0xFFFFFF,
};
var accentStructSize = Marshal.SizeOf(accentPolicy);
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accentPolicy, accentPtr, false);
var data = new User32.WINCOMPATTRDATA
{
Attribute = User32.WCA.WCA_ACCENT_POLICY,
SizeOfData = accentStructSize,
Data = accentPtr,
};
_ = User32.SetWindowCompositionAttribute(handle, ref data);
Marshal.FreeHGlobal(accentPtr);
return true;
}
/// <summary>
/// Tries to get currently selected Window accent color.
/// </summary>
public static Color GetDwmColor()
{
try
{
Dwmapi.DwmGetColorizationParameters(out var dwmParams);
var values = BitConverter.GetBytes(dwmParams.clrColor);
return Color.FromArgb(255, values[2], values[1], values[0]);
}
catch
{
var colorizationColorValue = Registry.GetValue(
@"HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM",
"ColorizationColor",
null
);
if (colorizationColorValue is not null)
{
try
{
var colorizationColor = (uint)(int)colorizationColorValue;
var values = BitConverter.GetBytes(colorizationColor);
return Color.FromArgb(255, values[2], values[1], values[0]);
}
catch { }
}
}
return GetDefaultWindowsAccentColor();
}
/// <summary>
/// Tries to set taskbar state for the selected window handle.
/// </summary>
/// <param name="hWnd">Window handle.</param>
/// <param name="taskbarFlag">Taskbar flag.</param>
internal static bool SetTaskbarState(IntPtr hWnd, ShObjIdl.TBPFLAG taskbarFlag)
{
if (hWnd == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(hWnd))
{
return false;
}
if (new ShObjIdl.CTaskbarList() is not ShObjIdl.ITaskbarList4 taskbarList)
{
return false;
}
taskbarList.HrInit();
taskbarList.SetProgressState(hWnd, taskbarFlag);
return true;
}
/// <summary>
/// Updates the taskbar progress bar value for a window.
/// </summary>
/// <param name="hWnd">The handle to the window.</param>
/// <param name="taskbarFlag">Progress state flag (paused, etc).</param>
/// <param name="current">Current progress value.</param>
/// <param name="total">Maximum progress value.</param>
/// <returns>True if successful updated, otherwise false.</returns>
internal static bool SetTaskbarValue(IntPtr hWnd, ShObjIdl.TBPFLAG taskbarFlag, int current, int total)
{
if (hWnd == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(hWnd))
{
return false;
}
/* TODO: Get existing taskbar class */
if (new ShObjIdl.CTaskbarList() is not ShObjIdl.ITaskbarList4 taskbarList)
{
return false;
}
taskbarList.HrInit();
taskbarList.SetProgressState(hWnd, taskbarFlag);
if (taskbarFlag is not ShObjIdl.TBPFLAG.TBPF_INDETERMINATE and not ShObjIdl.TBPFLAG.TBPF_NOPROGRESS)
{
taskbarList.SetProgressValue(hWnd, Convert.ToUInt64(current), Convert.ToUInt64(total));
}
return true;
}
public static bool RemoveWindowCaption(Window window)
{
if (window is null)
{
return false;
}
var windowHandle = new WindowInteropHelper(window).Handle;
return RemoveWindowCaption(windowHandle);
}
public static bool RemoveWindowCaption(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(hWnd))
{
return false;
}
var wtaOptions = new UxTheme.WTA_OPTIONS()
{
dwFlags = UxTheme.WTNCA.NODRAWCAPTION,
dwMask = UxTheme.WTNCA.VALIDBITS,
};
UxTheme.SetWindowThemeAttribute(
hWnd,
UxTheme.WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT,
ref wtaOptions,
(uint)Marshal.SizeOf(typeof(UxTheme.WTA_OPTIONS))
);
return true;
}
public static bool ExtendClientAreaIntoTitleBar(Window window)
{
if (window is null)
{
return false;
}
var windowHandle = new WindowInteropHelper(window).Handle;
return ExtendClientAreaIntoTitleBar(windowHandle);
}
public static bool ExtendClientAreaIntoTitleBar(IntPtr hWnd)
{
/*
* !! EXPERIMENTAl !!
* NOTE: WinRt has ExtendContentIntoTitlebar, but it needs some digging
*/
if (hWnd == IntPtr.Zero)
{
return false;
}
if (!User32.IsWindow(hWnd))
{
return false;
}
// #1 Remove titlebar elements
var wtaOptions = new UxTheme.WTA_OPTIONS()
{
dwFlags = UxTheme.WTNCA.NODRAWCAPTION | UxTheme.WTNCA.NODRAWICON | UxTheme.WTNCA.NOSYSMENU,
dwMask = UxTheme.WTNCA.VALIDBITS,
};
UxTheme.SetWindowThemeAttribute(
hWnd,
UxTheme.WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT,
ref wtaOptions,
(uint)Marshal.SizeOf(typeof(UxTheme.WTA_OPTIONS))
);
DisplayDpi windowDpi = DpiHelper.GetWindowDpi(hWnd);
// #2 Extend glass frame
Thickness deviceGlassThickness = DpiHelper.LogicalThicknessToDevice(
new Thickness(-1, -1, -1, -1),
windowDpi.DpiScaleX,
windowDpi.DpiScaleY
);
var dwmMargin = new UxTheme.MARGINS
{
// err on the side of pushing in glass an extra pixel.
cxLeftWidth = (int)Math.Ceiling(deviceGlassThickness.Left),
cxRightWidth = (int)Math.Ceiling(deviceGlassThickness.Right),
cyTopHeight = (int)Math.Ceiling(deviceGlassThickness.Top),
cyBottomHeight = (int)Math.Ceiling(deviceGlassThickness.Bottom),
};
// #3 Extend client area
Dwmapi.DwmExtendFrameIntoClientArea(hWnd, ref dwmMargin);
// #4 Clear rounding region
User32.SetWindowRgn(hWnd, IntPtr.Zero, User32.IsWindowVisible(hWnd));
return true;
}
/// <summary>
/// Checks whether the DWM composition is enabled.
/// </summary>
public static bool IsCompositionEnabled()
{
_ = Dwmapi.DwmIsCompositionEnabled(out var isEnabled);
return isEnabled == 0x1;
}
/// <summary>
/// Checks if provided pointer represents existing window.
/// </summary>
public static bool IsValidWindow(IntPtr hWnd)
{
return User32.IsWindow(hWnd);
}
/// <summary>
/// Tries to get the pointer to the window handle.
/// </summary>
/// <returns><see langword="true"/> if the handle is not <see cref="IntPtr.Zero"/>.</returns>
private static bool GetHandle(Window? window, out IntPtr windowHandle)
{
if (window is null)
{
windowHandle = IntPtr.Zero;
return false;
}
windowHandle = new WindowInteropHelper(window).Handle;
return windowHandle != IntPtr.Zero;
}
private static IntPtr SetWindowLong(IntPtr handle, User32.GWL nIndex, long windowStyleLong)
{
if (IntPtr.Size == 4)
{
return new IntPtr(User32.SetWindowLong(handle, (int)nIndex, (int)windowStyleLong));
}
return User32.SetWindowLongPtr(handle, (int)nIndex, checked((IntPtr)windowStyleLong));
}
private static Color GetDefaultWindowsAccentColor()
{
// Windows default accent color
// https://learn.microsoft.com/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-themes-windowcolor#values
return Color.FromArgb(0xff, 0x00, 0x78, 0xd7);
}
}