├── .gitignore ├── DpiHelper.cpp ├── DpiHelper.h ├── LICENSE.md ├── README.md ├── SetDpi.cpp ├── SetDpi.sln ├── SetDpi.vcxproj └── SetDpi.vcxproj.filters /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Nuget personal access tokens and Credentials 210 | nuget.config 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | *.appxbundle 227 | *.appxupload 228 | 229 | # Visual Studio cache files 230 | # files ending in .cache can be ignored 231 | *.[Cc]ache 232 | # but keep track of directories ending in .cache 233 | !?*.[Cc]ache/ 234 | 235 | # Others 236 | ClientBin/ 237 | ~$* 238 | *~ 239 | *.dbmdl 240 | *.dbproj.schemaview 241 | *.jfm 242 | *.pfx 243 | *.publishsettings 244 | orleans.codegen.cs 245 | 246 | # Including strong name files can present a security risk 247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 248 | #*.snk 249 | 250 | # Since there are multiple workflows, uncomment next line to ignore bower_components 251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 252 | #bower_components/ 253 | 254 | # RIA/Silverlight projects 255 | Generated_Code/ 256 | 257 | # Backup & report files from converting an old project file 258 | # to a newer Visual Studio version. Backup files are not needed, 259 | # because we have git ;-) 260 | _UpgradeReport_Files/ 261 | Backup*/ 262 | UpgradeLog*.XML 263 | UpgradeLog*.htm 264 | ServiceFabricBackup/ 265 | *.rptproj.bak 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | *.rptproj.rsuser 277 | *- [Bb]ackup.rdl 278 | *- [Bb]ackup ([0-9]).rdl 279 | *- [Bb]ackup ([0-9][0-9]).rdl 280 | 281 | # Microsoft Fakes 282 | FakesAssemblies/ 283 | 284 | # GhostDoc plugin setting file 285 | *.GhostDoc.xml 286 | 287 | # Node.js Tools for Visual Studio 288 | .ntvs_analysis.dat 289 | node_modules/ 290 | 291 | # Visual Studio 6 build log 292 | *.plg 293 | 294 | # Visual Studio 6 workspace options file 295 | *.opt 296 | 297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 298 | *.vbw 299 | 300 | # Visual Studio LightSwitch build output 301 | **/*.HTMLClient/GeneratedArtifacts 302 | **/*.DesktopClient/GeneratedArtifacts 303 | **/*.DesktopClient/ModelManifest.xml 304 | **/*.Server/GeneratedArtifacts 305 | **/*.Server/ModelManifest.xml 306 | _Pvt_Extensions 307 | 308 | # Paket dependency manager 309 | .paket/paket.exe 310 | paket-files/ 311 | 312 | # FAKE - F# Make 313 | .fake/ 314 | 315 | # CodeRush personal settings 316 | .cr/personal 317 | 318 | # Python Tools for Visual Studio (PTVS) 319 | __pycache__/ 320 | *.pyc 321 | 322 | # Cake - Uncomment if you are using it 323 | # tools/** 324 | # !tools/packages.config 325 | 326 | # Tabs Studio 327 | *.tss 328 | 329 | # Telerik's JustMock configuration file 330 | *.jmconfig 331 | 332 | # BizTalk build output 333 | *.btp.cs 334 | *.btm.cs 335 | *.odx.cs 336 | *.xsd.cs 337 | 338 | # OpenCover UI analysis results 339 | OpenCover/ 340 | 341 | # Azure Stream Analytics local run output 342 | ASALocalRun/ 343 | 344 | # MSBuild Binary and Structured Log 345 | *.binlog 346 | 347 | # NVidia Nsight GPU debugger configuration file 348 | *.nvuser 349 | 350 | # MFractors (Xamarin productivity tool) working folder 351 | .mfractor/ 352 | 353 | # Local History for Visual Studio 354 | .localhistory/ 355 | 356 | # BeatPulse healthcheck temp database 357 | healthchecksdb 358 | 359 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 360 | MigrationBackup/ 361 | 362 | # Ionide (cross platform F# VS Code tools) working folder 363 | .ionide/ 364 | 365 | # Fody - auto-generated XML schema 366 | FodyWeavers.xsd 367 | 368 | # VS Code files for those working on multiple tools 369 | .vscode/* 370 | !.vscode/settings.json 371 | !.vscode/tasks.json 372 | !.vscode/launch.json 373 | !.vscode/extensions.json 374 | *.code-workspace 375 | 376 | # Local History for Visual Studio Code 377 | .history/ 378 | 379 | # Windows Installer files from build outputs 380 | *.cab 381 | *.msi 382 | *.msix 383 | *.msm 384 | *.msp 385 | 386 | # JetBrains Rider 387 | .idea/ 388 | *.sln.iml -------------------------------------------------------------------------------- /DpiHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "DpiHelper.h" 2 | #include 3 | #include 4 | 5 | 6 | 7 | bool DpiHelper::GetPathsAndModes(std::vector& pathsV, std::vector& modesV, int flags) 8 | { 9 | UINT32 numPaths = 0, numModes = 0; 10 | auto status = GetDisplayConfigBufferSizes(flags, &numPaths, &numModes); 11 | if (ERROR_SUCCESS != status) 12 | { 13 | return false; 14 | } 15 | 16 | std::unique_ptr paths(new DISPLAYCONFIG_PATH_INFO[numPaths]); 17 | std::unique_ptr modes(new DISPLAYCONFIG_MODE_INFO[numModes]); 18 | status = QueryDisplayConfig(flags, &numPaths, paths.get(), &numModes, modes.get(), nullptr); 19 | if (ERROR_SUCCESS != status) 20 | { 21 | return false; 22 | } 23 | 24 | for (unsigned int i = 0; i < numPaths; i++) 25 | { 26 | pathsV.push_back(paths[i]); 27 | } 28 | 29 | for (unsigned int i = 0; i < numModes; i++) 30 | { 31 | modesV.push_back(modes[i]); 32 | } 33 | 34 | return true; 35 | } 36 | 37 | 38 | DpiHelper::DpiHelper() 39 | { 40 | } 41 | 42 | 43 | DpiHelper::~DpiHelper() 44 | { 45 | } 46 | 47 | 48 | DpiHelper::DPIScalingInfo DpiHelper::GetDPIScalingInfo(LUID adapterID, UINT32 sourceID) 49 | { 50 | DPIScalingInfo dpiInfo = {}; 51 | 52 | DpiHelper::DISPLAYCONFIG_SOURCE_DPI_SCALE_GET requestPacket = {}; 53 | requestPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DpiHelper::DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE; 54 | requestPacket.header.size = sizeof(requestPacket); 55 | assert(0x20 == sizeof(requestPacket));//if this fails => OS has changed somthing, and our reverse enginnering knowledge about the API is outdated 56 | requestPacket.header.adapterId = adapterID; 57 | requestPacket.header.id = sourceID; 58 | 59 | auto res = ::DisplayConfigGetDeviceInfo(&requestPacket.header); 60 | if (ERROR_SUCCESS == res) 61 | {//success 62 | if (requestPacket.curScaleRel < requestPacket.minScaleRel) 63 | { 64 | requestPacket.curScaleRel = requestPacket.minScaleRel; 65 | } 66 | else if (requestPacket.curScaleRel > requestPacket.maxScaleRel) 67 | { 68 | requestPacket.curScaleRel = requestPacket.maxScaleRel; 69 | } 70 | 71 | std::int32_t minAbs = abs((int)requestPacket.minScaleRel); 72 | if (DpiHelper::CountOf(DpiVals) >= (size_t)(minAbs + requestPacket.maxScaleRel + 1)) 73 | {//all ok 74 | dpiInfo.current = DpiVals[minAbs + requestPacket.curScaleRel]; 75 | dpiInfo.recommended = DpiVals[minAbs]; 76 | dpiInfo.maximum = DpiVals[minAbs + requestPacket.maxScaleRel]; 77 | dpiInfo.bInitDone = true; 78 | } 79 | else 80 | { 81 | //Error! Probably DpiVals array is outdated 82 | return dpiInfo; 83 | } 84 | } 85 | else 86 | { 87 | //DisplayConfigGetDeviceInfo() failed 88 | return dpiInfo; 89 | } 90 | 91 | return dpiInfo; 92 | } 93 | 94 | 95 | bool DpiHelper::SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet) 96 | { 97 | DPIScalingInfo dPIScalingInfo = GetDPIScalingInfo(adapterID, sourceID); 98 | 99 | if (dpiPercentToSet == dPIScalingInfo.current) 100 | { 101 | return true; 102 | } 103 | 104 | if (dpiPercentToSet < dPIScalingInfo.mininum) 105 | { 106 | dpiPercentToSet = dPIScalingInfo.mininum; 107 | } 108 | else if (dpiPercentToSet > dPIScalingInfo.maximum) 109 | { 110 | dpiPercentToSet = dPIScalingInfo.maximum; 111 | } 112 | 113 | int idx1 = -1, idx2 = -1; 114 | 115 | int i = 0; 116 | for (const auto& val : DpiVals) 117 | { 118 | if (val == dpiPercentToSet) 119 | { 120 | idx1 = i; 121 | } 122 | 123 | if (val == dPIScalingInfo.recommended) 124 | { 125 | idx2 = i; 126 | } 127 | i++; 128 | } 129 | 130 | if ((idx1 == -1) || (idx2 == -1)) 131 | { 132 | //Error cannot find dpi value 133 | return false; 134 | } 135 | 136 | int dpiRelativeVal = idx1 - idx2; 137 | 138 | DpiHelper::DISPLAYCONFIG_SOURCE_DPI_SCALE_SET setPacket = {}; 139 | setPacket.header.adapterId = adapterID; 140 | setPacket.header.id = sourceID; 141 | setPacket.header.size = sizeof(setPacket); 142 | assert(0x18 == sizeof(setPacket));//if this fails => OS has changed somthing, and our reverse enginnering knowledge about the API is outdated 143 | setPacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DpiHelper::DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE; 144 | setPacket.scaleRel = (UINT32)dpiRelativeVal; 145 | 146 | auto res = ::DisplayConfigSetDeviceInfo(&setPacket.header); 147 | if (ERROR_SUCCESS == res) 148 | { 149 | return true; 150 | } 151 | else 152 | { 153 | return false; 154 | } 155 | return true; 156 | } 157 | -------------------------------------------------------------------------------- /DpiHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | 6 | /* 7 | * OS reports DPI scaling values in relative terms, and not absolute terms. 8 | * eg. if current DPI value is 250%, and recommended value is 200%, then 9 | * OS will give us integer 2 for DPI scaling value (starting from recommended 10 | * DPI scaling move 2 steps to the right in this list). 11 | * values observed (and extrapolated) from system settings app (immersive control panel). 12 | */ 13 | static const UINT32 DpiVals[] = { 100,125,150,175,200,225,250,300,350, 400, 450, 500 }; 14 | 15 | class DpiHelper 16 | { 17 | public: 18 | template 19 | static size_t CountOf(const T (&arr)[sz]) 20 | { 21 | UNREFERENCED_PARAMETER(arr); 22 | return sz; 23 | } 24 | 25 | /* 26 | * @brief : Use QueryDisplayConfig() to get paths, and modes. 27 | * @param[out] pathsV : reference to a vector which will contain paths 28 | * @param[out] modesV : reference to a vector which will contain modes 29 | * @param[in] flags : determines the kind of paths to retrieve (only active paths by default) 30 | * return : false in case of failure, else true 31 | */ 32 | static bool GetPathsAndModes(std::vector& pathsV, std::vector& modesV, int flags = QDC_ONLY_ACTIVE_PATHS); 33 | 34 | //out own enum, similar to DISPLAYCONFIG_DEVICE_INFO_TYPE enum in wingdi.h 35 | enum class DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM : int 36 | { 37 | DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE = -3, //returns min, max, suggested, and currently applied DPI scaling values. 38 | DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE = -4, //set current dpi scaling value for a display 39 | }; 40 | 41 | /* 42 | * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET 43 | * @brief used to fetch min, max, suggested, and currently applied DPI scaling values. 44 | * All values are relative to the recommended DPI scaling value 45 | * Note that DPI scaling is a property of the source, and not of target. 46 | */ 47 | struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET 48 | { 49 | DISPLAYCONFIG_DEVICE_INFO_HEADER header; 50 | /* 51 | * @brief min value of DPI scaling is always 100, minScaleRel gives no. of steps down from recommended scaling 52 | * eg. if minScaleRel is -3 => 100 is 3 steps down from recommended scaling => recommended scaling is 175% 53 | */ 54 | std::int32_t minScaleRel; 55 | 56 | /* 57 | * @brief currently applied DPI scaling value wrt the recommended value. eg. if recommended value is 175%, 58 | * => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1, then current scale is 150% 59 | */ 60 | std::int32_t curScaleRel; 61 | 62 | /* 63 | * @brief maximum supported DPI scaling wrt recommended value 64 | */ 65 | std::int32_t maxScaleRel; 66 | }; 67 | 68 | /* 69 | * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET 70 | * @brief set DPI scaling value of a source 71 | * Note that DPI scaling is a property of the source, and not of target. 72 | */ 73 | struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET 74 | { 75 | DISPLAYCONFIG_DEVICE_INFO_HEADER header; 76 | /* 77 | * @brief The value we want to set. The value should be relative to the recommended DPI scaling value of source. 78 | * eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source 79 | */ 80 | int32_t scaleRel; 81 | }; 82 | 83 | /* 84 | * struct DPIScalingInfo 85 | * @brief DPI info about a source 86 | * mininum : minumum DPI scaling in terms of percentage supported by source. Will always be 100%. 87 | * maximum : maximum DPI scaling in terms of percentage supported by source. eg. 100%, 150%, etc. 88 | * current : currently applied DPI scaling value 89 | * recommended : DPI scaling value reommended by OS. OS takes resolution, physical size, and expected viewing distance 90 | * into account while calculating this, however exact formula is not known, hence must be retrieved from OS 91 | * For a system in which user has not explicitly changed DPI, current should eqaul recommended. 92 | * bInitDone : If true, it means that the members of the struct contain values, as fetched from OS, and not the default 93 | * ones given while object creation. 94 | */ 95 | struct DPIScalingInfo 96 | { 97 | UINT32 mininum = 100; 98 | UINT32 maximum = 100; 99 | UINT32 current = 100; 100 | UINT32 recommended = 100; 101 | bool bInitDone = false; 102 | }; 103 | 104 | DpiHelper(); 105 | ~DpiHelper(); 106 | static DpiHelper::DPIScalingInfo GetDPIScalingInfo(LUID adapterID, UINT32 sourceID); 107 | static bool SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet); 108 | }; 109 | 110 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SetDPI 2 | 3 | This is a sample and simple C++ console project to change windows Scale and Layout aka DPI scaling from command line parameters. It can work on single and multi-monitor setup. 4 | 5 | ## How to use 6 | 7 | It takes up to 2 parameters, first being the scale in percent. The second argument is the monitor index, which you get from windows display settings, if you click identify it shows you the index. 8 | 9 | `SetDPI.exe [DPI scale value] [monitor index]` 10 | 11 | If you have only one monitor or want only main monitor to change you can omit the monitor index. 12 | 13 | To get the current scale of a monitor, replace the scale argument with `get` to print the current scaling. For automation with `value` only the scale is printed. 14 | 15 | ## Examples 16 | 17 | - To set 2nd monitor to a scale value of 250 18 | - `SetDPI.exe 250 2` 19 | - To set main monitor to a scale value of 125 20 | - `SetDPI.exe 125` 21 | - To get the scale of the 2nd monitor 22 | - `SetDPI.exe get 2` prints `Current Resolution: 250` 23 | - `SetDPI.exe value 2` prints `250` 24 | 25 | ## Release 26 | 27 | Get the release build and a sample AutoHotkey script with hotkey example from here https://github.com/imniko/SetDPI/releases/ 28 | 29 | ## Credits 30 | 31 | The whole credits go to @lihas of this project, https://github.com/lihas/windows-DPI-scaling-sample it was not possible without his findings. 32 | -------------------------------------------------------------------------------- /SetDpi.cpp: -------------------------------------------------------------------------------- 1 | #include "DpiHelper.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | /*Get default DPI scaling percentage. 11 | The OS recommented value. 12 | */ 13 | int GetRecommendedDPIScaling() 14 | { 15 | int dpi = 0; 16 | 17 | auto retval = SystemParametersInfo(SPI_GETLOGICALDPIOVERRIDE, 0, (LPVOID)&dpi, 1); 18 | if (retval != 0) 19 | { 20 | int currDPI = DpiVals[dpi * -1]; 21 | return currDPI; 22 | } 23 | 24 | return -1; 25 | } 26 | 27 | //to store display info along with corresponding list item 28 | struct DisplayData { 29 | LUID m_adapterId; 30 | int m_targetID; 31 | int m_sourceID; 32 | 33 | DisplayData() 34 | { 35 | m_adapterId = {}; 36 | m_targetID = m_sourceID = -1; 37 | } 38 | }; 39 | 40 | std::vector GetDisplayData() 41 | { 42 | std::vector displayDataCache; 43 | std::vector pathsV; 44 | std::vector modesV; 45 | int flags = QDC_ONLY_ACTIVE_PATHS; 46 | if (false == DpiHelper::GetPathsAndModes(pathsV, modesV, flags)) 47 | { 48 | cout << "DpiHelper::GetPathsAndModes() failed\n"; 49 | } 50 | displayDataCache.resize(pathsV.size()); 51 | int idx = 0; 52 | for (const auto &path : pathsV) 53 | { 54 | //get display name 55 | auto adapterLUID = path.targetInfo.adapterId; 56 | auto targetID = path.targetInfo.id; 57 | auto sourceID = path.sourceInfo.id; 58 | 59 | DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName; 60 | deviceName.header.size = sizeof(deviceName); 61 | deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; 62 | deviceName.header.adapterId = adapterLUID; 63 | deviceName.header.id = targetID; 64 | if (ERROR_SUCCESS != DisplayConfigGetDeviceInfo(&deviceName.header)) 65 | { 66 | cout << "DisplayConfigGetDeviceInfo() failed"; 67 | } 68 | else 69 | { 70 | std::wstring nameString = std::to_wstring(idx) + std::wstring(L". ") + deviceName.monitorFriendlyDeviceName; 71 | if (DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL == deviceName.outputTechnology) 72 | { 73 | nameString += L"(internal display)"; 74 | } 75 | DisplayData dd = {}; 76 | dd.m_adapterId = adapterLUID; 77 | dd.m_sourceID = sourceID; 78 | dd.m_targetID = targetID; 79 | 80 | displayDataCache[idx] = dd; 81 | } 82 | idx++; 83 | } 84 | return displayDataCache; 85 | } 86 | 87 | bool DPIFound(int val) 88 | { 89 | bool found = false; 90 | for (int i = 0; i < 12; i++) 91 | { 92 | if (val == DpiVals[i]) 93 | { 94 | found = true; 95 | break; 96 | } 97 | } 98 | return found; 99 | } 100 | int main(int argc, char *argv[]) 101 | { 102 | auto dpiToSet = 0; 103 | auto displayIndex = 1; 104 | 105 | enum 106 | { 107 | RESOLUTION_SET, 108 | RESOLUTION_GET, 109 | RESOLUTION_VALUE, 110 | } resolutionMode = RESOLUTION_SET; 111 | 112 | if (argc <= 1) 113 | { 114 | cout << "1. argument: Resolution in percent, use \"get\" to print the current value instead and \"value\" to print without formatting\n"; 115 | cout << "2. argument: Monitor index, leave empty to use primary monitor\n"; 116 | return 0; 117 | } 118 | 119 | if (argc >= 2) 120 | { 121 | if (strcmp(argv[1], "get") == 0) 122 | { 123 | resolutionMode = RESOLUTION_GET; 124 | } 125 | else if (strcmp(argv[1], "value") == 0) 126 | { 127 | resolutionMode = RESOLUTION_VALUE; 128 | } 129 | else 130 | { 131 | dpiToSet = atoi(argv[1]); 132 | } 133 | } 134 | 135 | if (argc >= 3) 136 | { 137 | displayIndex = atoi(argv[2]); 138 | } 139 | 140 | auto displayDataCache = GetDisplayData(); 141 | if (displayIndex < 1 || displayDataCache.size() < displayIndex) 142 | { 143 | if (DPIFound(displayIndex) && 1 <= dpiToSet && dpiToSet <= displayDataCache.size()) 144 | { 145 | cout << "Please provide the scale as first and the index as second argument, program will continue for legacy purposes\n"; 146 | auto t = dpiToSet; 147 | dpiToSet = displayIndex; 148 | displayIndex = t; 149 | } 150 | else 151 | { 152 | cout << "Invalid Monitor ID: " << displayIndex; 153 | return 0; 154 | } 155 | } 156 | 157 | displayIndex -= 1; // change from 1...X to 0...(X-1) 158 | 159 | auto currentResolution = DpiHelper::GetDPIScalingInfo(displayDataCache[displayIndex].m_adapterId, displayDataCache[displayIndex].m_sourceID); 160 | if (resolutionMode == RESOLUTION_GET) 161 | { 162 | cout << "Current Resolution: " << currentResolution.current; 163 | return 0; 164 | } 165 | if (resolutionMode == RESOLUTION_VALUE) 166 | { 167 | cout << currentResolution.current; 168 | return 0; 169 | } 170 | if (!DPIFound(dpiToSet)) 171 | { 172 | cout << "Invalid DPI scale value: " << dpiToSet; 173 | return 0; 174 | } 175 | auto success = DpiHelper::SetDPIScaling(displayDataCache[displayIndex].m_adapterId, displayDataCache[displayIndex].m_sourceID, dpiToSet); 176 | if (success == false) 177 | { 178 | cout << "DpiHelper::SetDPIScaling() failed"; 179 | return 0; 180 | } 181 | else 182 | { 183 | if (displayIndex == 0) 184 | { 185 | HKEY hKey; 186 | LPCWSTR sKeyPath; 187 | DWORD value = static_cast(int(dpiToSet * 0.96)); 188 | int iResult; 189 | sKeyPath = L"Control Panel\\Desktop\\WindowMetrics\\"; 190 | iResult = RegOpenKeyEx(HKEY_CURRENT_USER, sKeyPath, NULL, KEY_ALL_ACCESS, &hKey); 191 | iResult = RegSetValueEx(hKey, L"AppliedDPI", NULL, REG_DWORD, (const BYTE*)&value, sizeof(value)); 192 | RegCloseKey(hKey); 193 | return 0; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /SetDpi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30413.136 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SetDpi", "SetDpi.vcxproj", "{C672CC56-BEB6-4F08-B461-9A7EA6790E15}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Debug|x64.ActiveCfg = Debug|x64 17 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Debug|x64.Build.0 = Debug|x64 18 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Debug|x86.ActiveCfg = Debug|Win32 19 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Debug|x86.Build.0 = Debug|Win32 20 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Release|x64.ActiveCfg = Release|x64 21 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Release|x64.Build.0 = Release|x64 22 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Release|x86.ActiveCfg = Release|Win32 23 | {C672CC56-BEB6-4F08-B461-9A7EA6790E15}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {D1B9CAD4-CEE9-4DC4-ADBF-A007B63DCF9A} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /SetDpi.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {c672cc56-beb6-4f08-b461-9a7ea6790e15} 25 | SetDpi 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /SetDpi.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | --------------------------------------------------------------------------------