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:
- Use EnumDisplaySettings to read the current display settings and enumerate all supported display settings.
- Use ChangeDisplaySettings to switch to new 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.