From 24dbb298279661a46061d148a95d3cf1a04779ef Mon Sep 17 00:00:00 2001 From: b0tmtl <109532492+b0tmtl@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:36:34 -0500 Subject: [PATCH 1/2] Added logic to change resolution remotely --- .../ChangeScreenResolution.ps1 | 394 +++++++++++++----- 1 file changed, 280 insertions(+), 114 deletions(-) diff --git a/ChangeScreenResolution/ChangeScreenResolution.ps1 b/ChangeScreenResolution/ChangeScreenResolution.ps1 index 3a054c4..e4bfbed 100644 --- a/ChangeScreenResolution/ChangeScreenResolution.ps1 +++ b/ChangeScreenResolution/ChangeScreenResolution.ps1 @@ -1,133 +1,299 @@ -<# - .Synopsis - Sets the Screen Resolution of the primary monitor - .Description - Uses Pinvoke and ChangeDisplaySettings Win32API to make the change - .Example - Set-ScreenResolution -Width 1024 -Height 768 - #> function Set-ScreenResolution(){ -param ( -[Parameter(Mandatory=$true, - Position = 0)] -[int] -$Width, -[Parameter(Mandatory=$true, - Position = 1)] -[int] -$Height -) -$pinvokeCode = @" + param ( + [Parameter(Mandatory=$true, Position = 0)] + [int]$Width, + + [Parameter(Mandatory=$true, Position = 1)] + [int]$Height, + + [Parameter(Mandatory=$false)] + [switch]$AsTask + ) + + $pinvokeCode = @" using System; using System.Runtime.InteropServices; namespace Resolution { - [StructLayout(LayoutKind.Sequential)] - public struct DEVMODE1 - { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] - public string dmDeviceName; - public short dmSpecVersion; - public short dmDriverVersion; - public short dmSize; - public short dmDriverExtra; - public int dmFields; - public short dmOrientation; - public short dmPaperSize; - public short dmPaperLength; - public short dmPaperWidth; - public short dmScale; - public short dmCopies; - public short dmDefaultSource; - public short dmPrintQuality; - public short dmColor; - public short dmDuplex; - public short dmYResolution; - public short dmTTOption; - public short dmCollate; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] - public string dmFormName; - public short dmLogPixels; - public short dmBitsPerPel; - public int dmPelsWidth; - public int dmPelsHeight; - public int dmDisplayFlags; - public int dmDisplayFrequency; - public int dmICMMethod; - public int dmICMIntent; - public int dmMediaType; - public int dmDitherType; - public int dmReserved1; - public int dmReserved2; - public int dmPanningWidth; - public int dmPanningHeight; - }; + [StructLayout(LayoutKind.Sequential)] + public struct DEVMODE1 + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string dmDeviceName; + public short dmSpecVersion; + public short dmDriverVersion; + public short dmSize; + public short dmDriverExtra; + public int dmFields; + public short dmOrientation; + public short dmPaperSize; + public short dmPaperLength; + public short dmPaperWidth; + public short dmScale; + public short dmCopies; + public short dmDefaultSource; + public short dmPrintQuality; + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string dmFormName; + public short dmLogPixels; + public short dmBitsPerPel; + public int dmPelsWidth; + public int dmPelsHeight; + public int dmDisplayFlags; + public int dmDisplayFrequency; + public int dmICMMethod; + public int dmICMIntent; + public int dmMediaType; + public int dmDitherType; + public int dmReserved1; + public int dmReserved2; + public int dmPanningWidth; + public int dmPanningHeight; + }; - class User_32 - { - [DllImport("user32.dll")] - public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode); - [DllImport("user32.dll")] - public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags); - public const int ENUM_CURRENT_SETTINGS = -1; - public const int CDS_UPDATEREGISTRY = 0x01; - public const int CDS_TEST = 0x02; - public const int DISP_CHANGE_SUCCESSFUL = 0; - public const int DISP_CHANGE_RESTART = 1; - public const int DISP_CHANGE_FAILED = -1; - } + class User_32 + { + [DllImport("user32.dll")] + public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode); + [DllImport("user32.dll")] + public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags); - public class PrmaryScreenResolution - { - static public string ChangeResolution(int width, int height) + public const int ENUM_CURRENT_SETTINGS = -1; + public const int CDS_UPDATEREGISTRY = 0x01; + public const int CDS_TEST = 0x02; + public const int DISP_CHANGE_SUCCESSFUL = 0; + public const int DISP_CHANGE_RESTART = 1; + public const int DISP_CHANGE_FAILED = -1; + } + + public class PrmaryScreenResolution { - DEVMODE1 dm = GetDevMode1(); - if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm)) - { - dm.dmPelsWidth = width; - dm.dmPelsHeight = height; - int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST); - if (iRet == User_32.DISP_CHANGE_FAILED) + static public string ChangeResolution(int width, int height) { - return "Unable To Process Your Request. Sorry For This Inconvenience."; + DEVMODE1 dm = GetDevMode1(); + if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm)) + { + dm.dmPelsWidth = width; + dm.dmPelsHeight = height; + int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST); + if (iRet == User_32.DISP_CHANGE_FAILED) + { + return "Unable To Process Your Request. Sorry For This Inconvenience."; + } + else + { + iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY); + switch (iRet) + { + case User_32.DISP_CHANGE_SUCCESSFUL: + return "Success"; + case User_32.DISP_CHANGE_RESTART: + return "You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode."; + default: + return "Failed To Change The Resolution"; + } + } + } + else + { + return "Failed To Change The Resolution."; + } } - else + + private static DEVMODE1 GetDevMode1() { - iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY); - switch (iRet) - { - case User_32.DISP_CHANGE_SUCCESSFUL: - { - return "Success"; - } - case User_32.DISP_CHANGE_RESTART: - { - return "You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode."; - } - default: - { - return "Failed To Change The Resolution"; - } - } + DEVMODE1 dm = new DEVMODE1(); + dm.dmDeviceName = new String(new char[32]); + dm.dmFormName = new String(new char[32]); + dm.dmSize = (short)Marshal.SizeOf(dm); + return dm; + } + } +} +"@ + + # Function to run the resolution change via scheduled task (for remote sessions) + function Set-ResolutionViaScheduledTask { + param([int]$W, [int]$H) + + # Get the currently logged-on interactive user + $interactiveUser = (Get-WmiObject -Class Win32_ComputerSystem).UserName + if (-not $interactiveUser) { + return "Error: No interactive user logged on to the console." } - } - else - { - return "Failed To Change The Resolution."; - } + $taskName = "SetResolution_$([guid]::NewGuid().ToString('N').Substring(0,8))" + $outputFile = "C:\Temp\resolution_result_$taskName.txt" + + # Create inline script that will run in the interactive session + $scriptContent = @" +`$pinvokeCode = @' +using System; +using System.Runtime.InteropServices; +namespace Resolution +{ + [StructLayout(LayoutKind.Sequential)] + public struct DEVMODE1 + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string dmDeviceName; + public short dmSpecVersion; + public short dmDriverVersion; + public short dmSize; + public short dmDriverExtra; + public int dmFields; + public short dmOrientation; + public short dmPaperSize; + public short dmPaperLength; + public short dmPaperWidth; + public short dmScale; + public short dmCopies; + public short dmDefaultSource; + public short dmPrintQuality; + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string dmFormName; + public short dmLogPixels; + public short dmBitsPerPel; + public int dmPelsWidth; + public int dmPelsHeight; + public int dmDisplayFlags; + public int dmDisplayFrequency; + public int dmICMMethod; + public int dmICMIntent; + public int dmMediaType; + public int dmDitherType; + public int dmReserved1; + public int dmReserved2; + public int dmPanningWidth; + public int dmPanningHeight; + }; + class User_32 + { + [DllImport("user32.dll")] + public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode); + [DllImport("user32.dll")] + public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags); + public const int ENUM_CURRENT_SETTINGS = -1; + public const int CDS_UPDATEREGISTRY = 0x01; + public const int CDS_TEST = 0x02; + public const int DISP_CHANGE_SUCCESSFUL = 0; + public const int DISP_CHANGE_RESTART = 1; + public const int DISP_CHANGE_FAILED = -1; } - private static DEVMODE1 GetDevMode1() + public class PrmaryScreenResolution { - DEVMODE1 dm = new DEVMODE1(); - dm.dmDeviceName = new String(new char[32]); - dm.dmFormName = new String(new char[32]); - dm.dmSize = (short)Marshal.SizeOf(dm); - return dm; + static public string ChangeResolution(int width, int height) + { + DEVMODE1 dm = GetDevMode1(); + if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm)) + { + dm.dmPelsWidth = width; + dm.dmPelsHeight = height; + int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST); + if (iRet == User_32.DISP_CHANGE_FAILED) + { + return "Unable To Process Your Request. Sorry For This Inconvenience."; + } + else + { + iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY); + switch (iRet) + { + case User_32.DISP_CHANGE_SUCCESSFUL: + return "Success"; + case User_32.DISP_CHANGE_RESTART: + return "You Need To Reboot For The Change To Happen."; + default: + return "Failed To Change The Resolution"; + } + } + } + else + { + return "Failed To Change The Resolution."; + } + } + private static DEVMODE1 GetDevMode1() + { + DEVMODE1 dm = new DEVMODE1(); + dm.dmDeviceName = new String(new char[32]); + dm.dmFormName = new String(new char[32]); + dm.dmSize = (short)Marshal.SizeOf(dm); + return dm; + } } - } } +'@ +Add-Type `$pinvokeCode +`$result = [Resolution.PrmaryScreenResolution]::ChangeResolution($W, $H) +`$result | Out-File -FilePath '$outputFile' -Encoding UTF8 "@ -Add-Type $pinvokeCode -ErrorAction SilentlyContinue -[Resolution.PrmaryScreenResolution]::ChangeResolution($width,$height) + + $scriptFile = "C:\TEMP\$taskName.ps1" + $scriptContent | Out-File -FilePath $scriptFile -Encoding UTF8 + + try { + # Create scheduled task that runs in the interactive session + $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptFile`"" + $principal = New-ScheduledTaskPrincipal -UserId $interactiveUser -LogonType Interactive + $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable + + Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Settings $settings -Force | Out-Null + + # Run the task immediately + Start-ScheduledTask -TaskName $taskName + + # Wait for completion (timeout after 30 seconds) + $timeout = 30 + $elapsed = 0 + while ($elapsed -lt $timeout) { + Start-Sleep -Milliseconds 500 + $elapsed += 0.5 + $taskInfo = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue + if ($taskInfo.State -eq 'Ready') { + break + } + } + + # Read result + Start-Sleep -Seconds 5 + if (Test-Path $outputFile) { + $result = Get-Content -Path $outputFile -Raw + return $result.Trim() + } + else { + return "Error: Could not retrieve result from scheduled task." + } + } + catch { + return "Error creating/running scheduled task: $_" + } + finally { + # Cleanup + Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue + Remove-Item -Path $scriptFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $outputFile -Force -ErrorAction SilentlyContinue + } + } + + # Main execution logic + if ($AsTask) { + Set-ResolutionViaScheduledTask -W $Width -H $Height + } + else { + if (-not ([System.Management.Automation.PSTypeName]'Resolution.PrmaryScreenResolution').Type) { + Add-Type $pinvokeCode + } + [Resolution.PrmaryScreenResolution]::ChangeResolution($Width, $Height) + } } From 877ec31ab4c4c9099d0b93e231a8cfd1dbd287ab Mon Sep 17 00:00:00 2001 From: b0tmtl <109532492+b0tmtl@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:37:37 -0500 Subject: [PATCH 2/2] Update README with remote session example Added example for running Set-ScreenResolution in a remote session. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f88ddb3..ccaa5a8 100644 --- a/README.md +++ b/README.md @@ -12,5 +12,7 @@ Install-Module ChangeScreenResolution ### Examples ``` Set-ScreenResolution 1920 1080 + # To run in a remote session (Enter-PSSession) + Set-ScreenResolution 1600 900 -AsTask ```