用Powershell控制指定进程窗口的位置和大小

用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
收藏分享
went

少校

 

帖子848 积分1713 技术226 捐助0 注册时间2017-9-22
2楼
发表于 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 pauseCOPY
1
评分人数

5i365: 乐于分享, 技术牛X技术 + 1
https://docs.qq.com/doc/DSVJ0d094Q1NtbGta
TOP
5i365

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
3楼
发表于 2022-2-16 20:42 | 只看该作者
回复 2# went


感谢大侠帮忙 , 真是牛X,
TOP
5i365

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
4楼
发表于 2022-2-16 20:57 | 只看该作者
回复 2# went


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

少校

 

帖子848 积分1713 技术226 捐助0 注册时间2017-9-22
5楼
发表于 2022-2-16 22:10 | 只看该作者
回复 4# 5i365
$p = Get-Process -Name 'notepad'
[void][WinApi]::SetWindowPos([int]$p.MainWindowHandle,$null,30,30,400,500,0)COPY
https://docs.qq.com/doc/DSVJ0d094Q1NtbGta
TOP
5i365

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
6楼
发表于 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 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
went

少校

 

帖子848 积分1713 技术226 捐助0 注册时间2017-9-22
7楼
发表于 2022-2-17 19:11 | 只看该作者
最大化最小化
[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
https://docs.qq.com/doc/DSVJ0d094Q1NtbGta
TOP
5i365

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
8楼
发表于 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"
TOP
went

少校

 

帖子848 积分1713 技术226 捐助0 注册时间2017-9-22
9楼
发表于 2022-2-17 20:11 | 只看该作者
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
https://docs.qq.com/doc/DSVJ0d094Q1NtbGta
TOP
went

少校

 

帖子848 积分1713 技术226 捐助0 注册时间2017-9-22
10楼
发表于 2022-2-17 20:12 | 只看该作者
powershell调用api这么麻烦,不如去用c++
https://docs.qq.com/doc/DSVJ0d094Q1NtbGta
TOP
5i365

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
11楼
发表于 2022-2-17 20:18 | 只看该作者
回复 9# went


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

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
12楼
发表于 2022-2-17 20:19 | 只看该作者
回复 10# went


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

少校

 

帖子848 积分1713 技术226 捐助0 注册时间2017-9-22
13楼
发表于 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 NotepadCOPY
https://docs.qq.com/doc/DSVJ0d094Q1NtbGta
TOP
5i365

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
14楼
发表于 2022-2-17 20:53 | 只看该作者
回复 13# went


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

少校

 

帖子848 积分1713 技术226 捐助0 注册时间2017-9-22
15楼
发表于 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

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
16楼跳转到 »
发表于 2022-2-17 21:18 | 只看该作者
回复 15# went


感谢, 太牛X了, 看不懂了, 先收藏了
TOP
5i365

上尉

 

帖子1013 积分1228 技术14 捐助0 注册时间2019-2-20
17楼
发表于 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)COPY
TOP
hui99995

列兵

 

帖子2 积分13 技术0 捐助0 注册时间2023-3-4
18楼
发表于 2023-3-4 11:45 | 只看该作者
回复 15# went


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

 

出处:http://bbs.bathome.net/thread-61628-1-1.html

=========================================================================================

个人使用

根据上面的文章和介绍,自己改一下,并把以下内容保存为reSize.ps1文件中,使用powershell运行。

Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    public class Win32
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

        [DllImport("user32.dll")]
        public static extern uint FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        public const uint SWP_NOSIZE = 0x1;
        public const uint SWP_NOMOVE = 0x2;
        public const uint SWP_SHOWWINDOW = 0x40;
        public const uint wndInsertAfter = 0x0;
        
        [DllImport("user32")]
        public static extern int GetSystemMetrics(int nIndex);
                
    }
"@
 
# 查找记事本窗口的句柄"wmware-view.exe","FC-JC"
$winHandle = [Win32]::FindWindow($null, '无标题 - 记事本')
$winHandle = [Win32]::FindWindow("wmware-view.exe",$null)
$winHandle = [Win32]::FindWindow($null,"FC-JC")
$winHandle = @(Get-Process -name "vmware-view")[0].MainWindowHandle
echo  winHandle=$winHandle
# 获取屏幕大小
$w = [Win32]::GetSystemMetrics(0)
$h = [Win32]::GetSystemMetrics(1)-40

# 移动记事本窗口到屏幕上的(100,100)位置,并设置窗口大小为400x400
#[Win32]::MoveWindow($notepadHandle, 100, 100, 400, 400, $true)
[Win32]::SetWindowPos($winHandle, [Win32]::wndInsertAfter, 0, 0, $w, $h, [Win32]::SWP_SHOWWINDOW)


#$notepadWindow = Get-Process notepad -ErrorAction SilentlyContinue | ForEach-Object { Get-WindowHandle -ProcessId $_.Id }
#foreach ($window in $notepadWindow) {
#  [Win32]::SetWindowPos($window, [IntPtr]::Zero, 0, 0, 0, 0, [Win32]::SWP_NOSIZE, [Win32]::SWP_NOMOVE, [Win32]::SWP_SHOWWINDOW)
#}

 
#使用Windows API调整当前命令行窗口大小和位置
$code=@'
    using System;
    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 IntPtr FindWindow(string lpClassName, string lpWindowName);
    }
'@
Add-Type -TypeDefinition $code
#设置位置及大小
$hwnd = [WinApi]::GetConsoleWindow()
[void][WinApi]::SetWindowPos($hwnd,$null,30,30,400,500,0)

 

posted on 2024-11-05 14:54  jack_Meng  阅读(9)  评论(0编辑  收藏  举报

导航