Board logo

标题: [问题求助] 用Powershell控制CMD窗口的位置和大小 [打印本页]

作者: 5i365    时间: 2022-2-16 19:54     标题: 用Powershell控制CMD窗口的位置和大小

我想控制下面这段bat代码执行时, 窗口的位置和大小, 里面其实是ps代码, 存成bat格式
  1. #@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
  2. "hello,  world"
  3. cmd /c pause
复制代码
我在国外找到了一个ps函数, 他可以控制某个窗口的位置和大小, 下面的代码就可以控制记事本窗口, 水平20, 竖直142, 那怎样加到上面的代码中呢? 例如, 我想让上面代码执行后打开的命令行窗口, 大小400*500, 水平30, 竖直30
  1. Function Set-Window
  2. {
  3. <#
  4. .SYNOPSIS
  5. Retrieve/Set the window size and coordinates of a process window.
  6. .DESCRIPTION
  7. Retrieve/Set the size (height,width) and coordinates (x,y)
  8. of a process window.
  9. .PARAMETER ProcessName
  10. Name of the process to determine the window characteristics.
  11. (All processes if omitted).
  12. .PARAMETER Id
  13. Id of the process to determine the window characteristics.
  14. .PARAMETER X
  15. Set the position of the window in pixels from the left.
  16. .PARAMETER Y
  17. Set the position of the window in pixels from the top.
  18. .PARAMETER Width
  19. Set the width of the window.
  20. .PARAMETER Height
  21. Set the height of the window.
  22. .PARAMETER Passthru
  23. Returns the output object of the window.
  24. .NOTES
  25. Name:   Set-Window
  26. Author: Boe Prox
  27. Version History:
  28.     1.0//Boe Prox - 11/24/2015 - Initial build
  29.     1.1//JosefZ   - 19.05.2018 - Treats more process instances
  30.                                  of supplied process name properly
  31.     1.2//JosefZ   - 21.02.2019 - Parameter Id
  32. .OUTPUTS
  33. None
  34. System.Management.Automation.PSCustomObject
  35. System.Object
  36. .EXAMPLE
  37. Get-Process powershell | Set-Window -X 20 -Y 40 -Passthru -Verbose
  38. VERBOSE: powershell (Id=11140, Handle=132410)
  39. Id          : 11140
  40. ProcessName : powershell
  41. Size        : 1134,781
  42. TopLeft     : 20,40
  43. BottomRight : 1154,821
  44. Description: Set the coordinates on the window for the process PowerShell.exe
  45. .EXAMPLE
  46. $windowArray = Set-Window -Passthru
  47. WARNING: cmd (1096) is minimized! Coordinates will not be accurate.
  48.     PS C:\>$windowArray | Format-Table -AutoSize
  49.   Id ProcessName    Size     TopLeft       BottomRight  
  50.   -- -----------    ----     -------       -----------  
  51. 1096 cmd            199,34   -32000,-32000 -31801,-31966
  52. 4088 explorer       1280,50  0,974         1280,1024   
  53. 6880 powershell     1280,974 0,0           1280,974     
  54. Description: Get the coordinates of all visible windows and save them into the
  55.              $windowArray variable. Then, display them in a table view.
  56. .EXAMPLE
  57. Set-Window -Id $PID -Passthru | Format-Table
  58. ​‌‍
  59.   Id ProcessName Size     TopLeft BottomRight
  60.   -- ----------- ----     ------- -----------
  61. 7840 pwsh        1024,638 0,0     1024,638
  62. Description: Display the coordinates of the window for the current
  63.              PowerShell session in a table view.
  64. #>
  65. [cmdletbinding(DefaultParameterSetName = 'Name')]
  66. Param (
  67. [parameter(Mandatory = $False,
  68.    ValueFromPipelineByPropertyName = $True, ParameterSetName = 'Name')]
  69. [string]$ProcessName = '*',
  70. [parameter(Mandatory = $True,
  71.    ValueFromPipeline = $False, ParameterSetName = 'Id')]
  72. [int]$Id,
  73. [int]$X,
  74. [int]$Y,
  75. [int]$Width,
  76. [int]$Height,
  77. [switch]$Passthru
  78. )
  79. Begin
  80. {
  81. Try
  82. {
  83. [void][Window]
  84. }
  85. Catch
  86. {
  87. Add-Type @"
  88.         using System;
  89.         using System.Runtime.InteropServices;
  90.         public class Window {
  91.         [DllImport("user32.dll")]
  92.         [return: MarshalAs(UnmanagedType.Bool)]
  93.         public static extern bool GetWindowRect(
  94.             IntPtr hWnd, out RECT lpRect);
  95.         [DllImport("user32.dll")]
  96.         [return: MarshalAs(UnmanagedType.Bool)]
  97.         public extern static bool MoveWindow(
  98.             IntPtr handle, int x, int y, int width, int height, bool redraw);
  99.         [DllImport("user32.dll")]
  100.         [return: MarshalAs(UnmanagedType.Bool)]
  101.         public static extern bool ShowWindow(
  102.             IntPtr handle, int state);
  103.         }
  104.         public struct RECT
  105.         {
  106.         public int Left;        // x position of upper-left corner
  107.         public int Top;         // y position of upper-left corner
  108.         public int Right;       // x position of lower-right corner
  109.         public int Bottom;      // y position of lower-right corner
  110.         }
  111. "@
  112. }
  113. }
  114. Process
  115. {
  116. $Rectangle = New-Object RECT
  117. If ($PSBoundParameters.ContainsKey('Id'))
  118. {
  119. $Processes = Get-Process -Id $Id -ErrorAction SilentlyContinue
  120. }
  121. else
  122. {
  123. $Processes = Get-Process -Name "$ProcessName" -ErrorAction SilentlyContinue
  124. }
  125. if ($null -eq $Processes)
  126. {
  127. If ($PSBoundParameters['Passthru'])
  128. {
  129. Write-Warning 'No process match criteria specified'
  130. }
  131. }
  132. else
  133. {
  134. $Processes | ForEach-Object {
  135. $Handle = $_.MainWindowHandle
  136. Write-Verbose "$($_.ProcessName) `(Id=$($_.Id), Handle=$Handle`)"
  137. if ($Handle -eq [System.IntPtr]::Zero) { return }
  138. $Return = [Window]::GetWindowRect($Handle, [ref]$Rectangle)
  139. If (-NOT $PSBoundParameters.ContainsKey('X'))
  140. {
  141. $X = $Rectangle.Left
  142. }
  143. If (-NOT $PSBoundParameters.ContainsKey('Y'))
  144. {
  145. $Y = $Rectangle.Top
  146. }
  147. If (-NOT $PSBoundParameters.ContainsKey('Width'))
  148. {
  149. $Width = $Rectangle.Right - $Rectangle.Left
  150. }
  151. If (-NOT $PSBoundParameters.ContainsKey('Height'))
  152. {
  153. $Height = $Rectangle.Bottom - $Rectangle.Top
  154. }
  155. If ($Return)
  156. {
  157. $Return = [Window]::MoveWindow($Handle, $x, $y, $Width, $Height, $True)
  158. }
  159. If ($PSBoundParameters['Passthru'])
  160. {
  161. $Rectangle = New-Object RECT
  162. $Return = [Window]::GetWindowRect($Handle, [ref]$Rectangle)
  163. If ($Return)
  164. {
  165. $Height = $Rectangle.Bottom - $Rectangle.Top
  166. $Width = $Rectangle.Right - $Rectangle.Left
  167. $Size = New-Object System.Management.Automation.Host.Size -ArgumentList $Width, $Height
  168. $TopLeft = New-Object System.Management.Automation.Host.Coordinates -ArgumentList $Rectangle.Left, $Rectangle.Top
  169. $BottomRight = New-Object System.Management.Automation.Host.Coordinates -ArgumentList $Rectangle.Right, $Rectangle.Bottom
  170. If ($Rectangle.Top -lt 0 -AND
  171. $Rectangle.Bottom -lt 0 -AND
  172. $Rectangle.Left -lt 0 -AND
  173. $Rectangle.Right -lt 0)
  174. {
  175. Write-Warning "$($_.ProcessName) `($($_.Id)`) is minimized! Coordinates will not be accurate."
  176. }
  177. $Object = [PSCustomObject]@{
  178. Id = $_.Id
  179. ProcessName = $_.ProcessName
  180. Size = $Size
  181. TopLeft = $TopLeft
  182. BottomRight = $BottomRight
  183. }
  184. $Object
  185. }
  186. }
  187. }
  188. }
  189. }
  190. }
  191. Get-Process notepad | Set-Window -X 20 -Y 142 -Passthru
复制代码

作者: went    时间: 2022-2-16 20:32

直接用winapi就行,别搞那么复杂
  1. #@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
  2. #Windows API
  3. $code=@'
  4.     using System.Runtime.InteropServices;
  5.     public static class WinApi{
  6.         [DllImport("user32.dll")]
  7.         public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
  8.         [DllImport("kernel32.dll")]
  9.         public static extern uint GetConsoleWindow();
  10.     }
  11. '@
  12. Add-Type -TypeDefinition $code
  13. #设置位置及大小
  14. $hwnd = [WinApi]::GetConsoleWindow()
  15. [void][WinApi]::SetWindowPos($hwnd,$null,30,30,400,500,0)
  16. "hello,  world"
  17. 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
  1. $p = Get-Process -Name 'notepad'
  2. [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#代码加代码的问题]
找到的代码:
  1. $sig = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
  2. Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
  3. Stop-Process -Name Notepad -ea 0; Notepad.exe
  4. $hwnd = @(Get-Process Notepad)[0].MainWindowHandle
  5. # Minimize window
  6. [Win32.NativeMethods]::ShowWindowAsync($hwnd, 2)
  7. # Restore window
  8. [Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)
  9. Stop-Process -Name Notepad
复制代码
自己修改后的代码:
  1. #Windows API
  2. $code = @'
  3.     using System.Runtime.InteropServices;
  4.     public static class WinApi{
  5.         [DllImport("user32.dll")]
  6.         public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
  7.         [DllImport("kernel32.dll")]
  8.         public static extern uint GetConsoleWindow();
  9. [DllImport("user32.dll")]
  10. public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
  11.     }
  12. '@
  13. Add-Type -TypeDefinition $code
  14. #设置CMD窗口位置及大小
  15. $hwnd = [WinApi]::GetConsoleWindow()
  16. [void][WinApi]::SetWindowPos($hwnd, $null, 30, 30, 400, 500, 0)
  17. #-------------------------------------------------------------------------
  18. #启动记事本
  19. Stop-Process -Name Notepad -ea 0; Notepad.exe
  20. $hwnd = @(Get-Process Notepad)[0].MainWindowHandle
  21. # 最小化窗口
  22. [void][WinApi]::ShowWindowAsync($hwnd, 2)
  23. # 恢复窗口
  24. [void][WinApi]::ShowWindowAsync($hwnd, 4)
  25. # 关闭记事本
  26. Stop-Process -Name Notepad
复制代码
2.有什么简单的办法, 让代码里的CMD窗口和记事本窗口在不同的分辨率下, 始终水平居中, 竖直方向的参数不变

3.我上面找到的代码中, 有一行 Stop-Process -Name Notepad -ea 0; Notepad.exe  这里有个 -ea 0 是什么用途, 没搜到信息
作者: went    时间: 2022-2-17 19:11

最大化最小化
  1. [DllImport("user32.dll")]
  2. public static extern bool ShowWindow(uint hWnd,uint show);
复制代码
  1. $SW_NORMAL = 1
  2. $SW_MAXIMIZE = 3
  3. $SW_MINIMIZE = 6
  4. $p = Get-Process -Name 'notepad'
  5. [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编程就不会有这么多疑问了
  1. [DllImport("user32.dll")]
  2. public static extern bool ShowWindowAsync(uint hWnd, int nCmdShow);
复制代码
  1. # 最小化窗口
  2. [void][WinApi]::ShowWindowAsync([int]$hwnd, 2)
  3. # 恢复窗口
  4. [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

  1. cls
  2. #Windows API
  3. $code = @'
  4.     using System.Runtime.InteropServices;
  5.     public static class WinApi{
  6.         [DllImport("user32.dll")]
  7.         public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
  8.         [DllImport("kernel32.dll")]
  9.         public static extern uint GetConsoleWindow();
  10. [DllImport("user32.dll")]
  11. public static extern bool ShowWindowAsync(uint hWnd, int nCmdShow);
  12.     }
  13. '@
  14. Add-Type -TypeDefinition $code
  15. #设置CMD窗口位置及大小
  16. $hwnd = [WinApi]::GetConsoleWindow()
  17. [void][WinApi]::SetWindowPos($hwnd, $null, 30, 30, 400, 500, 0)
  18. #-------------------------------------------------------------------------
  19. #启动记事本
  20. Stop-Process -Name 'notepad' -Force -ErrorAction SilentlyContinue
  21. Start-Process 'notepad'
  22. Start-Sleep -Seconds 1
  23. $hwnd = (Get-Process 'notepad')[0].MainWindowHandle
  24. # 最小化窗口
  25. [void][WinApi]::ShowWindowAsync([int]$hwnd, 2)
  26. # 恢复窗口
  27. [void][WinApi]::ShowWindowAsync([int]$hwnd, 4)
  28. # 关闭记事本
  29. #Stop-Process -Name Notepad
复制代码

作者: 5i365    时间: 2022-2-17 20:53

回复 13# went


    多谢, 取句柄, 我丢了 .MainWindowHandle
作者: went    时间: 2022-2-17 21:03

窗口居中比较麻烦,记事本窗口居中代码如下,cmd窗口同理
  1. #&cls&@powershell -c "Get-Content '%~0' | Out-String | Invoke-Expression" &pause&exit
  2. cls
  3. #Windows API
  4. $code=@'
  5.     using System;
  6.     using System.Runtime.InteropServices;
  7.     public struct RECT{
  8.         public uint left;
  9.         public uint top;
  10.         public uint right;
  11.         public uint bottom;
  12.     }
  13.     public static class WinApi{
  14.         [DllImport("user32.dll")]
  15.         public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
  16.         [DllImport("kernel32.dll")]
  17.         public static extern uint GetConsoleWindow();
  18.         [DllImport("user32.dll")]
  19.         public static extern bool GetWindowRect(uint hwnd, ref RECT rect);
  20.         [DllImport("user32.dll")]
  21.         public static extern uint GetDC(uint hwnd);
  22.         [DllImport("gdi32.dll")]
  23.         public static extern uint GetDeviceCaps(uint hdc, int index);
  24.         public static uint[] GetScreen(){
  25.             uint[] arr = {0,0};
  26.             uint hdc = GetDC(0);
  27.             arr[0] = GetDeviceCaps(hdc,118);
  28.             arr[1] = GetDeviceCaps(hdc,117);
  29.             return arr;
  30.         }
  31.     }
  32. '@
  33. Add-Type -TypeDefinition $code
  34. #获取记事本窗口句柄
  35. $hwnd = (Get-Process 'notepad')[0].MainWindowHandle
  36. #获取窗口信息
  37. $rect = New-Object 'RECT'
  38. [void][WinApi]::GetWindowRect([int]$hwnd,[ref]$rect)
  39. $screen = [WinApi]::GetScreen()
  40. #计算水平居中坐标
  41. $x = ($screen[0] - ($rect.right - $rect.left))/2
  42. #设置记事本水平居中
  43. [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
  1. #Windows API
  2. $code=@'
  3.     using System.Runtime.InteropServices;
  4.     public struct RECT{
  5.         public uint left;
  6.         public uint top;
  7.         public uint right;
  8.         public uint bottom;
  9.     }
  10.     public static class WinApi{
  11.         [DllImport("user32.dll")]
  12.         public static extern bool SetWindowPos(uint hWnd,uint hAfter,uint x,uint y,uint cx,uint cy,uint flags);
  13.         [DllImport("user32.dll")]
  14.         public static extern bool GetWindowRect(uint hwnd, ref RECT rect);
  15.     }
  16. '@
  17. Add-Type -TypeDefinition $code
  18. #获取记事本窗口句柄
  19. $hwnd = (Get-Process 'notepad')[0].MainWindowHandle
  20. #获取窗口信息
  21. $rect = New-Object 'RECT'
  22. [void][WinApi]::GetWindowRect([int]$hwnd,[ref]$rect)
  23. $screen_w = [Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Width
  24. #计算水平居中坐标
  25. $x = ($screen_w - ($rect.right - $rect.left))/2
  26. #设置记事本水平居中
  27. [WinApi]::SetWindowPos([int]$hwnd,$null,$x,$rect.top,0,0,1)
复制代码

作者: hui99995    时间: 2023-3-4 11:45

回复 15# went


    我尝试用这段代码来调整edge和chrome发现不行 是因为进程太多的原因吗?获取的句柄为0





欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2