【问题标题】:Use Windows API from C# to set primary monitor使用 C# 中的 Windows API 设置主监视器
【发布时间】:2016-05-02 00:14:58
【问题描述】:

我正在尝试使用 Windows API 来设置主监视器。它似乎不起作用 - 我的屏幕只是闪烁,没有任何反应。

    public const int DM_ORIENTATION = 0x00000001;
public const int DM_PAPERSIZE = 0x00000002;
public const int DM_PAPERLENGTH = 0x00000004;
public const int DM_PAPERWIDTH = 0x00000008;
public const int DM_SCALE = 0x00000010;
public const int DM_POSITION = 0x00000020;
public const int DM_NUP = 0x00000040;
public const int DM_DISPLAYORIENTATION = 0x00000080;
public const int DM_COPIES = 0x00000100;
public const int DM_DEFAULTSOURCE = 0x00000200;
public const int DM_PRINTQUALITY = 0x00000400;
public const int DM_COLOR = 0x00000800;
public const int DM_DUPLEX = 0x00001000;
public const int DM_YRESOLUTION = 0x00002000;
public const int DM_TTOPTION = 0x00004000;
public const int DM_COLLATE = 0x00008000;
public const int DM_FORMNAME = 0x00010000;
public const int DM_LOGPIXELS = 0x00020000;
public const int DM_BITSPERPEL = 0x00040000;
public const int DM_PELSWIDTH = 0x00080000;
public const int DM_PELSHEIGHT = 0x00100000;
public const int DM_DISPLAYFLAGS = 0x00200000;
public const int DM_DISPLAYFREQUENCY = 0x00400000;
public const int DM_ICMMETHOD = 0x00800000;
public const int DM_ICMINTENT = 0x01000000;
public const int DM_MEDIATYPE = 0x02000000;
public const int DM_DITHERTYPE = 0x04000000;
public const int DM_PANNINGWIDTH = 0x08000000;
public const int DM_PANNINGHEIGHT = 0x10000000;
public const int DM_DISPLAYFIXEDOUTPUT = 0x20000000;

public const int ENUM_CURRENT_SETTINGS = -1;
public const int CDS_UPDATEREGISTRY = 0x01;
public const int CDS_TEST = 0x02;
public const int CDS_SET_PRIMARY = 0x00000010;

public const long DISP_CHANGE_SUCCESSFUL = 0;
public const long DISP_CHANGE_RESTART = 1;
public const long DISP_CHANGE_FAILED = -1;
public const long DISP_CHANGE_BADMODE = -2;
public const long DISP_CHANGE_NOTUPDATED = -3;
public const long DISP_CHANGE_BADFLAGS = -4;
public const long DISP_CHANGE_BADPARAM = -5;
public const long DISP_CHANGE_BADDUALVIEW = -6;

    public static void SetPrimary(Screen screen)
{
  DISPLAY_DEVICE d = new DISPLAY_DEVICE();
  DEVMODE dm = new DEVMODE();
  d.cb = Marshal.SizeOf(d);
  uint deviceID = 1;
  User_32.EnumDisplayDevices(null, deviceID, ref  d, 0); // 
  User_32.EnumDisplaySettings(d.DeviceName, 0, ref dm);
  dm.dmPelsWidth = 2560;
  dm.dmPelsHeight = 1600;
  dm.dmPositionX = screen.Bounds.Right;
  dm.dmFields = DM_POSITION | DM_PELSWIDTH | DM_PELSHEIGHT;
  User_32.ChangeDisplaySettingsEx(d.DeviceName, ref dm, IntPtr.Zero, CDS_SET_PRIMARY, IntPtr.Zero);
}

我这样调用方法:

SetPrimary(Screen.AllScreens[1])

有什么想法吗?

【问题讨论】:

    标签: c# windows sdk


    【解决方案1】:

    以下是基于 ADBaley 解决方案的完整代码:

    public class MonitorChanger
    {
        public static void SetAsPrimaryMonitor(uint id)
        {
            var device = new DISPLAY_DEVICE();
            var deviceMode = new DEVMODE();
            device.cb = Marshal.SizeOf(device);
    
            NativeMethods.EnumDisplayDevices(null, id, ref device, 0);
            NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref deviceMode);
            var offsetx = deviceMode.dmPosition.x;
            var offsety = deviceMode.dmPosition.y;
            deviceMode.dmPosition.x = 0;
            deviceMode.dmPosition.y = 0;
    
            NativeMethods.ChangeDisplaySettingsEx(
                device.DeviceName,
                ref deviceMode,
                (IntPtr)null,
                (ChangeDisplaySettingsFlags.CDS_SET_PRIMARY | ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
                IntPtr.Zero);
    
            device = new DISPLAY_DEVICE();
            device.cb = Marshal.SizeOf(device);
    
            // Update remaining devices
            for (uint otherid = 0; NativeMethods.EnumDisplayDevices(null, otherid, ref device, 0); otherid++)
            {
                if (device.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop) && otherid != id)
                {
                    device.cb = Marshal.SizeOf(device);
                    var otherDeviceMode = new DEVMODE();
    
                    NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref otherDeviceMode);
    
                    otherDeviceMode.dmPosition.x -= offsetx;
                    otherDeviceMode.dmPosition.y -= offsety;
    
                    NativeMethods.ChangeDisplaySettingsEx(
                        device.DeviceName,
                        ref otherDeviceMode,
                        (IntPtr)null,
                        (ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
                        IntPtr.Zero);
    
                }
    
                device.cb = Marshal.SizeOf(device);
            }
    
            // Apply settings
            NativeMethods.ChangeDisplaySettingsEx(null, IntPtr.Zero, (IntPtr)null, ChangeDisplaySettingsFlags.CDS_NONE, (IntPtr)null);
        }
    }
    
    [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 UInt32 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; // See note below!
        [System.Runtime.InteropServices.FieldOffset(62)]
        public short dmDuplex; // See note below!
        [System.Runtime.InteropServices.FieldOffset(64)]
        public short dmYResolution;
        [System.Runtime.InteropServices.FieldOffset(66)]
        public short dmTTOption;
        [System.Runtime.InteropServices.FieldOffset(68)]
        public short dmCollate; // See note below!
        [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;
    }
    
    public enum DISP_CHANGE : int
    {
        Successful = 0,
        Restart = 1,
        Failed = -1,
        BadMode = -2,
        NotUpdated = -3,
        BadFlags = -4,
        BadParam = -5,
        BadDualView = -6
    }
    
    [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
    {
        /// <summary>The device is part of the desktop.</summary>
        AttachedToDesktop = 0x1,
        MultiDriver = 0x2,
        /// <summary>The device is part of the desktop.</summary>
        PrimaryDevice = 0x4,
        /// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
        MirroringDriver = 0x8,
        /// <summary>The device is VGA compatible.</summary>
        VGACompatible = 0x10,
        /// <summary>The device is removable; it cannot be the primary display.</summary>
        Removable = 0x20,
        /// <summary>The device has more display modes than its output devices support.</summary>
        ModesPruned = 0x8000000,
        Remote = 0x4000000,
        Disconnect = 0x2000000,
    }
    
    [Flags()]
    public enum ChangeDisplaySettingsFlags : uint
    {
        CDS_NONE = 0,
        CDS_UPDATEREGISTRY = 0x00000001,
        CDS_TEST = 0x00000002,
        CDS_FULLSCREEN = 0x00000004,
        CDS_GLOBAL = 0x00000008,
        CDS_SET_PRIMARY = 0x00000010,
        CDS_VIDEOPARAMETERS = 0x00000020,
        CDS_ENABLE_UNSAFE_MODES = 0x00000100,
        CDS_DISABLE_UNSAFE_MODES = 0x00000200,
        CDS_RESET = 0x40000000,
        CDS_RESET_EX = 0x20000000,
        CDS_NORESET = 0x10000000
    }
    
    public class NativeMethods
    {
        [DllImport("user32.dll")]
        public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);
    
        [DllImport("user32.dll")]
        // A signature for ChangeDisplaySettingsEx with a DEVMODE struct as the second parameter won't allow you to pass in IntPtr.Zero, so create an overload
        public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, IntPtr lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);
    
        [DllImport("user32.dll")]
        public 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);
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct POINTL
    {
        public int x;
        public int y;
    }
    

    【讨论】:

    • 非常感谢 ADBaley 和 Vladimir。我可以将它用于我们的学校安装(Teacher PC+Scaler->Beamer)这里是每个人的代码:github.com/Grunge/setDisplayRes Greetings and Thanks
    【解决方案2】:

    我遇到了完全相同的问题,无论是在 C# 中还是在按照此处的建议在 C++ 中尝试之后。我最终发现微软文档没有明确说明的是设置主监视器的请求将被忽略(但操作报告为成功!)除非您还将监视器的位置设置为 (0, 0 ) 在 DEVMODE 结构上。当然,这意味着您还需要移动其他显示器的位置,以便它们相对于新的主显示器保持在同一位置。根据文档 (http://msdn.microsoft.com/en-us/library/windows/desktop/dd183413%28v=vs.85%29.aspx),使用 CDS_NORESET 标志为每个监视器调用 ChangeDisplaySettingsEx,然后在所有内容为 null 的情况下进行最终调用。

    以下代码对我有用:

        public static void SetAsPrimaryMonitor(uint id)
        {
            var device = new DISPLAY_DEVICE();
            var deviceMode = new DEVMODE();
            device.cb = Marshal.SizeOf(device);
    
            NativeMethods.EnumDisplayDevices(null, id, ref device, 0);
            NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref deviceMode);
            var offsetx = deviceMode.dmPosition.x;
            var offsety = deviceMode.dmPosition.y;
            deviceMode.dmPosition.x = 0;
            deviceMode.dmPosition.y = 0;
    
            NativeMethods.ChangeDisplaySettingsEx(
                device.DeviceName, 
                ref deviceMode, 
                (IntPtr)null, 
                (ChangeDisplaySettingsFlags.CDS_SET_PRIMARY | ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET), 
                IntPtr.Zero);
    
            device = new DISPLAY_DEVICE();
            device.cb = Marshal.SizeOf(device);
    
            // Update remaining devices
            for (uint otherid = 0; NativeMethods.EnumDisplayDevices(null, otherid, ref device, 0); otherid++)
            {
                if (device.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop) && otherid != id)
                {
                    device.cb = Marshal.SizeOf(device);
                    var otherDeviceMode = new DEVMODE();
    
                    NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref otherDeviceMode);
    
                    otherDeviceMode.dmPosition.x -= offsetx;
                    otherDeviceMode.dmPosition.y -= offsety;
    
                    NativeMethods.ChangeDisplaySettingsEx(
                        device.DeviceName,
                        ref otherDeviceMode,
                        (IntPtr)null,
                        (ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
                        IntPtr.Zero);
    
                }
    
                device.cb = Marshal.SizeOf(device);
            }
    
            // Apply settings
            NativeMethods.ChangeDisplaySettingsEx(null, IntPtr.Zero, (IntPtr)null, ChangeDisplaySettingsFlags.CDS_NONE, (IntPtr)null);
        }
    

    请注意,使用 DEVMODE 结构作为第二个参数的 ChangeDisplaySettingsEx 签名显然不允许您传入 IntPtr.Zero。为同一个外部调用创建两个不同的签名,即

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

    【讨论】:

    • 我将尝试这段代码并进行实验,但是您能否评论一下移动其他显示器位置的策略应该是什么?我在 MSDN 上没有找到太多关于这些位置的含义以及它们需要如何相互关联、它们应该是负数还是正数等的文档。您的示例将所有其他监视器向左和向上移动了先前的位置新的主监视器——这样做的理由是什么?没看到。
    • 我猜猜猜——我假设整个 2D 平面是公平的游戏,无论是正面的还是负面的,而相对位置只对鼠标环绕很重要?
    • 我现在没有机会尝试@user1454265(所以希望你能验证我在说什么),但我记得左上角主显示器总是(0,0),所以我写上面的方式是当你改变主显示器时显示器保持它们的相对位置(对我来说至关重要,特别是因为我在奇怪的地方有三个不同尺寸的显示器) .
    • 这对我不起作用。我在 Windows 10 上。
    【解决方案3】:

    我无法真正帮助您解决 winapi 的问题,但如果您使用的是 Nvidia 卡,您可以查看NVcontrolPanel Api Documentation 然后您可以使用 rundll32.exe NvCpl.dll,dtcfg primary 2 将辅助输出设为主要输出 希望对您有所帮助。

    【讨论】:

      【解决方案4】:

      根据documentation for ChangeDisplaySettingsEx,“dmSize 成员必须初始化为 DEVMODE 结构的大小(以字节为单位)。”此外,the EnumDisplaySettings documentation 指出,“在调用 EnumDisplaySettings 之前,将 dmSize 成员设置为 sizeof(DEVMODE),并将 dmDriverExtra 成员设置为指示可用于接收私有驱动程序数据的额外空间的大小(以字节为单位)”。我没有在问题中给出的代码示例中看到这种情况;这就是它可能失败的原因之一。

      此外,您可能在 DEVMODE 和 DISPLAY_DEVICE 结构的定义中有错误,这些错误未包含在问题中。 Roger Lipscombe's suggestion 首先从 C/C++ 开始工作是排除此类问题的好方法。

      最后,检查 ChangeDisplaySettingsEx 的返回值,看看它是否提供了可能失败的线索。

      【讨论】:

        猜你喜欢
        • 2012-08-20
        • 2012-01-05
        • 2011-12-04
        • 1970-01-01
        • 2015-08-23
        • 2011-12-13
        • 1970-01-01
        • 1970-01-01
        • 2010-10-17
        相关资源
        最近更新 更多