3

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)
MKANET
  • 573
  • 6
  • 27
  • 51
  • 1
    It's certainly feasible to p/invokeit given the correct definition of the structure is provided. However, did you consider using [DwmSetWindowAttribute](https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute)? Seems to be doing the same thing but takes 'simple' input values though – odalet Feb 07 '21 at 04:16
  • Thanks. Any idea where I see an example of how to use it to enable Win10 Acrylic Blur effect to a Window? Ultimately, I would like to do that.. using Powershell. – MKANET Feb 07 '21 at 08:37
  • *Not an answer to this question*, but if you know how to do something in C#, then you can easily to it in PowerShell as well, Take a look at [this example](https://stackoverflow.com/a/60203173/3110834) to see how to run C# code in powershell. – Reza Aghaei Feb 08 '21 at 13:21

2 Answers2

4

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

PMental
  • 1,091
  • 6
  • 12
  • This is beautifully done! I am able to add blur to various Windows on my desktop; including Windows File Explorer. I wasn't able to add blur effect to the Windows 10 Taskbar though. Maybe, I'm not specifying the correct MainWindowHandle? Any tips to get this to add blur the Taskbar? – MKANET Feb 08 '21 at 01:41
  • @MKANET - I would have thought it would be the handle of the `exploer.exe` process. (and any changes would affect both file explorer and taskbar) – G42 Feb 08 '21 at 01:44
  • @PMental, thank you so much. It works great. I wasn't able to affect the Windows Taskbar in any way though; even when specifying explorer.exe's `MainWindowHandle`. Lastly, how do I can specify `gradientcolor` value of the blur effect? No worries, if it requires significant code changes. – MKANET Feb 08 '21 at 05:11
  • @MKANET It actually does work for the Task bar on my computer using the explorer.exe handle, but for some reason only if you open an Explorer window first. Not quite sure why. Not sure if the gradientcolor is for the blur effect, but I can take a look if I can find something for that. – PMental Feb 08 '21 at 16:32
  • @PMental Thank you so much. If you figure out a way to also blur color (R,G,B) without making any significant changes, please feel free to email me mkanet@yahoo.com or maybe edit your answer. In any case, please accept the +50point bounty. The current solution is very nicely done! – MKANET Feb 08 '21 at 18:35
  • @MKANET I did figure it out actually, with help from another reference. I've updated the code. Unfortunately using the new option (acrylic) seems to make windows a bit sluggish when moving about. It does work with color and you can select a transparency level for the color. Considering that issue I kept the old option as well and added the new one separately. – PMental Feb 08 '21 at 18:55
  • @PMental Your code is just awesome! The best way for me to learn is by manipulating real working examples like yours. I was also able to colorize my taskbar, etc. Very impressive. – MKANET Feb 08 '21 at 23:45
  • 1
    @MKANET `HWND handle = FindWindow(L"Shell_TrayWnd", NULL);` (C++ code) gets the correct taskbar handle. – AlephNot May 26 '21 at 15:15
0

I can see you invoke these Windows API functions by embedding/building C# source code inside your Powershell script. It's probably the way to go, although I think I wouldn't have bothered with trying to 'add' methods and construct the C# source code piece by piece (because declaring SetWindowCompositionAttribute using your system won't be that easy).

First, because SetWindowCompositionAttribute accepts complex arguments you'll have to declare the underlying structures in you C# code, then you can expose C# methods that simplify the use of SetWindowCompositionAttribute for consumption by the Powershell side.

By googling a bit (simply typed TheMethodINeedToWrap C#), I quickly found a few resources. I've mixed these findings into a C# class that exposes a (simple enough that it is Powershell-callable) method to blur a window. The results of my tests were not ideal as I ended up with a blurred window border but weird looking interior and controls (must have something to do with winforms and/or the window creation parameters), but at least I obtained something.

Because it's a bit long, I've saved this class as a gist here, but as I said it's made of a bunch of interop structures and methods definitions, then a public method simplifying access to SetWindowCompositionAttribute.

PS: If SetWindowCompositionAttribute proves too difficult to wrap, DwmSetWindowAttribute may be a viable aleternative. You'll also find that, although it sometimes needs tweaking, http://pinvoke.net/ proves a viable source for interop definitions.

odalet
  • 1,389
  • 10
  • 18