摘要: 本文描述如何以编程方式更改显示设备的方向、分辨率及其他方面。相应示例采用 C# 编写。
单击此处可下载本文的代码示例。
本页内容
简介
概述
使用示例
在托管代码中获取并更改显示设置
小结
简介
有些情况下,应用程序需要更改屏幕方向,因为有的功能被设计为在特定模式下运行得最好。其中一个例子就是 Microsoft Office PowerPoint 中的“幻灯片放映”:PowerPoint 以横向模式运行。即使正在纵向模式下使用 Tablet PC,当开始幻灯片放映时,应用程序也会切换到横向方向。当用户结束幻灯片放映时,PowerPoint 会切换回原来的设置。
概述
更改显示设置可以通过使用两个 Win32 API 来完成,这两个 API 都具有指向 DEVMODE 结构的指针,它们分别包含与显示设置有关的所有信息:
使用示例
要编译示例源代码,计算机中必须安装 Microsoft Visual Studio .NET 2003。该示例应用程序有一个用户界面,它具有以下功能:
-
查看当前显示设备所支持的所有显示设置。
-
查看当前显示设置的参数。
-
切换至任何受支持的显示设置。
-
顺时针和逆时针旋转屏幕方向。
获取当前显示设置
要获取当前显示设置,请将 iModeNum 参数中的 ENUM_CURRENT_SETTINGS 常量传递给 EnumDisplaySettings API,如以下 C++ 代码所示。
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.
}
枚举所有受支持的显示设置
要枚举当前显示设备支持的所有显示设置,请将 iModeNum 参数中的 0 传递给 EnumDisplaySettings API,然后继续以递增的 iModeNum 值调用它,直到该函数返回零,如以下 C++ 代码所示。
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.
}
更改显示设置
要更改显示设置,请将指向有效 DEVMODE 结构的指针传递给 ChangeDisplaySettings API。以下 C++ 代码演示如何使屏幕方向顺时针旋转 90 度。请注意,这段代码只对支持相应显示设置的设备起作用。遵守 ChangeDisplaySettings API 的返回值十分重要,因为有些操作为了在图形模式下工作,需要计算机重启。
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
}
}
在托管代码中获取并更改显示设置
映射 API
为了在托管代码中更改显示设置,必须使用平台调用服务 (PInvoke) 调用 EnumDisplaySettings 和 ChangeDisplaySettings API。对此,一个好的做法是创建一个名为 NativeMethods 的类,该类可以将封装这些 API 的公共静态方法公开。该类应该包含 API 所对应的所有必需的常量定义。以下代码示例演示了这种做法。该类的完整实现可在 NativeMethods.cs 文件中找到,该文件是示例应用程序的一部分。
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 ??
}
映射 DEVMODE 结构
在将 DEVMODE 结构映射到托管结构时,应该注意以下几个问题:
-
因为 DEVMODE 结构包含联合 (union),所以必须挑选出我们所需要的那些成员。
-
在 .NET Framework 中映射到字符串的数组必须作为相同大小的字符串封送。
-
为简单起见,可以将嵌套结构平面化(例如,将 POINTL 结构替换为两个托管的 int 类型。)
[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;
};
在 .NET Framework 中初始化 DEVMODE 结构的新实例时,请确保 dmDeviceName、dmFormName 和 dmSize 值设置恰当。为此,我在示例应用程序的 NativeMethods 类中添加了以下方法:
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;
}
在 C# 中旋转屏幕
以下 C# 代码将前面讨论的技术结合起来,并展示了如何在托管代码中顺时针旋转屏幕。请注意,这段代码只对支持相应显示设置的设备起作用。
// 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
}
}
小结
-
使用 EnumDisplaySettings API 来获取当前显示设置的相关信息。
-
使用 EnumDisplaySettings API 来枚举所有受支持的显示设置。
-
DEVMODE 结构包含关于给定的显示模式的所有信息。
-
使用 ChangeDisplaySettings 来切换到由有效的 DEVMODE 结构指定的新显示模式。
-
使用平台调用服务以从托管代码实现。