您的位置:首页 > 编程语言

WM平台旋转屏幕的编程实现

2010-11-28 15:53 471 查看
有些情况下,应用程序需要更改屏幕方向,因为有的功能被设计为在特定模式下运行得最好。其中一个例子就是 Microsoft Office PowerPoint 中的“幻灯片放映”:PowerPoint 以横向模式运行。即使正在纵向模式下使用 Tablet PC,当开始幻灯片放映时,应用程序也会切换到横向方向。当用户结束幻灯片放映时,PowerPoint 会切换回原来的设置。

更改显示设置可以通过使用两个 Win32 API 来完成,这两个 API 都具有指向 DEVMODE 结构的指针,它们分别包含与显示设置有关的所有信息:
使用 EnumDisplaySettings 读取当前显示设置,并枚举所有受支持的显示设置。
使用 ChangeDisplaySettings 切换到新的显示设置。

获取当前显示设置

要获取当前显示设置,请将 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
   }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: