3

Problem: We have a large television we use for presentations, that isn't exactly 1920×1080 px. It's off and I can't control it. What I can control is my windows laptop that I connect to it. I have been looking for ways to get whatever I need displayed, to move over to the projector, set a position that is offset for looking good on the tv, and set the width/height for the same.

What's been done: I can use Boe Prox's Get-Window script (https://blogs.technet.microsoft.com/heyscriptingguy/2015/12/26/weekend-scripter-manage-window-placement-by-using-pinvoke/), and that worked fine, to get me the dimensions and offset I need. However, his Set-Window errors on me in both WinPosh 5 and Posh 6 with and without Admin privilege. Other potential solutions produced similar errors, so I decided to stay with Prox's script, as he's an expert by my standards.

I am evaluating solutions mentioned here, https://stackoverflow.com/questions/10392620/how-can-a-batch-file-run-a-program-and-set-the-position-and-size-of-the-window/ as possible workarounds. However, a PowerShell solution without dependencies on anything third party would be ideal.

The question is: Has anyone solved how to get either Mr. Prox's Set-Window or anything else to set a windows position and size in straight v5 or v6 PowerShell?

Error Message in Posh 5:

Cannot convert argument "hWnd", with value: "System.Object[]", for "GetWindowRect" to type "System.IntPtr": "Cannot
convert the "System.Object[]" value of type "System.Object[]" to type "System.IntPtr"."
At Z:\scripts\Set-Window.ps1:90 char:9
+         $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Error Messages in Posh 6:

PS C:\> Set-Window -ProcessName notepad -X 1911 -Y "-369" -Width 266 -Height 113
Method invocation failed because [Window] does not contain a method named 'MoveWindow'.
At Z:\scripts\Set-Window.ps1:98 char:13
+             $Return = [Window]::MoveWindow($Handle, $x, $y, $Width, $ ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound


PS C:\> Set-Window -ProcessName firefox -X "-9" -Y "-9" -Width "1938" -Height "1050"
Cannot convert argument "hWnd", with value: "System.Object[]", for "GetWindowRect" to type "System.IntPtr": "Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.IntPtr"."
At Z:\scripts\Set-Window.ps1:90 char:9
+         $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Note: These errors occur in Windows 7 (no access to Win10 for a while).

Update: I had noticed Set (Many) Programs' Window Size/Position, but the UIAutomation module isn't maintained (codeplex is archived, and last blog post by the author for it, was Feb 2014).

4

By default, Get-Process cmdlet returns a System.Diagnostics.Process object - or an array of such object if there are more matching processes. Unfortunately, the original Set-Window.ps1 script does not reflect the latter scenario.

Output using improved script:

PS D:\PShell> Get-Process -ProcessName notepad

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    505      29    12104      41232       2,05   4208   1 notepad
    231      14     3068      13212       0,09   6732   1 notepad

PS D:\PShell> . D:\PShell\Downloaded\WindowManipulation\Set-Window.ps1

PS D:\PShell> Set-Window -ProcessName notepad -X 11 -Y 11 -Width 1200 -Height 900 -Passthru

ProcessName Size     TopLeft BottomRight
----------- ----     ------- -----------
notepad     1200,900 11,11   1211,911
notepad     1200,900 11,11   1211,911


PS D:\PShell>

Improved script (changes commented):

Function Set-Window {
    <#
        .SYNOPSIS
            Sets the window size (height,width) and coordinates (x,y) of
            a process window.

        .DESCRIPTION
            Sets the window size (height,width) and coordinates (x,y) of
            a process window.

        .PARAMETER ProcessName
            Name of the process to determine the window characteristics

        .PARAMETER X
            Set the position of the window in pixels from the top.

        .PARAMETER Y
            Set the position of the window in pixels from the left.

        .PARAMETER Width
            Set the width of the window.

        .PARAMETER Height
            Set the height of the window.

        .PARAMETER Passthru
            Display 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 (https://superuser.com/users/376602/josefz) - 19.05.2018
                    - treats more process instances of supplied process name properly

        .OUTPUT
            System.Automation.WindowInfo

        .EXAMPLE
            Get-Process powershell | Set-Window -X 2040 -Y 142 -Passthru

            ProcessName Size     TopLeft  BottomRight
            ----------- ----     -------  -----------
            powershell  1262,642 2040,142 3302,784   

            Description
            -----------
            Set the coordinates on the window for the process PowerShell.exe

    #>
    [OutputType('System.Automation.WindowInfo')]
    [cmdletbinding()]
    Param (
        [parameter(ValueFromPipelineByPropertyName=$True)]
        $ProcessName,
        [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")]
                public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
              }
              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
        $Handles = (Get-Process -Name $ProcessName).MainWindowHandle   ### 1.1//JosefZ
        foreach ( $Handle in $Handles ) {                              ### 1.1//JosefZ
            if ( $Handle -eq [System.IntPtr]::Zero ) { Continue }      ### 1.1//JosefZ
            $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
            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.ContainsKey('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.LEft -lt 0) {
                        Write-Warning "Window is minimized! Coordinates will not be accurate."
                    }
                    $Object = [pscustomobject]@{
                        ProcessName = $ProcessName
                        Size = $Size
                        TopLeft = $TopLeft
                        BottomRight = $BottomRight
                    }
                    $Object.PSTypeNames.insert(0,'System.Automation.WindowInfo')
                    $Object            
                }
            }
        }
    }
}
  • That successfully works in both v5 and v6. Thank you for teaching me something! – Bewc May 21 '18 at 12:46
  • I also had to change "User32.dll" to "user32.dll" on Windows 10 / PowerShell: ``` [DllImport("User32.dll")] public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw); } ``` to (note lower-case u) ``` [DllImport("user32.dll")] public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw); } ``` – Tuure Laurinolli Aug 27 '18 at 7:49
0

I also had to change "User32.dll" to "user32.dll" on Windows 10 / PowerShell:

[DllImport("User32.dll")]
    public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
}

to (note lower-case u)

[DllImport("user32.dll")]
    public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
}

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.