Click here to Skip to main content
Click here to Skip to main content

How to create a display switcher for Windows XP

, 6 Apr 2011
Rate this:
Please Sign up or sign in to vote.
This article shows how to use native APIs in C# to switch between multiple displays.

Introduction

Windows 7 comes with a built-in display switcher for multiple displays which can be activated using the (Windows + P) shortcut key. This article shows how to implement a similar utility to switch between multiple displays in older Operating Systems such as Windows XP and below.

Background

I had to frequently switch between multiple displays when I using Windows XP. Every time I wanted to switch display, I had to go to Display Properties and fiddle with the UI to extend/shift the display to the second display. I found this to be extremely annoying and decided to do it the API way. So I wrote a program to switch displays with two clicks. You can use the source code to create your own utility.

Using the code

This code is written in C# using the Win32 Managed API. I have divided the article into two easy parts:

  • Part 1: The native structures and function declarations needed for the program to work.
  • Part 2: How to use the APIs to switch the display.

Part 1

These variables hold the device information for the active and the inactive displays. The enumeration is required to tell the API to get display-device information from the Registry.

//Member variables
DEVMODE ddActive = new DEVMODE();
DEVMODE ddInactive = new DEVMODE();
string szActiveDeviceName = string.Empty;
string szInactiveDeviceName = string.Empty;
const int ENUM_REGISTRY_SETTINGS = -2;        

Next are the DllImports and these are the stars of our show. EnumDisplayDevices gets all the devices connected to your system. EnumDisplaySettings gets the detailed information of the selected device obtained from EnumDisplayDevices. This information can then be used to pass to ChangeDisplaySettingsEx to manipulate the display devices.

[DllImport("user32.dll")]
static extern bool EnumDisplayDevices(string lpDevice, 
       uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);

[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string deviceName, 
       int modeNum, ref DEVMODE devMode);

[DllImport("user32.dll")]
static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, 
       ref DEVMODE lpDevMode, IntPtr hwnd, uint dwflags, IntPtr lParam);

The last part of declarations are the enums which are used by the API and we need to construct them for the APIs to work properly.

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct DEVMODE
{
    public const int CCHDEVICENAME = 32;
    public const int CCHFORMNAME = 32;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
    [System.Runtime.InteropServices.FieldOffset(0)]
    public string dmDeviceName;
    [System.Runtime.InteropServices.FieldOffset(32)]
    public Int16 dmSpecVersion;
    [System.Runtime.InteropServices.FieldOffset(34)]
    public Int16 dmDriverVersion;
    [System.Runtime.InteropServices.FieldOffset(36)]
    public Int16 dmSize;
    [System.Runtime.InteropServices.FieldOffset(38)]
    public Int16 dmDriverExtra;
    [System.Runtime.InteropServices.FieldOffset(40)]
    public DM dmFields;
    [System.Runtime.InteropServices.FieldOffset(44)]
    Int16 dmOrientation;
    [System.Runtime.InteropServices.FieldOffset(46)]
    Int16 dmPaperSize;
    [System.Runtime.InteropServices.FieldOffset(48)]
    Int16 dmPaperLength;
    [System.Runtime.InteropServices.FieldOffset(50)]
    Int16 dmPaperWidth;
    [System.Runtime.InteropServices.FieldOffset(52)]
    Int16 dmScale;
    [System.Runtime.InteropServices.FieldOffset(54)]
    Int16 dmCopies;
    [System.Runtime.InteropServices.FieldOffset(56)]
    Int16 dmDefaultSource;
    [System.Runtime.InteropServices.FieldOffset(58)]
    Int16 dmPrintQuality;

    [System.Runtime.InteropServices.FieldOffset(44)]
    public POINTL dmPosition;
    [System.Runtime.InteropServices.FieldOffset(52)]
    public Int32 dmDisplayOrientation;
    [System.Runtime.InteropServices.FieldOffset(56)]
    public Int32 dmDisplayFixedOutput;

    [System.Runtime.InteropServices.FieldOffset(60)]
    public short dmColor;
    [System.Runtime.InteropServices.FieldOffset(62)]
    public short dmDuplex;
    [System.Runtime.InteropServices.FieldOffset(64)]
    public short dmYResolution;
    [System.Runtime.InteropServices.FieldOffset(66)]
    public short dmTTOption;
    [System.Runtime.InteropServices.FieldOffset(68)]
    public short dmCollate;
    [System.Runtime.InteropServices.FieldOffset(72)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
    public string dmFormName;
    [System.Runtime.InteropServices.FieldOffset(102)]
    public Int16 dmLogPixels;
    [System.Runtime.InteropServices.FieldOffset(104)]
    public Int32 dmBitsPerPel;
    [System.Runtime.InteropServices.FieldOffset(108)]
    public Int32 dmPelsWidth;
    [System.Runtime.InteropServices.FieldOffset(112)]
    public Int32 dmPelsHeight;
    [System.Runtime.InteropServices.FieldOffset(116)]
    public Int32 dmDisplayFlags;
    [System.Runtime.InteropServices.FieldOffset(116)]
    public Int32 dmNup;
    [System.Runtime.InteropServices.FieldOffset(120)]
    public Int32 dmDisplayFrequency;

}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
    [MarshalAs(UnmanagedType.U4)]
    public int cb;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string DeviceName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceString;
    [MarshalAs(UnmanagedType.U4)]
    public DisplayDeviceStateFlags StateFlags;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceKey;
}

[Flags()]
public enum DisplayDeviceStateFlags : int
{
    AttachedToDesktop = 0x1,
    MultiDriver = 0x2,
    PrimaryDevice = 0x4,
    MirroringDriver = 0x8,
    VGACompatible = 0x16,
    Removable = 0x20,
    ModesPruned = 0x8000000,
    Remote = 0x4000000,
    Disconnect = 0x2000000
}

[Flags()]
public enum DM : int
{
    Orientation = 0x1,
    PaperSize = 0x2,
    PaperLength = 0x4,
    PaperWidth = 0x8,
    Scale = 0x10,
    Position = 0x20,
    NUP = 0x40,
    DisplayOrientation = 0x80,
    Copies = 0x100,
    DefaultSource = 0x200,
    PrintQuality = 0x400,
    Color = 0x800,
    Duplex = 0x1000,
    YResolution = 0x2000,
    TTOption = 0x4000,
    Collate = 0x8000,
    FormName = 0x10000,
    LogPixels = 0x20000,
    BitsPerPixel = 0x40000,
    PelsWidth = 0x80000,
    PelsHeight = 0x100000,
    DisplayFlags = 0x200000,
    DisplayFrequency = 0x400000,
    ICMMethod = 0x800000,
    ICMIntent = 0x1000000,
    MediaType = 0x2000000,
    DitherType = 0x4000000,
    PanningWidth = 0x8000000,
    PanningHeight = 0x10000000,
    DisplayFixedOutput = 0x20000000
}

enum DISP_CHANGE : int
{
    Successful = 0,
    Restart = 1,
    Failed = -1,
    BadMode = -2,
    NotUpdated = -3,
    BadFlags = -4,
    BadParam = -5,
    BadDualView = -1
}

public struct POINTL
{
    public long x;
    public long y;
}

enum CDSFlags
{
    CDS_RESET = 0x40000000,
    CDS_UPDATEREGISTRY = 0x00000001,
    CDS_SET_PRIMARY = 0x00000010
};

Part 2: Time for some action

In my example, I am going to show you a function to toggle between your multiple displays. First, get the device information using this function. This will set the Active and the Inactive device information for your code to use.

  • Active device information:
    • ddActive
    • szActiveDeviceName
  • Inactive device information:
    • ddInactive
    • szInactiveDeviceName
public void GetDisplayInfo()
{
    ddActive.dmSize = (short)Marshal.SizeOf(ddActive);
    ddInactive.dmSize = (short)Marshal.SizeOf(ddInactive);
    uint iDeviceCntr = 0;
    DISPLAY_DEVICE dd = new DISPLAY_DEVICE();
    dd.cb = Marshal.SizeOf(dd);
    while(EnumDisplayDevices(null,iDeviceCntr, ref dd,0))
    {
        DEVMODE dMode = new DEVMODE();
        dMode.dmSize = (short)Marshal.SizeOf(dMode);
        if (EnumDisplaySettings(dd.DeviceName, ENUM_REGISTRY_SETTINGS, ref dMode))
        {
            if (dMode.dmPelsHeight > 0 && dMode.dmPelsWidth > 0)
            {
                if ((dd.StateFlags & DisplayDeviceStateFlags.PrimaryDevice) == 
                     DisplayDeviceStateFlags.PrimaryDevice)
                {
                    ddActive = dMode;
                    szActiveDeviceName =  dd.DeviceName;
                }
                else
                {
                    ddInactive = dMode;
                    szInactiveDeviceName =  dd.DeviceName;
                }
            }
        }
        iDeviceCntr++;
    }
}

The next function resets your display to set the primary display device as active and disable the second one. This uses the ChangeDisplaySettingsEx API.

private void ChangeToSingleDisplay()
{
    GetDisplayInfo();
    
    ddInactive.dmPosition.x = 0;
    ddInactive.dmPosition.y = 0;
    ddInactive.dmPelsHeight = 0;
    ddInactive.dmPelsWidth = 0;
    ddInactive.dmFields = DM.PelsHeight | DM.PelsWidth | DM.Position;

    ChangeDisplaySettingsEx(szInactiveDeviceName, ref ddInactive, 
      IntPtr.Zero, (int)(CDSFlags.CDS_RESET | CDSFlags.CDS_UPDATEREGISTRY), IntPtr.Zero);
}

Next, we will get the get the refreshed display information and try to toggle the displays (making active device inactive, and vice versa).

GetDisplayInfo();
ddInactive.dmPosition.x = 0;
ddInactive.dmPosition.y = 0;
ddInactive.dmFields = DM.Position;
if (DISP_CHANGE.Successful == ChangeDisplaySettingsEx(szInactiveDeviceName, 
    ref ddInactive, IntPtr.Zero, 
    (int)(CDSFlags.CDS_RESET | CDSFlags.CDS_UPDATEREGISTRY | 
     CDSFlags.CDS_SET_PRIMARY), IntPtr.Zero))
{
    ddActive.dmPosition.x = 0;
    ddActive.dmPosition.y = 0;
    ddActive.dmPelsHeight = 0;
    ddActive.dmPelsWidth = 0;
    ddActive.dmFields = DM.PelsHeight | DM.PelsWidth | DM.Position;

    ChangeDisplaySettingsEx(szActiveDeviceName, ref ddActive, 
      IntPtr.Zero, (int)(CDSFlags.CDS_RESET | 
      CDSFlags.CDS_UPDATEREGISTRY), IntPtr.Zero);
}

That's all.

License

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

Share

About the Author

nikhilogic
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
QuestionWin7 Windows-P Duplicate/Extend mode in code? Pinmemberk9mab8-Apr-11 16:17 
AnswerRe: Win7 Windows-P Duplicate/Extend mode in code? PinmemberNikhil Mujumdar10-Apr-11 17:47 
GeneralRe: Win7 Windows-P Duplicate/Extend mode in code? Pinmemberhxhgxytiger28-May-11 4:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 6 Apr 2011
Article Copyright 2011 by nikhilogic
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid