├── .github └── FUNDING.yml ├── icon.png ├── icon-128.png ├── icon-256.png ├── Binaries ├── sr.exe └── SetResolution.exe ├── SetResolution ├── install-localpackage.ps1 ├── signtool.exe ├── Properties │ ├── launchsettings.json │ ├── Resources.resx │ └── Resources.Designer.cs ├── build.ps1 ├── publish-nuget.ps1 ├── DisplayProfile.cs ├── Westwind.SetResolution.csprojExperimental ├── SetResolutionCommandLineParser.cs ├── AppConfiguration.cs ├── Westwind.SetResolution.csproj ├── Program.cs ├── CommandLine │ ├── CommandLineParser.cs │ └── ColorConsole.cs ├── SetResolutionProcessor.cs ├── DisplayManagerNative.cs ├── Utilities │ └── SerializationUtils.cs └── DisplayManager.cs ├── Assets ├── HelpScreen.png ├── ListDisplay.png └── SetResolutionMain.png ├── LICENSE.MD ├── SetResolution.sln ├── .gitattributes ├── .editorconfig ├── .gitignore └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [RickStrahl] -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/icon.png -------------------------------------------------------------------------------- /icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/icon-128.png -------------------------------------------------------------------------------- /icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/icon-256.png -------------------------------------------------------------------------------- /Binaries/sr.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/Binaries/sr.exe -------------------------------------------------------------------------------- /SetResolution/install-localpackage.ps1: -------------------------------------------------------------------------------- 1 | dotnet tool update -g SetResolution --add-source ./nupkg -------------------------------------------------------------------------------- /Assets/HelpScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/Assets/HelpScreen.png -------------------------------------------------------------------------------- /Assets/ListDisplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/Assets/ListDisplay.png -------------------------------------------------------------------------------- /Binaries/SetResolution.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/Binaries/SetResolution.exe -------------------------------------------------------------------------------- /SetResolution/signtool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/SetResolution/signtool.exe -------------------------------------------------------------------------------- /Assets/SetResolutionMain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickStrahl/SetResolution/HEAD/Assets/SetResolutionMain.png -------------------------------------------------------------------------------- /SetResolution/Properties/launchsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SetResolution list": { 4 | "commandName": "Project", 5 | "commandLineArgs": "LIST" 6 | }, 7 | "SetResolution Set": { 8 | "commandName": "Project", 9 | "commandLineArgs": "SET -h 1080 -w 1920 -o 0 -f 60" 10 | }, 11 | "SetResolution 4k": { 12 | "commandName": "Project", 13 | "commandLineArgs": "4k" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /SetResolution/build.ps1: -------------------------------------------------------------------------------- 1 | # build exe (net472) 2 | dotnet build -c Release 3 | 4 | # build dotnet tool (net6.0) 5 | dotnet build -c Release /p:DefineConstants="BUILD_DOTNET_TOOL" 6 | 7 | copy .\bin\Release\net472\SetResolution.exe ..\Binaries 8 | 9 | & ".\signtool.exe" sign /v /n "West Wind Technologies" /tr "http://timestamp.digicert.com" /td SHA256 /fd SHA256 "..\Binaries\SetResolution.exe" 10 | 11 | # Shortcut name 12 | copy ..\binaries\SetResolution.exe ..\binaries\sr.exe 13 | 14 | # My Utilities folder 15 | if (Test-Path ~\DropBox\utl) { 16 | copy ..\binaries\SetResolution.exe ~\DropBox\utl\SetResolution.exe 17 | copy ..\binaries\SetResolution.exe ~\DropBox\utl\sr.exe 18 | } 19 | 20 | -------------------------------------------------------------------------------- /SetResolution/publish-nuget.ps1: -------------------------------------------------------------------------------- 1 | # Dotnet Tool Publishing 2 | # ---------------------- 3 | # Make sure to set project DefineConstant to BUILD_DOTNET_TOOL 4 | # before running this file 5 | 6 | if (test-path ./nupkg) { 7 | remove-item ./nupkg -Force -Recurse 8 | } 9 | 10 | dotnet build -c Release /p:DefineConstants="BUILD_DOTNET_TOOL" 11 | 12 | $filename = gci "./nupkg/*.nupkg" | sort LastWriteTime | select -last 1 | select -ExpandProperty "Name" 13 | Write-host $filename 14 | $len = $filename.length 15 | 16 | if ($len -gt 0) { 17 | Write-Host "signing... $filename" 18 | nuget sign ".\nupkg\$filename" -CertificateSubject "West Wind Technologies" -timestamper " http://timestamp.digicert.com" 19 | Write-Host "Pushing to NuGet..." 20 | nuget push ".\nupkg\$filename" -source "https://nuget.org" 21 | Write-Host "Done." 22 | } -------------------------------------------------------------------------------- /SetResolution/DisplayProfile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Westwind.SetResolution 8 | { 9 | public class DisplayProfile 10 | { 11 | 12 | public string Name { get; set; } 13 | 14 | public int Width { get; set; } 15 | 16 | public int Height { get; set; } 17 | 18 | public int Frequency { get; set; } = 60; 19 | 20 | public int BitSize { get; set; } = 32; 21 | 22 | public Orientation Orientation { get; set; } = 0; 23 | 24 | public void UpdateCommandLine(SetResolutionCommandLineParser cmd) 25 | { 26 | cmd.Width = Width; 27 | cmd.Height = Height; 28 | cmd.Frequency = Frequency; 29 | cmd.BitCount = BitSize; 30 | cmd.Orientation = Orientation; 31 | } 32 | 33 | public override string ToString() 34 | { 35 | return $"{Name}: {Width} x {Height}, {BitSize}, {Frequency}"; 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE.MD: -------------------------------------------------------------------------------- 1 | West Wind Set Resolution 2 | ============================================= 3 | 4 | MIT License 5 | ----------- 6 | 7 | Copyright (c) 2019-2022 West Wind Technologies 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. -------------------------------------------------------------------------------- /SetResolution.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33122.133 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Westwind.SetResolution", "SetResolution\Westwind.SetResolution.csproj", "{D5715759-1922-4203-961A-C10DE8EA36A0}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{15259836-2069-4908-922C-1B7714FB51A3}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | README.md = README.md 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {D5715759-1922-4203-961A-C10DE8EA36A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {D5715759-1922-4203-961A-C10DE8EA36A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {D5715759-1922-4203-961A-C10DE8EA36A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {D5715759-1922-4203-961A-C10DE8EA36A0}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B0C754ED-2A91-498E-8F4D-5B1FF806DCAA} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /SetResolution/Westwind.SetResolution.csprojExperimental: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net472 5 | Exe 6 | 0.1.4 7 | SetResolution 8 | Westwind.SetResolution 9 | Set Windows Display Resolution 10 | SetResolution: Set Windows Display Resolution from Terminal 11 | 12 | Allows you set Windows display Resolution from the Terminal via simple commands. 13 | 14 | Using this tool you can: 15 | 16 | * Set a specific resolution, frequency, bitness and rotation 17 | * List all available display modes 18 | * Create and use profiles for easy access 19 | * Supports multiple Monitors 20 | 21 | Rick Strahl, West Wind Technologies 22 | West Wind Technologies, 2022-2023 23 | 24 | 25 | 26 | 27 | 28 | True 29 | True 30 | Resources.resx 31 | 32 | 33 | 34 | PublicResXFileCodeGenerator 35 | Resources.Designer.cs 36 | 37 | 38 | 39 | 40 | true 41 | setresolution 42 | setresolution 43 | 44 | ./nupkg 45 | true 46 | 47 | 48 | -------------------------------------------------------------------------------- /SetResolution/SetResolutionCommandLineParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Threading; 4 | using HtmlPackager.ConsoleApp; 5 | 6 | namespace Westwind.SetResolution 7 | { 8 | public class SetResolutionCommandLineParser : CommandLineParser 9 | { 10 | 11 | public int Width { get; set; } 12 | 13 | public int Height { get; set; } 14 | 15 | public int Frequency { get; set; } = 60; 16 | 17 | public int BitCount { get; set; } = 32; 18 | 19 | public Orientation Orientation { get; set; } = 0; 20 | 21 | public string Profile { get; set; } 22 | 23 | 24 | public bool Help { get; set; } 25 | 26 | public bool StartTray { get; set; } 27 | 28 | public bool CreateProfile { get; set; } 29 | public bool ShowResolutions { get; set; } 30 | public bool ListAll { get; set; } 31 | 32 | public int MonitorId { get; set; } 33 | 34 | /// 35 | /// Don't ask for confirmation 36 | /// 37 | public bool NoPrompt { get; set; } 38 | 39 | public bool NoPersist { get; set; } 40 | 41 | 42 | public SetResolutionCommandLineParser(string[] args = null, string cmdLine = null) 43 | : base(args, cmdLine) 44 | { 45 | } 46 | 47 | public override void Parse() 48 | { 49 | Width = ParseIntParameterSwitch("-w", 0); 50 | Height = ParseIntParameterSwitch("-h", 0); 51 | Frequency = ParseIntParameterSwitch("-f", 60); 52 | BitCount = ParseIntParameterSwitch("-b", 32); 53 | int or = ParseIntParameterSwitch("-o", 0); 54 | Orientation = (Orientation) or; 55 | ListAll = ParseParameterSwitch("-la"); 56 | 57 | Profile = ParseStringParameterSwitch("-p"); 58 | MonitorId = ParseIntParameterSwitch("-m"); 59 | 60 | NoPrompt = ParseParameterSwitch("-noprompt"); 61 | NoPersist = ParseParameterSwitch("-nopersist"); 62 | Help = ParseParameterSwitch("-h"); 63 | } 64 | 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /SetResolution/AppConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Xml.Serialization; 5 | using Westwind.Utilities; 6 | 7 | namespace Westwind.SetResolution 8 | { 9 | [XmlRoot(ElementName = "SetResolution")] 10 | public class AppConfiguration 11 | { 12 | public static AppConfiguration Current { get; set; } 13 | 14 | /// 15 | /// Display Profiles 16 | /// 17 | public List Profiles { get; set; } = new List(); 18 | 19 | /// 20 | /// Minimum Resolution 21 | /// 22 | public int MinResolutionWidth { get; set; } = 1024; 23 | 24 | public static void Load() 25 | { 26 | var file = Path.Combine(Program.StartupPath, "SetResolution.xml"); 27 | 28 | if (File.Exists(file)) 29 | Current = SerializationUtils.DeSerializeObject(file, typeof(AppConfiguration), false) as AppConfiguration; 30 | 31 | if (Current == null) 32 | { 33 | Current = new AppConfiguration(); 34 | Current.Profiles.Add(new DisplayProfile() 35 | { 36 | Name = "1080", 37 | Width= 1920, 38 | Height= 1080, 39 | Frequency = 60, 40 | BitSize= 32 41 | }); 42 | Current.Profiles.Add(new DisplayProfile() 43 | { 44 | Name = "4k", 45 | Width = 3840 , 46 | Height = 2160, 47 | Frequency = 60, 48 | BitSize = 32 49 | }); 50 | Current.Profiles.Add(new DisplayProfile() 51 | { 52 | Name = "1440", 53 | Width = 2560, 54 | Height = 1440, 55 | Frequency = 60, 56 | BitSize = 32 57 | }); 58 | Current.Profiles.Add(new DisplayProfile() 59 | { 60 | Name = "720", 61 | Width = 1280, 62 | Height = 720, 63 | Frequency = 60, 64 | BitSize = 32 65 | }); 66 | } 67 | } 68 | 69 | public static bool Save() 70 | { 71 | var file = Path.Combine(Program.StartupPath, "SetResolution.xml"); 72 | try 73 | { 74 | SerializationUtils.SerializeObject(Current, file); 75 | } 76 | catch 77 | { 78 | return false; 79 | } 80 | return true; 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /SetResolution/Westwind.SetResolution.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | Exe 9 | 0.3.2 10 | SetResolution 11 | Westwind.SetResolution 12 | Set Windows Display Resolution 13 | SetResolution: Set Windows Display Resolution from Terminal 14 | 15 | Allows you set Windows display Resolution from the Terminal via simple commands. 16 | 17 | This tool lets you: 18 | 19 | * Set a specific resolution, frequency, bitness and rotation 20 | * List all available display modes 21 | * Create and use profiles for quick mode switches 22 | * Support multiple Monitors 23 | * Prompts for confirmation to avoid possibly invalid display modes 24 | 25 | Rick Strahl, West Wind Technologies 26 | West Wind Technologies, 2022-2025 27 | 28 | 29 | icon.png 30 | LICENSE.MD 31 | README.md 32 | http://github.com/rickstrahl/SetResolution 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | True 41 | True 42 | Resources.resx 43 | 44 | 45 | 46 | PublicResXFileCodeGenerator 47 | Resources.Designer.cs 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | net472 60 | 61 | 62 | 63 | 64 | net9.0 65 | 66 | true 67 | SetResolution 68 | setresolution 69 | 70 | ./nupkg 71 | true 72 | 73 | 74 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | # Don't use tabs for indentation. 5 | [*] 6 | indent_style = space 7 | trim_trailing_whitespace = false 8 | 9 | # Code files 10 | [*.{cs,csx,vb,vbx}] 11 | indent_size = 4 12 | insert_final_newline = true 13 | charset = utf-8-bom 14 | trim_trailing_whitespace = true 15 | 16 | [*.less] 17 | charset = utf-8 18 | end_of_line = lf 19 | indent_size = 2 20 | insert_final_newline = true 21 | trim_trailing_whitespace = true 22 | 23 | [**.cshtml] 24 | indent_size = 4 25 | 26 | # size matters when we render html emails 27 | [StackOverflow/Views/Email/**.Html.cshtml] 28 | indent_style = tab 29 | tab_width = 4 30 | trim_trailing_whitespace = true 31 | 32 | # Xml project files 33 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] 34 | indent_size = 2 35 | 36 | # Xml config files 37 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] 38 | indent_size = 2 39 | 40 | # JSON files 41 | [*.json] 42 | indent_size = 2 43 | 44 | # Dotnet code style settings: 45 | [*.{cs,vb}] 46 | # Sort using and Import directives with System.* appearing first 47 | dotnet_sort_system_directives_first = true 48 | 49 | # Avoid "this." and "Me." if not necessary 50 | dotnet_style_qualification_for_field = false:suggestion 51 | dotnet_style_qualification_for_property = false:suggestion 52 | dotnet_style_qualification_for_method = false:suggestion 53 | dotnet_style_qualification_for_event = false:suggestion 54 | 55 | # Use language keywords instead of framework type names for type references 56 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 57 | dotnet_style_predefined_type_for_member_access = true:suggestion 58 | 59 | # Suggest more modern language features when available 60 | dotnet_style_object_initializer = true:suggestion 61 | dotnet_style_collection_initializer = true:suggestion 62 | dotnet_style_coalesce_expression = true:suggestion 63 | dotnet_style_null_propagation = true:suggestion 64 | dotnet_style_explicit_tuple_names = true:suggestion 65 | 66 | # CSharp code style settings: 67 | [*.cs] 68 | # Prefer "var" everywhere 69 | #csharp_style_var_for_built_in_types = true:suggestion 70 | csharp_style_var_when_type_is_apparent = true:suggestion 71 | csharp_style_var_elsewhere = true:suggestion 72 | 73 | # Prefer method-like constructs to have a expression-body 74 | csharp_style_expression_bodied_methods = true:none 75 | csharp_style_expression_bodied_constructors = true:none 76 | csharp_style_expression_bodied_operators = true:none 77 | 78 | # Prefer property-like constructs to have an expression-body 79 | csharp_style_expression_bodied_properties = true:none 80 | csharp_style_expression_bodied_indexers = true:none 81 | csharp_style_expression_bodied_accessors = true:none 82 | 83 | # Suggest more modern language features when available 84 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 85 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 86 | csharp_style_inlined_variable_declaration = true:suggestion 87 | csharp_style_throw_expression = true:suggestion 88 | csharp_style_conditional_delegate_call = true:suggestion 89 | 90 | # Newline settings 91 | csharp_new_line_before_open_brace = all 92 | csharp_new_line_before_else = true 93 | csharp_new_line_before_catch = true 94 | csharp_new_line_before_finally = true 95 | csharp_new_line_before_members_in_object_initializers = true 96 | csharp_new_line_before_members_in_anonymous_types = true 97 | 98 | # Space settings 99 | csharp_space_after_keywords_in_control_flow_statements = true:suggestion -------------------------------------------------------------------------------- /SetResolution/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Threading; 6 | using Westwind.SetResolution.CommandLine; 7 | 8 | namespace Westwind.SetResolution 9 | { 10 | public class Program 11 | { 12 | public static string StartupPath { get; set; } 13 | 14 | static void Main(string[] args) 15 | { 16 | StartupPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); 17 | AppConfiguration.Load(); 18 | 19 | var cmdLine = new SetResolutionCommandLineParser(); 20 | cmdLine.Parse(); 21 | 22 | var version = Assembly.GetExecutingAssembly().GetName().Version; 23 | var ver = version.Major + "." + version.Minor + (version.Build > 0 ? "." + version.Build : string.Empty); 24 | 25 | string text = $"Set Resolution v{ver}"; 26 | ColorConsole.WriteLine(text, ConsoleColor.Yellow); 27 | ColorConsole.WriteLine(new string('-',text.Length), ConsoleColor.Yellow); 28 | ColorConsole.WriteLine($"(c) West Wind Technologies, 2022-{DateTime.Now.Year}", ConsoleColor.DarkGray); 29 | 30 | if (args == null || args.Length == 0 || args[0] == "HELP" || args[0] == "/?") 31 | { 32 | 33 | Console.WriteLine("\nSet Monitor Display Resolution to any available machine display mode."); 34 | 35 | 36 | string options = $@" 37 | [cyan]Syntax[/cyan] 38 | ------ 39 | [yellow]SetResolution |SET|LIST|PROFILES|CREATEPROFILE 40 | -w 1920 -h 1080 -f 60 -b 32 -o 0 -p ProfileName[/yellow] 41 | 42 | [cyan]Commands[/cyan] 43 | -------- 44 | HELP || /? This help display 45 | Apply Display settings from a named Profile 46 | SET Sets Display Settings - 47 | provide either a profile (-p) or display options -w/-h/-f/-b/-o 48 | LIST Lists all available display modes and monitors 49 | PROFILES Lists all saved profiles (stored in SetResolution.xml) 50 | CREATEPROFILE Creates a new profile by specifying name and display options 51 | - {Path.Combine(StartupPath,"SetResolution.xml")} 52 | 53 | [cyan]Display Settings[/cyan] 54 | ---------------- 55 | -w Display Width 56 | -h Display Height 57 | -f Display Frequency in Hertz (60*) 58 | -o Orientation - 0 (default*), 1 (90deg), 2 (180deg), 3 (270deg) 59 | -p Profile name 60 | -noprompt Don't prompt for confirmation of new settings 61 | -nopersist Don't persist settings across Windows reboots (default persists) 62 | 63 | [cyan]Command Modifiers[/cyan] 64 | ----------------- 65 | -m Monitor Id to apply command to (1,2,3 etc - use LIST to see Ids) 66 | applies to: LIST, SET. If not specified, Default monitor is used. 67 | -la List all Display modes (LIST command). Default only shows current matches 68 | 69 | [cyan]Examples[/cyan] 70 | -------- 71 | SetResolution MyProfile 72 | SetResolution SET -p MyProfile -m2 73 | SetResolution SET -w 1920 -h 1080 -f 60 -m2 -noprompt 74 | SetResolution LIST -m2 75 | SetResolution PROFILES 76 | SetResolution CREATEPROFILE -p ""My Profile"" -w 1920 -h 1080 -f 60 77 | "; 78 | ColorConsole.WriteEmbeddedColorLine(options); 79 | } 80 | else 81 | { 82 | Console.WriteLine(); 83 | 84 | var processor = new SetResolutionProcessor(cmdLine); 85 | processor.Process(); 86 | 87 | Console.WriteLine(); 88 | } 89 | 90 | 91 | AppConfiguration.Save(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /SetResolution/CommandLine/CommandLineParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace HtmlPackager.ConsoleApp 5 | { 6 | /// 7 | /// Basic Command Line Parser class that can deal with simple 8 | /// switch based command line arguments 9 | /// 10 | /// supports: FirstParm (first commandline argume 11 | /// -pString or -p"String" 12 | /// -f switch/flag parameters 13 | /// 14 | public abstract class CommandLineParser 15 | { 16 | /// 17 | /// The Command Line arguments string array. 18 | /// Note unlike Environment.GetCommandLineArguments() this 19 | /// holds only the arguments and not the executable 20 | /// 21 | public string[] Arguments { get; set; } 22 | 23 | /// 24 | /// The full command line including the executable 25 | /// 26 | public string CommandLine { get; set; } 27 | 28 | /// 29 | /// The first argument (if any). Useful for a 30 | /// command/action parameter 31 | /// 32 | public string FirstParameter { get; set; } 33 | 34 | public CommandLineParser(string[] args = null, string cmdLine = null) 35 | { 36 | if (string.IsNullOrEmpty(cmdLine)) 37 | CommandLine = Environment.CommandLine; 38 | else 39 | CommandLine = cmdLine; 40 | 41 | if (args == null) 42 | args = Environment.GetCommandLineArgs(); 43 | 44 | List argList = new List(args); 45 | 46 | if (argList.Count > 1) 47 | { 48 | FirstParameter = argList[1]; 49 | 50 | // argument array contains startup exe - remove 51 | argList.RemoveAt(0); 52 | Arguments = argList.ToArray(); 53 | } 54 | else 55 | { 56 | FirstParameter = string.Empty; 57 | // empty array - not null to match args array 58 | Arguments = new string[0]; 59 | } 60 | } 61 | 62 | /// 63 | /// Override to provide parse switches\parameter 64 | /// into object structure 65 | /// 66 | public abstract void Parse(); 67 | 68 | 69 | /// 70 | /// Parses a string Parameter switch in the format of: 71 | /// 72 | /// -p"c:\temp files\somefile.txt" 73 | /// -pc:\somefile.txt 74 | /// 75 | /// Note no spaces are allowed between swich and value. 76 | /// 77 | /// parameter switch key 78 | /// Value to return if no match is found 79 | /// Match or non-matching value 80 | protected string ParseStringParameterSwitch(string parm, string nonMatchingValue = null) 81 | { 82 | int at = CommandLine.IndexOf(parm, 0, StringComparison.OrdinalIgnoreCase); 83 | 84 | if (at > -1) 85 | { 86 | string rest = CommandLine.Substring(at + parm.Length).Trim(); 87 | 88 | if (rest.StartsWith("\"")) 89 | { 90 | // read to end quote 91 | at = rest.IndexOf('"', 2); 92 | if (at == -1) 93 | return CommandLine; 94 | 95 | return rest.Substring(1, at - 1); 96 | } 97 | else if (rest == " ") 98 | { 99 | // no spaces after parameters 100 | return CommandLine; 101 | } 102 | else 103 | { 104 | // read to next space 105 | rest = rest.Trim(); 106 | at = (rest + " ").IndexOf(' '); 107 | string stringParm = rest.Substring(0, at); 108 | if (!string.IsNullOrEmpty(stringParm)) 109 | stringParm = stringParm.Trim('"', '\''); 110 | 111 | return stringParm; 112 | } 113 | } 114 | 115 | return nonMatchingValue; 116 | } 117 | 118 | protected int ParseIntParameterSwitch(string parm, int failedValue = -1) 119 | { 120 | string val = ParseStringParameterSwitch(parm); 121 | int res = failedValue; 122 | if (!int.TryParse(val, out res)) 123 | res = failedValue; 124 | 125 | return res; 126 | } 127 | 128 | protected bool ParseParameterSwitch(string parm) 129 | { 130 | int at = CommandLine.IndexOf(parm, 0, StringComparison.OrdinalIgnoreCase); 131 | 132 | if (at > -1) 133 | return true; 134 | 135 | return false; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #!SetResolution.exe 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # DNX 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | *.VC.VC.opendb 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | *.sap 94 | 95 | # TFS 2012 Local Workspace 96 | $tf/ 97 | 98 | # Guidance Automation Toolkit 99 | *.gpState 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper*/ 103 | *.[Rr]e[Ss]harper 104 | *.DotSettings.user 105 | 106 | # JustCode is a .NET coding add-in 107 | .JustCode 108 | 109 | # TeamCity is a build add-in 110 | _TeamCity* 111 | 112 | # DotCover is a Code Coverage Tool 113 | *.dotCover 114 | 115 | # NCrunch 116 | _NCrunch_* 117 | .*crunch*.local.xml 118 | nCrunchTemp_* 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | # TODO: Comment the next line if you want to checkin your web deploy settings 147 | # but database connection strings (with potential passwords) will be unencrypted 148 | #*.pubxml 149 | *.publishproj 150 | 151 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 152 | # checkin your Azure Web App publish settings, but sensitive information contained 153 | # in these scripts will be unencrypted 154 | PublishScripts/ 155 | 156 | # NuGet Packages 157 | *.nupkg 158 | # The packages folder can be ignored because of Package Restore 159 | **/packages/* 160 | # except build/, which is used as an MSBuild target. 161 | !**/packages/build/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Windows Store app package directories and files 177 | AppPackages/ 178 | BundleArtifacts/ 179 | Package.StoreAssociation.xml 180 | _pkginfo.txt 181 | 182 | # Visual Studio cache files 183 | # files ending in .cache can be ignored 184 | *.[Cc]ache 185 | # but keep track of directories ending in .cache 186 | !*.[Cc]ache/ 187 | 188 | # Others 189 | ClientBin/ 190 | ~$* 191 | *~ 192 | *.dbmdl 193 | *.dbproj.schemaview 194 | *.jfm 195 | *.pfx 196 | *.publishsettings 197 | node_modules/ 198 | orleans.codegen.cs 199 | 200 | # Since there are multiple workflows, uncomment next line to ignore bower_components 201 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 202 | #bower_components/ 203 | 204 | # RIA/Silverlight projects 205 | Generated_Code/ 206 | 207 | # Backup & report files from converting an old project file 208 | # to a newer Visual Studio version. Backup files are not needed, 209 | # because we have git ;-) 210 | _UpgradeReport_Files/ 211 | Backup*/ 212 | UpgradeLog*.XML 213 | UpgradeLog*.htm 214 | 215 | # SQL Server files 216 | *.mdf 217 | *.ldf 218 | 219 | # Business Intelligence projects 220 | *.rdl.data 221 | *.bim.layout 222 | *.bim_*.settings 223 | 224 | # Microsoft Fakes 225 | FakesAssemblies/ 226 | 227 | # GhostDoc plugin setting file 228 | *.GhostDoc.xml 229 | 230 | # Node.js Tools for Visual Studio 231 | .ntvs_analysis.dat 232 | 233 | # Visual Studio 6 build log 234 | *.plg 235 | 236 | # Visual Studio 6 workspace options file 237 | *.opt 238 | 239 | # Visual Studio LightSwitch build output 240 | **/*.HTMLClient/GeneratedArtifacts 241 | **/*.DesktopClient/GeneratedArtifacts 242 | **/*.DesktopClient/ModelManifest.xml 243 | **/*.Server/GeneratedArtifacts 244 | **/*.Server/ModelManifest.xml 245 | _Pvt_Extensions 246 | 247 | # Paket dependency manager 248 | .paket/paket.exe 249 | paket-files/ 250 | 251 | # FAKE - F# Make 252 | .fake/ 253 | 254 | # JetBrains Rider 255 | .idea/ 256 | *.sln.iml 257 | 258 | # CodeRush 259 | .cr/ 260 | 261 | # Python Tools for Visual Studio (PTVS) 262 | __pycache__/ 263 | *.pyc -------------------------------------------------------------------------------- /SetResolution/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | The settings change was unsuccessful because system is DualView capable. 122 | 123 | 124 | An invalid set of flags was passed in. 125 | 126 | 127 | The graphics mode is not supported. 128 | 129 | 130 | An invalid parameter was passed in. This can include an invalid flag or combination of flags. 131 | 132 | 133 | The display driver failed the specified graphics mode. 134 | 135 | 136 | Unable to write settings to the registry. 137 | 138 | 139 | The computer must be restarted in order for the graphics mode to work. 140 | 141 | 142 | Fatal error. 143 | 144 | 145 | Your about to change display settings to 146 | {0} 147 | Are you sure you want to continue? 148 | 149 | 150 | You are about to close the application. Are you sure you want to change screen settings to its original state? 151 | {0} 152 | 153 | 154 | Are you sure you want to reset screen settings to its original state? 155 | {0} 156 | 157 | 158 | Are you sure you want to rotate the screen? 159 | 160 | 161 | The settings change was successful. 162 | 163 | -------------------------------------------------------------------------------- /SetResolution/CommandLine/ColorConsole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Westwind.SetResolution.CommandLine 5 | { 6 | 7 | /// 8 | /// Console Color Helper class that provides coloring to individual commands 9 | /// 10 | /// 11 | /// Console Color Helper class that provides coloring to individual commands 12 | /// 13 | public static class ColorConsole 14 | { 15 | /// 16 | /// WriteLine with color 17 | /// 18 | /// 19 | /// 20 | public static void WriteLine(string text, ConsoleColor? color = null) 21 | { 22 | if (color.HasValue) 23 | { 24 | var oldColor = System.Console.ForegroundColor; 25 | if (color == oldColor) 26 | Console.WriteLine(text); 27 | else 28 | { 29 | Console.ForegroundColor = color.Value; 30 | Console.WriteLine(text); 31 | Console.ForegroundColor = oldColor; 32 | } 33 | } 34 | else 35 | Console.WriteLine(text); 36 | } 37 | 38 | /// 39 | /// Writes out a line with a specific color as a string 40 | /// 41 | /// Text to write 42 | /// A console color. Must match ConsoleColors collection names (case insensitive) 43 | public static void WriteLine(string text, string color) 44 | { 45 | if (string.IsNullOrEmpty(color)) 46 | { 47 | WriteLine(text); 48 | return; 49 | } 50 | 51 | if (!Enum.TryParse(color, true, out ConsoleColor col)) 52 | { 53 | WriteLine(text); 54 | } 55 | else 56 | { 57 | WriteLine(text, col); 58 | } 59 | } 60 | 61 | /// 62 | /// Write with color 63 | /// 64 | /// 65 | /// 66 | public static void Write(string text, ConsoleColor? color = null) 67 | { 68 | if (color.HasValue) 69 | { 70 | var oldColor = System.Console.ForegroundColor; 71 | if (color == oldColor) 72 | Console.Write(text); 73 | else 74 | { 75 | Console.ForegroundColor = color.Value; 76 | Console.Write(text); 77 | Console.ForegroundColor = oldColor; 78 | } 79 | } 80 | else 81 | Console.Write(text); 82 | } 83 | 84 | /// 85 | /// Writes out a line with color specified as a string 86 | /// 87 | /// Text to write 88 | /// A console color. Must match ConsoleColors collection names (case insensitive) 89 | public static void Write(string text, string color) 90 | { 91 | if (string.IsNullOrEmpty(color)) 92 | { 93 | Write(text); 94 | return; 95 | } 96 | 97 | if (!ConsoleColor.TryParse(color, true, out ConsoleColor col)) 98 | { 99 | Write(text); 100 | } 101 | else 102 | { 103 | Write(text, col); 104 | } 105 | } 106 | 107 | #region Wrappers and Templates 108 | 109 | 110 | /// 111 | /// Writes a line of header text wrapped in a in a pair of lines of dashes: 112 | /// ----------- 113 | /// Header Text 114 | /// ----------- 115 | /// and allows you to specify a color for the header. The dashes are colored 116 | /// 117 | /// Header text to display 118 | /// wrapper character (-) 119 | /// Color for header text (yellow) 120 | /// Color for dashes (gray) 121 | public static void WriteWrappedHeader(string headerText, 122 | char wrapperChar = '-', 123 | ConsoleColor headerColor = ConsoleColor.Yellow, 124 | ConsoleColor dashColor = ConsoleColor.DarkGray) 125 | { 126 | if (string.IsNullOrEmpty(headerText)) 127 | return; 128 | 129 | string line = new string(wrapperChar, headerText.Length); 130 | 131 | WriteLine(line, dashColor); 132 | WriteLine(headerText, headerColor); 133 | WriteLine(line, dashColor); 134 | } 135 | 136 | private static Lazy colorBlockRegEx = new Lazy( 137 | () => new Regex("\\[(?.*?)\\](?[^[]*)\\[/\\k\\]", RegexOptions.IgnoreCase), 138 | isThreadSafe: true); 139 | 140 | /// 141 | /// Allows a string to be written with embedded color values using: 142 | /// This is [red]Red[/red] text and this is [cyan]Blue[/blue] text 143 | /// 144 | /// Text to display 145 | /// Base text color 146 | public static void WriteEmbeddedColorLine(string text, ConsoleColor? baseTextColor = null) 147 | { 148 | if (baseTextColor == null) 149 | baseTextColor = Console.ForegroundColor; 150 | 151 | if (string.IsNullOrEmpty(text)) 152 | { 153 | WriteLine(string.Empty); 154 | return; 155 | } 156 | 157 | int at = text.IndexOf("["); 158 | int at2 = text.IndexOf("]"); 159 | if (at == -1 || at2 <= at) 160 | { 161 | WriteLine(text, baseTextColor); 162 | return; 163 | } 164 | 165 | while (true) 166 | { 167 | var match = colorBlockRegEx.Value.Match(text); 168 | if (match.Length < 1) 169 | { 170 | Write(text, baseTextColor); 171 | break; 172 | } 173 | 174 | // write up to expression 175 | Write(text.Substring(0, match.Index), baseTextColor); 176 | 177 | // strip out the expression 178 | string highlightText = match.Groups["text"].Value; 179 | string colorVal = match.Groups["color"].Value; 180 | 181 | Write(highlightText, colorVal); 182 | 183 | // remainder of string 184 | text = text.Substring(match.Index + match.Value.Length); 185 | } 186 | 187 | Console.WriteLine(); 188 | } 189 | 190 | #endregion 191 | 192 | #region Success, Error, Info, Warning Wrappers 193 | 194 | /// 195 | /// Write a Success Line - green 196 | /// 197 | /// Text to write out 198 | public static void WriteSuccess(string text) 199 | { 200 | WriteLine(text, ConsoleColor.Green); 201 | } 202 | 203 | /// 204 | /// Write a Error Line - Red 205 | /// 206 | /// Text to write out 207 | public static void WriteError(string text) 208 | { 209 | WriteLine(text, ConsoleColor.Red); 210 | } 211 | 212 | /// 213 | /// Write a Warning Line - Yellow 214 | /// 215 | /// Text to Write out 216 | public static void WriteWarning(string text) 217 | { 218 | WriteLine(text, ConsoleColor.DarkYellow); 219 | } 220 | 221 | 222 | /// 223 | /// Write a Info Line - dark cyan 224 | /// 225 | /// Text to write out 226 | public static void WriteInfo(string text) 227 | { 228 | WriteLine(text, ConsoleColor.DarkCyan); 229 | } 230 | 231 | #endregion 232 | } 233 | 234 | } 235 | -------------------------------------------------------------------------------- /SetResolution/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Westwind.SetResolution.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Westwind.SetResolution.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to The settings change was unsuccessful because system is DualView capable.. 65 | /// 66 | public static string InvalidOperation_Disp_Change_BadDualView { 67 | get { 68 | return ResourceManager.GetString("InvalidOperation_Disp_Change_BadDualView", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to An invalid set of flags was passed in.. 74 | /// 75 | public static string InvalidOperation_Disp_Change_BadFlags { 76 | get { 77 | return ResourceManager.GetString("InvalidOperation_Disp_Change_BadFlags", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to The graphics mode is not supported.. 83 | /// 84 | public static string InvalidOperation_Disp_Change_BadMode { 85 | get { 86 | return ResourceManager.GetString("InvalidOperation_Disp_Change_BadMode", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to An invalid parameter was passed in. This can include an invalid flag or combination of flags.. 92 | /// 93 | public static string InvalidOperation_Disp_Change_BadParam { 94 | get { 95 | return ResourceManager.GetString("InvalidOperation_Disp_Change_BadParam", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to The display driver failed the specified graphics mode.. 101 | /// 102 | public static string InvalidOperation_Disp_Change_Failed { 103 | get { 104 | return ResourceManager.GetString("InvalidOperation_Disp_Change_Failed", resourceCulture); 105 | } 106 | } 107 | 108 | /// 109 | /// Looks up a localized string similar to Unable to write settings to the registry.. 110 | /// 111 | public static string InvalidOperation_Disp_Change_NotUpdated { 112 | get { 113 | return ResourceManager.GetString("InvalidOperation_Disp_Change_NotUpdated", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks up a localized string similar to The computer must be restarted in order for the graphics mode to work.. 119 | /// 120 | public static string InvalidOperation_Disp_Change_Restart { 121 | get { 122 | return ResourceManager.GetString("InvalidOperation_Disp_Change_Restart", resourceCulture); 123 | } 124 | } 125 | 126 | /// 127 | /// Looks up a localized string similar to Fatal error.. 128 | /// 129 | public static string InvalidOperation_FatalError { 130 | get { 131 | return ResourceManager.GetString("InvalidOperation_FatalError", resourceCulture); 132 | } 133 | } 134 | 135 | /// 136 | /// Looks up a localized string similar to Your about to change display settings to 137 | ///{0} 138 | ///Are you sure you want to continue?. 139 | /// 140 | public static string Msg_Disp_Change { 141 | get { 142 | return ResourceManager.GetString("Msg_Disp_Change", resourceCulture); 143 | } 144 | } 145 | 146 | /// 147 | /// Looks up a localized string similar to You are about to close the application. Are you sure you want to change screen settings to its original state? 148 | ///{0}. 149 | /// 150 | public static string Msg_Disp_Change_Original { 151 | get { 152 | return ResourceManager.GetString("Msg_Disp_Change_Original", resourceCulture); 153 | } 154 | } 155 | 156 | /// 157 | /// Looks up a localized string similar to Are you sure you want to reset screen settings to its original state? 158 | ///{0}. 159 | /// 160 | public static string Msg_Disp_Change_Reset { 161 | get { 162 | return ResourceManager.GetString("Msg_Disp_Change_Reset", resourceCulture); 163 | } 164 | } 165 | 166 | /// 167 | /// Looks up a localized string similar to Are you sure you want to rotate the screen?. 168 | /// 169 | public static string Msg_Disp_Change_Rotate { 170 | get { 171 | return ResourceManager.GetString("Msg_Disp_Change_Rotate", resourceCulture); 172 | } 173 | } 174 | 175 | /// 176 | /// Looks up a localized string similar to The settings change was successful.. 177 | /// 178 | public static string Msg_Disp_Change_Successful { 179 | get { 180 | return ResourceManager.GetString("Msg_Disp_Change_Successful", resourceCulture); 181 | } 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /SetResolution/SetResolutionProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using Westwind.SetResolution.CommandLine; 6 | 7 | namespace Westwind.SetResolution 8 | { 9 | public class SetResolutionProcessor 10 | { 11 | private SetResolutionCommandLineParser CommandLine { get; } 12 | 13 | public SetResolutionProcessor(SetResolutionCommandLineParser commandLine) 14 | { 15 | CommandLine= commandLine; 16 | } 17 | 18 | public void Process() 19 | { 20 | if (CommandLine.FirstParameter.Equals("Set", StringComparison.OrdinalIgnoreCase)) 21 | { 22 | SetResolution(); 23 | } 24 | 25 | if (CommandLine.FirstParameter.Equals("List", StringComparison.OrdinalIgnoreCase)) 26 | { 27 | ListDisplayModes(CommandLine.ListAll); 28 | } 29 | 30 | if (CommandLine.FirstParameter.Equals("Profiles", StringComparison.OrdinalIgnoreCase)) 31 | { 32 | ListProfiles(); 33 | } 34 | 35 | if (CommandLine.FirstParameter.Equals("CreateProfile", StringComparison.OrdinalIgnoreCase)) 36 | { 37 | CreateProfile(); 38 | } 39 | 40 | // No Action command (help is handled on startup) 41 | if (CommandLine.FirstParameter.StartsWith("-")) 42 | { 43 | // assume we're using SetResolution with parameters 44 | SetResolution(); 45 | } 46 | 47 | // Launch Profile by name 48 | if (AppConfiguration.Current.Profiles.Any(pro => pro.Name.Equals(CommandLine.FirstParameter, StringComparison.OrdinalIgnoreCase))) 49 | { 50 | CommandLine.Profile = CommandLine.FirstParameter; 51 | SetResolution(); 52 | } 53 | } 54 | 55 | private void CreateProfile() 56 | { 57 | if (string.IsNullOrEmpty(CommandLine.Profile)) 58 | { 59 | ColorConsole.WriteError("You have to specify a profile to create."); 60 | return; 61 | } 62 | 63 | if (CommandLine.Width == 0 || CommandLine.Height == 0) 64 | { 65 | ColorConsole.WriteError("Please specify at minimum a Width and Height for the profile."); 66 | return; 67 | } 68 | 69 | var profile = new DisplayProfile() 70 | { 71 | Name = CommandLine.Profile, 72 | Width = CommandLine.Width, 73 | Height = CommandLine.Height, 74 | Frequency = CommandLine.Frequency, 75 | BitSize = CommandLine.BitCount, 76 | Orientation = CommandLine.Orientation, 77 | }; 78 | 79 | var set = AppConfiguration.Current.Profiles.FirstOrDefault(p => 80 | p.Name.Equals(profile.Name, StringComparison.OrdinalIgnoreCase)); 81 | if (set != null) 82 | AppConfiguration.Current.Profiles.Remove(set); 83 | 84 | AppConfiguration.Current.Profiles.Add(profile); 85 | AppConfiguration.Save(); 86 | 87 | ColorConsole.WriteSuccess($"Created new Profile: {profile.Name}. {profile}"); 88 | 89 | ListProfiles(); 90 | } 91 | 92 | private void SetResolution() 93 | { 94 | var devices = DisplayManager.GetAllDisplayDevices(); 95 | var monitor = devices.FirstOrDefault(d => d.IsSelected); // main monitor 96 | var currentSettings = DisplayManager.GetCurrentDisplaySetting(); 97 | 98 | if (CommandLine.MonitorId > 0) 99 | { 100 | monitor = devices.FirstOrDefault(d => d.Index == CommandLine.MonitorId); 101 | devices.ForEach(d=> d.IsSelected = false); 102 | monitor.IsSelected = true; 103 | } 104 | 105 | 106 | if (!string.IsNullOrEmpty(CommandLine.Profile)) 107 | { 108 | var profile = AppConfiguration.Current.Profiles.FirstOrDefault(p=> p.Name.Equals(CommandLine.Profile, StringComparison.OrdinalIgnoreCase)); 109 | if (profile == null) 110 | { 111 | ColorConsole.WriteError($"Couldn't find Display Profile {CommandLine.Profile}"); 112 | Console.WriteLine(); 113 | ListProfiles(); 114 | return; 115 | } 116 | 117 | profile.UpdateCommandLine(CommandLine); 118 | } 119 | 120 | if (CommandLine.Width == 0 || CommandLine.Height == 0) 121 | { 122 | ColorConsole.WriteError("Please specify at minimum Width and Height parameters."); 123 | return; 124 | } 125 | 126 | if (CommandLine.Frequency == 0) 127 | { 128 | CommandLine.Frequency = 60; 129 | } 130 | 131 | var list = DisplayManager.GetAllDisplaySettings(monitor.DriverDeviceName); 132 | 133 | var set = list.FirstOrDefault(d=> d.Width == CommandLine.Width && 134 | d.Height == CommandLine.Height && 135 | d.Frequency == CommandLine.Frequency && 136 | d.BitCount == CommandLine.BitCount && 137 | d.Orientation == (Orientation) CommandLine.Orientation ); 138 | 139 | if (set == null) 140 | { 141 | ColorConsole.WriteError($"Couldn't find a matching Display Mode."); 142 | Console.WriteLine(); 143 | ListDisplayModes(); 144 | return; 145 | } 146 | 147 | set.NoPersist = CommandLine.NoPersist; 148 | 149 | try 150 | { 151 | DisplayManager.SetDisplaySettings(set, monitor.DriverDeviceName); 152 | ColorConsole.WriteEmbeddedColorLine($"Switched Display Mode on Monitor [green]{monitor.DisplayName}[/green] to:\n[green]{set.ToString()}[/green]"); 153 | } 154 | catch(Exception ex) 155 | { 156 | ColorConsole.WriteError("Unable to set Display Mode to " + set.ToString() + "\nError: " + ex.Message); 157 | return; 158 | } 159 | 160 | if (!CommandLine.NoPrompt) 161 | { 162 | ColorConsole.WriteLine("\npress any key to confirm new resolution within 5 seconds.",ConsoleColor.Yellow); 163 | bool keyPressed = false; 164 | for (int i = 0; i < 55; i++) 165 | { 166 | Thread.Sleep(100); 167 | if (Console.KeyAvailable) 168 | { 169 | Console.ReadKey(true); 170 | keyPressed = true; 171 | break; 172 | } 173 | } 174 | 175 | if (!keyPressed) 176 | { 177 | ColorConsole.WriteWarning("No key pressed: Resetting Display Mode to previous settings."); 178 | DisplayManager.SetDisplaySettings(currentSettings, deviceName: monitor.DriverDeviceName); 179 | } 180 | else 181 | { 182 | ColorConsole.WriteSuccess("Successfully changed resolution."); 183 | } 184 | } 185 | } 186 | 187 | private void ListDisplayModes(bool showAll = false) 188 | { 189 | string text; 190 | var devices = DisplayManager.GetAllDisplayDevices(); 191 | var monitor = devices.FirstOrDefault(d => d.IsSelected); // main monitor 192 | if (CommandLine.MonitorId > 0) 193 | { 194 | monitor = devices.FirstOrDefault(d => d.Index == CommandLine.MonitorId); 195 | devices.ForEach(d => d.IsSelected = false); 196 | monitor.IsSelected = true; 197 | } 198 | 199 | var displayModes = DisplayManager.GetAllDisplaySettings(monitor.DriverDeviceName); 200 | var current = DisplayManager.GetCurrentDisplaySetting(monitor.DriverDeviceName); 201 | 202 | ColorConsole.WriteLine("Available Monitors", ConsoleColor.Yellow); 203 | ColorConsole.WriteLine("------------------", ConsoleColor.Yellow); 204 | foreach (var device in devices) 205 | { 206 | if (device.IsSelected) 207 | ColorConsole.WriteLine(device.ToString(), ConsoleColor.Green); 208 | else 209 | Console.WriteLine(device); 210 | } 211 | Console.WriteLine(); 212 | 213 | 214 | IList filtered = displayModes; 215 | if (!CommandLine.ListAll) 216 | { 217 | filtered = displayModes.Where(d => 218 | d.Width >= AppConfiguration.Current.MinResolutionWidth && 219 | d.Frequency == current.Frequency && 220 | d.Orientation == current.Orientation) 221 | .OrderByDescending(d=> d.Width) 222 | // unique 223 | .GroupBy(d => new {d.Width, d.Height, d.Frequency, d.Orientation}) 224 | .Select(g => g.First()) 225 | .ToList(); 226 | } 227 | else 228 | { 229 | filtered = filtered.OrderByDescending(d => d.Width).ToList(); 230 | } 231 | 232 | text = $"Available Display Modes ({filtered.Count})" ; 233 | ColorConsole.WriteLine(text, ConsoleColor.Yellow); 234 | ColorConsole.WriteLine(new string('-', text.Length), ConsoleColor.Yellow); 235 | 236 | 237 | foreach (var set in filtered) 238 | { 239 | if (set.Equals(current)) 240 | ColorConsole.WriteLine(set.ToString(!CommandLine.ListAll) + " *", ConsoleColor.Green); 241 | else 242 | Console.WriteLine(set.ToString(!CommandLine.ListAll)); 243 | } 244 | } 245 | 246 | private void ListProfiles() 247 | { 248 | var list = DisplayManager.GetAllDisplaySettings(); 249 | 250 | ColorConsole.WriteLine("Available Profiles", ConsoleColor.Yellow); 251 | ColorConsole.WriteLine("-----------------------", ConsoleColor.Yellow); 252 | 253 | 254 | foreach (var profile in AppConfiguration.Current.Profiles) 255 | { 256 | Console.WriteLine(profile.ToString()); 257 | } 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /SetResolution/DisplayManagerNative.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on original code from: 3 | * 4 | * (c) Mohammad Elsheimy 5 | * Changing Display Settings Programmatically 6 | * 7 | * https://www.c-sharpcorner.com/uploadfile/GemingLeader/changing-display-settings-programmatically/ 8 | * 9 | * Added support for: 10 | * 11 | * * Listing Monitors and Monitor specific display modes 12 | * * Set display mode for a specific monitor/driver 13 | * 14 | */ 15 | 16 | using System; 17 | using System.Runtime.InteropServices; 18 | 19 | namespace Westwind.SetResolution 20 | { 21 | static class DisplayManagerNative 22 | { 23 | #region Enum Display Settings 24 | 25 | [DllImport("User32.dll", SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] 26 | [return: MarshalAs(UnmanagedType.Bool)] 27 | public static extern bool EnumDisplaySettings( 28 | //[param: MarshalAs(UnmanagedType.LPTStr)] 29 | //string lpszDeviceName, //display device 30 | byte[] lpszDeviceName, // display device ToLPTStr 31 | [param: MarshalAs(UnmanagedType.U4)] 32 | int iModeNum, // graphics mode 33 | [In, Out] 34 | ref DEVMODE lpDevMode // graphics mode settings 35 | ); 36 | 37 | public const int ENUM_CURRENT_SETTINGS = -1; 38 | public const int DMDO_DEFAULT = 0; 39 | public const int DMDO_90 = 1; 40 | public const int DMDO_180 = 2; 41 | public const int DMDO_270 = 3; 42 | 43 | [Flags()] 44 | public enum DmFlags : int 45 | { 46 | DM_ORIENTATION = 0x00000001, 47 | DM_PAPERSIZE = 0x00000002, 48 | DM_PAPERLENGTH = 0x00000004, 49 | DM_PAPERWIDTH = 0x00000008, 50 | DM_SCALE = 0x00000010, 51 | DM_POSITION = 0x00000020, 52 | DM_NUP = 0x00000040, 53 | DM_DISPLAYORIENTATION = 0x00000080, 54 | DM_COPIES = 0x00000100, 55 | DM_DEFAULTSOURCE = 0x00000200, 56 | DM_PRINTQUALITY = 0x00000400, 57 | DM_COLOR = 0x00000800, 58 | DM_DUPLEX = 0x00001000, 59 | DM_YRESOLUTION = 0x00002000, 60 | DM_TTOPTION = 0x00004000, 61 | DM_COLLATE = 0x00008000, 62 | DM_FORMNAME = 0x00010000, 63 | DM_LOGPIXELS = 0x00020000, 64 | DM_BITSPERPEL = 0x00040000, 65 | DM_PELSWIDTH = 0x00080000, 66 | DM_PELSHEIGHT = 0x00100000, 67 | DM_DISPLAYFLAGS = 0x00200000, 68 | DM_DISPLAYFREQUENCY = 0x00400000, 69 | DM_ICMMETHOD = 0x00800000, 70 | DM_ICMINTENT = 0x01000000, 71 | DM_MEDIATYPE = 0x02000000, 72 | DM_DITHERTYPE = 0x04000000, 73 | DM_PANNINGWIDTH = 0x08000000, 74 | DM_PANNINGHEIGHT = 0x10000000, 75 | DM_DISPLAYFIXEDOUTPUT = 0x20000000 76 | } 77 | 78 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 79 | public struct DEVMODE 80 | { 81 | // You can define the following constant 82 | // but OUTSIDE the structure because you know 83 | // that size and layout of the structure is very important 84 | // CCHDEVICENAME = 32 = 0x50 85 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 86 | public string dmDeviceName; 87 | // In addition you can define the last character array 88 | // as following: 89 | //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 90 | //public Char[] dmDeviceName; 91 | 92 | // After the 32-bytes array 93 | [MarshalAs(UnmanagedType.U2)] 94 | public ushort dmSpecVersion; 95 | 96 | [MarshalAs(UnmanagedType.U2)] 97 | public ushort dmDriverVersion; 98 | 99 | [MarshalAs(UnmanagedType.U2)] 100 | public ushort dmSize; 101 | 102 | [MarshalAs(UnmanagedType.U2)] 103 | public ushort dmDriverExtra; 104 | 105 | [MarshalAs(UnmanagedType.U4)] 106 | public DmFlags dmFields; 107 | 108 | public POINTL dmPosition; 109 | 110 | [MarshalAs(UnmanagedType.U4)] 111 | public uint dmDisplayOrientation; 112 | 113 | [MarshalAs(UnmanagedType.U4)] 114 | public uint dmDisplayFixedOutput; 115 | 116 | [MarshalAs(UnmanagedType.I2)] 117 | public short dmColor; 118 | 119 | [MarshalAs(UnmanagedType.I2)] 120 | public short dmDuplex; 121 | 122 | [MarshalAs(UnmanagedType.I2)] 123 | public short dmYResolution; 124 | 125 | [MarshalAs(UnmanagedType.I2)] 126 | public short dmTTOption; 127 | 128 | [MarshalAs(UnmanagedType.I2)] 129 | public short dmCollate; 130 | 131 | // CCHDEVICENAME = 32 = 0x50 132 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 133 | public string dmFormName; 134 | // Also can be defined as 135 | //[MarshalAs(UnmanagedType.ByValArray, 136 | // SizeConst = 32, ArraySubType = UnmanagedType.U1)] 137 | //public Byte[] dmFormName; 138 | 139 | [MarshalAs(UnmanagedType.U2)] 140 | public ushort dmLogPixels; 141 | 142 | [MarshalAs(UnmanagedType.U4)] 143 | public uint dmBitsPerPel; 144 | 145 | [MarshalAs(UnmanagedType.U4)] 146 | public uint dmPelsWidth; 147 | 148 | [MarshalAs(UnmanagedType.U4)] 149 | public uint dmPelsHeight; 150 | 151 | [MarshalAs(UnmanagedType.U4)] 152 | public uint dmDisplayFlags; 153 | 154 | [MarshalAs(UnmanagedType.U4)] 155 | public uint dmDisplayFrequency; 156 | 157 | [MarshalAs(UnmanagedType.U4)] 158 | public uint dmICMMethod; 159 | 160 | [MarshalAs(UnmanagedType.U4)] 161 | public uint dmICMIntent; 162 | 163 | [MarshalAs(UnmanagedType.U4)] 164 | public uint dmMediaType; 165 | 166 | [MarshalAs(UnmanagedType.U4)] 167 | public uint dmDitherType; 168 | 169 | [MarshalAs(UnmanagedType.U4)] 170 | public uint dmReserved1; 171 | 172 | [MarshalAs(UnmanagedType.U4)] 173 | public uint dmReserved2; 174 | 175 | [MarshalAs(UnmanagedType.U4)] 176 | public uint dmPanningWidth; 177 | 178 | [MarshalAs(UnmanagedType.U4)] 179 | public uint dmPanningHeight; 180 | 181 | /// 182 | /// Initializes the structure variables. 183 | /// 184 | public void Initialize() 185 | { 186 | this.dmDeviceName = new string(new char[32]); 187 | this.dmFormName = new string(new char[32]); 188 | this.dmSize = (ushort)Marshal.SizeOf(this); 189 | } 190 | } 191 | 192 | 193 | // 8-bytes structure 194 | [StructLayout(LayoutKind.Sequential)] 195 | public struct POINTL 196 | { 197 | public int x; 198 | public int y; 199 | } 200 | 201 | #endregion 202 | 203 | 204 | #region Enum DisplayDevices 205 | 206 | [DllImport("user32.dll")] 207 | public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags); 208 | 209 | 210 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 211 | public struct DISPLAY_DEVICE 212 | { 213 | [MarshalAs(UnmanagedType.U4)] 214 | public int cb; 215 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 216 | public string DeviceName; 217 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 218 | public string DeviceString; 219 | [MarshalAs(UnmanagedType.U4)] 220 | public DisplayDeviceStateFlags StateFlags; 221 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 222 | public string DeviceID; 223 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 224 | public string DeviceKey; 225 | } 226 | 227 | [Flags()] 228 | public enum DisplayDeviceStateFlags : int 229 | { 230 | /// The device is part of the desktop. 231 | AttachedToDesktop = 0x1, 232 | MultiDriver = 0x2, 233 | /// The device is part of the desktop. 234 | PrimaryDevice = 0x4, 235 | /// Represents a pseudo device used to mirror application drawing for remoting or other purposes. 236 | MirroringDriver = 0x8, 237 | /// The device is VGA compatible. 238 | VGACompatible = 0x10, 239 | /// The device is removable; it cannot be the primary display. 240 | Removable = 0x20, 241 | /// The device has more display modes than its output devices support. 242 | ModesPruned = 0x8000000, 243 | Remote = 0x4000000, 244 | Disconnect = 0x2000000 245 | } 246 | #endregion 247 | 248 | 249 | #region Errors 250 | 251 | [DllImport("User32.dll", SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] 252 | [return: MarshalAs(UnmanagedType.I4)] 253 | public static extern int ChangeDisplaySettings( 254 | [In, Out] 255 | ref DEVMODE lpDevMode, 256 | [param: MarshalAs(UnmanagedType.U4)] 257 | uint dwflags); 258 | 259 | 260 | [DllImport("user32.dll")] 261 | public static extern int ChangeDisplaySettingsEx(string lpszDeviceName, 262 | ref DEVMODE lpDevMode, 263 | IntPtr hwnd, 264 | uint dwflags, 265 | IntPtr lParam); 266 | 267 | 268 | 269 | [DllImport("kernel32.dll", SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] 270 | [return: MarshalAs(UnmanagedType.Bool)] 271 | public static extern uint FormatMessage( 272 | [param: MarshalAs(UnmanagedType.U4)] 273 | uint dwFlags, 274 | [param: MarshalAs(UnmanagedType.U4)] 275 | uint lpSource, 276 | [param: MarshalAs(UnmanagedType.U4)] 277 | uint dwMessageId, 278 | [param: MarshalAs(UnmanagedType.U4)] 279 | uint dwLanguageId, 280 | [param: MarshalAs(UnmanagedType.LPTStr)] 281 | out string lpBuffer, 282 | [param: MarshalAs(UnmanagedType.U4)] 283 | uint nSize, 284 | [param: MarshalAs(UnmanagedType.U4)] 285 | uint arguments); 286 | 287 | public const uint FORMAT_MESSAGE_FROM_HMODULE = 0x800; 288 | 289 | public const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100; 290 | public const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x200; 291 | public const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x1000; 292 | public const uint FORMAT_MESSAGE_FLAGS = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM; 293 | 294 | #endregion 295 | 296 | #region Helpers 297 | 298 | public static byte[] ToLPTStr(string str) 299 | { 300 | if (str == null) return null; 301 | 302 | var lptArray = new byte[str.Length + 1]; 303 | 304 | var index = 0; 305 | foreach (char c in str.ToCharArray()) 306 | lptArray[index++] = Convert.ToByte(c); 307 | 308 | lptArray[index] = Convert.ToByte('\0'); 309 | 310 | return lptArray; 311 | } 312 | 313 | #endregion 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /SetResolution/Utilities/SerializationUtils.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | ************************************************************** 4 | * Author: Rick Strahl 5 | * © West Wind Technologies, 2008 - 2009 6 | * http://www.west-wind.com/ 7 | * 8 | * Created: 09/08/2008 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, 14 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the 16 | * Software is furnished to do so, subject to the following 17 | * conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be 20 | * included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 24 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 27 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 29 | * OTHER DEALINGS IN THE SOFTWARE. 30 | ************************************************************** 31 | */ 32 | #endregion 33 | 34 | using System; 35 | using System.IO; 36 | using System.Text; 37 | using System.Reflection; 38 | 39 | using System.Xml; 40 | using System.Xml.Serialization; 41 | using System.Runtime.Serialization.Formatters.Binary; 42 | using System.Diagnostics; 43 | using System.Runtime.Serialization; 44 | 45 | namespace Westwind.Utilities 46 | { 47 | 48 | // Serialization specific code 49 | 50 | public static class SerializationUtils 51 | { 52 | 53 | /// 54 | /// Serializes an object instance to a file. 55 | /// 56 | /// the object instance to serialize 57 | /// 58 | /// determines whether XML serialization or binary serialization is used 59 | /// 60 | public static bool SerializeObject(object instance, string fileName) 61 | { 62 | bool retVal = true; 63 | 64 | XmlTextWriter writer = null; 65 | try 66 | { 67 | XmlSerializer serializer = 68 | new XmlSerializer(instance.GetType()); 69 | 70 | // Create an XmlTextWriter using a FileStream. 71 | Stream fs = new FileStream(fileName, FileMode.Create); 72 | writer = new XmlTextWriter(fs, new UTF8Encoding()); 73 | writer.Formatting = Formatting.Indented; 74 | writer.IndentChar = ' '; 75 | writer.Indentation = 3; 76 | 77 | // Serialize using the XmlTextWriter. 78 | serializer.Serialize(writer, instance); 79 | } 80 | catch (Exception ex) 81 | { 82 | Debug.Write("SerializeObject failed with : " + ex.Message, "West Wind"); 83 | retVal = false; 84 | } 85 | finally 86 | { 87 | if (writer != null) 88 | writer.Close(); 89 | } 90 | 91 | 92 | return retVal; 93 | } 94 | 95 | /// 96 | /// Overload that supports passing in an XML TextWriter. 97 | /// 98 | /// 99 | /// Note the Writer is not closed when serialization is complete 100 | /// so the caller needs to handle closing. 101 | /// 102 | /// object to serialize 103 | /// XmlTextWriter instance to write output to 104 | /// Determines whether false is returned on failure or an exception is thrown 105 | /// 106 | public static bool SerializeObject(object instance, XmlTextWriter writer, bool throwExceptions) 107 | { 108 | bool retVal = true; 109 | 110 | try 111 | { 112 | XmlSerializer serializer = 113 | new XmlSerializer(instance.GetType()); 114 | 115 | // Create an XmlTextWriter using a FileStream. 116 | writer.Formatting = Formatting.Indented; 117 | writer.IndentChar = ' '; 118 | writer.Indentation = 3; 119 | 120 | // Serialize using the XmlTextWriter. 121 | serializer.Serialize(writer, instance); 122 | } 123 | catch (Exception ex) 124 | { 125 | Debug.Write("SerializeObject failed with : " + ex.GetBaseException().Message + "\r\n" + (ex.InnerException != null ? ex.InnerException.Message : ""), "West Wind"); 126 | 127 | if (throwExceptions) 128 | throw; 129 | 130 | retVal = false; 131 | } 132 | 133 | return retVal; 134 | } 135 | 136 | 137 | /// 138 | /// Serializes an object into an XML string variable for easy 'manual' serialization 139 | /// 140 | /// object to serialize 141 | /// resulting XML string passed as an out parameter 142 | /// true or false 143 | public static bool SerializeObject(object instance, out string xmlResultString) 144 | { 145 | return SerializeObject(instance, out xmlResultString, false); 146 | } 147 | 148 | /// 149 | /// Serializes an object into a string variable for easy 'manual' serialization 150 | /// 151 | /// 152 | /// Out parm that holds resulting XML string 153 | /// If true causes exceptions rather than returning false 154 | /// 155 | public static bool SerializeObject(object instance, out string xmlResultString, bool throwExceptions) 156 | { 157 | xmlResultString = string.Empty; 158 | MemoryStream ms = new MemoryStream(); 159 | 160 | XmlTextWriter writer = new XmlTextWriter(ms, new UTF8Encoding()); 161 | 162 | if (!SerializeObject(instance, writer,throwExceptions)) 163 | { 164 | ms.Close(); 165 | return false; 166 | } 167 | 168 | xmlResultString = Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length); 169 | 170 | ms.Close(); 171 | writer.Close(); 172 | 173 | return true; 174 | } 175 | 176 | 177 | /// 178 | 179 | /// 180 | /// Serializes an object to an XML string. Unlike the other SerializeObject overloads 181 | /// this methods *returns a string* rather than a bool result! 182 | /// 183 | /// 184 | /// Determines if a failure throws or returns null 185 | /// 186 | /// null on error otherwise the Xml String. 187 | /// 188 | /// 189 | /// If null is passed in null is also returned so you might want 190 | /// to check for null before calling this method. 191 | /// 192 | public static string SerializeObjectToString(object instance, bool throwExceptions = false) 193 | { 194 | string xmlResultString = string.Empty; 195 | 196 | if (!SerializeObject(instance, out xmlResultString, throwExceptions)) 197 | return null; 198 | 199 | return xmlResultString; 200 | } 201 | 202 | 203 | 204 | 205 | 206 | /// 207 | /// Deserializes an object from file and returns a reference. 208 | /// 209 | /// name of the file to serialize to 210 | /// The Type of the object. Use typeof(yourobject class) 211 | /// determines whether we use Xml or Binary serialization 212 | /// determines whether failure will throw rather than return null on failure 213 | /// Instance of the deserialized object or null. Must be cast to your object type 214 | public static object DeSerializeObject(string fileName, Type objectType, bool throwExceptions) 215 | { 216 | object instance = null; 217 | 218 | 219 | XmlReader reader = null; 220 | XmlSerializer serializer = null; 221 | FileStream fs = null; 222 | try 223 | { 224 | // Create an instance of the XmlSerializer specifying type and namespace. 225 | serializer = new XmlSerializer(objectType); 226 | 227 | // A FileStream is needed to read the XML document. 228 | fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); 229 | reader = new XmlTextReader(fs); 230 | 231 | instance = serializer.Deserialize(reader); 232 | } 233 | catch(Exception ex) 234 | { 235 | if (throwExceptions) 236 | throw; 237 | 238 | string message = ex.Message; 239 | return null; 240 | } 241 | finally 242 | { 243 | if (fs != null) 244 | fs.Close(); 245 | 246 | if (reader != null) 247 | reader.Close(); 248 | } 249 | 250 | 251 | return instance; 252 | } 253 | 254 | /// 255 | /// Deserialize an object from an XmlReader object. 256 | /// 257 | /// 258 | /// 259 | /// 260 | public static object DeSerializeObject(XmlReader reader, Type objectType) 261 | { 262 | XmlSerializer serializer = new XmlSerializer(objectType); 263 | object Instance = serializer.Deserialize(reader); 264 | reader.Close(); 265 | 266 | return Instance; 267 | } 268 | 269 | public static object DeSerializeObject(string xml, Type objectType) 270 | { 271 | XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Document, null); 272 | return DeSerializeObject(reader, objectType); 273 | } 274 | 275 | 276 | /// 277 | /// Returns a string of all the field value pairs of a given object. 278 | /// Works only on non-statics. 279 | /// 280 | /// 281 | /// 282 | /// 283 | public static string ObjectToString(object instanc, string separator, ObjectToStringTypes type) 284 | { 285 | FieldInfo[] fi = instanc.GetType().GetFields(); 286 | 287 | string output = string.Empty; 288 | 289 | if (type == ObjectToStringTypes.Properties || type == ObjectToStringTypes.PropertiesAndFields) 290 | { 291 | foreach (PropertyInfo property in instanc.GetType().GetProperties()) 292 | { 293 | try 294 | { 295 | output += property.Name + ":" + property.GetValue(instanc, null).ToString() + separator; 296 | } 297 | catch 298 | { 299 | output += property.Name + ": n/a" + separator; 300 | } 301 | } 302 | } 303 | 304 | if (type == ObjectToStringTypes.Fields || type == ObjectToStringTypes.PropertiesAndFields) 305 | { 306 | foreach (FieldInfo field in fi) 307 | { 308 | try 309 | { 310 | output = output + field.Name + ": " + field.GetValue(instanc).ToString() + separator; 311 | } 312 | catch 313 | { 314 | output = output + field.Name + ": n/a" + separator; 315 | } 316 | } 317 | } 318 | return output; 319 | } 320 | 321 | } 322 | 323 | public enum ObjectToStringTypes 324 | { 325 | Properties, 326 | PropertiesAndFields, 327 | Fields 328 | } 329 | } 330 | 331 | 332 | 333 | 334 | 335 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SetResolution: Set Windows Display Resolution from the Command Line 2 | 3 | [![](https://img.shields.io/nuget/v/SetResolution.svg)](https://www.nuget.org/packages/SetResolution/) 4 | [![](https://img.shields.io/nuget/dt/SetResolution.svg)](https://www.nuget.org/packages/SetResolution/) 5 | 6 | ![](https://raw.githubusercontent.com/RickStrahl/SetResolution/master/icon-256.png) 7 | 8 | This small command line utility allows you to quickly set Windows Display Resolutions to any of the available display modes available for your active Monitors or virtual display devices. 9 | 10 | * Set an explicit Display Resolution 11 | * Create and use Display Mode Profiles for quick access 12 | * List all available Display Modes and Monitors 13 | * Supports multiple Monitors 14 | * Prompts for confirmation by default to avoid invalid display modes 15 | * Runs as standalone, single file Windows EXE 16 | * Can install and run as `dotnet tool` *(>=0.3 (.NET 9.0), >v0.2 (.NET 8.0), 17 | 18 | ![](https://raw.githubusercontent.com/RickStrahl/SetResolution/master/Assets/SetResolutionMain.png) 19 | 20 | ## Basic Usage 21 | Most common usage is via a **pre-defined profile name**: 22 | 23 | ```powershell 24 | # Set to a profile named 1080 on default monitor 25 | SetResolution 1080 26 | 27 | # or shortcut version (sr.exe) on Monitor 2 28 | sr 4k -m2 29 | ``` 30 | 31 | We ship a few default profiles: 32 | 33 | * 4k 34 | * 1080 35 | * 1440 36 | * 720 37 | 38 | but you can also edit these or create your own named profiles. 39 | 40 | Alternately you can explicitly pick a resolution, frequency, bit rate and Orientation: 41 | 42 | ```powershell 43 | sr SET -w 2560 -h 1600 -f 60 -b 32 -o 0 44 | ``` 45 | *Frequency, BitRate and Orientation are optional* 46 | 47 | To see available resolutions for a specific monitor: 48 | 49 | ```powershell 50 | # default list is filtered to current frequency/bitrate/orientation 51 | # and the `MinResolutionWidth` configuration setting 52 | sr LIST -m1 53 | 54 | # list ALL resolutions/modes 55 | sr LIST -m1 -la 56 | ``` 57 | 58 | To create a new profile: 59 | 60 | ```powershell 61 | # Create a new profile 62 | sr CREATEPROFILE -w 2560 -h 1600 -f 60 -b 32 -o 0 63 | 64 | # List all profiles with their settings 65 | sr PROFILES 66 | ``` 67 | 68 | ## Installation 69 | You can install this tool in a couple of ways (for now). 70 | 71 | ### Download Single-File EXE Binary 72 | This tool is a small, self-contained Console EXE application. For now, you can download the `SetResolution.exe` (or `sr.exe` file directly from here): 73 | 74 | [Download SetResolution.exe](https://github.com/RickStrahl/SetResolution/raw/master/Binaries/SetResolution.exe) 75 | 76 | I recommend you copy to a folder location that is in your Windows path or add it to your path, so you can run `SetResolution` from any location. 77 | 78 | ### Install as Dotnet Tool (.NET 9.0 SDK required) 79 | For .NET developers the easiest way to install and keep the tool up to date is via Dotnet Tool installation. This requires that the [.NET 9.0 (or later) SDK](https://dotnet.microsoft.com/en-us/download). 80 | 81 | You can install, update and use it with: 82 | 83 | ```powershell 84 | # install 85 | dotnet tool install -g SetResolution 86 | 87 | SetResolution 1080 -m2 88 | 89 | #update 90 | dotnet tool update -g SetResolution 91 | ``` 92 | 93 | ### Full Syntax 94 | To show available syntax, run `SetResolution.exe` or `sr.exe` without any parameters or `/?` or `HELP`. 95 | The help information is as follows: 96 | 97 | ![Help Screen](https://raw.githubusercontent.com/RickStrahl/SetResolution/master/Assets/HelpScreen.png) 98 | 99 | ## Multi-Monitor Support 100 | This tool supports multiple monitors via the `-m ` command line switch. By default the **Main Windows Monitor** monitor is used which corresponds to the **Main Monitor** setting configured in the Windows Display settings. 101 | 102 | Both the `SET`, `PROFILE` and `LIST` commands support the `-m` switch to specify the monitor that the command applies to. Creating a new profile does not specify a monitor. 103 | 104 | The `-m` switch uses a numbering scheme from 1-n, with monitor numbers identified in the `LIST` command. The numbers also reflect the same value you see in the Windows Display Settings dialog. 105 | 106 | ## List Available Monitors and Display Modes 107 | You can use the `LIST` command to show available Monitors and Display Modes as well as the currently selected monitor and display mode. The display modes available are specific for the Monitor/Video Driver combination that is active. 108 | 109 | If you don't specify the `-m` switch which selects a monitor, the **Windows Main Monitor** is used. The list of Display Modes is always specific to the selected monitor. You can explicitly select a monitor via the `-m` switch. 110 | 111 | The selected monitor and display mode are highlighted in the list (green and *). 112 | 113 | ![](https://raw.githubusercontent.com/RickStrahl/SetResolution/master/Assets/ListDisplay.png) 114 | 115 | ```powershell 116 | sr LIST -m1 117 | ``` 118 | This shows a list of display modes available. By default the list only shows: 119 | 120 | * Sizes with the Width > 800 pixels 121 | * Frequencies that match the current display frequency 122 | * Orientation that match the current orientation 123 | 124 | This list is similar to the list you see in the Windows Display Resolution drop down list. 125 | 126 | If you want to see `all display modes` available for your monitor/video driver combination use the `-la` command line switch: 127 | 128 | ```powershell 129 | sr LIST -m1 -la 130 | ``` 131 | 132 | The list displays the selected monitor and display mode for this command in green and with the `*` at the end. 133 | 134 | This displays all displays modes for all sizes, orientations and frequencies. This list tends to very large with many duplicate and overlapping values. However it can be useful to match an exact display mode. 135 | 136 | Use these display modes when you create new Profiles and ensure your Profile matches the Display Modes that are supported. 137 | 138 | ## Profiles 139 | Profiles are 'shortcuts' to a specific set of saved Display Settings with a name that you can access simply by specifying the profile name: 140 | 141 | ```powershell 142 | sr -m1 143 | ``` 144 | Profiles are the preferred way to switch resolutions as they give you quick access via a single profile name string, instead of having to specify all the settings individually. The `-m1` switch specifies the monitor to apply to - if not specified the Main Windows Monitor is used. 145 | 146 | A Profile stores the following values: 147 | 148 | * Height and Width 149 | * Monitor Frequency (60) 150 | * Color Bit Size (32) 151 | * Orientation (0) 152 | 153 | *values in parenthesis are optional default values if not specified* 154 | 155 | > ##### Profiles do not store Monitor numbers 156 | > If you need to apply to a specific monitor make sure you add the `-m` switch to explicit specify the specific monitor you want to apply the profile to. 157 | 158 | ### Create a new Profile 159 | You can create a profile with: 160 | 161 | ```powershell 162 | SetResolution CREATEPROFILE -p -w 1280 -h 768 -f 59 163 | ``` 164 | #### Manually Edit SetResolution.xml 165 | Profiles are stored in `SetResolution.xml` in the same folder as the .exe, so you can manually edit the XML file to add or edit profiles. In order to remove profiles you have to edit the `SetResolution.xml` file and remove the entry. 166 | 167 | ```xml 168 | 169 | 170 | 171 | 172 | 1080 173 | 1920 174 | 1080 175 | 60 176 | 32 177 | Default 178 | 179 | 180 | 4k 181 | 3840 182 | 2160 183 | 60 184 | 32 185 | Default 186 | 187 | 188 | 1440 189 | 2560 190 | 1440 191 | 60 192 | 32 193 | Default 194 | 195 | 196 | 720 197 | 1280 198 | 720 199 | 60 200 | 32 201 | Default 202 | 203 | 204 | 1280 205 | 206 | ``` 207 | 208 | > #### XML? Really? 209 | > Yeah I know XML - but to keep the EXE as a fully self-contained, single EXE file and avoid external dependencies, XML serialization is used since it's built into the core framework runtime. 210 | 211 | > #### Write Permissions required for Profile File 212 | > In order to save a new profile using `CREATEPROFILE` you have to have **Write Permissions in the same folder `SetResolution.exe` or `sr.exe`**. This means that preferrably you'll install this tool into a location that supports writing files. 213 | > 214 | > Alternately you can manually edit the file, or give the file itself full read/write access. 215 | 216 | 217 | ### Setting a Profile 218 | Once a profile has been created you can invoke it. 219 | 220 | ```powershell 221 | # Shortcut way - on monitor 2 222 | SetResolution -m2 223 | 224 | # Full syntax - on main monitor (not specified) 225 | SetResolution SET -p -m1 226 | ``` 227 | 228 | ### Default Profiles 229 | A number of default profiles are added for common 16:9 resolutions @ 60hz which is most common: 230 | 231 | ```text 232 | Available Profiles 233 | ------------------ 234 | 1080: 1920 x 1080, 32, 60 235 | 4k: 3840 x 2160, 32, 60 236 | 1440: 2560 x 1440, 32, 60 237 | 720: 1280 x 720, 32, 60 238 | ``` 239 | 240 | These are loaded on first load of the application and stored in the saved profile file (if writable). 241 | 242 | ### Profiles File Location: SetResolution.xml 243 | Profiles are stored on disk in `SetResolution.xml` in the same folder as the `.exe` and you can add and remove additional profiles there or add via the `CREATEPROFILE` action as described above. 244 | 245 | > **Note:** If you installed the EXE in a location that has no write access, saving of new Profile entries with `CREATEPROFILE` will fail silently. Either give `SetResolution.xml` read/write access or move the application to a location where you are allowed to write files. 246 | 247 | 248 | ## Fark: I set a Resolution that doesn't work. Now what? 249 | While it's really, really difficult to do this, if you somehow managed to accidentally set your monitor into a display mode that isn't supported or just doesn't work with your monitor, it's possible that your screen becomes inaccessible. Because this tool switches the default display settings, once a wrong setting is made the screen simply will be blank and it's not just a simple matter of rebooting as the setting is applied to the Windows settings and persists on a reboot. 250 | 251 | > To reset a non-working display setting you have to **boot into Windows Safe Mode** and select another display mode, then reboot. 252 | 253 | As mentioned it should be really difficult to get the monitor/driver into a non-working state because we: 254 | 255 | * **Check settings for valid display resolutions** 256 | You can't set a resolution that is not supported by the driver/monitor combo at the time of setting the mode. The only way you can get an invalid resolution is if the driver supports a mode, but for some reason the monitor does not. The list SetResolution retrieves for display modes is filtered by Windows to match driver/monitor combos that are expected to work. 257 | 258 | * **Prompt for Confirmation of Mode Change** 259 | We prompt for confirmation after the resolution change and if you don't confirm in 5 seconds the display reverts to the previous mode. This ensures if your screen for some reason goes blank, you will revert back to your last working configuration. While I'm not a fan of confirmation prompts, in this case it's both prudent, and only slightly intrusive as you can simply press a key after submitting the shell command. If it really bugs you you can also run with `-noprompt` which skips the prompt (*not recommended though*). 260 | 261 | ## Changelog 262 | 263 | ### 0.3.2 264 | 265 | * **Fix Persistance across Reboots by Default** 266 | Resolution changes previously did not persist in the registry and so when the machine was rebooted the changed setting would not persist and the last saved setting (from Windows) would be used for resulution. Now by default the resolution - if set successfully - is saved and restored on reboot. You can use the `--nopersist` flag to not store the new settings in the registry and revert back to the previous resolution. 267 | 268 | * **Fix Setting the Frequency** 269 | Frequency changes were ignored in previous versions and they should be properly applied now. 270 | 271 | * **Dotnet Tool now uses the .NET 9.0 SDK** 272 | We switched to the .NET 9.0 SDK for the dotnet tool operation for v0.3 which is required for this update. Older SDK versions are supported with older versions of this tool (v0.2.x .NET 8.0 SDK). 273 | 274 | ## Credits 275 | The initial code that manages retrieving and setting display modes is based on this excellent article on C# Corner by [Mohammad Elseheimy](https://www.c-sharpcorner.com/members/mohammad-elsheimy): 276 | 277 | * [Changing Display Settings Programmatically](https://www.c-sharpcorner.com/uploadfile/GemingLeader/changing-display-settings-programmatically/) 278 | 279 | This derivative tool adds an easy to use Console front-end and provides for multi-monitor support. **Most of the credit for this tool goes to Mohammad's work!** 280 | 281 | ## To do 282 | 283 | nothing -------------------------------------------------------------------------------- /SetResolution/DisplayManager.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on original code from: 3 | * 4 | * (c) Mohammad Elsheimy 5 | * Changing Display Settings Programmatically 6 | * 7 | * https://www.c-sharpcorner.com/uploadfile/GemingLeader/changing-display-settings-programmatically/ 8 | * 9 | * Added support for: 10 | * 11 | * * Listing Monitors and Monitor specific display modes 12 | * * Set display mode for a specific monitor/driver 13 | * 14 | */ 15 | 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Runtime.InteropServices; 19 | using static Westwind.SetResolution.DisplayManagerNative; 20 | 21 | namespace Westwind.SetResolution 22 | { 23 | /// 24 | /// Represents a wrapper to the native methods. 25 | /// 26 | public static class DisplayManager 27 | { 28 | /// 29 | /// Returns a DisplaySettings object encapsulates the current display settings. 30 | /// 31 | public static DisplaySettings GetCurrentSettings(string deviceName = null) 32 | { 33 | return CreateDisplaySettingsObject(-1, GetDeviceMode(deviceName)); 34 | } 35 | 36 | /// 37 | /// Changes the current display settings with the new settings provided. May throw InvalidOperationException if failed. Check the exception message for more details. 38 | /// 39 | /// The new settings. 40 | /// 41 | /// Internally calls ChangeDisplaySettings() native function. 42 | /// 43 | public static void SetDisplaySettings(DisplaySettings set, string deviceName = null) 44 | { 45 | DisplayManagerNative.DEVMODE mode = GetDeviceMode(deviceName); 46 | 47 | mode.dmPelsWidth = (uint)set.Width; 48 | mode.dmPelsHeight = (uint)set.Height; 49 | mode.dmDisplayOrientation = (uint)set.Orientation; 50 | mode.dmBitsPerPel = (uint)set.BitCount; 51 | mode.dmDisplayFrequency = (uint)set.Frequency; 52 | mode.dmFields = DmFlags.DM_PELSWIDTH | DmFlags.DM_PELSHEIGHT | DmFlags.DM_DISPLAYFREQUENCY | DmFlags.DM_BITSPERPEL | DmFlags.DM_DISPLAYORIENTATION; 53 | 54 | 55 | uint CDS_UPDATEREGISTRY = set.NoPersist ? 0u : 1; // force to persist settings in registry 56 | DisplayChangeResult result = (DisplayChangeResult)DisplayManagerNative.ChangeDisplaySettingsEx(deviceName, ref mode, IntPtr.Zero, CDS_UPDATEREGISTRY, IntPtr.Zero); 57 | 58 | string msg = null; 59 | switch (result) 60 | { 61 | case DisplayChangeResult.BadDualView: 62 | msg = Properties.Resources.InvalidOperation_Disp_Change_BadDualView; 63 | break; 64 | case DisplayChangeResult.BadParam: 65 | msg = Properties.Resources.InvalidOperation_Disp_Change_BadParam; 66 | break; 67 | case DisplayChangeResult.BadFlags: 68 | msg = Properties.Resources.InvalidOperation_Disp_Change_BadFlags; 69 | break; 70 | case DisplayChangeResult.NotUpdated: 71 | msg = Properties.Resources.InvalidOperation_Disp_Change_NotUpdated; 72 | break; 73 | case DisplayChangeResult.BadMode: 74 | msg = Properties.Resources.InvalidOperation_Disp_Change_BadMode; 75 | break; 76 | case DisplayChangeResult.Failed: 77 | msg = Properties.Resources.InvalidOperation_Disp_Change_Failed; 78 | break; 79 | case DisplayChangeResult.Restart: 80 | msg = Properties.Resources.InvalidOperation_Disp_Change_Restart; 81 | break; 82 | } 83 | 84 | if (msg != null) 85 | throw new InvalidOperationException(msg); 86 | } 87 | 88 | 89 | /// 90 | /// Returns the current display mode setting 91 | /// 92 | /// 93 | public static DisplaySettings GetCurrentDisplaySetting(string deviceName = null) 94 | { 95 | var mode = GetDeviceMode(deviceName); 96 | return CreateDisplaySettingsObject(0, mode); 97 | } 98 | 99 | /// 100 | /// Returns a list of all the display settings 101 | /// 102 | /// 103 | public static List GetAllDisplaySettings(string deviceName = null) 104 | { 105 | var list = new List(); 106 | DisplayManagerNative.DEVMODE mode = new DisplayManagerNative.DEVMODE(); 107 | 108 | mode.Initialize(); 109 | 110 | int idx = 0; 111 | 112 | while (DisplayManagerNative.EnumDisplaySettings(DisplayManagerNative.ToLPTStr(deviceName), idx, ref mode)) 113 | //while (DisplayManagerNative.EnumDisplaySettings(deviceName, idx, ref mode)) 114 | list.Add(CreateDisplaySettingsObject(idx++, mode)); 115 | 116 | return list; 117 | } 118 | 119 | public static List GetAllDisplayDevices() 120 | { 121 | var list = new List(); 122 | uint idx = 0; 123 | uint size = 256; 124 | 125 | var device = new DisplayManagerNative.DISPLAY_DEVICE(); 126 | device.cb = Marshal.SizeOf(device); 127 | int displayIndex = 0; 128 | 129 | while (DisplayManagerNative.EnumDisplayDevices(null, idx, ref device, size) ) 130 | { 131 | if (device.StateFlags.HasFlag(DisplayManagerNative.DisplayDeviceStateFlags.AttachedToDesktop)) 132 | { 133 | var isPrimary = device.StateFlags.HasFlag(DisplayManagerNative.DisplayDeviceStateFlags.PrimaryDevice); 134 | var deviceName = device.DeviceName; 135 | 136 | DisplayManagerNative.EnumDisplayDevices(device.DeviceName, 0, ref device, 0); 137 | displayIndex++; 138 | var dev = new DisplayDevice() 139 | { 140 | Index = displayIndex, 141 | Id = device.DeviceID, 142 | 143 | MonitorDeviceName = device.DeviceName, 144 | DriverDeviceName = deviceName, 145 | 146 | DisplayName = device.DeviceString, 147 | IsPrimary = isPrimary, 148 | IsSelected = isPrimary 149 | }; 150 | list.Add(dev); 151 | } 152 | 153 | idx++; 154 | 155 | device = new DisplayManagerNative.DISPLAY_DEVICE(); 156 | device.cb = Marshal.SizeOf(device); 157 | } 158 | 159 | return list; 160 | } 161 | 162 | public class DisplayDevice 163 | { 164 | public int Index { get; set; } 165 | public string MonitorDeviceName { get; set; } 166 | public string Id { get; set; } 167 | public string DriverDeviceName { get; set; } 168 | public string DisplayName { get; set; } 169 | public bool IsPrimary { get; set; } 170 | public bool IsSelected { get; set; } 171 | 172 | public override string ToString() 173 | { 174 | return $"{Index} {DisplayName}{(IsSelected ? " *" : "")}{(IsPrimary ? " (Main)" : "")}"; 175 | } 176 | } 177 | 178 | /// 179 | /// Rotates the screen from its current location by 90 degrees either clockwise or anti-clockwise. 180 | /// 181 | /// Set to true to rotate the screen 90 degrees clockwise from its current location, or false to rotate it anti-clockwise. 182 | public static void RotateScreen(bool clockwise) 183 | { 184 | DisplaySettings set = DisplayManager.GetCurrentSettings(); 185 | 186 | int tmp = set.Height; 187 | set.Height = set.Width; 188 | set.Width = tmp; 189 | 190 | if (clockwise) 191 | set.Orientation++; 192 | else 193 | set.Orientation--; 194 | 195 | if (set.Orientation < Orientation.Default) 196 | set.Orientation = Orientation.Rotate270; 197 | else if (set.Orientation > Orientation.Rotate270) 198 | set.Orientation = Orientation.Default; 199 | 200 | SetDisplaySettings(set); 201 | } 202 | 203 | 204 | /// 205 | /// A private helper methods used to derive a DisplaySettings object from the DEVMODE structure. 206 | /// 207 | /// The mode index attached with the settings. Starts form zero. Is -1 for the current settings. 208 | /// The current DEVMODE object represents the display information to derive the DisplaySettings object from. 209 | private static DisplaySettings CreateDisplaySettingsObject(int idx, DisplayManagerNative.DEVMODE mode) 210 | { 211 | return new DisplaySettings() 212 | { 213 | Index = idx, 214 | Width = (int)mode.dmPelsWidth, 215 | Height = (int)mode.dmPelsHeight, 216 | Orientation = (Orientation)mode.dmDisplayOrientation, 217 | BitCount = (int)mode.dmBitsPerPel, 218 | Frequency = (int)mode.dmDisplayFrequency 219 | }; 220 | } 221 | 222 | /// 223 | /// A private helper method used to retrieve current display settings as a DEVMODE object. 224 | /// 225 | /// 226 | /// Internally calls EnumDisplaySettings() native function with the value ENUM_CURRENT_SETTINGS (-1) to retrieve the current settings. 227 | /// 228 | private static DisplayManagerNative.DEVMODE GetDeviceMode(string deviceName = null) 229 | { 230 | var mode = new DisplayManagerNative.DEVMODE(); 231 | 232 | mode.Initialize(); 233 | 234 | if (DisplayManagerNative.EnumDisplaySettings(DisplayManagerNative.ToLPTStr(deviceName), DisplayManagerNative.ENUM_CURRENT_SETTINGS, ref mode)) 235 | return mode; 236 | else 237 | throw new InvalidOperationException(GetLastError()); 238 | } 239 | 240 | private static string GetLastError() 241 | { 242 | int err = Marshal.GetLastWin32Error(); 243 | 244 | if (DisplayManagerNative.FormatMessage(DisplayManagerNative.FORMAT_MESSAGE_FLAGS, 245 | DisplayManagerNative.FORMAT_MESSAGE_FROM_HMODULE, 246 | (uint)err, 0, out var msg, 0, 0) == 0) 247 | return Properties.Resources.InvalidOperation_FatalError; 248 | else 249 | return msg; 250 | } 251 | } 252 | 253 | public enum Orientation 254 | { 255 | Default = 0, 256 | Rotate90 = 1, 257 | Rotate180 = 2, 258 | Rotate270 = 3 259 | } 260 | 261 | public class DisplaySettings 262 | { 263 | public int Index { get; set; } 264 | public int Width { get; set; } 265 | public int Height { get; set; } 266 | public Orientation Orientation { get; set; } 267 | public int BitCount { get; set; } 268 | public int Frequency { get; set; } 269 | 270 | /// 271 | /// Determines whether the settings are stored in the registry for 272 | /// persistence for a reboot. 273 | /// 274 | public bool NoPersist { get; set; } 275 | 276 | /// 277 | /// Display Mode string display with full detail 278 | /// 279 | public override string ToString() 280 | { 281 | return ToString(false); 282 | } 283 | 284 | /// 285 | /// Display Mode string display 286 | /// 287 | /// only return height and width 288 | /// 289 | public string ToString(bool noDetails) 290 | { 291 | if (noDetails) 292 | { 293 | return string.Format(System.Globalization.CultureInfo.CurrentCulture, $"{Width} x {Height}"); 294 | } 295 | 296 | return string.Format(System.Globalization.CultureInfo.CurrentCulture, 297 | $"{Width} x {Height}, {Frequency}hz, {BitCount}bit{(Orientation != Orientation.Default ? ", " + Orientation.ToString() : "")}"); 298 | } 299 | 300 | 301 | public override bool Equals(object d) 302 | { 303 | var disp = d as DisplaySettings; 304 | return (disp.Width == Width && disp.Height == Height && 305 | disp.Frequency == Frequency && 306 | disp.BitCount == BitCount && 307 | disp.Orientation == Orientation); 308 | } 309 | 310 | 311 | public override int GetHashCode() 312 | { 313 | return ("" + "W" + Width + "H" + Height + "F" + Frequency + "B" + BitCount + "O" + Orientation) 314 | .GetHashCode(); 315 | } 316 | } 317 | 318 | enum DisplayChangeResult 319 | { 320 | /// 321 | /// Windows XP: The settings change was unsuccessful because system is DualView capable. 322 | /// 323 | BadDualView = -6, 324 | /// 325 | /// An invalid parameter was passed in. This can include an invalid flag or combination of flags. 326 | /// 327 | BadParam = -5, 328 | /// 329 | /// An invalid set of flags was passed in. 330 | /// 331 | BadFlags = -4, 332 | /// 333 | /// Windows NT/2000/XP: Unable to write settings to the registry. 334 | /// 335 | NotUpdated = -3, 336 | /// 337 | /// The graphics mode is not supported. 338 | /// 339 | BadMode = -2, 340 | /// 341 | /// The display driver failed the specified graphics mode. 342 | /// 343 | Failed = -1, 344 | /// 345 | /// The settings change was successful. 346 | /// 347 | Successful = 0, 348 | /// 349 | /// The computer must be restarted in order for the graphics mode to work. 350 | /// 351 | Restart = 1 352 | } 353 | 354 | 355 | } 356 | --------------------------------------------------------------------------------