返回列表 发帖

[问题求助] 用Powershell控制CMD窗口的位置和大小

我想控制下面这段bat代码执行时, 窗口的位置和大小, 里面其实是ps代码, 存成bat格式
#@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
"hello,  world"
cmd /c pauseCOPY
我在国外找到了一个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 -PassthruCOPY

直接用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 pauseCOPY
1

评分人数

    • 5i365: 乐于分享, 技术牛X技术 + 1

TOP

回复 2# went


    感谢大侠帮忙 , 真是牛X,

TOP

回复 2# went


    大侠, 请教一下, 如果要是再现在代码的基础上,再加上调整外部 记事本程序的窗口大小 的功能, 应该怎么改呢

TOP

回复 4# 5i365
$p = Get-Process -Name 'notepad'
[void][WinApi]::SetWindowPos([int]$p.MainWindowHandle,$null,30,30,400,500,0)COPY

TOP

本帖最后由 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 NotepadCOPY
自己修改后的代码:
#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 NotepadCOPY
2.有什么简单的办法, 让代码里的CMD窗口和记事本窗口在不同的分辨率下, 始终水平居中, 竖直方向的参数不变

3.我上面找到的代码中, 有一行 Stop-Process -Name Notepad -ea 0; Notepad.exe  这里有个 -ea 0 是什么用途, 没搜到信息

TOP

最大化最小化
[DllImport("user32.dll")]
public static extern bool ShowWindow(uint hWnd,uint show);COPY
$SW_NORMAL = 1
$SW_MAXIMIZE = 3
$SW_MINIMIZE = 6
$p = Get-Process -Name 'notepad'
[WinApi]::ShowWindow([int]$p.MainWindowHandle,$SW_MINIMIZE)COPY

TOP

本帖最后由 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"

TOP

IntPtr实质就是一个uint,你去看看win32编程就不会有这么多疑问了
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(uint hWnd, int nCmdShow);COPY
# 最小化窗口
[void][WinApi]::ShowWindowAsync([int]$hwnd, 2)
# 恢复窗口
[void][WinApi]::ShowWindowAsync([int]$hwnd, 4)COPY

TOP

powershell调用api这么麻烦,不如去用c++

TOP

回复 9# went


    不行, 报下面的错
ERROR: Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".

TOP

回复 10# went


    我的编程水平太low了, powershell都看不太懂, C++ 更不敢想了

TOP

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 NotepadCOPY

TOP

回复 13# went


    多谢, 取句柄, 我丢了 .MainWindowHandle

TOP

窗口居中比较麻烦,记事本窗口居中代码如下,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)COPY
1

评分人数

    • 5i365: 技术牛X, 乐于助人技术 + 1

TOP

返回列表