使用User32.ChangeDisplaySettings设置分辨率仅在最大分辨率上失败(' badmode'错误)

时间:2012-03-18 06:06:23

标签: c#

所以我要做的是使用ChangeDisplaySettings在C#的主显示屏上设置屏幕分辨率。我已经在我拥有的多台Windows 7计算机上测试了这个,结果总是一样的:任何*有效的分辨率(右键单击桌面时在“屏幕分辨率”菜单中列出的那些)都可以正常工作,除了对于可以在菜单中选择最大的分辨率,这将导致User32.ChangeDisplaySettings返回-2,这是DISP_CHANGE_BADMODE的,这意味着所请求的显示模式是无效的。

*在某些具有较大主显示屏的计算机上,我没有费心去测试每个分辨率,只选择了一些任意小的分辨率。最大值,因为每一个都有太多的测试。在我的测试中我有足够的信心说最高分辨率总是失败,而较小的分辨率通常/总是成功(我的测试期间从未有过任何失败)。

有关ChangeDisplaySettings的文档:http://msdn.microsoft.com/en-us/library/dd183411%28VS.85%29.aspx

它使用的DEVMODE结构的文档:http://msdn.microsoft.com/en-us/library/dd183565%28v=vs.85%29.aspx

举个例子,假设我在1920x1080显示器上运行。

我手动(或以编程方式)将解决方案设置为其他内容,无论它是什么或如何更改,然后运行以下代码:

DEVMODE dm = new DEVMODE();
dm.dmDeviceName = new String(new char[32]);
dm.dmFormName = new String(new char[32]);
dm.dmSize = (short)Marshal.SizeOf(dm);
if (User32.EnumDisplaySettings(null, User32.ENUM_CURRENT_SETTINGS, ref dm) != 0)
{
     dm.dmPelsWidth = 1920;
     dm.dmPelsHeight = 1080;
     Console.WriteLine("" + User32.ChangeDisplaySettings(ref dm, User32.CDS_UPDATEREGISTRY) + "\n");
}

*请注意,这实际上不是程序中的代码。我刚刚制作了这个简化版本,将其简化为必需品,以说明这一点。

该程序将打印出来:

-2

如前所述,这是DISP_CHANGE_BADMODE的值,分辨率将无法更改。

现在,如果我将1080和1920的值分别更改为900和1600,此监视器上另一个支持的分辨率,然后将分辨率设置为1600x900以外的其他值,然后运行此程序,它实际上会更改分辨率为1600x900,并返回DISP_CHANGE_SUCCESSFUL。

请注意,使用其他标志(例如CDS_RESET(0x40000000或1073741824)代替CDS_UPDATEREGISTRY)也会导致相同的错误。

这是我发现的帮助我入门的教程:

www.codeproject.com/Articles/6810/Dynamic-Screen-Resolution

[由于明显的垃圾邮件防范系统,我删除了超链接。鉴于第一个是msdn.microsoft链接,这是一个代码项目,但是,w / e]

请注意,在评论部分中,似乎有人直接使用了提供的源文件,并且遇到了类似的问题。引用它们:

  你好,   我在我的c#应用程序上使用Resolution.cs   它不适用于像“1366 * 768”这样的高分辨率。 “1280 * 720”   任何人都可以帮忙???

然而,尽管通常如何似乎ChangeDisplaySettings通过教程推荐,我不能找到解决这个问题(这很可能是特定于操作系统的任何信息,但我目前没有任何非Windows 7的计算机上测试,即使我这样做了,也无法解决它在Windows 7计算机上无法运行的问题)

3 个答案:

答案 0 :(得分:4)

事实证明,我正在使用的教程假设没有任何东西可以将任何显示模式参数更改为在较低分辨率期间无效的东西(例如提高刷新率,这是因为我使用相同的两个显示器进行测试,并且最大分辨率的最大刷新率低于任何其他分辨率,我会遇到这个问题。)

比本教程中所示的更安全的方法是使用显示模式的索引或仅使用EnumDisplayModes并且从不触摸DEVMODE结构内的数据。

以下是我掀起的示例程序的摘录,该程序将分辨率更改为指定的参数,然后再返回。

            int selB, selG, selF, selH, selW;
            ... //these values get defined, etc.
            DEVMODE OSpecs = new DEVMODE();
            getCurrentRes(ref OSpecs);
            int Ondx = getDMbySpecs(OSpecs.dmPelsHeight, OSpecs.dmPelsWidth, OSpecs.dmDisplayFrequency, OSpecs.dmDisplayFlags, OSpecs.dmBitsPerPel, ref OSpecs);
            Screen Srn = Screen.PrimaryScreen;
            Console.WriteLine("Current res is " + OSpecs.dmPelsHeight + " by " + OSpecs.dmPelsWidth + "\n");
            DEVMODE NSpecs = new DEVMODE();
            int Nndx = getDMbySpecs(selH, selW, selF, selG, selB, ref NSpecs);
                //Note that this function sets both the DEVMODE to the selected display mode and returns the index value of this display mode. It returns -1 if it fails (-1 is the value of ENUM_CURRENT_SETTINGS), and sets the DEVMODE to the current display mode.
            if (Nndx == -1)
            {
                 Console.WriteLine("Could not find specified mode");
            }
            else if (setDisplayMode(ref NSpecs) || setDisplayMode(Nndx)) //This is just to illustrate both ways of doing it. One or the other may be more convenient (ie, the latter if you are getting this from a file, the former if you already have the DEVMODE in your program, etc.)
            {
                //reset display mode to original after waiting long enough to see it changed
                Console.WriteLine("Successful change. Waiting 4 seconds.");
                Thread.Sleep(4000);
                if (setDisplayMode(ref OSpecs) || setDisplayMode(Ondx))
                {
                    //success!
                    Console.WriteLine("Mode reversion succeeded.");
                }
                else
                {
                    Console.WriteLine("Mode reversion failed. Manual reset required.");
                }
            }
            else
            {
                //return
                Console.WriteLine("Resolution change failed. Aborting");
            }

这里使用的函数如下:

    static bool setDisplayMode(int i)
    {
        DEVMODE DM = new DEVMODE();
        DM.dmSize = (short)Marshal.SizeOf(DM);
        User32.EnumDisplaySettings(null, i, ref DM);
        if (User32.ChangeDisplaySettings(ref DM, User32.CDS_TEST) == 0 && User32.ChangeDisplaySettings(ref DM, User32.CDS_UPDATEREGISTRY) == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    static bool setDisplayMode(ref DEVMODE DM)
    {
        if (User32.ChangeDisplaySettings(ref DM, User32.CDS_TEST) == 0 && User32.ChangeDisplaySettings(ref DM, User32.CDS_UPDATEREGISTRY) == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    static int getDMbySpecs(int H, int W, int F, int G, int B, ref DEVMODE DM)
    {
        DM.dmSize = (short)Marshal.SizeOf(DM);
        DEVMODE SelDM = new DEVMODE();
        SelDM.dmSize = (short)Marshal.SizeOf(SelDM);
        int iOMI = 0;
        for (iOMI = 0; User32.EnumDisplaySettings(null, iOMI, ref SelDM) != 0; iOMI++)
        {
            if (( B == -1 || B == SelDM.dmBitsPerPel) && ( H == -1 || H == SelDM.dmPelsHeight) && ( W == -1 || W == SelDM.dmPelsWidth) && ( G == -1 || G == SelDM.dmDisplayFlags) && ( F == -1 || F == SelDM.dmDisplayFrequency))

                break;
        }
        if (User32.EnumDisplaySettings(null, iOMI, ref DM) == 0)
        {
            iOMI = -1;
            getCurrentRes(ref DM);
        }
        return iOMI;
    }
    static void getCurrentRes(ref DEVMODE dm)
    {
        dm = new DEVMODE();
        dm.dmSize = (short)Marshal.SizeOf(dm);
        User32.EnumDisplaySettings(null, User32.ENUM_CURRENT_SETTINGS, ref dm);
        return;
    }

答案 1 :(得分:2)

除了@Avan给出的答案之外,我发现当你将错误的参数传递给DevMode结构的dmDisplayFrequency时也会发生这个问题

要解决此问题,您可以先从图形设备获取所有可用的屏幕分辨率,然后在将其发送到ChangeDisplaySettings功能之前更新频率。

要获取显示设置列表,请使用EnumDisplaySettings

答案 2 :(得分:1)

要切换到最高的原始分辨率,必须使用有效值填充dm.dmFields结构。它似乎。在调用ChangeDisplaysettings()之前。