Changing Screen Orientation Programmatically

 

Stefan Wick
Microsoft Corporation

October 2004

Applies to:
   Microsoft Windows XP

Summary: This article describes how to programmatically change orientation, resolution, and other aspects of the display device. The corresponding sample is written in C#. (9 printed pages)

Click here to download the code sample for this article.

Contents

Introduction
Overview
Using the Sample
Getting the Current Display Settings
Enumerating All Supported Display Settings
Changing Display Settings
Getting and Changing Display Settings in Managed Code
Mapping the APIs
Mapping the DEVMODE Structure
Rotating the Screen in C#
Conclusions

Introduction

In some scenarios your application may change the screen orientation, because a feature has been designed to run best in a specific mode. One example of this is during a Slide Show in Microsoft Office PowerPoint: PowerPoint runs in landscape mode. Even if you are using a Tablet PC in portrait mode, the application switches to a landscape orientation when you begin a Slide Show. PowerPoint switches back to the original setting when the user ends the Slide Show.

Overview

Changing display settings can be accomplished by using two Win32 APIs, both of which take pointers to DEVMODE structures that contain all the information about the respective display settings:

Using the Sample

In order to compile the sample source code, you must have Microsoft Visual Studio .NET 2003 installed on your computer. The sample application has a user interface that enables you to:

  • View all supported display settings for the current display device.
  • View the parameters of the current display settings.
  • Switch to any supported display setting.
  • Rotate the screen orientation clockwise and anti-clockwise.

Getting the Current Display Settings

To obtain the current display settings, pass the ENUM_CURRENT_SETTINGS constant in the iModeNum parameter to the EnumDisplaySettings API, as illustrated by the following C++ code.

   DEVMODE dm;
   // initialize the DEVMODE structure
   ZeroMemory(&dm, sizeof(dm));
   dm.dmSize = sizeof(dm);

   if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
   {
      // inspect the DEVMODE structure to obtain details
      // about the display settings such as
      //  - Orientation
      //  - Width and Height
      //  - Frequency
      //  - etc.
   }

Enumerating All Supported Display Settings

To enumerate all display settings supported by the current display device pass zero in the iModeNum parameter to the EnumDisplaySettings API and then continue calling it with incremented iModeNum values until the function returns zero, as shown in the following C++ code.

   int index = 0;
   DEVMODE dm;
   // initialize the DEVMODE structure
   ZeroMemory(&dm, sizeof(dm));
   dm.dmSize = sizeof(dm);

   while (0 != EnumDisplaySettings(NULL, index++, &dm))
   {
      // inspect the DEVMODE structure to obtain details
      // about the display settings such as
      //  - Orientation
      //  - Width and Height
      //  - Frequency
      //  - etc.
   }

Changing Display Settings

To change the display settings pass in a pointer to a valid DEVMODE structure to the ChangeDisplaySettings API. The following C++ code demonstrates how to rotate the screen orientation clockwise by 90 degrees. Note that this code will only work with devices that support the respective display settings. It is important to obey the return value of the ChangeDisplaySettings API as some operations may require the computer to be restarted in order for the graphics mode to work.

   DEVMODE dm;
   // initialize the DEVMODE structure
   ZeroMemory(&dm, sizeof(dm));
   dm.dmSize = sizeof(dm);

   if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
   {
      // swap height and width
      DWORD dwTemp = dm.dmPelsHeight;
      dm.dmPelsHeight= dm.dmPelsWidth;
      dm.dmPelsWidth = dwTemp;

      // determine new orientaion
      switch (dm.dmDisplayOrientation)
      {
      case DMDO_DEFAULT:
         dm.dmDisplayOrientation = DMDO_270;
         break;
      case DMDO_270:
         dm.dmDisplayOrientation = DMDO_180;
         break;
      case DMDO_180:
         dm.dmDisplayOrientation = DMDO_90;
         break;
      case DMDO_90:
         dm.dmDisplayOrientation = DMDO_DEFAULT;
         break;
      default:
         // unknown orientation value
         // add exception handling here
         break;
      }
      long lRet = ChangeDisplaySettings(&dm, 0);
      if (DISP_CHANGE_SUCCESSFUL != lRet)
      {
         // add exception handling here
      }
   }

Getting and Changing Display Settings in Managed Code

Mapping the APIs

In order to change display settings in managed code, the EnumDisplaySettings and ChangeDisplaySettings APIs must be called by using Platform Invocation Services (PInvoke). For this purpose it is a good practice to create a class called NativeMethods that exposes public static methods that wrap those APIs. This class should also contain all the required constant definitions for the respective APIs. The following code sample demonstrates this practice. The full implementation of this class can be found in the NativeMethods.cs file that is part of the sample application.

using System.Runtime.InteropServices;
...
public class NativeMethods
{
   // PInvoke declaration for EnumDisplaySettings Win32 API
   [DllImport("user32.dll", CharSet=CharSet.Ansi)]
   public static extern int EnumDisplaySettings(
string lpszDeviceName,
int iModeNum,
ref DEVMODE lpDevMode);         

   // PInvoke declaration for ChangeDisplaySettings Win32 API
   [DllImport("user32.dll, CharSet=CharSet.Ansi")]
   public static extern int ChangeDisplaySettings(
ref DEVMODE lpDevMode,
int dwFlags);

   // add more functions as needed …

   // constants
   public const int ENUM_CURRENT_SETTINGS = -1;
   public const int DMDO_DEFAULT = 0;
   public const int DMDO_90 = 1;
   public const int DMDO_180 = 2;
   public const int DMDO_270 = 3;
   // add more constants as needed …
}

Mapping the DEVMODE Structure

When mapping the DEVMODE structure to a managed structure, a couple of things should be noted:

  • Because the DEVMODE structure contains unions, we have to pick and choose those members that are relevant for our purposes.
  • Arrays that map to strings in the .NET Framework must be marshaled as same size strings.
  • For simplicity, nested structures can be flattened (For example, I replaced the POINTL structure with two managed int types.)
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DEVMODE 
{
   [MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)]
   public string dmDeviceName;
   
   public short  dmSpecVersion;
   public short  dmDriverVersion;
   public short  dmSize;
   public short  dmDriverExtra;
   public int    dmFields;
   public int    dmPositionX;
   public int    dmPositionY;
   public int    dmDisplayOrientation;
   public int    dmDisplayFixedOutput;
   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;
};

When initializing a new instance of a DEVMODE structure in the .NET Framework, be sure to set the dmDeviceName, dmFormName and dmSize values appropriately. In the sample application I added the following method to the NativeMethods class to accomplish this:

public static DEVMODE CreateDevmode()
{
   DEVMODE dm = new DEVMODE();
   dm.dmDeviceName = new String(new char[32]);
   dm.dmFormName = new String(new char[32]);
   dm.dmSize = (short)Marshal.SizeOf(dm);
   return dm;
}

Rotating the Screen in C#

The following C# code combines the techniques discussed earlier and illustrates how to rotate the screen clockwise in managed code. Note that the code will only work if your device supports the respective display settings.

// initialize the DEVMODE structure
DEVMODE dm = new DEVMODE();
dm.dmDeviceName = new string(new char[32]);
dm.dmFormName = new string(new char[32]);
dm.dmSize = Marshal.SizeOf(dm);

if (0 != NativeMethods.EnumDisplaySettings(
null,
NativeMethods.ENUM_CURRENT_SETTINGS,
ref dm))
{
   // swap width and height
   int temp = dm.dmPelsHeight;
   dm.dmPelsHeight = dm.dmPelsWidth;
   dm.dmPelsWidth = temp;

   // determine new orientation
   switch(dm.dmDisplayOrientation)
   {
      case NativeMethods.DMDO_DEFAULT:
         dm.dmDisplayOrientation = NativeMethods.DMDO_270;
         break;
      case NativeMethods.DMDO_270:
         dm.dmDisplayOrientation = NativeMethods.DMDO_180;
         break;
      case NativeMethods.DMDO_180:
         dm.dmDisplayOrientation = NativeMethods.DMDO_90;
         break;
      case NativeMethods.DMDO_90:
         dm.dmDisplayOrientation = NativeMethods.DMDO_DEFAULT;
         break;
      default:
         // unknown orientation value
         // add exception handling here
         break;
   }

   int iRet = NativeMethods.ChangeDisplaySettings(ref dm, 0);
   if (NativeMethods.DISP_CHANGE_SUCCESSFUL != iRet)
   {
      // add exception handling here
   }
}

Conclusions

  • Use EnumDisplaySettings API to obtain information about the current display settings.
  • Use EnumDisplaySettings API to enumerate all supported display settings.
  • The DEVMODE structure contains all the information about a given display mode.
  • Use ChangeDisplaySettings to switch to a new display mode specified by a valid DEVMODE structure.
  • Use Platform Invocation Services to do this from managed code.