标题: [问题求助] 用Powershell控制CMD窗口的位置和大小 [打印本页]
作者: 5i365 时间: 2022-2-16 19:54 标题: 用Powershell控制CMD窗口的位置和大小
我想控制下面这段bat代码执行时, 窗口的位置和大小, 里面其实是ps代码, 存成bat格式- #@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
-
- "hello, world"
-
- cmd /c pause
复制代码
我在国外找到了一个ps函数, 他可以控制某个窗口的位置和大小, 下面的代码就可以控制记事本窗口, 水平20, 竖直142, 那怎样加到上面的代码中呢? 例如, 我想让上面代码执行后打开的命令行窗口, 大小400*500, 水平30, 竖直30- Function Set-Window
- {
- <#
- .SYNOPSIS
- Retrieve/Set the window size and coordinates of a process window.
-
- .DESCRIPTION
- Retrieve/Set the size (height,width) and coordinates (x,y)
- of a process window.
-
- .PARAMETER ProcessName
- Name of the process to determine the window characteristics.
- (All processes if omitted).
-
- .PARAMETER Id
- Id of the process to determine the window characteristics.
-
- .PARAMETER X
- Set the position of the window in pixels from the left.
-
- .PARAMETER Y
- Set the position of the window in pixels from the top.
-
- .PARAMETER Width
- Set the width of the window.
-
- .PARAMETER Height
- Set the height of the window.
-
- .PARAMETER Passthru
- Returns the output object of the window.
-
- .NOTES
- Name: Set-Window
- Author: Boe Prox
- Version History:
- 1.0//Boe Prox - 11/24/2015 - Initial build
- 1.1//JosefZ - 19.05.2018 - Treats more process instances
- of supplied process name properly
- 1.2//JosefZ - 21.02.2019 - Parameter Id
-
- .OUTPUTS
- None
- System.Management.Automation.PSCustomObject
- System.Object
-
- .EXAMPLE
- Get-Process powershell | Set-Window -X 20 -Y 40 -Passthru -Verbose
- VERBOSE: powershell (Id=11140, Handle=132410)
-
- Id : 11140
- ProcessName : powershell
- Size : 1134,781
- TopLeft : 20,40
- BottomRight : 1154,821
-
- Description: Set the coordinates on the window for the process PowerShell.exe
-
- .EXAMPLE
- $windowArray = Set-Window -Passthru
- WARNING: cmd (1096) is minimized! Coordinates will not be accurate.
-
- PS C:\>$windowArray | Format-Table -AutoSize
-
- Id ProcessName Size TopLeft BottomRight
- -- ----------- ---- ------- -----------
- 1096 cmd 199,34 -32000,-32000 -31801,-31966
- 4088 explorer 1280,50 0,974 1280,1024
- 6880 powershell 1280,974 0,0 1280,974
-
- Description: Get the coordinates of all visible windows and save them into the
- $windowArray variable. Then, display them in a table view.
-
- .EXAMPLE
- Set-Window -Id $PID -Passthru | Format-Table
-
- Id ProcessName Size TopLeft BottomRight
- -- ----------- ---- ------- -----------
- 7840 pwsh 1024,638 0,0 1024,638
-
- Description: Display the coordinates of the window for the current
- PowerShell session in a table view.
-
-
-
- #>
- [cmdletbinding(DefaultParameterSetName = 'Name')]
- Param (
- [parameter(Mandatory = $False,
- ValueFromPipelineByPropertyName = $True, ParameterSetName = 'Name')]
- [string]$ProcessName = '*',
- [parameter(Mandatory = $True,
- ValueFromPipeline = $False, ParameterSetName = 'Id')]
- [int]$Id,
- [int]$X,
- [int]$Y,
- [int]$Width,
- [int]$Height,
- [switch]$Passthru
- )
- Begin
- {
- Try
- {
- [void][Window]
- }
- Catch
- {
- Add-Type @"
- using System;
- using System.Runtime.InteropServices;
- public class Window {
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool GetWindowRect(
- IntPtr hWnd, out RECT lpRect);
-
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- public extern static bool MoveWindow(
- IntPtr handle, int x, int y, int width, int height, bool redraw);
-
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool ShowWindow(
- IntPtr handle, int state);
- }
- public struct RECT
- {
- public int Left; // x position of upper-left corner
- public int Top; // y position of upper-left corner
- public int Right; // x position of lower-right corner
- public int Bottom; // y position of lower-right corner
- }
- "@
- }
- }
- Process
- {
- $Rectangle = New-Object RECT
- If ($PSBoundParameters.ContainsKey('Id'))
- {
- $Processes = Get-Process -Id $Id -ErrorAction SilentlyContinue
- }
- else
- {
- $Processes = Get-Process -Name "$ProcessName" -ErrorAction SilentlyContinue
- }
- if ($null -eq $Processes)
- {
- If ($PSBoundParameters['Passthru'])
- {
- Write-Warning 'No process match criteria specified'
- }
- }
- else
- {
- $Processes | ForEach-Object {
- $Handle = $_.MainWindowHandle
- Write-Verbose "$($_.ProcessName) `(Id=$($_.Id), Handle=$Handle`)"
- if ($Handle -eq [System.IntPtr]::Zero) { return }
- $Return = [Window]::GetWindowRect($Handle, [ref]$Rectangle)
- If (-NOT $PSBoundParameters.ContainsKey('X'))
- {
- $X = $Rectangle.Left
- }
- If (-NOT $PSBoundParameters.ContainsKey('Y'))
- {
- $Y = $Rectangle.Top
- }
- If (-NOT $PSBoundParameters.ContainsKey('Width'))
- {
- $Width = $Rectangle.Right - $Rectangle.Left
- }
- If (-NOT $PSBoundParameters.ContainsKey('Height'))
- {
- $Height = $Rectangle.Bottom - $Rectangle.Top
- }
- If ($Return)
- {
- $Return = [Window]::MoveWindow($Handle, $x, $y, $Width, $Height, $True)
- }
- If ($PSBoundParameters['Passthru'])
- {
- $Rectangle = New-Object RECT
- $Return = [Window]::GetWindowRect($Handle, [ref]$Rectangle)
- If ($Return)
- {
- $Height = $Rectangle.Bottom - $Rectangle.Top
- $Width = $Rectangle.Right - $Rectangle.Left
- $Size = New-Object System.Management.Automation.Host.Size -ArgumentList $Width, $Height
- $TopLeft = New-Object System.Management.Automation.Host.Coordinates -ArgumentList $Rectangle.Left, $Rectangle.Top
- $BottomRight = New-Object System.Management.Automation.Host.Coordinates -ArgumentList $Rectangle.Right, $Rectangle.Bottom
- If ($Rectangle.Top -lt 0 -AND
- $Rectangle.Bottom -lt 0 -AND
- $Rectangle.Left -lt 0 -AND
- $Rectangle.Right -lt 0)
- {
- Write-Warning "$($_.ProcessName) `($($_.Id)`) is minimized! Coordinates will not be accurate."
- }
- $Object = [PSCustomObject]@{
- Id = $_.Id
- ProcessName = $_.ProcessName
- Size = $Size
- TopLeft = $TopLeft
- BottomRight = $BottomRight
- }
- $Object
- }
- }
- }
- }
- }
- }
-
- Get-Process notepad | Set-Window -X 20 -Y 142 -Passthru
复制代码
作者: went 时间: 2022-2-16 20:32
直接用winapi就行,别搞那么复杂- #@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
-
-
-
- #Windows API
- $code=@'
- using System.Runtime.InteropServices;
- public static class WinApi{
- [DllImport("user32.dll")]
- public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
- [DllImport("kernel32.dll")]
- public static extern uint GetConsoleWindow();
- }
- '@
- Add-Type -TypeDefinition $code
- #设置位置及大小
- $hwnd = [WinApi]::GetConsoleWindow()
- [void][WinApi]::SetWindowPos($hwnd,$null,30,30,400,500,0)
-
-
-
-
-
- "hello, world"
-
- cmd /c pause
复制代码
作者: 5i365 时间: 2022-2-16 20:42
回复 2# went
感谢大侠帮忙 , 真是牛X,
作者: 5i365 时间: 2022-2-16 20:57
回复 2# went
大侠, 请教一下, 如果要是再现在代码的基础上,再加上调整外部 记事本程序的窗口大小 的功能, 应该怎么改呢
作者: went 时间: 2022-2-16 22:10
回复 4# 5i365 - $p = Get-Process -Name 'notepad'
- [void][WinApi]::SetWindowPos([int]$p.MainWindowHandle,$null,30,30,400,500,0)
复制代码
作者: 5i365 时间: 2022-2-17 09:21
本帖最后由 5i365 于 2022-2-17 11:51 编辑
回复 5# went
多谢!!!
还有3个小问题, 想请教大侠!
1.我找了一段 最小化, 恢复窗口的代码, 想加到您的代码里面, 自己尝试加了一下, 但是执行后报错, 不知错在哪里, [主要是想知道在原C#代码加代码的问题]
找到的代码:- $sig = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
- Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
- Stop-Process -Name Notepad -ea 0; Notepad.exe
- $hwnd = @(Get-Process Notepad)[0].MainWindowHandle
- # Minimize window
- [Win32.NativeMethods]::ShowWindowAsync($hwnd, 2)
- # Restore window
- [Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)
- Stop-Process -Name Notepad
复制代码
自己修改后的代码:- #Windows API
- $code = @'
- using System.Runtime.InteropServices;
- public static class WinApi{
- [DllImport("user32.dll")]
- public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
- [DllImport("kernel32.dll")]
- public static extern uint GetConsoleWindow();
-
- [DllImport("user32.dll")]
- public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
- }
- '@
- Add-Type -TypeDefinition $code
- #设置CMD窗口位置及大小
- $hwnd = [WinApi]::GetConsoleWindow()
- [void][WinApi]::SetWindowPos($hwnd, $null, 30, 30, 400, 500, 0)
- #-------------------------------------------------------------------------
- #启动记事本
- Stop-Process -Name Notepad -ea 0; Notepad.exe
- $hwnd = @(Get-Process Notepad)[0].MainWindowHandle
- # 最小化窗口
- [void][WinApi]::ShowWindowAsync($hwnd, 2)
- # 恢复窗口
- [void][WinApi]::ShowWindowAsync($hwnd, 4)
- # 关闭记事本
- Stop-Process -Name Notepad
复制代码
2.有什么简单的办法, 让代码里的CMD窗口和记事本窗口在不同的分辨率下, 始终水平居中, 竖直方向的参数不变
3.我上面找到的代码中, 有一行 Stop-Process -Name Notepad -ea 0; Notepad.exe 这里有个 -ea 0 是什么用途, 没搜到信息
作者: went 时间: 2022-2-17 19:11
最大化最小化- [DllImport("user32.dll")]
- public static extern bool ShowWindow(uint hWnd,uint show);
复制代码
- $SW_NORMAL = 1
- $SW_MAXIMIZE = 3
- $SW_MINIMIZE = 6
-
- $p = Get-Process -Name 'notepad'
- [WinApi]::ShowWindow([int]$p.MainWindowHandle,$SW_MINIMIZE)
复制代码
作者: 5i365 时间: 2022-2-17 20:02
本帖最后由 5i365 于 2022-2-17 20:05 编辑
回复 7# went
感谢分享!
-ea 0 我查到了, 是-erroraction 的别名, 0应该是失败时继续
--------------------------------------------------------------------------------------
还是不明白为什么都是 user32.dll 下面的加到您的代码中就不行, 而单独执行下面找到的那段代码就行
------------------------------------------------------------------------------------
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
--------------------------------------------------------------------------------------
下面的代码执行很完美:
$sig = '[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
#启动
Stop-Process -Name calc -ea 0
start calc
sleep 2
#取句柄
$hwnd = (Get-Process calc).MainWindowHandle
#最小化
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 2)
sleep 3
#恢复
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)
sleep 3
#关闭
Stop-Process -Name "calc"
作者: went 时间: 2022-2-17 20:11
IntPtr实质就是一个uint,你去看看win32编程就不会有这么多疑问了- [DllImport("user32.dll")]
- public static extern bool ShowWindowAsync(uint hWnd, int nCmdShow);
复制代码
- # 最小化窗口
- [void][WinApi]::ShowWindowAsync([int]$hwnd, 2)
- # 恢复窗口
- [void][WinApi]::ShowWindowAsync([int]$hwnd, 4)
复制代码
作者: went 时间: 2022-2-17 20:12
powershell调用api这么麻烦,不如去用c++
作者: 5i365 时间: 2022-2-17 20:18
回复 9# went
不行, 报下面的错
ERROR: Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
作者: 5i365 时间: 2022-2-17 20:19
回复 10# went
我的编程水平太low了, powershell都看不太懂, C++ 更不敢想了
作者: went 时间: 2022-2-17 20:25
- cls
- #Windows API
- $code = @'
- using System.Runtime.InteropServices;
- public static class WinApi{
- [DllImport("user32.dll")]
- public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
- [DllImport("kernel32.dll")]
- public static extern uint GetConsoleWindow();
-
- [DllImport("user32.dll")]
- public static extern bool ShowWindowAsync(uint hWnd, int nCmdShow);
- }
- '@
- Add-Type -TypeDefinition $code
- #设置CMD窗口位置及大小
- $hwnd = [WinApi]::GetConsoleWindow()
- [void][WinApi]::SetWindowPos($hwnd, $null, 30, 30, 400, 500, 0)
- #-------------------------------------------------------------------------
- #启动记事本
- Stop-Process -Name 'notepad' -Force -ErrorAction SilentlyContinue
- Start-Process 'notepad'
- Start-Sleep -Seconds 1
- $hwnd = (Get-Process 'notepad')[0].MainWindowHandle
- # 最小化窗口
- [void][WinApi]::ShowWindowAsync([int]$hwnd, 2)
- # 恢复窗口
- [void][WinApi]::ShowWindowAsync([int]$hwnd, 4)
- # 关闭记事本
- #Stop-Process -Name Notepad
复制代码
作者: 5i365 时间: 2022-2-17 20:53
回复 13# went
多谢, 取句柄, 我丢了 .MainWindowHandle
作者: went 时间: 2022-2-17 21:03
窗口居中比较麻烦,记事本窗口居中代码如下,cmd窗口同理- #&cls&@powershell -c "Get-Content '%~0' | Out-String | Invoke-Expression" &pause&exit
-
- cls
-
- #Windows API
- $code=@'
- using System;
- using System.Runtime.InteropServices;
- public struct RECT{
- public uint left;
- public uint top;
- public uint right;
- public uint bottom;
- }
- public static class WinApi{
- [DllImport("user32.dll")]
- public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
- [DllImport("kernel32.dll")]
- public static extern uint GetConsoleWindow();
- [DllImport("user32.dll")]
- public static extern bool GetWindowRect(uint hwnd, ref RECT rect);
- [DllImport("user32.dll")]
- public static extern uint GetDC(uint hwnd);
- [DllImport("gdi32.dll")]
- public static extern uint GetDeviceCaps(uint hdc, int index);
-
- public static uint[] GetScreen(){
- uint[] arr = {0,0};
- uint hdc = GetDC(0);
- arr[0] = GetDeviceCaps(hdc,118);
- arr[1] = GetDeviceCaps(hdc,117);
- return arr;
- }
- }
- '@
- Add-Type -TypeDefinition $code
-
- #获取记事本窗口句柄
- $hwnd = (Get-Process 'notepad')[0].MainWindowHandle
- #获取窗口信息
- $rect = New-Object 'RECT'
- [void][WinApi]::GetWindowRect([int]$hwnd,[ref]$rect)
- $screen = [WinApi]::GetScreen()
- #计算水平居中坐标
- $x = ($screen[0] - ($rect.right - $rect.left))/2
- #设置记事本水平居中
- [WinApi]::SetWindowPos([int]$hwnd,$null,$x,$rect.top,0,0,1)
复制代码
作者: 5i365 时间: 2022-2-17 21:18
回复 15# went
感谢, 太牛X了, 看不懂了, 先收藏了
作者: 5i365 时间: 2022-2-18 12:09
本帖最后由 5i365 于 2022-2-18 12:12 编辑
回复 15# went
使用了@idwma 大侠这个贴子的代码, 取得屏幕宽度, 现在代码少了些
http://www.bathome.net/redirect. ... 1487&pid=251249- #Windows API
- $code=@'
- using System.Runtime.InteropServices;
- public struct RECT{
- public uint left;
- public uint top;
- public uint right;
- public uint bottom;
- }
- public static class WinApi{
- [DllImport("user32.dll")]
- public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
- [DllImport("user32.dll")]
- public static extern bool GetWindowRect(uint hwnd, ref RECT rect);
- }
- '@
- Add-Type -TypeDefinition $code
-
- #获取记事本窗口句柄
- $hwnd = (Get-Process 'notepad')[0].MainWindowHandle
- #获取窗口信息
- $rect = New-Object 'RECT'
- [void][WinApi]::GetWindowRect([int]$hwnd,[ref]$rect)
- $screen_w = [Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Width
- #计算水平居中坐标
- $x = ($screen_w - ($rect.right - $rect.left))/2
- #设置记事本水平居中
- [WinApi]::SetWindowPos([int]$hwnd,$null,$x,$rect.top,0,0,1)
复制代码
作者: hui99995 时间: 2023-3-4 11:45
回复 15# went
我尝试用这段代码来调整edge和chrome发现不行 是因为进程太多的原因吗?获取的句柄为0
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |