Search code examples
c#powershellpinvoke

Pinvoking user32.dll "SetWindowCompositionAttribute" via PowerShell


I've been struggling to get PowerShell to control SetWindowCompositionAttribute via Pinvoke (to add Windows 10 Acrylic Blur effect to the Windows taskbar and other windows via PowerShell).

I am comfortable sending simple parameters like HWND and respective values (as shown in two of my examples below). However, I’m not sure how to enhance the PowerShell code below so it can also handle sending parameters packed in a struct. See SetWindowCompositionAttribute.

I've seen code examples in Delphi and AutoIt (instead of PowerShell) that do this. Unfortunately, I couldn't figure them out.

Anyway, below is my working PowerShell code and usage examples that demonstrate basic interaction with the Windows API. I'm hoping someone could help me enhance this code (with a couple of examples) to also control various features offered by SetWindowCompositionAttribute.

Ultimately, I'd like to be able to specify hWnd, Windows class name, and/or Window title name of the component I wish to add blur too; and, specify the amount of blur/transparency if possible.

Working Functions and example Usage:

$script:nativeMethods = @();

function Register-NativeMethod([string]$dll, [string]$methodSignature) {
    $script:nativeMethods += [PSCustomObject]@{ Dll = $dll; Signature = $methodSignature; }
}

function Add-NativeMethods() {
    $nativeMethodsCode = $script:nativeMethods | % { "
        [DllImport(`"$($_.Dll)`")]
        public static extern $($_.Signature);
    " }

    Add-Type @"
        using System;
        using System.Runtime.InteropServices;
        public class NativeMethods {
            $nativeMethodsCode
        }
"@
}

#Build class and registers them:
Add-NativeMethods


#Example 1:
Register-NativeMethod "user32.dll" "bool SetForegroundWindow(IntPtr hWnd)"
[NativeMethods]::SetForegroundWindow((Get-Process -name notepad).MainWindowHandle)

#Example 2:
Register-NativeMethod "user32.dll" "bool ShowWindow(IntPtr hWnd, int nCmdShow)"
[NativeMethods]::ShowWindow((Get-Process -name notepad).MainWindowHandle, 0)

Solution

  • EDIT: Added an option for "acrylic" blur with color tinting. It seems to be a bit slow though when moving the windows about.

    Is this what you're after?

    Window before running function:

    Before function

    Window after running function (Set-WindowBlur -MainWindowHandle 853952 -Enable):

    After function

    Main code:

    $SetWindowComposition = @'
    [DllImport("user32.dll")]
    public static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
    
    [StructLayout(LayoutKind.Sequential)]
    public struct WindowCompositionAttributeData {
        public WindowCompositionAttribute Attribute;
        public IntPtr Data;
        public int SizeOfData;
    }
    
    public enum WindowCompositionAttribute {
        WCA_ACCENT_POLICY = 19
    }
    
    public enum AccentState {
        ACCENT_DISABLED = 0,
        ACCENT_ENABLE_BLURBEHIND = 3,
        ACCENT_ENABLE_ACRYLICBLURBEHIND = 4
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct AccentPolicy {
        public AccentState AccentState;
        public int AccentFlags;
        public int GradientColor;
        public int AnimationId;
    }
    '@
    Add-Type -MemberDefinition $SetWindowComposition -Namespace 'WindowStyle' -Name 'Blur'
    function Set-WindowBlur {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory)]
            [int]
            $MainWindowHandle,
            [Parameter(ParameterSetName='Enable',Mandatory)]
            [switch]
            $Enable,
            [Parameter(ParameterSetName='Acrylic',Mandatory)]
            [switch]
            $Acrylic,
            # Color in BGR hex format (for ease, will just be used as an integer), eg. for red use 0x0000FF
            [Parameter(ParameterSetName='Acrylic')]
            [ValidateRange(0x000000, 0xFFFFFF)]
            [int]
            $Color= 0x000000,
            # Transparency 0-255, 0 full transparency and 255 is a solid $Color
            [Parameter(ParameterSetName='Acrylic')]
            [ValidateRange(0, 255)]
            [int]
            $Transparency = 80,
            [Parameter(ParameterSetName='Disable',Mandatory)]
            [switch]
            $Disable
        )
        $Accent = [WindowStyle.Blur+AccentPolicy]::new()
        switch ($PSCmdlet.ParameterSetName) {
            'Enable' {
                $Accent.AccentState = [WindowStyle.Blur+AccentState]::ACCENT_ENABLE_BLURBEHIND
            }
            'Acrylic' {
                $Accent.AccentState = [WindowStyle.Blur+AccentState]::ACCENT_ENABLE_ACRYLICBLURBEHIND
                $Accent.GradientColor = $Transparency -shl 24 -bor ($Color -band 0xFFFFFF)
            }
            'Disable' {
                $Accent.AccentState = [WindowStyle.Blur+AccentState]::ACCENT_DISABLED
            }
        }
        $AccentStructSize = [System.Runtime.InteropServices.Marshal]::SizeOf($Accent)
        $AccentPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($AccentStructSize)
        [System.Runtime.InteropServices.Marshal]::StructureToPtr($Accent,$AccentPtr,$false)
        $Data = [WindowStyle.Blur+WindowCompositionAttributeData]::new()
        $Data.Attribute = [WindowStyle.Blur+WindowCompositionAttribute]::WCA_ACCENT_POLICY
        $Data.SizeOfData = $AccentStructSize
        $Data.Data = $AccentPtr
        $Result = [WindowStyle.Blur]::SetWindowCompositionAttribute($MainWindowHandle,[ref]$Data)
        if ($Result -eq 1) {
            Write-Verbose "Successfully set Window Blur status."
        }
        else {
            Write-Verbose "Warning, couldn't set Window Blur status."
        }
        [System.Runtime.InteropServices.Marshal]::FreeHGlobal($AccentPtr)
    }
    

    To use call with something like:

    Set-WindowBlur -MainWindowHandle 1114716 -Acrylic -Color 0xFF0000 -Transparency 50
    Set-WindowBlur -MainWindowHandle 1114716 -Disable
    Set-WindowBlur -MainWindowHandle 1114716 -Enable
    

    Adapted from: https://gist.github.com/riverar/fd6525579d6bbafc6e48 and from: https://github.com/riverar/sample-win32-acrylicblur/blob/master/MainWindow.xaml.cs