Reputation: 711
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:
ii
either, anything that works)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
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