使用PowerShell隐藏Windows终端控制台窗口



背景

  • 我想在PowerShell脚本中隐藏控制台窗口。

    • 编辑:我让这个脚本停留在系统托盘图标中,并隐藏在任务栏中。此脚本使用OneDrive存储屏幕截图。当你运行此脚本时,你必须向OneDrive进行身份验证,因此首先你不能使用-WindowStyle Hidden选项运行此脚本(应该显示身份验证窗口)。验证后,我想从任务栏中隐藏终端,并显示系统托盘图标
  • 在Windows11上,当您将Windows Console Host设置为";默认终端应用程序";在Windows终端的"启动"设置中,可以隐藏控制台窗口,如下所示:

$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$hwnd = (Get-Process -PID $pid).MainWindowHandle
if ($hwnd -ne [System.IntPtr]::Zero) {
$hidden = $asyncwindow::ShowWindowAsync($hwnd, 0)
}

问题

在Windows11上,当您将Windows Terminal设置为";默认终端应用程序";在Windows终端的"启动"设置中,使用上面的代码无法获得控制台窗口的窗口句柄。

代替上面的代码,你可以得到这样的窗口句柄:

Add-Type -Name ConsoleAPI -Namespace Win32Util -MemberDefinition '[DllImport("Kernel32.dll")] public static extern IntPtr GetConsoleWindow();'
$hwnd = [Win32Util.ConsoleAPI]::GetConsoleWindow()
$hidden = $asyncwindow::ShowWindowAsync($hwnd, 0)

但是在这个代码中,ShowWindowAsync($hwnd, 0)不能正常工作。根据ShowWindowAsync的文档,当您将0作为第二个参数传递时,它会隐藏窗口。当我运行上面的代码时,Windows终端窗口是最小化的,而不是隐藏的。

问题

当您将Windows Terminal设置为";默认终端应用程序";在Windows 11上的Windows终端的启动设置中?

所以,根据我的评论。在ISE/控制台/窗口终端。。。只需这样做:

$host.UI.RawUI.WindowTitle = 'YourCoolScriptName'
$stringbuilder             = New-Object System.Text.StringBuilder 256
$windowcode                = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow               = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$hwnd                      = (Get-Process -PID $pid).MainWindowHandle
$count                     = [UserWindows]::GetWindowText($hwnd, $stringbuilder, 256)
"The name of this window is: $($stringbuilder.ToString())"

更新:

根据我对"@Luuk"的评论

从很早的DOS/Windows开始,你就可以用这种方式做同样的事情。

title YourCoolScriptName

甚至.Net命名空间

[System.Console]::Title = 'YourCoolScriptName'

我确实认为您试图隐藏错误的窗口。我(试图)添加WindowTitle,但它的不正确(不是预期值)

我把代码改为只响10次,然后停止,并添加了一些在这里找到的代码:https://stackoverflow.com/a/40354761/724039

Add-Type @"
using System;
using System.Runtime.InteropServices;
public class UserWindows {
[DllImport("user32.dll")]
public static extern IntPtr GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count);
}
"@
$stringbuilder = New-Object System.Text.StringBuilder 256
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$hwnd = (Get-Process -PID $pid).MainWindowHandle
$count = [UserWindows]::GetWindowText($hwnd, $stringbuilder, 256)
"The name of this window is: $($stringbuilder.ToString())"
if ($hwnd -ne [System.IntPtr]::Zero) {
$hidden = $asyncwindow::ShowWindowAsync($hwnd, 0)
}
$run = $true
$x=1
while ($x -le 10) {
[console]::beep(500,100)
Start-Sleep 1
$x++
}

使用快捷方式启动时:pwsh.exe -File "d:tempbeep1.ps1"

打开一个带有文本The name of this window is:的窗口,我听到10声蜂鸣声,执行停止。

当从"内部"执行时;Windows Powershell ISE";,该IDE的窗口关闭。经过一些调试,我发现输出是";此窗口的名称为:Windows PowerShell ISE";。

我确实认为获取WindowText的返回值应该像快捷方式的标题一样。(但是,不幸的是,我不知道如何解决这个问题)

基于@postanote和@Luuk的回答,我制作了一个函数来隐藏控制台窗口,如下所示:

function Hide-ConsoleWindow() {
$ShowWindowAsyncCode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$ShowWindowAsync = Add-Type -MemberDefinition $ShowWindowAsyncCode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$hwnd = (Get-Process -PID $pid).MainWindowHandle
if ($hwnd -ne [System.IntPtr]::Zero) {
# When you got HWND of the console window:
# (It would appear that Windows Console Host is the default terminal application)
$ShowWindowAsync::ShowWindowAsync($hwnd, 0)
} else {
# When you failed to get HWND of the console window:
# (It would appear that Windows Terminal is the default terminal application)
# Mark the current console window with a unique string.
$UniqueWindowTitle = New-Guid
$Host.UI.RawUI.WindowTitle = $UniqueWindowTitle
$StringBuilder = New-Object System.Text.StringBuilder 1024
# Search the process that has the window title generated above.
$TerminalProcess = (Get-Process | Where-Object { $_.MainWindowTitle -eq $UniqueWindowTitle })
# Get the window handle of the terminal process.
# Note that GetConsoleWindow() in Win32 API returns the HWND of
# powershell.exe itself rather than the terminal process.
# When you call ShowWindowAsync(HWND, 0) with the HWND from GetConsoleWindow(),
# the Windows Terminal window will be just minimized rather than hidden.
$hwnd = $TerminalProcess.MainWindowHandle
if ($hwnd -ne [System.IntPtr]::Zero) {
$ShowWindowAsync::ShowWindowAsync($hwnd, 0)
} else {
Write-Host "Failed to hide the console window."
}
}
}

最新更新