PowerShell控制台玻璃主题

给你的PowerShell控制台一款玻璃主题


有没有那么一天你坐在电脑桌面旁边用着这个PowerShell蓝底白字控制台,发出哀怨:“你怎么老是这样一成不变,就不能换身马甲?比如换一身玻璃主题嘛?”

果真这样,朋友,你有福了。我将会和你一起把这个控制台转换成玻璃控制台,当然这本身提高不了你的编码能力,但是可以让别人经过你身旁时好奇的问一句:“咦,你刚才打字的这个窗口,是个什么东东?”

为了完成这件壮举,我不能不穿越至平台调用(P/Invoke)的世界,Win32 APIs的世界还是挺乱的。不过很幸运,我发现个神奇网站 (它就是 PInvoke.net?),这网站是干哈的呢,它专门列出一些我们可以调用的API和方法签名。

PowerShell玻璃主题的控制台

PowerShell玻璃主题的控制台

PowerShell玻璃主题的控制台

PowerShell玻璃主题的控制台

正如你前两张图片看到的那样,我会使用 dwmapi.dll 中两个函数dwmextendframeintoclientareaDwmIsCompositionEnabled 。通常情况下,我们会提出把一些放在 Here-String中 C# 代码,通过Add-Type来编译,仅此而已,然后它就会被加载进PowerShell会话。

但是今天我们不这样做,我会另辟蹊径,通过反射来把所有东西加载进内存,而不是编译成一个文件。为什么我要这样做?因为它提供给了我们另外一条思路。

继续往下走,先创建一个模块生成器,以它为基础,剩下的事也就是我要编译的:

#region Module Builder

$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object System.Reflection.AssemblyName('AeroAssembly')

# Only run in memory
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('AeroModule', $False)

#endregion Module Builder

这里,我创建了一个程序集(在内存中),然后编译这个模块。接下里需要一个能够支持窗口(该场景中,也就是windows PowerShell控制台了)margin的函数。要创建它,须要使用我的模块生成器。

#region STRUCTs 
#region Margins
$Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
$TypeBuilder = $ModuleBuilder.DefineType('MARGINS', $Attributes, [System.ValueType], 1, 0x10)
[void]$TypeBuilder.DefineField('left', [Int], 'Public')
[void]$TypeBuilder.DefineField('right', [Int], 'Public')
[void]$TypeBuilder.DefineField('top', [Int], 'Public')
[void]$TypeBuilder.DefineField('bottom', [Int], 'Public')

 #Create STRUCT Type
[void]$TypeBuilder.CreateType()

#endregion Margins
#endregion STRUCTs

现在我有Structs了,可以这样非常简单的创建对象:

PS C:\> New-Object MARGINS

left right top bottom

---- ----- --- ------

0     0      0     0

接下里我开始生成了我的类型,PInvoke 函数将在这里作为一个方法。这和我们从一个类型accelerator中调用方法是类似的,比如:[math]

#region DllImport
$TypeBuilder = $ModuleBuilder.DefineType('Aero', 'Public, Class')

Aero类型实际上还没有创建好,所以我们尝试调用 [Aero] 会以失败告终。接下里开始定义函数,并且作为方法把它加载成类型。

#region DwmExtendFrameIntoClientArea Method

$PInvokeMethod = $TypeBuilder.DefineMethod( 'DwmExtendFrameIntoClientArea',
 #Method Name
 [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
 [Void], #Method Return Type
 [Type[]] @([IntPtr],[Margins]) #Method Parameters

)

$DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
$FieldArray = [Reflection.FieldInfo[]] @(
 [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
 [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')

)

 $FieldValueArray = [Object[]] @(
 'DwmExtendFrameIntoClientArea', 
 #CASE SENSITIVE!!
 $False
)

$CustomAttributeBuilder = New-Object Reflection.Emit.CustomAttributeBuilder(
 $DllImportConstructor,
 @('dwmapi.dll'),
 $FieldArray,
 $FieldValueArray

)

$PInvokeMethod.SetCustomAttribute($CustomAttributeBuilder)
#endregion DwmExtendFrameIntoClientArea Method

这里我使用我的类型开始定义方法。在之前的图片中显示了函数期望的返回类型(这里,使用了[void],虽然方法签名显示为INT,但是我没有定义它)。
图片中还定义了这个方法所需的参数,我们也要相应处理,防止它抛出异常。这里,我要提供了INTPTR 和之前定义的MARGINS 结构,因为我会把两个方法都定义在一个dll,所以会使用已经定义好的类型开始构造下一个方法。

#region DwmIsCompositionEnabled Method

$PInvokeMethod = $TypeBuilder.DefineMethod(
 'DwmIsCompositionEnabled', #Method Name
 [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
 [Bool], #Method Return Type
 $Null #Method Parameters

)

$DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
$FieldArray = [Reflection.FieldInfo[]] @(
 [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
 [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')

)

$FieldValueArray = [Object[]] @(
 'DwmIsCompositionEnabled', #CASE SENSITIVE!!
 $False
)

$CustomAttributeBuilder = New-Object Reflection.Emit.CustomAttributeBuilder(
 $DllImportConstructor,
 @('dwmapi.dll'),
 $FieldArray,
 $FieldValueArray
)

$PInvokeMethod.SetCustomAttribute($CustomAttributeBuilder)

#endregion DwmIsCompositionEnabled Method

这和我之前所做的是一样的,我定义了另一个方法(DwmIsCompositionEnabled)并且提供了正确的返回类型,这里不需要参数,所以我没碰它。

借助于定义在该类型中两个方法,下一步只需完成类型创建,并把它加载进Windows PowerShell控制台。

[void]$TypeBuilder.CreateType()
#endregion DllImport

这样有用吗?我们看一下这个类型和它支持的方法:

和期望的一样,不旦创建的类型有用,而且定义的方法也包含了所须的参数。最后仅需运行一点代码创建MARGINS 对象,并把它应用到Windows PowerShell控制台:

# Desktop Window Manager (DWM) is always enabled in Windows 8

# Calling DwmIsCompsitionEnabled() only applies if running Vista or Windows 7
If ([Aero]::DwmIsCompositionEnabled()) {
 $hwnd = (Get-Process -Id $PID).mainwindowhandle
 $margin = New-Object 'MARGINS'

 Switch ($PSCmdlet.ParameterSetName) {
 'Enable' {
 # Negative values create the 'glass' effect
 $margin.top = -1
 $margin.left = -1 
 $margin.right = -1 
 $margin.bottom = -1 
 $host.ui.RawUI.BackgroundColor = "black"
 $host.ui.rawui.Foregroundcolor = "white" 
 Clear-Host
 }
 }

 [Aero]::DwmExtendFrameIntoClientArea($hwnd, $margin)
} Else {
 Write-Warning "Aero is either not available or not enabled on this workstation."
}

最后的结果就是让Windows PowerShell 控制台拥有了玻璃主题:

PowerShell控制台玻璃主题

PowerShell控制台玻璃主题

我更喜欢把控制台背景设置成黑色,这样玻璃主题应用后控制台标题栏和控制台本是颜色融为一体,当然你也可以试着改成其它颜色,不过别忘了改了控制台背景色后要使用clear-host命令来刷新控制台。另外尽量把背景色和前景色设置地不要太相近,这样看文字才会更清楚。

要把控制台改回原来的样子,也很简单,只需把MARGINS设置重置成0即可。

$margin.top = 0
$margin.left = 0 
$margin.right = 0 
$margin.bottom = 0 
[Aero]::DwmExtendFrameIntoClientArea($hwnd, $margin)

当然我把整理后的脚本发布到了脚本中心,你可以在ISE的脚本浏览器中搜索:“Enable Glass Console Theme”。

然后这样调用即可:

. .\Set-Aeroglass.ps1
Set-AeroGlass -Enable
Set-AeroGlass -Disable

原文作者:Boe Proxy
原文链接Give Your PowerShell Console a Glassy Theme

本文链接: https://www.pstips.net/give-you-a-glassy-console-theme.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!

关于 Mooser Lee

我是一个Powershell的爱好者,创建了PowerShell中文博客,热衷于Powershell技术的搜集和分享。本站部分内容来源于互联网,不足之处敬请谅解,并欢迎您批评指正。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注