Skip to content

Instantly share code, notes, and snippets.

@AlexDev404
Created August 9, 2025 20:46
Show Gist options
  • Save AlexDev404/118e5365c1914bebbae0f141805f73a3 to your computer and use it in GitHub Desktop.
Save AlexDev404/118e5365c1914bebbae0f141805f73a3 to your computer and use it in GitHub Desktop.
Add-Type @"
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
public class Win32 {
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetShellWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
public static IntPtr FindMainWindow(uint processId) {
IntPtr windowHandle = IntPtr.Zero;
EnumWindows(delegate (IntPtr hWnd, IntPtr lParam) {
uint windowProcessId;
GetWindowThreadProcessId(hWnd, out windowProcessId);
if (windowProcessId == processId && IsWindowVisible(hWnd) && hWnd != GetShellWindow()) {
windowHandle = hWnd;
return false; // stop enumerating
}
return true; // continue enumerating
}, IntPtr.Zero);
return windowHandle;
}
}
"@
# Constants for SetWindowPos
$SWP_SHOWWINDOW = 0x0040
$HWND_TOP = [IntPtr]::Zero
$SW_SHOWNORMAL = 1
Add-Type -AssemblyName System.Windows.Forms
$monitors = [System.Windows.Forms.Screen]::AllScreens
# If only one monitor, exit
if ($monitors.Count -lt 2) {
Write-Host "Only one monitor detected. Exiting..."
exit
}
# Application path
$appPath = "C:\Program Files (x86)\Xibo Player\XiboClient.exe"
function Start-And-MoveWindow {
param(
[string]$appPath,
[System.Drawing.Rectangle]$bounds,
[PSCredential]$cred = $null,
[string]$workingDir = $null
)
if ($null -ne $cred) {
$process = Start-Process -FilePath $appPath -PassThru -Credential $cred -WorkingDirectory $workingDir
} else {
$process = Start-Process $appPath -PassThru
}
# Wait for main window to appear
$hwnd = [IntPtr]::Zero
for ($i=0; $i -lt 20; $i++) {
Start-Sleep -Milliseconds 500
$hwnd = [Win32]::FindMainWindow($process.Id)
if ($hwnd -ne [IntPtr]::Zero) { break }
}
if ($hwnd -ne [IntPtr]::Zero) {
[Win32]::ShowWindow($hwnd, $SW_SHOWNORMAL) | Out-Null
[Win32]::SetWindowPos($hwnd, $HWND_TOP, $bounds.X, $bounds.Y, $bounds.Width, $bounds.Height, $SWP_SHOWWINDOW) | Out-Null
Write-Host ("Moved and resized window to monitor at X={0}, Y={1}, W={2}, H={3}" -f $bounds.X, $bounds.Y, $bounds.Width, $bounds.Height)
} else {
Write-Host "Could not find the application window within timeout."
}
}
$cred = Get-StoredCredential -Target "XIBO2"
# Handle 3 or more monitors
if ($monitors.Count -gt 2) {
# First instance (second monitor) under XIBO2
Start-And-MoveWindow -appPath $appPath -bounds $monitors[1].Bounds -cred $cred -workingDir "C:\Windows\System32"
# Second instance (third monitor) as current user
Start-And-MoveWindow -appPath $appPath -bounds $monitors[2].Bounds
}
if ($monitors.Count -eq 2) {
# First instance (first monitor) under XIBO2
Start-And-MoveWindow -appPath $appPath -bounds $monitors[0].Bounds -cred $cred -workingDir "C:\Windows\System32"
# Second instance (second monitor) as current user
Start-And-MoveWindow -appPath $appPath -bounds $monitors[1].Bounds
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment