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

编程入门 行业动态 更新时间:2024-10-25 06:20:05
使用User32.ChangeDisplaySettings设置分辨率仅在最大分辨率上失败('badmode'错误)(using User32.ChangeDisplaySettings to set resolution fails only on max resolution ('badmode' error))

所以我要做的是使用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计算机上不起作用的问题)

So what I am trying to do is set a screen resolution on the primary display in C# using ChangeDisplaySettings. I have tested this across multiple windows 7 computers that I own, and the result is always the same: any* valid resolution (those that are listed in the "screen resolution" menu upon right-clicking the desktop) will work just fine, except for the largest resolution that can be selected in that menu, which will cause User32.ChangeDisplaySettings to return -2, which is DISP_CHANGE_BADMODE, meaning that the requested display mode was invalid.

*On some of the computers with larger primary displays, I didn't bother to test every resolution, and just picked some arbitrary smaller ones & the maximum, as there were too many to test on every single one. I feel confident enough in my testing to say that the maximum resolution ALWAYS fails, while smaller resolutions usually/always succeed (I never had any of them fail during my tests anyways).

Documentation on ChangeDisplaySettings: http://msdn.microsoft.com/en-us/library/dd183411%28VS.85%29.aspx

Documentation on the DEVMODE struct it uses: http://msdn.microsoft.com/en-us/library/dd183565%28v=vs.85%29.aspx

So for an example, lets say I'm running on a 1920x1080 display.

I set my resolution manually (or programmatically) to something else, doesn't matter what it is or how it was changed, and then run the following code:

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"); }

*Note that this is not actually the code from the program. I just made this simplified version to cut it down to the bare necessities to illustrate the point.

The program would print out:

-2

Which, as mentioned previously, is the value of DISP_CHANGE_BADMODE, and the resolution would fail to change.

Now, if I changed the values of 1080 and 1920 to 900 and 1600 respectively, another supported resolution on this monitor, and then set the resolution to something other than 1600x900, and then ran this program, it would in fact change the resolution to 1600x900, and return DISP_CHANGE_SUCCESSFUL.

Note that using other flags, such as CDS_RESET (0x40000000 or 1073741824) in place of CDS_UPDATEREGISTRY also result in the same error.

This is a tutorial I found that helped me get started:

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

[I removed the hyperlink because of the apparent spam prevention system. Sort of silly given that the first to were msdn.microsoft links, and this is a codeproject one, but, w/e]

Note that in the comments section, it seems there is someone who used the provided source files directly, and is experiencing a similar problem. To quote them:

hello , i'm using Resolution.cs on my c# application and it doesn't work with high resolutions like " 1366*768 " & " 1280*720 " can any one help ???

However, despite how commonly ChangeDisplaySettings seems to be recommended by tutorials, I can't find any information on resolving this issue (which could very well be operating system specific, but I currently lack any non-Windows 7 computers to test on, and even if I did, it would not solve the issue that it doesn't work on Windows 7 computers)

最满意答案

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

比本教程中所示的更安全的方法是使用显示模式的索引或仅使用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; }

As it turns out, the tutorial I was using was making the assumption that nothing would change any of the display mode parameters to something invalid during the time that it was at a lower resolution (Such as increasing the refresh rate, which is capped on the monitor's side. And since I was using the same two monitors for the testing, and the max resolution had a lower max refresh rate than any of the other resolutions, I would encounter this issue.)

A safer method than what is illustrated in the tutorial is to work with the indexes of the display modes or work only with EnumDisplayModes and never touch the data inside the DEVMODE struct.

The following is an excerpt of an example program I whipped up that changes the resolution to the specified parameters, and then back again.

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"); }

where the functions used here are as follows:

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; }

更多推荐

本文发布于:2023-04-27 22:48:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1329651.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:分辨率   错误   badmode   ChangeDisplaySettings   error

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!