title: PowerShell script to open four different Windows Explorer paths and position each window
body: <p>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).</p>
  
  <p>I am new to PowerShell scripting so I've managed to get <em>some</em> 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:</p>
  
  <pre><code>ii $path1
  ii $path2
  ii $path3
  ii $path4
  </code></pre>
  
  <p>I was also able to open <em>Internet Explorer</em> to a set size using <code>-ComObject</code>:</p>
  
  <pre><code>$IE = New-Object -ComObject Internetexplorer.application
  
  $IE.Left = 0
  $IE.Width = 500
  $IE.Top = 0
  $IE.Height = 500
  
  $IE.Navigate($URL)
  
  $IE.Visible = $True
  </code></pre>
  
  <p>I can't get it to work with Windows Explorer though. I've also managed to get the resolution for the monitor:</p>
  
  <pre><code>$monitor = Get-Wmiobject Win32_Videocontroller
  $monitor.CurrentHorizontalResolution
  $monitor.CurrentVerticalResolution
  </code></pre>
  
  <p>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.</p>
  
  <p>So with what I currently have, I would specifically like to know:</p>
  
  <ol>
  <li>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 <code>ii</code> either, anything that works)</li>
  <li>How to extract a single value for monitor resolution that I can do some maths with to get the optimum sizing.</li>
  </ol>
  
  <p>Thanks.</p>
  
  <hr />
  
  <p><strong>EDIT:</strong> 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:</p>
  
  <pre><code>$Shell = New-Object -ComObject Shell.Application
  
  ii $path1
  ii $path2
  ii $path3
  ii $path4
  
  $Shell.TileHorizontally()
  </code></pre>
  
  <p>This should work, because it's likely that the user will have only these four windows open and nothing else. Unfortunately, <code>TileHorizontally()</code>works on all open windows, but it looks like the <code>ii</code> 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 <code>-Wait</code> option would help me here, but it doesn't seem to work with <code>ii</code>. Anyone see a solution to this?</p>
  
  <p><strong>EDIT 2:</strong> My current code, using <code>Set-Window</code> with doc comments removed:</p>
  
  <pre><code>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
  </code></pre>