Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Change Resolution Before Starting Application

4.92/5 (7 votes)
27 Dec 2013CPOL2 min read 21.1K   1K  
How to change resolution before starting an application

Introduction

In December 2013, it was time for me to buy a new laptop. I looked around and ended up with one of the best laptops I could find... a DELL XPS 15 (2013 model). I really like the laptop but it has one disadvantage. The desktop resolution is insanely high. It is a whopping 3200 x 1800. Most programs look good on this resolution when you have at least Windows 8.1. But games that are played in a lower resolution all had black borders around them. For some reason, the NVIDIA 750 graphics drivers don't seem to scale the games right. So, since I'm a developer, I decided to fix this. After testing, I found out that the games did work without black borders when I first switched back the desktop resolution to a lower setting. Since I didn't want to do that every time, I decided to write a simple console program for it.

The program had to do a few things for me:
  • Change the resolution to a lower setting
  • Start a program (game)
  • Wait until the program (game) is closed
  • Change the resolution back to the old settings

Using the Code

I decided to call the program ResChanger (resolution changer). First, I created a new C# console app in Visual Studio 2013 as you can see in the code below:

C#
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ResChanger
{
    internal class Program
    {
        #region DllImport
        [DllImport("kernel32.dll")]
        static extern bool FreeConsole();
        #endregion

        private static void Main(string[] args)
        {
            FreeConsole();

            if (args.Length != 2)
            {
                ShowMessage("Not enough command line arguments given.");
                return;
            }

            var newResolution = args[0].Split('x');
            int newWidth;
            int newHeight;

            if (newResolution.Length != 2)
            {
                ShowMessage("Invalid resolution format given.");
                return;
            }

            if (!int.TryParse(newResolution[0], out newWidth) || !int.TryParse(newResolution[1], out newHeight))
            {
                ShowMessage("Invalid resolution given, could not parse to integer.");
                return;
            }

            var resolution = new Resolution();
            string supportedModes;

            if (!resolution.IsDisplayModeSupported(newWidth, newHeight, out supportedModes))
            {
                ShowMessage("The mode '" + args[0] + "' is not supported, 
                supported modes are: " + Environment.NewLine + supportedModes +
                            Environment.NewLine);
                return;
            }

            if (!File.Exists(args[1]))
            {
                ShowMessage("The file '" + args[1] + "' does not exists.");
                return;
            }

            if (!resolution.ChangeDisplaySettings(newWidth, newHeight))
                return;

            var runningProgram = Process.Start(args[1]);
            if (runningProgram != null) runningProgram.WaitForExit();

            resolution.RestoreDisplaySettings();
        }

        #region ShowMessage
        private static void ShowMessage(string errorMessage)
        {
            MessageBox.Show("Invalid command line arguments given:" + 
            Environment.NewLine + Environment.NewLine +
                "Error: " + errorMessage + Environment.NewLine + Environment.NewLine +
                "Expected \"<resolution>\" \"<program to start, 
                with full path\">" + Environment.NewLine +
                Environment.NewLine +
                "For example: " + Path.GetFileName(Environment.GetCommandLineArgs()[0]) +
                " \"1920x1080\"  
                \"C:\\Program Files (x86)\\SQUARE ENIX\\Tombraider\\TombRaider.exe\"" +
                Environment.NewLine + Environment.NewLine +
                "This will first switch the resolution to 1920x1080 and then start Tombraider." +
                Environment.NewLine +
                "After you exit the game the resolution will be switched back to the old configuration.");
        }
        #endregion
    }
} 

I also needed a class to change the resolution itself. Since it is not possible to do this with standard C# .NET, I did have to switch to DLLImports. I found some nice articles on CodeProject that I could use for this. I ended up with the following class called Resolution:

C#
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ResChanger
{
    #region Struct Pointl
    [StructLayout(LayoutKind.Sequential)]
    public struct Pointl
    {
        [MarshalAs(UnmanagedType.I4)]
        public int x;
        [MarshalAs(UnmanagedType.I4)]
        public int y;
    }
    #endregion

    #region Struct Devmode
    [StructLayout(LayoutKind.Sequential,
        CharSet = CharSet.Ansi)]
    public struct Devmode
    {
        // You can define the following constant
        // but OUTSIDE the structure because you know
        // that size and layout of the structure
        // is very important
        // CCHDEVICENAME = 32 = 0x50
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmDeviceName;
        // In addition you can define the last character array
        // as following:
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        //public Char[] dmDeviceName;

        // After the 32-bytes array
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmSpecVersion;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmDriverVersion;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmSize;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmDriverExtra;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmFields;

        public Pointl dmPosition;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayOrientation;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFixedOutput;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmColor;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmDuplex;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmYResolution;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmTTOption;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmCollate;

        // CCHDEVICENAME = 32 = 0x50
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmFormName;
        // Also can be defined as
        //[MarshalAs(UnmanagedType.ByValArray,
        //    SizeConst = 32, ArraySubType = UnmanagedType.U1)]
        //public Byte[] dmFormName;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmLogPixels;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmBitsPerPel;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPelsWidth;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPelsHeight;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFlags;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFrequency;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmICMMethod;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmICMIntent;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmMediaType;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDitherType;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmReserved1;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmReserved2;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPanningWidth;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPanningHeight;
    }
    #endregion

    internal class Resolution
    {
        #region Fields
        private static Devmode _oldDevmode;
        #endregion

        #region Consts
        private const int EnumCurrentSettings = -1;     // Retrieves the current display mode.
        private const int DispChangeSuccessful = 0;     // Indicates that the function succeeded.
        private const int DispChangeBadmode = -2;       // The graphics mode is not supported.
        private const int DispChangeRestart = 1;        // The computer must be restarted 
        				// for the graphics mode to work.
        #endregion

        #region DllImport
        [DllImport("User32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern Boolean EnumDisplaySettings(
            [param: MarshalAs(UnmanagedType.LPTStr)] string lpszDeviceName,
            [param: MarshalAs(UnmanagedType.U4)] int iModeNum,
            [In, Out] ref Devmode lpDevMode);

        [DllImport("User32.dll")]
        [return: MarshalAs(UnmanagedType.I4)]
        private static extern int ChangeDisplaySettings(
            [In, Out] ref Devmode lpDevMode,
            [param: MarshalAs(UnmanagedType.U4)] uint dwflags);
        #endregion

        #region IsDisplayModeSupported
        /// <summary>
        /// Checks if the given display mode is supported
        /// </summary>
        /// <param name="width" />
        /// <param name="height" />
        /// <param name="supportedModes" />
        /// <returns>
        public bool IsDisplayModeSupported(int width, int height, out string supportedModes)
        {
            var mode = new Devmode();
            mode.dmSize = (ushort) Marshal.SizeOf(mode);

            var modeIndex = 0; // 0 = The first mode
            supportedModes = string.Empty;
            var previousSupportedMode = string.Empty;

            while (EnumDisplaySettings(null,
                modeIndex,
                ref mode)) // Mode found
            {
                if (mode.dmPelsWidth == (uint) width && mode.dmPelsHeight == (uint) height)
                    return true;

                var newSupportedMode = mode.dmPelsWidth + "x" + mode.dmPelsHeight;
                if (newSupportedMode != previousSupportedMode)
                {
                    if (supportedModes == string.Empty)
                        supportedModes += newSupportedMode;
                    else
                        supportedModes += ", " + newSupportedMode;
                    
                    previousSupportedMode = newSupportedMode;
                }

                modeIndex++; // The next mode
            }

            return false;
        }
        #endregion

        #region ChangeDisplaySettings
        /// <summary>
        /// Changes the display settings
        /// </summary>
        /// <param name="width" />
        /// <param name="height" />
        public bool ChangeDisplaySettings(int width, int height)
        {
            _oldDevmode = new Devmode();
            _oldDevmode.dmSize = (ushort) Marshal.SizeOf(_oldDevmode);

            // Retrieving current settings
            // to edit them
            EnumDisplaySettings(null,
                EnumCurrentSettings,
                ref _oldDevmode);

            // Making a copy of the current settings
            // to allow reseting to the original mode
            var newMode = _oldDevmode;

            // Changing the settings
            newMode.dmPelsWidth = (uint)width;
            newMode.dmPelsHeight = (uint)height;

            // Capturing the operation result, 1 = update registry
            var result =
                ChangeDisplaySettings(ref newMode, 1);

            switch (result)
            {
                case DispChangeSuccessful:
                    return true;

                case DispChangeBadmode:
                    MessageBox.Show("Mode not supported.");
                    return false;

                case DispChangeRestart:
                    MessageBox.Show("Restart required.");
                    return false;

                default:
                    MessageBox.Show("Failed. Error code = " + result);
                    return false;
            }
        }
        #endregion

        #region RestoreDisplaySettings
        /// <summary>
        /// Restores the old display settings
        /// </summary>
        public void RestoreDisplaySettings()
        {
            ChangeDisplaySettings(ref _oldDevmode, 1);
        }
        #endregion
    }
}

After putting everything together, we end up with a very simple console program that can be called like this: Reschanger.exe "resolution" "<program to start, including path" for example Reschanger.exe "1920x1080" "c:\Program Files\totalcmd\totalcmd.exe"

This will switch the desktop resolution to 1920 x 1080 and then start totalcmd.exe. You can make a shortcut on the Windows desktop to make everything easier.

And that's it... a very simple solution to an annoying problem.

History

  • 2013-12-27 - First version
  • 2014-01-03 - Version 1.1, cleaned up the code somewhat and added EXE file to ZIP file

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)