通过 PowerShell 调用 user32.dll 中的 "SetWindowCompositionAttribute" 函数。

3
我一直在努力让PowerShell通过Pinvoke控制SetWindowCompositionAttribute (通过PowerShell为Windows任务栏和其他窗口添加Windows 10亚克力模糊效果)。 我可以发送像HWND和相应值这样的简单参数(如下面两个示例所示)。 但是,我不知道如何改进下面的PowerShell代码,以便还可以处理打包在结构中的参数。 请参见SetWindowCompositionAttribute
我看到了DelphiAutoIt(而不是PowerShell)中的代码示例可以做到这一点。 不幸的是,我无法理解它们。

无论如何,下面是我的PowerShell代码和使用示例,演示了与Windows API的基本交互。我希望有人能帮助我增强这个代码(带有几个示例),以控制SetWindowCompositionAttribute提供的各种功能。

最终,如果可能的话,我想能够指定组件的hWnd、Windows类名称和/或窗口标题名称,并指定模糊/透明度的程度。

可用函数和示例用法:

$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)

1
如果提供了正确的结构定义,使用p/invoke是完全可行的。但是,您是否考虑过使用DwmSetWindowAttribute?它似乎可以完成相同的工作,但只需要“简单”的输入值。 - odalet
谢谢。你知道在哪里可以找到如何使用它来启用Win10亚克力模糊效果的窗口的示例吗?最终,我想使用PowerShell实现这一点。 - MKANET
虽然这不是对这个问题的回答,但如果你知道如何在C#中做某件事情,那么你也可以很容易地在PowerShell中实现它。请看这个例子,了解如何在PowerShell中运行C#代码。 - Reza Aghaei
2个回答

4

编辑:添加了“亚克力”模糊和色调渲染选项。不过当移动窗口时似乎有点慢。

这是你想要的吗?

运行函数之前的窗口:

Before function

运行函数之后的窗口(Set-WindowBlur -MainWindowHandle 853952 -Enable):

After function

主要代码:

$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)
}

使用时可用如下方式调用:
Set-WindowBlur -MainWindowHandle 1114716 -Acrylic -Color 0xFF0000 -Transparency 50
Set-WindowBlur -MainWindowHandle 1114716 -Disable
Set-WindowBlur -MainWindowHandle 1114716 -Enable

翻译自:https://gist.github.com/riverar/fd6525579d6bbafc6e48 和: https://github.com/riverar/sample-win32-acrylicblur/blob/master/MainWindow.xaml.cs


这个做得很好!我能够将模糊效果添加到桌面上的各种窗口,包括Windows文件资源管理器。但是,我无法为Windows 10任务栏添加模糊效果。也许我没有指定正确的MainWindowHandle?有什么提示可以让它模糊化任务栏吗? - MKANET
@PMental,非常感谢。它运行得很好。但是我无法以任何方式影响Windows任务栏,即使指定了explorer.exe的MainWindowHandle。最后,我该如何指定模糊效果的gradientcolor值?如果需要重大代码更改也不用担心。 - MKANET
@MKANET 在我的电脑上,使用 explorer.exe 句柄确实可以在任务栏上工作,但出于某种原因,只有在您先打开一个资源管理器窗口时才能正常工作。不太确定渐变颜色是否用于模糊效果,但如果我能找到相关信息,我可以看一下。 - PMental
@PMental,你的代码真是太棒了!对我来说,最好的学习方式就是通过像你这样的真实工作示例进行操作。我还能够给我的任务栏上色等等。非常令人印象深刻。 - MKANET
1
@MKANET HWND handle = FindWindow(L"Shell_TrayWnd", NULL);(C++代码)可以获取正确的任务栏句柄。 - AlephNot
显示剩余3条评论

0

我可以看到你通过在 Powershell 脚本中嵌入/构建 C# 源代码来调用这些 Windows API 函数。这可能是正确的方法,尽管我认为我不会费心去尝试逐步“添加”方法并构建 C# 源代码(因为使用系统声明 SetWindowCompositionAttribute 不会那么容易)。

首先,因为 SetWindowCompositionAttribute 接受复杂参数,所以您必须在 C# 代码中声明基础结构,然后您可以公开 C# 方法,简化 Powershell 端使用 SetWindowCompositionAttribute 的方法。

通过谷歌一下(只需键入 TheMethodINeedToWrap C#),我很快找到了一些资源。我将这些发现混合到一个 C# 类中,该类公开了一个(足够简单,可供 Powershell 调用的)模糊窗口的方法。我的测试结果并不理想,因为我最终得到了一个模糊的窗口边框,但内部和控件看起来很奇怪(可能与 winforms 和/或窗口创建参数有关),但至少我得到了一些东西。

因为它有点长,所以我将这个类保存为一个gist 这里,但正如我所说,它是由一堆互操作结构和方法定义组成的,然后是一个公共方法,简化了对SetWindowCompositionAttribute的访问。
PS:如果SetWindowCompositionAttribute难以包装,DwmSetWindowAttribute可能是一个可行的替代方案。您还会发现,尽管有时需要调整,http://pinvoke.net/ 也是一个可行的互操作定义来源。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接