PSModuleUtils/Functions/Set-Window.ps1

# ===========================================================================
# Set-Window.ps1 ----------------------------------------------------------
# ===========================================================================

# function ----------------------------------------------------------------
# ---------------------------------------------------------------------------
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
     
    .PARAMETER Id
     
    .PARAMETER X
     
    .PARAMETER Y
     
    .PARAMETER Width
     
    .PARAMETER Height
      
    .PARAMETER Passthru
     
    .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
    Link: https://superuser.com/questions/1324007/setting-window-size-and-position-in-powershell-5-and-6
 
    .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(PositionalBinding=$True, DefaultParameterSetName="Name")]

    Param (

        [Parameter(Mandatory=$False, ValueFromPipelineByPropertyName=$True, ParameterSetName="Name", HelpMessage="Name of the process to determine the window characteristics. (All processes if omitted).")]
        [String]$ProcessName="*",

        [Parameter(Mandatory=$True, ValueFromPipeline=$False,       ParameterSetName="Id", HelpMessage="Id of the process to determine the window characteristics. ")]
        [Int]$Id,

        [Parameter(HelpMessage="Set the position of the window in pixels from the left.")]
        [Int]$X,

        [Parameter(HelpMessage="Set the position of the window in pixels from the top.")]
        [Int]$Y,

        [Parameter(HelpMessage="Set the width of the window.")]
        [Int]$Width,

        [Parameter(HelpMessage="Set the height of the window.")]
        [Int]$Height,

        [Parameter(HelpMessage="Returns the output object of the window.")]
        [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
                    }
                }
            }
        }
    }
}