Jack Parkinson
Jack Parkinson

Reputation: 711

PowerShell script to open four different Windows Explorer paths and position each window

I am trying to write a script that opens up Windows Explorer four times (each window with a different path) and positions each window in a corner of the screen so that they cover equal areas. The script will be used on machines with different screen resolutions so ideally it should be based on window size, but this isn't an absolute must, just as long as they're not overlapping (I think the min screen resolution it will be used on is 1366x768).

I am new to PowerShell scripting so I've managed to get some things working, I'm just struggling to fit it all together. I was able to get four separate Windows Explorer windows to open where I wanted:

ii $path1
ii $path2
ii $path3
ii $path4

I was also able to open Internet Explorer to a set size using -ComObject:

$IE = New-Object -ComObject Internetexplorer.application

$IE.Left = 0
$IE.Width = 500
$IE.Top = 0
$IE.Height = 500

$IE.Navigate($URL)

$IE.Visible = $True

I can't get it to work with Windows Explorer though. I've also managed to get the resolution for the monitor:

$monitor = Get-Wmiobject Win32_Videocontroller
$monitor.CurrentHorizontalResolution
$monitor.CurrentVerticalResolution

Unfortunately this provides 2 resolutions because I have two monitors, and I'm not sure how to extract the resolution for just one of them. Additionally, I don't know what that would mean if the script was run on a machine with only one monitor.

So with what I currently have, I would specifically like to know:

  1. How to open my Windows Explorer windows to a certain size and position as I have done with IE (doesn't have to be with ii either, anything that works)
  2. How to extract a single value for monitor resolution that I can do some maths with to get the optimum sizing.

Thanks.


EDIT: I have found a quick-fix but it's not quite there. By examining the commands available to me with the shell, and came up with this:

$Shell = New-Object -ComObject Shell.Application

ii $path1
ii $path2
ii $path3
ii $path4

$Shell.TileHorizontally()

This should work, because it's likely that the user will have only these four windows open and nothing else. Unfortunately, TileHorizontally()works on all open windows, but it looks like the ii commands don't execute fast enough or something, because they don't tile when the script is run. They do if you run it a second time, once the script is already open. I thought that the -Wait option would help me here, but it doesn't seem to work with ii. Anyone see a solution to this?

EDIT 2: My current code, using Set-Window with doc comments removed:

Function Set-Window {
    [OutputType('System.Automation.WindowInfo')]
    [cmdletbinding()]
    Param (
        [parameter(ValueFromPipelineByPropertyName=$True)]
        $ProcessId,
        [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
        $Handle = (Get-Process -Id $ProcessId).MainWindowHandle
        $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]@{
                    Id = $ProcessId
                    Size = $Size
                    TopLeft = $TopLeft
                    BottomRight = $BottomRight
                }
                $Object.PSTypeNames.insert(0,'System.Automation.WindowInfo')
                $Object            
            }
        }
    }
}


Get-Process -Id (Start-Process -FilePath C:\windows\explorer.exe -ArgumentList "." -Wait -Passthru).Id | Set-Window -X 500 -Y 500 -Height 500 -Width 500 -Passthru

Upvotes: 3

Views: 4544

Answers (1)

Hello Gator
Hello Gator

Reputation: 11

I finally figured it out. I didn't realize I needed a sleep for a few seconds.

$Shell = New-Object -ComObject Shell.Application

$Shell.Open("C:\Folder1")
$Shell.Open("C:\Folder2")
$Shell.Open("C:\Folder3")
$Shell.Open("C:\Folder4")
Start-Sleep -Seconds 10 # Wait for the processes to complete

$Shell.Windows()[0].Left = -3847
$Shell.Windows()[0].Top = -273
$Shell.Windows()[0].Width = 1934
$Shell.Windows()[0].Height = 1063

$Shell.Windows()[1].Left = -1927
$Shell.Windows()[1].Top = -273
$Shell.Windows()[1].Width = 1934
$Shell.Windows()[1].Height = 1063

$Shell.Windows()[2].Left = -1927
$Shell.Windows()[2].Top = -1329
$Shell.Windows()[2].Width = 1934
$Shell.Windows()[2].Height = 1063

$Shell.Windows()[3].Left = -3847
$Shell.Windows()[3].Top = -1329
$Shell.Windows()[3].Width = 1934
$Shell.Windows()[3].Height = 1063

Upvotes: 1

Related Questions