How can I change Windows 10 Display Scaling Programmatically using C#
我正在尝试找到一种方法来使用C#以编程方式更改Windows 10中的显示比例。
我还要说的是,我并不是要创建一个自动强制用户屏幕更改分辨率/缩放比例的应用程序。它只是我可以从托盘切换秤的工具,这是我经常需要进行测试的东西。因此专门为此动作而设计。
因此,当用户通过如下所示的官方对话框手动执行此操作时,我能够跟踪设置了哪些注册表项(HKEY_CURRENT_USER \ Control Panel \ Desktop):
但是,显然直接使用注册表意味着我需要重新启动计算机才能生效。
我知道您可以使用Pinvoke更改屏幕分辨率:
设置我的显示分辨率
我想知道是否也可以为给定的屏幕更改此"%"?也就是说,我上方的屏幕显示为150%,我希望能够以编程方式在100-500%的整个范围内进行更改。
这是我在系统设置应用程序(沉浸式控制面板)上所做的RnD的学习。 (有关我根据此学习创建的简单C ++ API的信息,请参见我的其他答案-https://stackoverflow.com/a/58066736/981766)
好的。
我使用WinDbg来处理此应用程序发出的呼叫。我发现,一旦执行特定功能-
好的。
我无法在此函数上设置断点,但能够在
好的。
DisplayConfigSetDeviceInfo(msdn链接)是一个公共功能,但似乎设置应用程序正在向其发送未记录的参数。
这是我在调试会话期间发现的参数。
好的。
1 2 3 4 5 6 7 8 9 | ((user32!DISPLAYCONFIG_DEVICE_INFO_HEADER *)0x55df8fba30) : 0x55df8fba30 [Type: DISPLAYCONFIG_DEVICE_INFO_HEADER *] [+0x000] type : -4 [Type: DISPLAYCONFIG_DEVICE_INFO_TYPE] [+0x004] size : 0x18 [Type: unsigned int] [+0x008] adapterId [Type: _LUID] [+0x010] id : 0x0 [Type: unsigned int] 0:003> dx -r1 (*((user32!_LUID *)0x55df8fba38)) (*((user32!_LUID *)0x55df8fba38)) [Type: _LUID] [+0x000] LowPart : 0xcbae [Type: unsigned long] [+0x004] HighPart : 0 [Type: long] |
基本上,传递给
好的。
1 2 3 | type : -4 size : 0x18 adapterId : LowPart : 0xcbae HighPart :0 |
wingdi.h中定义的枚举类型为:
好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | typedef enum { DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7, DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8, DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9, DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10, DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF } DISPLAYCONFIG_DEVICE_INFO_TYPE; |
在设置应用尝试发送-4的类型时,我们可以看到枚举没有负值。
好的。
如果我们能够完全对它进行反向工程,我们将有一个有效的API来设置监视器的DPI。
好的。
微软为自己的应用程序提供了一些其他人无法使用的特殊API,这似乎非常不公平。
好的。
更新1:
为了验证我的理论,我复制(使用WinDbg)作为参数发送到
好的。
然后,我编写了一个简单的C程序,将这些字节(24字节-0x18字节)发送到
然后,我将DPI缩放比例更改回100%,并运行了我的代码。果然,在运行代码时DPI缩放比例确实发生了变化!!!
好的。
1 2 3 | BYTE buf[] = { 0xFC,0xFF,0xFF,0xFF,0x18,0x00,0x00,0x00,0xAE,0xCB,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00 }; DISPLAYCONFIG_DEVICE_INFO_HEADER* packet = (DISPLAYCONFIG_DEVICE_INFO_HEADER*)buf; DisplayConfigSetDeviceInfo(packet); |
请注意,与LUID相同的代码可能对您不起作用,并且指向系统上的显示的id参数会有所不同(LUID通常用于GPU,id可以是源ID,目标ID或其他一些ID ,此参数取决于DISPLAYCONFIG_DEVICE_INFO_HEADER :: type)。
好的。
我现在必须弄清楚这24个字节的含义。
好的。
更新2:
这是尝试设置175%dpi缩放比例时得到的字节数。
好的。
1 | BYTE buf[] = { 0xFC,0xFF,0xFF,0xFF,0x18,0x00,0x00,0x00,0xAE,0xCB,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00 }; |
如果我们比较两个字节缓冲区,我们可以得出以下结论。
好的。
现在剩下的唯一事情就是弄清楚如何获得建议的显示器DPI缩放比例值,然后我们将能够编写以下形式的API-
好的。
更新3:
如果我们检查@Dodge的答案中提到的注册表项,我们就会知道这些整数存储为DWORD,并且由于我的计算机是低端字节序的,因此这意味着将使用最后4个字节(21至24字节)。因此,要发送负数,我们将必须使用DWORD的2的补码,并将字节写为little endian。
好的。
更新4:
我还一直在研究Windows如何尝试生成用于存储DPI缩放值的Monitor ID。
对于任何监视器,用户选择的DPI缩放值存储在:
好的。
1 2 | HKEY_CURRENT_USER\\Control Panel\\Desktop\\PerMonitorSettings\\ *MonitorID* |
对于连接到我的计算机的Dell显示器,显示器ID为
我能够弄清监视器ID的结构。我用4种不同的显示器验证了我的理论。
好的。
对于Dell显示器(dpi缩放比例存储在
DELA0BC9DRXV68A0LWL_21_07E0_33^7457214C9330EFC0300669BF736A5297
好的。
好的。
从本质上讲,从EDID到构造监视器ID所需的数据如下。
好的。
好的。
好的。
好的。
好的。
好的。
好的。
请注意,仅需要EDID的前128个字节。
好的。
关于备用的注意事项
如果不存在构造监视器ID所需的某些数据,则OS使用回退。上面的列表中列出了我在Windows 10计算机上观察到的构造监视器ID所需的每个数据的备用。我手动编辑了DELL显示器的EDID(链接1,链接2,链接3-注意-链接3中建议的方法可能会损坏您的系统,只有在确定的情况下才可以继续操作;强烈建议使用链接1)以删除上面给出的所有6项,显示器ID为我构建的操作系统(不带MD5后缀)为
好的。
更新4:
DPI缩放是源的属性,而不是目标的属性,因此
好的。
上面建议的注册表方法在大多数情况下应该可以正常工作,但是有两个缺点。 一个是,它不能使我们与系统设置应用程序保持一致(就设置生效时间而言)。 其次,在极少数情况下(无法再复制),我已经看到OS生成的Monitor ID字符串略有不同-它具有更多组件,如上图所示。
好的。
我已经成功创建了一个API,我们可以使用该API以与系统设置应用程序完全相同的方式获取/设置DPI缩放比例。 将发布一个新的答案,因为这更多地是我寻找解决方案所采用的方法。
好的。
好。
我能够对系统设置应用程序进行反向工程,并提供了一个API。
它的代码在我的github存储库https://github.com/lihas/windows-DPI-scaling-sample中。
我已经在此问题的上一个回答(https://stackoverflow.com/a/57397039/981766)中跳过了很多关于该术语的解释。
API的摘要
获取显示器的DPI信息
用adapterID和sourceID调用
1 | DpiHelper::DPIScalingInfo DpiHelper::GetDPIScalingInfo(LUID adapterID, UINT32 sourceID) |
设置显示器的DPI
调用SetDPIScaling()并设置adapterID,sourceID和DPI缩放比例。例如。如果要将源的DPI缩放比例设置为175%,请在最后一个参数中传递175。
1 | bool DpiHelper::SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet) |
存储库中的DpiHelper.h拥有这两种方法的详尽文档。
另请阅读DpiHelper.h中的文档以及该存储库的自述文件。
我已经在公共领域发布了回购中的所有代码,因此可以使用任何方式使用它。
示例应用
我还创建了一个MFC应用程序,该应用程序使用此帮助程序库来获取/设置DPI缩放比例。
这将帮助您了解如何使用DpiHelper类。
这是它的外观。
关于Windows上DPI缩放的注意事项
我使用WinDbg Preview(MS Store)和Ghidra进行逆向工程。有时候我会因为缺乏IDA Pro许可证而放弃,有人建议我Ghidra。从那以后我一直是粉丝。
非常感谢Ghidra !!!
在搜索完全相同的同时,我找到了您的问题并找到了可能的解决方案。
我发现此%值的每个监视器切换位于
对于我的24英寸1080p屏幕,
不幸的是,仅更改注册表值是不够的。但是您可以在写入注册表后通过更改分辨率来刷新dpi。
下面的代码设置dpi,然后将分辨率切换为低和高以触发dpi更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | using System; using System.Windows.Forms; using System.Runtime.InteropServices; using Microsoft.Win32; namespace SetDpiScale { public partial class Form1 : Form { public enum DMDO { DEFAULT = 0, D90 = 1, D180 = 2, D270 = 3 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] struct DEVMODE { public const int DM_PELSWIDTH = 0x80000; public const int DM_PELSHEIGHT = 0x100000; private const int CCHDEVICENAME = 32; private const int CCHFORMNAME = 32; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] 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 DMDO dmDisplayOrientation; public int dmDisplayFixedOutput; public short dmColor; public short dmDuplex; public short dmYResolution; public short dmTTOption; public short dmCollate; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)] public string dmFormName; public short dmLogPixels; public int 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; } [DllImport("user32.dll", CharSet = CharSet.Auto)] static extern int ChangeDisplaySettings([In] ref DEVMODE lpDevMode, int dwFlags); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { ChangeDPI(0); // 100% } private void button2_Click(object sender, EventArgs e) { ChangeDPI(1); // 125% } void ChangeDPI(int dpi) { RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel", true); key = key.OpenSubKey("Desktop", true); key = key.OpenSubKey("PerMonitorSettings", true); key = key.OpenSubKey("*monitor id where to change the dpi*", true); // my second monitor here key.SetValue("DpiValue", dpi); SetResolution(1920, 1080); // this sets the resolution on primary screen SetResolution(2560, 1440); // returning back to my primary screens default resolution } private static void SetResolution(int w, int h) { long RetVal = 0; DEVMODE dm = new DEVMODE(); dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE)); dm.dmPelsWidth = w; dm.dmPelsHeight = h; dm.dmFields = DEVMODE.DM_PELSWIDTH | DEVMODE.DM_PELSHEIGHT; RetVal = ChangeDisplaySettings(ref dm, 0); } } } |
如果要更改系统范围的DPI缩放比例(系统DPI缩放比例-如果设置了多个显示器或只有一个显示器,则是主显示器的缩放比例),而不是每个显示器的DPI缩放比例,
可以使用SystemParametersInfo()。
该API具有一个未记录的参数,可以实现以下目的:
从Microsoft文档:
1 2 | SPI_SETLOGICALDPIOVERRIDE Do not use. 0x009F |
用法:
1 | SystemParametersInfo(SPI_SETLOGICALDPIOVERRIDE, relativeIndex, (LPVOID)0, 1); |
要弄清楚上面的
简而言之,
所有步骤的大小可能不相同。
1 | 100,125,150,175,200,225,250,300,350, 400, 450, 500 |
直到250%,步长以25%为单位增加,之后以50%为单位增加。
因此,必须首先通过
1 | SystemParametersInfo(SPI_GETLOGICALDPIOVERRIDE, 0, (LPVOID)&dpi, 1); |
上面的dpi变量中返回的值也应以特殊方式理解。
该值将为负,其大小将指示上面列表中DPI缩放百分比的指数。
因此,如果此API返回-1,则建议的DPI缩放比例值将为125%。
样例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #include <iostream> #include <Windows.h> using namespace std; static const UINT32 DpiVals[] = { 100,125,150,175,200,225,250,300,350, 400, 450, 500 }; /*Get default DPI scaling percentage. The OS recommented value. */ int GetRecommendedDPIScaling() { int dpi = 0; auto retval = SystemParametersInfo(SPI_GETLOGICALDPIOVERRIDE, 0, (LPVOID)&dpi, 1); if (retval != 0) { int currDPI = DpiVals[dpi * -1]; return currDPI; } return -1; } void SetDpiScaling(int percentScaleToSet) { int recommendedDpiScale = GetRecommendedDPIScaling(); if (recommendedDpiScale > 0) { int index = 0, recIndex = 0, setIndex = 0 ; for (const auto& scale : DpiVals) { if (recommendedDpiScale == scale) { recIndex = index; } if (percentScaleToSet == scale) { setIndex = index; } index++; } int relativeIndex = setIndex - recIndex; SystemParametersInfo(SPI_SETLOGICALDPIOVERRIDE, relativeIndex, (LPVOID)0, 1); } } int main() { for (;;) { int n = 0, dpiToSet = 0; cout << R"( 1. Show Recommended DPI 2. Set DPI Anything else to exit )"; cin >> n; switch (n) { case 1: cout <<"recommened scaling:" << GetRecommendedDPIScaling() <<"%" << endl; break; case 2: cout <<"enter scaling to set in percentage" << endl; cin >> dpiToSet; SetDpiScaling(dpiToSet); break; default: exit(0); break; } } return 0; } |
源代码:https://github.com/lihas/windows-DPI-scaling-sample。
这是一个示例运行。
优点和缺点
关于我以前的方法(https://stackoverflow.com/a/58066736/981766,https://stackoverflow.com/a/57397039/981766)
优点
缺点
参考文献
除了Sahil Singh的回答。
MonitorID可以作为
这是我基于@Sahil Singh的代码:
DLL项目包装了C ++ API:
stdafx.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #pragma once #include"targetver.h" #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files #include <windows.h> // reference additional headers your program requires here #ifdef __cplusplus extern"C" { #endif extern __declspec(dllexport) void PrintDpiInfo(); extern __declspec(dllexport) void SetDPIScaling(INT32 adapterIDHigh, UINT32 adapterIDlow, UINT32 sourceID, UINT32 dpiPercentToSet); extern __declspec(dllexport) void RestoreDPIScaling(); #ifdef __cplusplus } #endif |
DpiHelper.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | // DpiHelper.cpp : Defines the exported functions for the DLL application. // #include"stdafx.h" #include"DpiHelper.h" #include <memory> #include <cassert> #include <string> #include <map> bool DpiHelper::GetPathsAndModes(std::vector<DISPLAYCONFIG_PATH_INFO>& pathsV, std::vector<DISPLAYCONFIG_MODE_INFO>& modesV, int flags) { UINT32 numPaths = 0, numModes = 0; auto status = GetDisplayConfigBufferSizes(flags, &numPaths, &numModes); if (ERROR_SUCCESS != status) { return false; } std::unique_ptr<DISPLAYCONFIG_PATH_INFO[]> paths(new DISPLAYCONFIG_PATH_INFO[numPaths]); std::unique_ptr<DISPLAYCONFIG_MODE_INFO[]> modes(new DISPLAYCONFIG_MODE_INFO[numModes]); status = QueryDisplayConfig(flags, &numPaths, paths.get(), &numModes, modes.get(), nullptr); if (ERROR_SUCCESS != status) { return false; } for (unsigned int i = 0; i < numPaths; i++) { pathsV.push_back(paths[i]); } for (unsigned int i = 0; i < numModes; i++) { modesV.push_back(modes[i]); } return true; } DpiHelper::DpiHelper() { } DpiHelper::~DpiHelper() { } DpiHelper::DPIScalingInfo DpiHelper::GetDPIScalingInfo(LUID adapterID, UINT32 sourceID) { DPIScalingInfo dpiInfo = {}; DpiHelper::DISPLAYCONFIG_SOURCE_DPI_SCALE_GET requestPacket = {}; requestPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DpiHelper::DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE; requestPacket.header.size = sizeof(requestPacket); assert(0x20 == sizeof(requestPacket));//if this fails => OS has changed somthing, and our reverse enginnering knowledge about the API is outdated requestPacket.header.adapterId = adapterID; requestPacket.header.id = sourceID; auto res = ::DisplayConfigGetDeviceInfo(&requestPacket.header); if (ERROR_SUCCESS == res) {//success if (requestPacket.curScaleRel < requestPacket.minScaleRel) { requestPacket.curScaleRel = requestPacket.minScaleRel; } else if (requestPacket.curScaleRel > requestPacket.maxScaleRel) { requestPacket.curScaleRel = requestPacket.maxScaleRel; } std::int32_t minAbs = abs((int)requestPacket.minScaleRel); if (DpiHelper::CountOf(DpiVals) >= (size_t)(minAbs + requestPacket.maxScaleRel + 1)) {//all ok dpiInfo.current = DpiVals[minAbs + requestPacket.curScaleRel]; dpiInfo.recommended = DpiVals[minAbs]; dpiInfo.maximum = DpiVals[minAbs + requestPacket.maxScaleRel]; dpiInfo.bInitDone = true; } else { //Error! Probably DpiVals array is outdated return dpiInfo; } } else { //DisplayConfigGetDeviceInfo() failed return dpiInfo; } return dpiInfo; } std::wstring GetTargetName(LUID adapterLUID, UINT32 sourceId) { std::vector<DISPLAYCONFIG_PATH_INFO> pathsV; std::vector<DISPLAYCONFIG_MODE_INFO> modesV; int flags = QDC_ONLY_ACTIVE_PATHS; if (false == DpiHelper::GetPathsAndModes(pathsV, modesV, flags)) { wprintf(L"DpiHelper::GetPathsAndModes() failed\ \ "); } for (const auto& path : pathsV) { if (adapterLUID.LowPart == path.targetInfo.adapterId.LowPart && adapterLUID.HighPart == path.targetInfo.adapterId.HighPart && sourceId == path.sourceInfo.id) { DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName; deviceName.header.size = sizeof(deviceName); deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; deviceName.header.adapterId = adapterLUID; deviceName.header.id = path.targetInfo.id; if (ERROR_SUCCESS != DisplayConfigGetDeviceInfo(&deviceName.header)) { wprintf(L"DisplayConfigGetDeviceInfo() failed\ \ "); } else { std::wstring nameString = deviceName.monitorFriendlyDeviceName; if (DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL == deviceName.outputTechnology) { nameString += L"(internal display)"; } return nameString; } } } return L"N/A"; } void printOne(LUID adapterLUID, UINT32 sourceID) { wprintf(L"GPU=%ld.%u,Desktop_Index_In_GPU=%d,Monitor=%ls\ \ " ,adapterLUID.HighPart , adapterLUID.LowPart , sourceID , GetTargetName(adapterLUID, sourceID).data()); } bool DpiHelper::SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet) { wprintf(L"setting dpi scale to %d:", dpiPercentToSet); printOne(adapterID, sourceID); DPIScalingInfo dPIScalingInfo = GetDPIScalingInfo(adapterID, sourceID); if (dpiPercentToSet == dPIScalingInfo.current) { return true; } if (dpiPercentToSet < dPIScalingInfo.mininum) { dpiPercentToSet = dPIScalingInfo.mininum; } else if (dpiPercentToSet > dPIScalingInfo.maximum) { dpiPercentToSet = dPIScalingInfo.maximum; } int idx1 = -1, idx2 = -1; int i = 0; for (const auto& val : DpiVals) { if (val == dpiPercentToSet) { idx1 = i; } if (val == dPIScalingInfo.recommended) { idx2 = i; } i++; } if ((idx1 == -1) || (idx2 == -1)) { //Error cannot find dpi value return false; } int dpiRelativeVal = idx1 - idx2; DpiHelper::DISPLAYCONFIG_SOURCE_DPI_SCALE_SET setPacket = {}; setPacket.header.adapterId = adapterID; setPacket.header.id = sourceID; setPacket.header.size = sizeof(setPacket); assert(0x18 == sizeof(setPacket));//if this fails => OS has changed somthing, and our reverse enginnering knowledge about the API is outdated setPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DpiHelper::DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE; setPacket.scaleRel = (UINT32)dpiRelativeVal; auto res = ::DisplayConfigSetDeviceInfo(&setPacket.header); if (ERROR_SUCCESS == res) { return true; } else { return false; } return true; } #define MAX_ID 10 LUID GpuId[MAX_ID]; UINT32 DesktopIndexInGpu[MAX_ID]; UINT32 oldDPI[MAX_ID]; void PrintDpiInfo() { std::vector<DISPLAYCONFIG_PATH_INFO> pathsV; std::vector<DISPLAYCONFIG_MODE_INFO> modesV; int flags = QDC_ONLY_ACTIVE_PATHS; if (false == DpiHelper::GetPathsAndModes(pathsV, modesV, flags)) { wprintf(L"DpiHelper::GetPathsAndModes() failed"); } int i = 0; for (const auto& path : pathsV) { //get display name auto adapterLUID = path.targetInfo.adapterId; auto sourceID = path.sourceInfo.id; std::wstring monitor_name = GetTargetName(adapterLUID, sourceID); printOne(adapterLUID, sourceID); DpiHelper::DPIScalingInfo dpiInfo = DpiHelper::GetDPIScalingInfo(adapterLUID, sourceID); GpuId[i] = adapterLUID; DesktopIndexInGpu[i] = sourceID; oldDPI[i] = dpiInfo.current; wprintf(L"Available DPI:\ \ "); int curdpi = 0; for (const auto& dpi : DpiVals) { if ((dpi >= dpiInfo.mininum) && (dpi <= dpiInfo.maximum)) wprintf(L" %d\ \ ",dpi); } wprintf(L" current DPI: %d\ \ ",dpiInfo.current); i++; if (i >= MAX_ID) { wprintf(L"To many desktops\ \ "); break; } } } void SetDPIScaling(INT32 adapterIDHigh, UINT32 adapterIDlow, UINT32 sourceID, UINT32 dpiPercentToSet) { LUID adapterId; adapterId.HighPart = adapterIDHigh; adapterId.LowPart = adapterIDlow; DpiHelper::SetDPIScaling(adapterId, sourceID, dpiPercentToSet); } void RestoreDPIScaling() { wprintf(L"Now restore DPI settings...\ \ "); for (int i = 0;i < MAX_ID;i++) { if (GpuId[i].LowPart == 0 && GpuId[i].HighPart==0) break; DpiHelper::SetDPIScaling(GpuId[i], DesktopIndexInGpu[i], oldDPI[i]); } } |
DpiHelper.h与引用的答案相同。在Visual Studio中创建一个C ++ Dll项目,并在上面的代码中添加/放入并在下面的C#应用??程序中使用dll。
一个C#控制台应用程序,它根据命令行参数设置DPI并在按任意键时将其还原:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace DispSetEx { class Program { [DllImport("DpiHelper.dll")] static public extern void PrintDpiInfo(); [DllImport("DpiHelper.dll")] static public extern int SetDPIScaling(Int32 adapterIDHigh, UInt32 adapterIDlow, UInt32 sourceID, UInt32 dpiPercentToSet); [DllImport("DpiHelper.dll")] static public extern void RestoreDPIScaling(); static void Main(string[] args) { if ((args.Length % 3) != 0) { Console.WriteLine("wrong parameters"); return; } //print the DPI info, you need to set the command line parameters //according to this PrintDpiInfo(); //commandline parameters should be of groups of three //each groups's tree paramters control a desktop's setting //in each group: //GPUIdhigh.GPUIdlow DesktopIndexInGPU DPIScalingValue //for example: // 0.1234 0 100 //set the DPI scaling to 100 for desktop 0 on GPU 0.1234 // 0.4567 0 125 //set the DPI scaling to 125 for desktop 0 on GPU 0.5678 // 0.4567 1 150 //set the DPI scaling to 150 for desktop 1 on GPU 0.5678 //in most cases GPUIdhigh is 0. //you can use the monitor name to identify which is which easily //you need to set the command line parameters according to the result of PrintDpiInfo //e.g. you should only set the DPI scaling to a value that is supported by //that desktop. for (int i = 0; i < args.Length / 3; i++) { string[] sa = args[i * 3].Split(new char[] { '.' }); Int32 adapterHigh = Int32.Parse(sa[0]); UInt32 adapterLow = UInt32.Parse(sa[1]); UInt32 source = UInt32.Parse(args[i * 3 + 1]); UInt32 dpiscale = UInt32.Parse(args[i * 3 + 2]); SetDPIScaling(adapterHigh, adapterLow, source,dpiscale); } Console.WriteLine("Press any key to resotre the settings..."); Console.ReadKey(); RestoreDPIScaling(); } } } |