Hey, Scripting Guy! How Can I Change My Desktop Monitor Resolution via Windows PowerShell?


Hey, Scripting Guy! QuestionHey, Scripting Guy! I need to be able to use a Windows PowerShell 2.0 script to change the resolution on my desktop monitor. Is this possible?

— AS


Hey, Scripting Guy! AnswerHello AS,

Microsoft Scripting Guy Ed Wilson here, I have been looking for a script to change my monitor resolution for years! When I used to travel all over the world and taught scripting workshops to Microsoft Premier customers, I always shifted my laptop resolution down to 1024 x 768 to ensure the screen was large enough for students in the back of the room to read – a real important consideration when a person will be reading code for 40 solid hours! Anyway, it simply was impossible to do this via script until recently. This is because Windows PowerShell 2.0 makes it relatively easy to call native code from within Windows PowerShell. I was talking to my good friend Andy Schneider about writing a guest article, and I had a great idea; I would let Andy write a Windows PowerShell script using the Win32 API, and I would write the same script using DirectX. The problem is that the DirectX methodology is no longer supported (I found that out after spending several hours troubleshooting my script). So here is the best part of a great idea–guest blogger week continues with Andy Schneider. Let’s let Andy introduce himself and carry on with today’s blog.

Photo of Andy Schneider

About Andy Schneider: I am a systems engineer at Avanade, Inc. I am on Avanade’s Internal IT team that works with Compute Utility and Storage infrastructure. I’ve been a professional in the IT industry since 1999. I remember the first time I started playing with VBScript. I ran into my boss’s office exclaiming “Holy Schnikees! Look what we can do with this stuff!” Seeing what I could do with automation, my view of IT changed forever. Now I am pretty much a pure Windows PowerShell fanatic. Windows PowerShell has provided me with a fairly smooth glide path into the world of development with .NET and C#. I’m not a developer, but I have a strong passion for bridging the world of development and systems engineering together by being able to interface with developers and other systems engineers. Learning Windows PowerShell has really given me the tools I need to become more educated about the world of development. You can find my blog at http://get-powershell.com/.

There are two factors that encourage me to increase my skillset when it comes to writing Windows PowerShell code. The first is pure fascination and learning for the sake of learning. The second factor for me is a real problem that can either be solved only with a new addition to my skill set or can be solved much more easily with the new skill set. In this blog post, I would like to talk about how I was able to start using some new skills and eventually developed a solution for the problem I was having. Just keep in mind, no matter where you are in your skill set with Windows PowerShell; the glide path up to the next level is not very steep.

I am pretty comfortable with Windows PowerShell and using .NET directly in Windows PowerShell. However, I am an IT Pro, not a developer. I am willing to crack open some C# code every once in a while, but I really only know just enough to be dangerous. In this case, I wanted to figure out how to use inline C# and the Win32 API to script some things that could only be accessed using P-Invoke. There were a couple things I was looking at, but for this article, I will talk about using P-Invoke to change the screen resolution.

The first step in figuring out how something works is to find out how someone else has already solved the problem, or has come close to solving it for you. Bing is your friend! I spent 30 to 45 minutes one night poking around until I found this article up on C-Sharp Corner. Attached to this article is some source code written in C# that I used as my starting point. In this solution was a file called resolution.cs which pretty much had all the code I needed.

Once I had the code, there were a few things I wanted to change. The first was to remove the dependency on Winforms since this was going to be used in a PowerShell console window. I pulled out all the MessageBox.Show() method calls. The second thing I wanted to change was how the ChangeResolution() method was being called. I changed it to a static method that returned a string. This way, I didn’t have to create a new instance of the object every time I called the method.

It took me a while to figure out how this was all pieced together. In order to go between managed .NET code in C# and the Win32 API, there are a few crazy hoops we have to jump through. Lucky for me, someone has already jumped through them. The first was the creation of the DEVMODE struct. DEVMODE was a parameter type that the ChangeDisplaySettings function was using. The second piece that was interesting to me was the use of [DllImport(“user32.dll”)]. This is telling the compiler to expose some function in the Win32 API using unmanaged code. The two functions used in this script are EnumDisplaySettings() and ChangeDisplaySettings().Once I had the C# code nailed down, wrapping it into a PowerShell Advanced Function was fairly straightforward.


Function Set-ScreenResolution {
param (
           Position = 0)]
           Position = 1)]
$pinvokeCode = @”
using System;
using System.Runtime.InteropServices;
namespace Resolution
    public struct DEVMODE1
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmDeviceName;
        public short dmSpecVersion;
        public short dmDriverVersion;
        public short dmSize;
        public short dmDriverExtra;
        public int dmFields;
        public short dmOrientation;
        public short dmPaperSize;
        public short dmPaperLength;
        public short dmPaperWidth;
        public short dmScale;
        public short dmCopies;
        public short dmDefaultSource;
        public short dmPrintQuality;
        public short dmColor;
        public short dmDuplex;
        public short dmYResolution;
        public short dmTTOption;
        public short dmCollate;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmFormName;
        public short dmLogPixels;
        public short dmBitsPerPel;
        public int dmPelsWidth;
        public int dmPelsHeight;
        public int dmDisplayFlags;
        public int dmDisplayFrequency;
        public int dmICMMethod;
        public int dmICMIntent;
        public int dmMediaType;
        public int dmDitherType;
        public int dmReserved1;
        public int dmReserved2;
        public int dmPanningWidth;
        public int dmPanningHeight;
    class User_32
        public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode);
        public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags);
        public const int ENUM_CURRENT_SETTINGS = -1;
        public const int CDS_UPDATEREGISTRY = 0x01;
        public const int CDS_TEST = 0x02;
        public const int DISP_CHANGE_SUCCESSFUL = 0;
        public const int DISP_CHANGE_RESTART = 1;
        public const int DISP_CHANGE_FAILED = -1;
    public class PrmaryScreenResolution
        static public string ChangeResolution(int width, int height)
            DEVMODE1 dm = GetDevMode1();
            if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm))
                dm.dmPelsWidth = width;
                dm.dmPelsHeight = height;
                int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST);
                if (iRet == User_32.DISP_CHANGE_FAILED)
                    return “Unable To Process Your Request. Sorry For This Inconvenience.”;
                    iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY);
                    switch (iRet)
                        case User_32.DISP_CHANGE_SUCCESSFUL:
                                return “Success”;
                        case User_32.DISP_CHANGE_RESTART:
                                return “You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode.”;
                                return “Failed To Change The Resolution”;
                return “Failed To Change The Resolution.”;
        private static DEVMODE1 GetDevMode1()
            DEVMODE1 dm = new DEVMODE1();
            dm.dmDeviceName = new String(new char[32]);
            dm.dmFormName = new String(new char[32]);
            dm.dmSize = (short)Marshal.SizeOf(dm);
            return dm;
Add-Type $pinvokeCode -ErrorAction SilentlyContinue

Again, the point of going through this is not to provide a definitive guide on how to interop with the Win32 API. I know next to nothing about it. The key here that I want to share is to not be intimidated by technology we don’t understand. This was a long process for me to figure all of it out, but the process brought up a bunch of questions and answers, and eventually allowed me to solve the problem with a new set of tools at my disposal.

Where ever you are in your skill set with PowerShell, what is your next step? Maybe you have been using the interactive shell and now you could write a function. Maybe you’ve written a function and can start using advanced functions. Maybe you’ve got a chunk of functions and could start experimenting with Modules. Maybe you’re really comfortable with Windows PowerShell and want to start playing with C#. Whatever the case may be, don’t be afraid to step it up a notch and go for it!

The complete Set-ScreenResolution.ps1 script is available in the Script Repository.

AS that is all there is to using Windows PowerShell 2.0 to change the monitor resolution. Guest blogger week will continue tomorrow when we will talk about … wait a minute.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.


Ed Wilson and Craig Liebendorfer, Scripting Guys


Comments (14)

  1. jrv says:

    Monitor resolution is set per-user and not per-computer.  Each user decides their own preferred resolution.

  2. Anonymous says:

    I need this script to run on 100 remote machines. Unfortunately, it's not set up to take a "computername" parameter. Anybody know how this might be accomplished? I've tried various incarnations of invoke-command with no success.

  3. hypothesis says:

    Very valuable script indeed!

    Thanks a lot.

  4. Jon says:

    Hey, Scripting Guy! It's possible to set the best resolution ?



  5. Andy Schneider says:


    Yes, this is definitely possible. I actually had some VB.NET code about 3 years ago that would enumerate the highest resolution supported by the monitor that was attached to a computer. I'll poke around and see if I can find the code… if I can.. i don't think porting it to PS would be very hard.


  6. Andy Schneider says:


    I think the .NET framework added some functionality since I last started playing with this. It looks like you can use the following code to get the maximum resolution for your monitor

    $screen = [System.Windows.Forms.Screen]::PrimaryScreen

    $height = $screen.bounds.Size.Width                                      

    $width = $screen.bounds.Size.Height

  7. JD says:

    Brilliant. I will incorporate this into a Microsoft Deployment Toolkit Task Sequence to automatically adjust the screen resolution to its maximum size once a new windows 7 image is built (which it doesn't seem to do at present). Cheers.

  8. Jakob3 says:

    Nice script.

    I am new to .net and Powershell so I wold like som hints to incorporate the "best resolution" code.

  9. modifying for a second monitor says:

    Any hints on how to make this target a secondary display? I was looking thru the code and the comments and saw that using this code allows me to see the secondary device's name but I dont know how to modify the script.

    $screen = [System.Windows.Forms.Screen]::AllScreens


    BitsPerPixel : 32

    Bounds       : {X=0,Y=0,Width=1920,Height=1080}

    DeviceName   : \.DISPLAY1

    Primary      : True

    WorkingArea  : {X=0,Y=0,Width=1920,Height=1040}

    BitsPerPixel : 32

    Bounds       : {X=1920,Y=0,Width=1024,Height=768}

    DeviceName   : \.DISPLAY2

    Primary      : False

    WorkingArea  : {X=1920,Y=0,Width=1024,Height=768}

  10. Secondary monitor says:

    how to apply this screen change for secondary monitor?

  11. Mike McAnulty says:

    Hi Andy

    I like your script, but I have a problem with it.
    I thought I would create a powershell script that would run in MDT. It could query the computer model or the monitor attached to the machine. The idea is to reduce the number of entries in the Task Sequence to one and have all the hard work done in the script.
    At present I have loads of queries using WMI Query in the TS.

    Here’s an example of querying a laptop. It works fine

    If(Get-WmiObject -Class:Win32_ComputerSystem -Filter:"Model LIKE ‘%Latitude D520%’" ComputerName:localhost)
    {Set-ScreenResolution 1024 768}

    Here’s the monitor query.
    If(Get-WmiObject -Class:Win32_DesktopMonitor -Filter:"PNPDeviceID LIKE ‘%DEL4004%’")
    {Set-ScreenResolution 1280 1024}

    Here’s the error I get.

    Add-Type : Cannot add type. The type name ‘Resolution.DEVMODE1’ already exists.
    At C:Usersstmcamik01DesktopUntitled1.ps1:123 char:1
    + Add-Type $pinvokeCode -ErrorAction SilentlyContinue
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Resolution.DEVMODE1:String) [Add-Type], Exception
    + FullyQualifiedErrorId : TYPE_ALREADY_EXISTS,Microsoft.PowerShell.Commands.AddTypeCommand

    I have used more or less the same query in MDT natively to determine the monitor model and set the resolution accordingly (using SetRes to do so).

    Any help would be appreciated.


  12. TP says:

    Has anyone managed to get this to work / device? I need to set a resolution for screen 1 and another for screen 2.

  13. Bob Hyatt says:

    I got this to work once, now it always gives me the "Failed to Change Screen Resolution". Any ideas?

  14. Andre'' Siedentopf says:

    Hi Andy,

    Thanks for this script. We’ve been using for a couple years now. We have a certain test in our assessment department that needs to run at 800×600. I run it in the startup folder for this specific user. About a month ago, out of the blue it stopped working.
    We have made no changes to the computers, as a matter of fact we a program called Deep Freeze to protect the computer from any changes at all. Any ideas?

    I don’t see anywhere to put my email address to see if you reply. If you do see this can pop me an email?


    Andre Siedentopf