PowerShell脚本实现窗口最小化/关闭时隐藏至系统托盘
实现PowerShell窗口关闭/最小化到系统托盘的解决方案
核心思路
要实现需求,关键是通过Win32 API拦截窗口消息:
- 拦截
WM_CLOSE消息:不执行默认的关闭进程操作,改为隐藏窗口并保留托盘图标 - 拦截
WM_SYSCOMMAND中的SC_MINIMIZE命令:不将窗口缩到任务栏,直接隐藏窗口 - 结合已有的托盘图标与菜单,保证显示/隐藏功能联动
完整实现代码
# 导入必要的Win32 API函数 Add-Type @" using System; using System.Runtime.InteropServices; using System.Windows.Forms; public class WindowHook { // 窗口消息常量 public const int WM_CLOSE = 0x0010; public const int WM_SYSCOMMAND = 0x0112; public const int SC_MINIMIZE = 0xF020; public const int GWL_WNDPROC = -4; // Win32 API声明 [DllImport("user32.dll")] public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, WndProc newProc); [DllImport("user32.dll")] public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow(); // 窗口委托 public delegate IntPtr WndProc(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); public static WndProc prevWndProc; public static IntPtr consoleHandle; // 自定义窗口过程 public static IntPtr CustomWndProc(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam) { switch (Msg) { // 处理关闭按钮点击 case WM_CLOSE: // 隐藏窗口,不关闭进程 ShowWindow(consoleHandle, 0); // 0 = SW_HIDE return IntPtr.Zero; // 阻止默认关闭行为 // 处理最小化命令 case WM_SYSCOMMAND: if ((wParam.ToInt32() & 0xFFF0) == SC_MINIMIZE) { // 隐藏窗口,不缩到任务栏 ShowWindow(consoleHandle, 0); return IntPtr.Zero; // 阻止默认最小化行为 } break; } // 其他消息交给原窗口过程处理 return CallWindowProc(prevWndProc, hWnd, Msg, wParam, IntPtr.Zero); } // 初始化窗口钩子 public static void HookConsoleWindow() { consoleHandle = GetConsoleWindow(); prevWndProc = (WndProc)Marshal.GetDelegateForFunctionPointer( SetWindowLongPtr(consoleHandle, GWL_WNDPROC, CustomWndProc), typeof(WndProc) ); } } "@ # 初始化窗口钩子,拦截消息 [WindowHook]::HookConsoleWindow() # 替换为你已有的系统托盘图标与菜单代码 # 示例托盘菜单框架(可直接替换为你的现有代码) Add-Type -AssemblyName System.Windows.Forms $notifyIcon = New-Object System.Windows.Forms.NotifyIcon $notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -Id $PID).Path) $notifyIcon.Visible = $true # 创建菜单 $contextMenu = New-Object System.Windows.Forms.ContextMenuStrip $showItem = New-Object System.Windows.Forms.ToolStripMenuItem("显示窗口") $showItem.Add_Click({ # 显示窗口并恢复正常状态 [WindowHook]::ShowWindow([WindowHook]::consoleHandle, 5) # 5 = SW_SHOW }) $exitItem = New-Object System.Windows.Forms.ToolStripMenuItem("退出") $exitItem.Add_Click({ # 退出时清理托盘图标并终止进程 $notifyIcon.Visible = $false $notifyIcon.Dispose() exit }) $contextMenu.Items.Add($showItem) $contextMenu.Items.Add($exitItem) $notifyIcon.ContextMenuStrip = $contextMenu # 保持脚本运行,防止进程退出 $null = [System.Windows.Forms.Application]::Run()
关键代码说明
- 窗口消息拦截:通过
SetWindowLongPtr替换默认窗口过程,在CustomWndProc中拦截目标消息,调用ShowWindow隐藏窗口而非执行默认操作。 - 托盘菜单联动:点击“显示窗口”时调用
ShowWindow恢复窗口显示;点击“退出”时清理托盘资源并终止进程。 - 脚本存活保障:用
[System.Windows.Forms.Application]::Run()启动消息循环,避免PowerShell脚本执行完毕后进程自动终止。
注意事项
- 若脚本以管理员权限运行,需确认窗口句柄获取逻辑正常(管理员窗口的句柄隔离可能导致差异)。
- 任务计划程序配置时,可设置“不管用户是否登录都要运行”,并根据需求选择是否勾选“不启动命令窗口”。
- 若已有托盘代码结构不同,只需保留
WindowHook类部分,将托盘菜单的“显示”按钮逻辑替换为调用[WindowHook]::ShowWindow([WindowHook]::consoleHandle, 5)即可。
内容的提问来源于stack exchange,提问作者Gangula
相关产品推荐
相关产品推荐

