├── .gitignore ├── Console_Using_SystemParametersInfo_Api.PNG ├── Console_Using_SystemParametersInfo_Api ├── Console_Using_SystemParametersInfo_Api.vcxproj ├── Console_Using_SystemParametersInfo_Api.vcxproj.filters └── TrySetDpi.cpp ├── DPIHelper ├── DPIHelper.vcxproj ├── DPIHelper.vcxproj.filters ├── DpiHelper.cpp └── DpiHelper.h ├── DPIScalingMFCApp.sln ├── DPIScalingMFCApp ├── DPIScalingMFCApp.cpp ├── DPIScalingMFCApp.h ├── DPIScalingMFCApp.rc ├── DPIScalingMFCApp.vcxproj ├── DPIScalingMFCApp.vcxproj.filters ├── DPIScalingMFCAppDlg.cpp ├── DPIScalingMFCAppDlg.h ├── ReadMe.txt ├── md5helper.h ├── res │ ├── DPIScalingMFCApp.ico │ └── DPIScalingMFCApp.rc2 ├── resource.h ├── stdafx.cpp ├── stdafx.h └── targetver.h ├── DPIScalingMFCAppScreenHelp.png ├── DPIScalingMFCAppScreenHelp.svg ├── DPIScalingMFCAppScreenshot.JPG ├── README.md ├── UNLICENSE.txt ├── monitorUniqueID.jpg ├── monitorUniqueID_MFC.png └── x64 └── Debug ├── DPIHelper.lib └── DPIScalingMFCApp.exe /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/visualstudio 3 | # Edit at https://www.gitignore.io/?templates=visualstudio 4 | 5 | ### VisualStudio ### 6 | ## Ignore Visual Studio temporary files, build results, and 7 | ## files generated by popular Visual Studio add-ons. 8 | ## 9 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 10 | 11 | # User-specific files 12 | *.rsuser 13 | *.suo 14 | *.user 15 | *.userosscache 16 | *.sln.docstates 17 | 18 | # User-specific files (MonoDevelop/Xamarin Studio) 19 | *.userprefs 20 | 21 | # Mono auto generated files 22 | mono_crash.* 23 | 24 | # Build results 25 | [Dd]ebug/ 26 | [Dd]ebugPublic/ 27 | [Rr]elease/ 28 | [Rr]eleases/ 29 | x64/ 30 | x86/ 31 | [Aa][Rr][Mm]/ 32 | [Aa][Rr][Mm]64/ 33 | bld/ 34 | [Bb]in/ 35 | [Oo]bj/ 36 | [Ll]og/ 37 | 38 | # Visual Studio 2015/2017 cache/options directory 39 | .vs/ 40 | # Uncomment if you have tasks that create the project's static files in wwwroot 41 | #wwwroot/ 42 | 43 | # Visual Studio 2017 auto generated files 44 | Generated\ Files/ 45 | 46 | # MSTest test Results 47 | [Tt]est[Rr]esult*/ 48 | [Bb]uild[Ll]og.* 49 | 50 | # NUnit 51 | *.VisualState.xml 52 | TestResult.xml 53 | nunit-*.xml 54 | 55 | # Build Results of an ATL Project 56 | [Dd]ebugPS/ 57 | [Rr]eleasePS/ 58 | dlldata.c 59 | 60 | # Benchmark Results 61 | BenchmarkDotNet.Artifacts/ 62 | 63 | # .NET Core 64 | project.lock.json 65 | project.fragment.lock.json 66 | artifacts/ 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 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # JustCode is a .NET coding add-in 135 | .JustCode 136 | 137 | # TeamCity is a build add-in 138 | _TeamCity* 139 | 140 | # DotCover is a Code Coverage Tool 141 | *.dotCover 142 | 143 | # AxoCover is a Code Coverage Tool 144 | .axoCover/* 145 | !.axoCover/settings.json 146 | 147 | # Visual Studio code coverage results 148 | *.coverage 149 | *.coveragexml 150 | 151 | # NCrunch 152 | _NCrunch_* 153 | .*crunch*.local.xml 154 | nCrunchTemp_* 155 | 156 | # MightyMoose 157 | *.mm.* 158 | AutoTest.Net/ 159 | 160 | # Web workbench (sass) 161 | .sass-cache/ 162 | 163 | # Installshield output folder 164 | [Ee]xpress/ 165 | 166 | # DocProject is a documentation generator add-in 167 | DocProject/buildhelp/ 168 | DocProject/Help/*.HxT 169 | DocProject/Help/*.HxC 170 | DocProject/Help/*.hhc 171 | DocProject/Help/*.hhk 172 | DocProject/Help/*.hhp 173 | DocProject/Help/Html2 174 | DocProject/Help/html 175 | 176 | # Click-Once directory 177 | publish/ 178 | 179 | # Publish Web Output 180 | *.[Pp]ublish.xml 181 | *.azurePubxml 182 | # Note: Comment the next line if you want to checkin your web deploy settings, 183 | # but database connection strings (with potential passwords) will be unencrypted 184 | *.pubxml 185 | *.publishproj 186 | 187 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 188 | # checkin your Azure Web App publish settings, but sensitive information contained 189 | # in these scripts will be unencrypted 190 | PublishScripts/ 191 | 192 | # NuGet Packages 193 | *.nupkg 194 | # NuGet Symbol Packages 195 | *.snupkg 196 | # The packages folder can be ignored because of Package Restore 197 | **/[Pp]ackages/* 198 | # except build/, which is used as an MSBuild target. 199 | !**/[Pp]ackages/build/ 200 | # Uncomment if necessary however generally it will be regenerated when needed 201 | #!**/[Pp]ackages/repositories.config 202 | # NuGet v3's project.json files produces more ignorable files 203 | *.nuget.props 204 | *.nuget.targets 205 | 206 | # Microsoft Azure Build Output 207 | csx/ 208 | *.build.csdef 209 | 210 | # Microsoft Azure Emulator 211 | ecf/ 212 | rcf/ 213 | 214 | # Windows Store app package directories and files 215 | AppPackages/ 216 | BundleArtifacts/ 217 | Package.StoreAssociation.xml 218 | _pkginfo.txt 219 | *.appx 220 | *.appxbundle 221 | *.appxupload 222 | 223 | # Visual Studio cache files 224 | # files ending in .cache can be ignored 225 | *.[Cc]ache 226 | # but keep track of directories ending in .cache 227 | !?*.[Cc]ache/ 228 | 229 | # Others 230 | ClientBin/ 231 | ~$* 232 | *~ 233 | *.dbmdl 234 | *.dbproj.schemaview 235 | *.jfm 236 | *.pfx 237 | *.publishsettings 238 | orleans.codegen.cs 239 | 240 | # Including strong name files can present a security risk 241 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 242 | #*.snk 243 | 244 | # Since there are multiple workflows, uncomment next line to ignore bower_components 245 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 246 | #bower_components/ 247 | 248 | # RIA/Silverlight projects 249 | Generated_Code/ 250 | 251 | # Backup & report files from converting an old project file 252 | # to a newer Visual Studio version. Backup files are not needed, 253 | # because we have git ;-) 254 | _UpgradeReport_Files/ 255 | Backup*/ 256 | UpgradeLog*.XML 257 | UpgradeLog*.htm 258 | ServiceFabricBackup/ 259 | *.rptproj.bak 260 | 261 | # SQL Server files 262 | *.mdf 263 | *.ldf 264 | *.ndf 265 | 266 | # Business Intelligence projects 267 | *.rdl.data 268 | *.bim.layout 269 | *.bim_*.settings 270 | *.rptproj.rsuser 271 | *- [Bb]ackup.rdl 272 | *- [Bb]ackup ([0-9]).rdl 273 | *- [Bb]ackup ([0-9][0-9]).rdl 274 | 275 | # Microsoft Fakes 276 | FakesAssemblies/ 277 | 278 | # GhostDoc plugin setting file 279 | *.GhostDoc.xml 280 | 281 | # Node.js Tools for Visual Studio 282 | .ntvs_analysis.dat 283 | node_modules/ 284 | 285 | # Visual Studio 6 build log 286 | *.plg 287 | 288 | # Visual Studio 6 workspace options file 289 | *.opt 290 | 291 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 292 | *.vbw 293 | 294 | # Visual Studio LightSwitch build output 295 | **/*.HTMLClient/GeneratedArtifacts 296 | **/*.DesktopClient/GeneratedArtifacts 297 | **/*.DesktopClient/ModelManifest.xml 298 | **/*.Server/GeneratedArtifacts 299 | **/*.Server/ModelManifest.xml 300 | _Pvt_Extensions 301 | 302 | # Paket dependency manager 303 | .paket/paket.exe 304 | paket-files/ 305 | 306 | # FAKE - F# Make 307 | .fake/ 308 | 309 | # CodeRush personal settings 310 | .cr/personal 311 | 312 | # Python Tools for Visual Studio (PTVS) 313 | __pycache__/ 314 | *.pyc 315 | 316 | # Cake - Uncomment if you are using it 317 | # tools/** 318 | # !tools/packages.config 319 | 320 | # Tabs Studio 321 | *.tss 322 | 323 | # Telerik's JustMock configuration file 324 | *.jmconfig 325 | 326 | # BizTalk build output 327 | *.btp.cs 328 | *.btm.cs 329 | *.odx.cs 330 | *.xsd.cs 331 | 332 | # OpenCover UI analysis results 333 | OpenCover/ 334 | 335 | # Azure Stream Analytics local run output 336 | ASALocalRun/ 337 | 338 | # MSBuild Binary and Structured Log 339 | *.binlog 340 | 341 | # NVidia Nsight GPU debugger configuration file 342 | *.nvuser 343 | 344 | # MFractors (Xamarin productivity tool) working folder 345 | .mfractor/ 346 | 347 | # Local History for Visual Studio 348 | .localhistory/ 349 | 350 | # BeatPulse healthcheck temp database 351 | healthchecksdb 352 | 353 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 354 | MigrationBackup/ 355 | 356 | # End of https://www.gitignore.io/api/visualstudio -------------------------------------------------------------------------------- /Console_Using_SystemParametersInfo_Api.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/Console_Using_SystemParametersInfo_Api.PNG -------------------------------------------------------------------------------- /Console_Using_SystemParametersInfo_Api/Console_Using_SystemParametersInfo_Api.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 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9} 24 | Win32Proj 25 | TrySetDpi 26 | 8.1 27 | 2_Console_Using_SystemParametersInfo_Api 28 | 29 | 30 | 31 | Application 32 | true 33 | v142 34 | Unicode 35 | 36 | 37 | Application 38 | false 39 | v142 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | true 46 | v140 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | Level3 91 | true 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | MultiThreadedDebug 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | true 107 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | MultiThreadedDebug 110 | 111 | 112 | Console 113 | true 114 | 115 | 116 | 117 | 118 | 119 | 120 | Level3 121 | true 122 | true 123 | true 124 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 125 | true 126 | 127 | 128 | Console 129 | true 130 | true 131 | true 132 | 133 | 134 | 135 | 136 | 137 | 138 | Level3 139 | true 140 | true 141 | true 142 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 143 | true 144 | 145 | 146 | Console 147 | true 148 | true 149 | true 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /Console_Using_SystemParametersInfo_Api/Console_Using_SystemParametersInfo_Api.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;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 | -------------------------------------------------------------------------------- /Console_Using_SystemParametersInfo_Api/TrySetDpi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | 7 | static const UINT32 DpiVals[] = { 100,125,150,175,200,225,250,300,350, 400, 450, 500 }; 8 | 9 | /*Get default DPI scaling percentage. 10 | The OS recommented value. 11 | */ 12 | int GetRecommendedDPIScaling() 13 | { 14 | int dpi = 0; 15 | auto retval = SystemParametersInfo(SPI_GETLOGICALDPIOVERRIDE, 0, (LPVOID)&dpi, 1); 16 | 17 | if (retval != 0) 18 | { 19 | int currDPI = DpiVals[dpi * -1]; 20 | return currDPI; 21 | } 22 | 23 | return -1; 24 | } 25 | 26 | void SetDpiScaling(int percentScaleToSet) 27 | { 28 | int recommendedDpiScale = GetRecommendedDPIScaling(); 29 | 30 | if (recommendedDpiScale > 0) 31 | { 32 | int index = 0, recIndex = 0, setIndex = 0 ; 33 | for (const auto& scale : DpiVals) 34 | { 35 | if (recommendedDpiScale == scale) 36 | { 37 | recIndex = index; 38 | } 39 | if (percentScaleToSet == scale) 40 | { 41 | setIndex = index; 42 | } 43 | index++; 44 | } 45 | 46 | int relativeIndex = setIndex - recIndex; 47 | SystemParametersInfo(SPI_SETLOGICALDPIOVERRIDE, relativeIndex, (LPVOID)0, 1); 48 | } 49 | } 50 | 51 | int main() 52 | { 53 | for (;;) 54 | { 55 | int n = 0, dpiToSet = 0; 56 | cout << R"( 57 | 1. Show Recommended DPI 58 | 2. Set DPI 59 | Anything else to exit 60 | )"; 61 | cin >> n; 62 | switch (n) 63 | { 64 | case 1: 65 | cout << "recommened scaling: " << GetRecommendedDPIScaling() << "%" << endl; 66 | break; 67 | case 2: 68 | cout << "enter scaling to set in percentage" << endl; 69 | cin >> dpiToSet; 70 | SetDpiScaling(dpiToSet); 71 | break; 72 | default: 73 | exit(0); 74 | break; 75 | } 76 | } 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /DPIHelper/DPIHelper.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 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82} 23 | Win32Proj 24 | DPIHelper 25 | 8.1 26 | 0_DPIHelper 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v140 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v140 39 | true 40 | Unicode 41 | 42 | 43 | StaticLibrary 44 | true 45 | v140 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v140 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 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_WINDOWS;_USRDLL;DPIHELPER_EXPORTS;%(PreprocessorDefinitions) 92 | 93 | 94 | Windows 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | _DEBUG;_WINDOWS;_USRDLL;DPIHELPER_EXPORTS;%(PreprocessorDefinitions) 105 | MultiThreadedDebug 106 | 107 | 108 | Windows 109 | true 110 | 111 | 112 | 113 | 114 | Level3 115 | 116 | 117 | MaxSpeed 118 | true 119 | true 120 | WIN32;NDEBUG;_WINDOWS;_USRDLL;DPIHELPER_EXPORTS;%(PreprocessorDefinitions) 121 | 122 | 123 | Windows 124 | true 125 | true 126 | true 127 | 128 | 129 | 130 | 131 | Level3 132 | 133 | 134 | MaxSpeed 135 | true 136 | true 137 | NDEBUG;_WINDOWS;_USRDLL;DPIHELPER_EXPORTS;%(PreprocessorDefinitions) 138 | 139 | 140 | Windows 141 | true 142 | true 143 | true 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /DPIHelper/DPIHelper.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;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 | Header Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /DPIHelper/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 | 158 | 159 | std::wstring DpiHelper::GetDisplayUniqueName(LUID adapterID, UINT32 targetID) 160 | { 161 | _DISPLAYCONFIG_GET_MONITOR_INTERNAL_INFO mi = {}; 162 | mi.header.adapterId = adapterID; 163 | mi.header.id = targetID; 164 | mi.header.size = sizeof(mi); 165 | mi.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DpiHelper::DISPLAYCONFIG_DEVICE_INFO_TYPE_CUSTOM::DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_UNIQUE_NAME; 166 | 167 | LONG res = ::DisplayConfigGetDeviceInfo(&mi.header); 168 | if (ERROR_SUCCESS == res) 169 | { 170 | return std::wstring(mi.monitorUniqueName); 171 | } 172 | 173 | return std::wstring(); 174 | } -------------------------------------------------------------------------------- /DPIHelper/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 | DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_BRIGHTNESS_INFO = -7, //Get monitor brightness info 40 | DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_INTERNAL_INFO = DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_BRIGHTNESS_INFO, //alias for DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_BRIGHTNESS_INFO since it returns values other than brightness 41 | DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_UNIQUE_NAME = DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_BRIGHTNESS_INFO, //Another Alias since we are using the parameter mainly for getting the display unique name 42 | }; 43 | 44 | /* 45 | * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET 46 | * @brief used to fetch min, max, suggested, and currently applied DPI scaling values. 47 | * All values are relative to the recommended DPI scaling value 48 | * Note that DPI scaling is a property of the source, and not of target. 49 | */ 50 | struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET 51 | { 52 | DISPLAYCONFIG_DEVICE_INFO_HEADER header; 53 | /* 54 | * @brief min value of DPI scaling is always 100, minScaleRel gives no. of steps down from recommended scaling 55 | * eg. if minScaleRel is -3 => 100 is 3 steps down from recommended scaling => recommended scaling is 175% 56 | */ 57 | std::int32_t minScaleRel; 58 | 59 | /* 60 | * @brief currently applied DPI scaling value wrt the recommended value. eg. if recommended value is 175%, 61 | * => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1, then current scale is 150% 62 | */ 63 | std::int32_t curScaleRel; 64 | 65 | /* 66 | * @brief maximum supported DPI scaling wrt recommended value 67 | */ 68 | std::int32_t maxScaleRel; 69 | }; 70 | 71 | /* 72 | * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET 73 | * @brief set DPI scaling value of a source 74 | * Note that DPI scaling is a property of the source, and not of target. 75 | */ 76 | struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET 77 | { 78 | DISPLAYCONFIG_DEVICE_INFO_HEADER header; 79 | /* 80 | * @brief The value we want to set. The value should be relative to the recommended DPI scaling value of source. 81 | * eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source 82 | */ 83 | int32_t scaleRel; 84 | }; 85 | 86 | /* 87 | * struct DPIScalingInfo 88 | * @brief DPI info about a source 89 | * mininum : minumum DPI scaling in terms of percentage supported by source. Will always be 100%. 90 | * maximum : maximum DPI scaling in terms of percentage supported by source. eg. 100%, 150%, etc. 91 | * current : currently applied DPI scaling value 92 | * recommended : DPI scaling value reommended by OS. OS takes resolution, physical size, and expected viewing distance 93 | * into account while calculating this, however exact formula is not known, hence must be retrieved from OS 94 | * For a system in which user has not explicitly changed DPI, current should eqaul recommended. 95 | * bInitDone : If true, it means that the members of the struct contain values, as fetched from OS, and not the default 96 | * ones given while object creation. 97 | */ 98 | struct DPIScalingInfo 99 | { 100 | UINT32 mininum = 100; 101 | UINT32 maximum = 100; 102 | UINT32 current = 100; 103 | UINT32 recommended = 100; 104 | bool bInitDone = false; 105 | }; 106 | 107 | DpiHelper(); 108 | ~DpiHelper(); 109 | 110 | /* 111 | * When DPI scaling value is stored by OS in registry location Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings 112 | * a unique string is generated for displays. 113 | * eg. Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings\LEN41410_00_07E3_24^A8DD7E34BCF1555F032E26E990ABC597 114 | * For every display which has its DPI scaling value set to a non custom value an entry like this would be there. 115 | * If we want to set the registry value for a display manually when no such registry entry exists for it, then getting the unique 116 | * string isn't a straighforward process. The algorithm is analyzed partially here - https://stackoverflow.com/a/57397039/981766 117 | * but it doesn't cover all scenarios. 118 | * I recently stumbled across another undocumented Windows API which seems to give us this string for a display - DisplayConfigGetDeviceInfo(-7 or 0xfffffff9), 119 | * ie. type parameter set to -7. 120 | */ 121 | static std::wstring GetDisplayUniqueName(LUID adapterID, UINT32 sourceID); 122 | static DpiHelper::DPIScalingInfo GetDPIScalingInfo(LUID adapterID, UINT32 sourceID); 123 | static bool SetDPIScaling(LUID adapterID, UINT32 sourceID, UINT32 dpiPercentToSet); 124 | 125 | /*Data structures for DisplayConfigGetDeviceInfo(-7)*/ 126 | 127 | struct _DISPLAYCONFIG_BRIGHTNESS_CAPS_u_312_s_0 { 128 | unsigned int bLegacySupported : 1; 129 | unsigned int bNitsSupported : 1; 130 | unsigned int bCalibrated : 1; 131 | unsigned int bSmoothBrightnessSupported : 1; 132 | unsigned int bAdaptiveBrightnessSupported : 1; 133 | 134 | }; 135 | 136 | struct _DISPLAYCONFIG_BRIGHTNESS_NIT_RANGE { 137 | unsigned int MinMillinits; 138 | unsigned int MaxMillinits; 139 | unsigned int StepSizeMillinits; 140 | }; 141 | 142 | union _DISPLAYCONFIG_BRIGHTNESS_CAPS_u_312 { 143 | struct _DISPLAYCONFIG_BRIGHTNESS_CAPS_u_312_s_0 _s_0; 144 | unsigned int value; 145 | }; 146 | 147 | struct _DISPLAYCONFIG_BRIGHTNESS_CAPS { 148 | unsigned char LegacyLevels[101]; 149 | unsigned int LegacyLevelCount; 150 | struct _DISPLAYCONFIG_BRIGHTNESS_NIT_RANGE NitRanges[16]; 151 | unsigned int NormalRangeCount; 152 | unsigned int TotalRangeCount; 153 | unsigned int PreferredMaximumBrightness; 154 | union _DISPLAYCONFIG_BRIGHTNESS_CAPS_u_312 field6_0x138; 155 | }; 156 | 157 | /*From windows.graphics.dll*/ 158 | typedef enum _DISPLAYCONFIG_HDR_CERTIFICATIONS { 159 | DISPLAYHDR_OVERRIDE_OFF = -2147483648, 160 | DISPLAYHDR_NONE = 0, 161 | DISPLAYHDR_GENERIC = 1, 162 | DISPLAYHDR_10_400 = 2, 163 | DISPLAYHDR_10_600 = 4, 164 | DISPLAYHDR_10_1000 = 8, 165 | DISPLAYHDR_10_1400 = 16, 166 | DISPLAYHDR_10_400_TRUEBLACK = 32, 167 | DISPLAYHDR_10_500_TRUEBLACK = 64, 168 | DISPLAYHDR_11_400 = 128, 169 | DISPLAYHDR_11_500 = 256, 170 | DISPLAYHDR_11_600 = 512, 171 | DISPLAYHDR_11_1000 = 1024, 172 | DISPLAYHDR_11_1400 = 2048, 173 | DISPLAYHDR_11_2000 = 4096, 174 | DISPLAYHDR_11_400_TRUEBLACK = 8192, 175 | DISPLAYHDR_11_500_TRUEBLACK = 16384, 176 | DISPLAYHDR_11_600_TRUEBLACK = 32768, 177 | DISPLAYHDR_11_1000_TRUEBLACK = 65536, 178 | DOLBYVISION_GENERIC = 131072, 179 | DOLBYVISION_LOWLATENCY = 262144, 180 | NVIDIA_HDR = 524288, 181 | NVIDIA_GSYNC_ULTIMATE = 1048576, 182 | AMD_FREESYNC_WITH_HDR = 2097152, 183 | AMD_FREESYNC_PREMIUM_PRO = 4194304, 184 | DISPLAYHDR_OEM_OVERRIDE_ON = 536870912, 185 | DISPLAYHDR_OVERRIDE_ON = 1073741824 186 | } _DISPLAYCONFIG_HDR_CERTIFICATIONS; 187 | 188 | 189 | struct _DISPLAYCONFIG_GET_MONITOR_INTERNAL_INFO { 190 | struct DISPLAYCONFIG_DEVICE_INFO_HEADER header; 191 | wchar_t monitorUniqueName[260]; 192 | unsigned int RedPrimary[2]; 193 | unsigned int GreenPrimary[2]; 194 | unsigned int BluePrimary[2]; 195 | unsigned int WhitePoint[2]; 196 | unsigned long MinLuminance; 197 | unsigned long MaxLuminance; 198 | unsigned long MaxFullFrameLuminance; 199 | std::int32_t ColorspaceSupport;//4 bytes 200 | std::int32_t Flags;//4 bytes 201 | /* 202 | * FLAGS may internally have the following 203 | * LuminanceValuesRaw 204 | */ 205 | struct _DISPLAYCONFIG_BRIGHTNESS_CAPS BrightnessCaps; 206 | unsigned int UsageSubClass; 207 | unsigned int DisplayTech; 208 | unsigned int NativeWidth; 209 | unsigned int NativeHeight; 210 | unsigned int PhysicalWidthInMm; 211 | unsigned int PhysicalHeightInMm; 212 | enum DISPLAYCONFIG_ROTATION DockedOrientation; 213 | enum _DISPLAYCONFIG_HDR_CERTIFICATIONS DisplayHdrCertifications; 214 | }; 215 | 216 | }; 217 | 218 | -------------------------------------------------------------------------------- /DPIScalingMFCApp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33829.357 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "1_DPIScalingMFCApp", "DPIScalingMFCApp\DPIScalingMFCApp.vcxproj", "{428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "0_DPIHelper", "DPIHelper\DPIHelper.vcxproj", "{4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "2_Console_Using_SystemParametersInfo_Api", "Console_Using_SystemParametersInfo_Api\Console_Using_SystemParametersInfo_Api.vcxproj", "{A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6DEF74E2-8944-4C24-B189-5AD64AD01E8B}" 13 | ProjectSection(SolutionItems) = preProject 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|x64 = Debug|x64 20 | Debug|x86 = Debug|x86 21 | Release|x64 = Release|x64 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Debug|x64.ActiveCfg = Debug|x64 26 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Debug|x64.Build.0 = Debug|x64 27 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Debug|x86.ActiveCfg = Debug|Win32 28 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Debug|x86.Build.0 = Debug|Win32 29 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Release|x64.ActiveCfg = Release|x64 30 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Release|x64.Build.0 = Release|x64 31 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Release|x86.ActiveCfg = Release|Win32 32 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2}.Release|x86.Build.0 = Release|Win32 33 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Debug|x64.ActiveCfg = Debug|x64 34 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Debug|x64.Build.0 = Debug|x64 35 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Debug|x86.ActiveCfg = Debug|Win32 36 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Debug|x86.Build.0 = Debug|Win32 37 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Release|x64.ActiveCfg = Release|x64 38 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Release|x64.Build.0 = Release|x64 39 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Release|x86.ActiveCfg = Release|Win32 40 | {4D02B80A-9A6B-4E8C-B13B-A7223AD0BE82}.Release|x86.Build.0 = Release|Win32 41 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Debug|x64.ActiveCfg = Debug|x64 42 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Debug|x64.Build.0 = Debug|x64 43 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Debug|x86.ActiveCfg = Debug|Win32 44 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Debug|x86.Build.0 = Debug|Win32 45 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Release|x64.ActiveCfg = Release|x64 46 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Release|x64.Build.0 = Release|x64 47 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Release|x86.ActiveCfg = Release|Win32 48 | {A3E2E66B-FF58-4AE2-ADB8-2BB1170047F9}.Release|x86.Build.0 = Release|Win32 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | GlobalSection(ExtensibilityGlobals) = postSolution 54 | SolutionGuid = {A226F7F4-3605-42DA-AC06-BB4C955B7844} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/DPIScalingMFCApp.cpp: -------------------------------------------------------------------------------- 1 | 2 | // DPIScalingMFCApp.cpp : Defines the class behaviors for the application. 3 | // 4 | 5 | #include "stdafx.h" 6 | #include "DPIScalingMFCApp.h" 7 | #include "DPIScalingMFCAppDlg.h" 8 | 9 | #ifdef _DEBUG 10 | #define new DEBUG_NEW 11 | #endif 12 | 13 | 14 | // CDPIScalingMFCAppApp 15 | 16 | BEGIN_MESSAGE_MAP(CDPIScalingMFCAppApp, CWinApp) 17 | ON_COMMAND(ID_HELP, &CWinApp::OnHelp) 18 | END_MESSAGE_MAP() 19 | 20 | 21 | // CDPIScalingMFCAppApp construction 22 | 23 | CDPIScalingMFCAppApp::CDPIScalingMFCAppApp() 24 | { 25 | // TODO: add construction code here, 26 | // Place all significant initialization in InitInstance 27 | } 28 | 29 | 30 | // The one and only CDPIScalingMFCAppApp object 31 | 32 | CDPIScalingMFCAppApp theApp; 33 | 34 | 35 | // CDPIScalingMFCAppApp initialization 36 | 37 | BOOL CDPIScalingMFCAppApp::InitInstance() 38 | { 39 | // InitCommonControlsEx() is required on Windows XP if an application 40 | // manifest specifies use of ComCtl32.dll version 6 or later to enable 41 | // visual styles. Otherwise, any window creation will fail. 42 | INITCOMMONCONTROLSEX InitCtrls; 43 | InitCtrls.dwSize = sizeof(InitCtrls); 44 | // Set this to include all the common control classes you want to use 45 | // in your application. 46 | InitCtrls.dwICC = ICC_WIN95_CLASSES; 47 | InitCommonControlsEx(&InitCtrls); 48 | 49 | CWinApp::InitInstance(); 50 | 51 | 52 | // Create the shell manager, in case the dialog contains 53 | // any shell tree view or shell list view controls. 54 | CShellManager *pShellManager = new CShellManager; 55 | 56 | // Activate "Windows Native" visual manager for enabling themes in MFC controls 57 | CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); 58 | 59 | // Standard initialization 60 | // If you are not using these features and wish to reduce the size 61 | // of your final executable, you should remove from the following 62 | // the specific initialization routines you do not need 63 | // Change the registry key under which our settings are stored 64 | // TODO: You should modify this string to be something appropriate 65 | // such as the name of your company or organization 66 | SetRegistryKey(_T("Local AppWizard-Generated Applications")); 67 | 68 | CDPIScalingMFCAppDlg dlg; 69 | m_pMainWnd = &dlg; 70 | INT_PTR nResponse = dlg.DoModal(); 71 | if (nResponse == IDOK) 72 | { 73 | // TODO: Place code here to handle when the dialog is 74 | // dismissed with OK 75 | } 76 | else if (nResponse == IDCANCEL) 77 | { 78 | // TODO: Place code here to handle when the dialog is 79 | // dismissed with Cancel 80 | } 81 | else if (nResponse == -1) 82 | { 83 | TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n"); 84 | TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n"); 85 | } 86 | 87 | // Delete the shell manager created above. 88 | if (pShellManager != NULL) 89 | { 90 | delete pShellManager; 91 | } 92 | 93 | #ifndef _AFXDLL 94 | ControlBarCleanUp(); 95 | #endif 96 | 97 | // Since the dialog has been closed, return FALSE so that we exit the 98 | // application, rather than start the application's message pump. 99 | return FALSE; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/DPIScalingMFCApp.h: -------------------------------------------------------------------------------- 1 | 2 | // DPIScalingMFCApp.h : main header file for the PROJECT_NAME application 3 | // 4 | 5 | #pragma once 6 | 7 | #ifndef __AFXWIN_H__ 8 | #error "include 'stdafx.h' before including this file for PCH" 9 | #endif 10 | 11 | #include "resource.h" // main symbols 12 | 13 | 14 | // CDPIScalingMFCAppApp: 15 | // See DPIScalingMFCApp.cpp for the implementation of this class 16 | // 17 | 18 | class CDPIScalingMFCAppApp : public CWinApp 19 | { 20 | public: 21 | CDPIScalingMFCAppApp(); 22 | 23 | // Overrides 24 | public: 25 | virtual BOOL InitInstance(); 26 | 27 | // Implementation 28 | 29 | DECLARE_MESSAGE_MAP() 30 | }; 31 | 32 | extern CDPIScalingMFCAppApp theApp; -------------------------------------------------------------------------------- /DPIScalingMFCApp/DPIScalingMFCApp.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/DPIScalingMFCApp/DPIScalingMFCApp.rc -------------------------------------------------------------------------------- /DPIScalingMFCApp/DPIScalingMFCApp.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 | {428A9DE4-00B4-4C4A-87B6-24C534BBAEE2} 23 | DPIScalingMFCApp 24 | 8.1 25 | MFCProj 26 | 1_DPIScalingMFCApp 27 | 28 | 29 | 30 | Application 31 | true 32 | v140 33 | Unicode 34 | Static 35 | 36 | 37 | Application 38 | false 39 | v140 40 | true 41 | Unicode 42 | Static 43 | 44 | 45 | Application 46 | true 47 | v140 48 | Unicode 49 | Static 50 | 51 | 52 | Application 53 | false 54 | v140 55 | true 56 | Unicode 57 | Static 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | false 88 | 89 | 90 | 91 | Use 92 | Level3 93 | Disabled 94 | WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) 95 | 96 | 97 | Windows 98 | 99 | 100 | false 101 | true 102 | _DEBUG;%(PreprocessorDefinitions) 103 | 104 | 105 | 0x0409 106 | _DEBUG;%(PreprocessorDefinitions) 107 | $(IntDir);%(AdditionalIncludeDirectories) 108 | 109 | 110 | 111 | 112 | Use 113 | Level3 114 | Disabled 115 | _WINDOWS;_DEBUG;%(PreprocessorDefinitions) 116 | 117 | 118 | Windows 119 | 120 | 121 | false 122 | true 123 | _DEBUG;%(PreprocessorDefinitions) 124 | 125 | 126 | 0x0409 127 | _DEBUG;%(PreprocessorDefinitions) 128 | $(IntDir);%(AdditionalIncludeDirectories) 129 | 130 | 131 | false 132 | 133 | 134 | 135 | 136 | Level3 137 | Use 138 | MaxSpeed 139 | true 140 | true 141 | WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) 142 | 143 | 144 | Windows 145 | true 146 | true 147 | 148 | 149 | false 150 | true 151 | NDEBUG;%(PreprocessorDefinitions) 152 | 153 | 154 | 0x0409 155 | NDEBUG;%(PreprocessorDefinitions) 156 | $(IntDir);%(AdditionalIncludeDirectories) 157 | 158 | 159 | 160 | 161 | Level3 162 | Use 163 | MaxSpeed 164 | true 165 | true 166 | _WINDOWS;NDEBUG;%(PreprocessorDefinitions) 167 | 168 | 169 | Windows 170 | true 171 | true 172 | 173 | 174 | false 175 | true 176 | NDEBUG;%(PreprocessorDefinitions) 177 | 178 | 179 | 0x0409 180 | NDEBUG;%(PreprocessorDefinitions) 181 | $(IntDir);%(AdditionalIncludeDirectories) 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | Create 200 | Create 201 | Create 202 | Create 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | {4d02b80a-9a6b-4e8c-b13b-a7223ad0be82} 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/DPIScalingMFCApp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;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 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | 52 | 53 | Resource Files 54 | 55 | 56 | 57 | 58 | Resource Files 59 | 60 | 61 | 62 | 63 | Resource Files 64 | 65 | 66 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/DPIScalingMFCAppDlg.cpp: -------------------------------------------------------------------------------- 1 | 2 | // DPIScalingMFCAppDlg.cpp : implementation file 3 | // 4 | 5 | #include "stdafx.h" 6 | #include "DPIScalingMFCApp.h" 7 | #include "DPIScalingMFCAppDlg.h" 8 | #include "afxdialogex.h" 9 | #include "../DPIHelper/DpiHelper.h" 10 | #include 11 | #include "md5helper.h" 12 | #include 13 | 14 | #ifdef _DEBUG 15 | #define new DEBUG_NEW 16 | #endif 17 | 18 | #define LOGW(x) { \ 19 | CString str; \ 20 | GetDlgItemTextW(IDC_EDIT_LOG, str); \ 21 | str += x; \ 22 | str += L"\r\n"; \ 23 | SetDlgItemTextW(IDC_EDIT_LOG, str); \ 24 | } 25 | 26 | // CDPIScalingMFCAppDlg dialog 27 | 28 | 29 | 30 | CDPIScalingMFCAppDlg::CDPIScalingMFCAppDlg(CWnd* pParent /*=NULL*/) 31 | : CDialogEx(IDD_DPISCALINGMFCAPP_DIALOG, pParent) 32 | { 33 | m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 34 | } 35 | 36 | void CDPIScalingMFCAppDlg::DoDataExchange(CDataExchange* pDX) 37 | { 38 | CDialogEx::DoDataExchange(pDX); 39 | DDX_Control(pDX, IDC_COMBO1, m_displayList); 40 | DDX_Control(pDX, IDC_EDIT_CURRENT_DPI, m_currentDPI); 41 | DDX_Control(pDX, IDC_EDIT_CURRENT_DPI2, m_recommendedDPI); 42 | DDX_Control(pDX, IDC_LIST_SELECT_DPI, m_dpiList); 43 | DDX_Control(pDX, IDC_EDIT_DISPLAY_UNIQUE_NAME, m_displayUniqueName); 44 | } 45 | 46 | BEGIN_MESSAGE_MAP(CDPIScalingMFCAppDlg, CDialogEx) 47 | ON_WM_PAINT() 48 | ON_WM_QUERYDRAGICON() 49 | ON_CBN_SELCHANGE(IDC_COMBO1, &CDPIScalingMFCAppDlg::OnCbnSelchangeCombo1) 50 | ON_BN_CLICKED(IDC_BUTTON2, &CDPIScalingMFCAppDlg::OnBnClickedButton2) 51 | ON_BN_CLICKED(IDC_BUTTON1, &CDPIScalingMFCAppDlg::OnBnClickedApply) 52 | END_MESSAGE_MAP() 53 | 54 | 55 | // CDPIScalingMFCAppDlg message handlers 56 | 57 | BOOL CDPIScalingMFCAppDlg::OnInitDialog() 58 | { 59 | CDialogEx::OnInitDialog(); 60 | 61 | // Set the icon for this dialog. The framework does this automatically 62 | // when the application's main window is not a dialog 63 | SetIcon(m_hIcon, TRUE); // Set big icon 64 | SetIcon(m_hIcon, FALSE); // Set small icon 65 | 66 | // TODO: Add extra initialization here 67 | Refresh(); 68 | 69 | return TRUE; // return TRUE unless you set the focus to a control 70 | } 71 | 72 | bool CDPIScalingMFCAppDlg::FillDisplayInfo(LUID adapterID, int sourceID, int targetID) 73 | { 74 | DpiHelper::DPIScalingInfo dpiInfo = DpiHelper::GetDPIScalingInfo(adapterID, sourceID); 75 | 76 | if (targetID != 0) 77 | { 78 | std::wstring displayUniqueNameW = DpiHelper::GetDisplayUniqueName(adapterID, targetID); 79 | std::wstring displayUniqueStringW = displayUniqueNameW; 80 | 81 | if (displayUniqueNameW.length() > 0) 82 | { 83 | std::string displayUniqueNameA(displayUniqueNameW.begin(), displayUniqueNameW.end()); 84 | 85 | /* 86 | * Important to get md5 on ascii string and not wchar as the bytes would be differrent and hence resultant hashes too. 87 | * Windows seems to use ascii hashes at the moment, eg.Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings\LEN41410_00_07E3_24^A8DD7E34BCF1555F032E26E990ABC597 88 | * A8DD7E34BCF1555F032E26E990ABC597 is MD5 of "LEN41410_00_07E3_24", and not of L"LEN41410_00_07E3_24" 89 | */ 90 | std::string displayUniqueNameMD5A = GetHashText(displayUniqueNameA.c_str(), (displayUniqueNameA.length()) * sizeof(displayUniqueNameA[0])); 91 | 92 | 93 | if (displayUniqueNameMD5A.length() == 32) 94 | { 95 | std::wstring displayUniqueNameMD5W(displayUniqueNameMD5A.begin(), displayUniqueNameMD5A.end()); 96 | displayUniqueStringW += L"^" + displayUniqueNameMD5W; 97 | } 98 | } 99 | 100 | std::transform(displayUniqueStringW.begin(), displayUniqueStringW.end(), displayUniqueStringW.begin(), ::toupper); 101 | m_displayUniqueName.SetWindowTextW(displayUniqueStringW.c_str()); 102 | 103 | } 104 | else 105 | { 106 | LOGW(L"Debug! Not fetching display unique name as target ID is 0"); 107 | } 108 | 109 | m_currentDPI.SetWindowTextW(std::to_wstring(dpiInfo.current).c_str()); 110 | m_recommendedDPI.SetWindowTextW(std::to_wstring(dpiInfo.recommended).c_str()); 111 | m_dpiList.ResetContent(); 112 | 113 | int iIndex = 0; 114 | int currentIndex = -1; 115 | for (const UINT32 dpi : DpiVals) 116 | { 117 | if ((dpi >= dpiInfo.mininum) && (dpi <= dpiInfo.maximum)) 118 | { 119 | m_dpiList.AddString(std::to_wstring(dpi).c_str()); 120 | m_dpiList.SetItemData(iIndex, dpi); 121 | if (dpi == dpiInfo.current) 122 | { 123 | currentIndex = iIndex; 124 | } 125 | iIndex++; 126 | } 127 | } 128 | 129 | if (-1 == currentIndex) 130 | { 131 | LOGW(L"Error! Could not find currently set DPI"); 132 | return false; 133 | } 134 | else 135 | { 136 | m_dpiList.SetCurSel(currentIndex); 137 | } 138 | 139 | return true; 140 | } 141 | 142 | bool CDPIScalingMFCAppDlg::Refresh() 143 | { 144 | m_displayDataCache.clear(); 145 | m_displayList.ResetContent(); 146 | 147 | std::vector pathsV; 148 | std::vector modesV; 149 | int flags = QDC_ONLY_ACTIVE_PATHS; 150 | if (false == DpiHelper::GetPathsAndModes(pathsV, modesV, flags)) 151 | { 152 | LOGW(L"DpiHelper::GetPathsAndModes() failed") 153 | } 154 | else 155 | { 156 | LOGW(L"DpiHelper::GetPathsAndModes() successful") 157 | } 158 | 159 | 160 | int iIndex = 0; 161 | for (const auto& path : pathsV) 162 | { 163 | //get display name 164 | auto adapterLUID = path.targetInfo.adapterId; 165 | auto targetID = path.targetInfo.id; 166 | auto sourceID = path.sourceInfo.id; 167 | 168 | DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName; 169 | deviceName.header.size = sizeof(deviceName); 170 | deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; 171 | deviceName.header.adapterId = adapterLUID; 172 | deviceName.header.id = targetID; 173 | if (ERROR_SUCCESS != DisplayConfigGetDeviceInfo(&deviceName.header)) 174 | { 175 | LOGW(L"DisplayConfigGetDeviceInfo() failed") 176 | } 177 | else 178 | { 179 | LOGW(CString(L"display name obtained: ") + CString(deviceName.monitorFriendlyDeviceName)) 180 | std::wstring nameString = std::to_wstring(iIndex) + std::wstring(L". ") + deviceName.monitorFriendlyDeviceName; 181 | if (DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL == deviceName.outputTechnology) 182 | { 183 | nameString += L"(internal display)"; 184 | } 185 | 186 | m_displayList.AddString(nameString.c_str()); 187 | 188 | DisplayData dd = {}; 189 | dd.m_adapterId = adapterLUID; 190 | dd.m_sourceID = sourceID; 191 | dd.m_targetID = targetID; 192 | 193 | m_displayDataCache[iIndex] = dd; 194 | m_displayList.SetItemData(iIndex, iIndex); 195 | iIndex++; 196 | } 197 | } 198 | 199 | if (0 == m_displayList.GetCount()) 200 | { 201 | LOGW(L"no displays found!!") 202 | } 203 | else 204 | { 205 | m_displayList.SetCurSel(0);//select 1st entry by default 206 | FillDisplayInfo(m_displayDataCache[0].m_adapterId, m_displayDataCache[0].m_sourceID, m_displayDataCache[0].m_targetID); 207 | } 208 | return true; 209 | } 210 | 211 | // If you add a minimize button to your dialog, you will need the code below 212 | // to draw the icon. For MFC applications using the document/view model, 213 | // this is automatically done for you by the framework. 214 | 215 | void CDPIScalingMFCAppDlg::OnPaint() 216 | { 217 | if (IsIconic()) 218 | { 219 | CPaintDC dc(this); // device context for painting 220 | 221 | SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); 222 | 223 | // Center icon in client rectangle 224 | int cxIcon = GetSystemMetrics(SM_CXICON); 225 | int cyIcon = GetSystemMetrics(SM_CYICON); 226 | CRect rect; 227 | GetClientRect(&rect); 228 | int x = (rect.Width() - cxIcon + 1) / 2; 229 | int y = (rect.Height() - cyIcon + 1) / 2; 230 | 231 | // Draw the icon 232 | dc.DrawIcon(x, y, m_hIcon); 233 | } 234 | else 235 | { 236 | CDialogEx::OnPaint(); 237 | } 238 | } 239 | 240 | // The system calls this function to obtain the cursor to display while the user drags 241 | // the minimized window. 242 | HCURSOR CDPIScalingMFCAppDlg::OnQueryDragIcon() 243 | { 244 | return static_cast(m_hIcon); 245 | } 246 | 247 | 248 | 249 | void CDPIScalingMFCAppDlg::OnCbnSelchangeCombo1() 250 | { 251 | // TODO: Add your control notification handler code here 252 | auto currSelIndex = m_displayList.GetCurSel(); 253 | auto itr = m_displayDataCache.find(currSelIndex); 254 | if (m_displayDataCache.end() == itr) 255 | {//not found in cache 256 | LOGW(L"m_displayDataCache cache hit failed") 257 | return; 258 | } 259 | 260 | FillDisplayInfo(itr->second.m_adapterId, itr->second.m_sourceID, itr->second.m_targetID); 261 | } 262 | 263 | 264 | void CDPIScalingMFCAppDlg::OnBnClickedButton2() 265 | { 266 | Refresh(); 267 | } 268 | 269 | 270 | void CDPIScalingMFCAppDlg::OnBnClickedApply() 271 | { 272 | // TODO: Add your control notification handler code here 273 | auto currDpiSel = m_dpiList.GetCurSel(); 274 | UINT32 dpiToSet = UINT32(m_dpiList.GetItemData(currDpiSel)); 275 | auto currDisplaySel = m_displayList.GetCurSel(); 276 | int CurrentDpiVal = GetDlgItemInt(m_currentDPI.GetDlgCtrlID()); 277 | if (dpiToSet == CurrentDpiVal) 278 | { 279 | LOGW(L"Trying to set DPI which is already set. Nothing to do") 280 | return; 281 | } 282 | 283 | int recommendedVal = GetDlgItemInt(m_recommendedDPI.GetDlgCtrlID()); 284 | int cacheIndex = int(m_displayList.GetItemData(currDisplaySel)); 285 | auto itr = m_displayDataCache.find(cacheIndex); 286 | 287 | if (m_displayDataCache.end() == itr) 288 | { 289 | LOGW(L"Cache miss"); 290 | return; 291 | } 292 | 293 | auto res = DpiHelper::SetDPIScaling(itr->second.m_adapterId, itr->second.m_sourceID, dpiToSet); 294 | 295 | if (false == res) 296 | { 297 | LOGW(L"DpiHelper::SetDPIScaling() failed"); 298 | return; 299 | } 300 | 301 | Refresh(); 302 | } 303 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/DPIScalingMFCAppDlg.h: -------------------------------------------------------------------------------- 1 | 2 | // DPIScalingMFCAppDlg.h : header file 3 | // 4 | 5 | #pragma once 6 | #include "afxwin.h" 7 | #include 8 | 9 | 10 | // CDPIScalingMFCAppDlg dialog 11 | class CDPIScalingMFCAppDlg : public CDialogEx 12 | { 13 | // Construction 14 | public: 15 | CDPIScalingMFCAppDlg(CWnd* pParent = NULL); // standard constructor 16 | 17 | // Dialog Data 18 | #ifdef AFX_DESIGN_TIME 19 | enum { IDD = IDD_DPISCALINGMFCAPP_DIALOG }; 20 | #endif 21 | 22 | protected: 23 | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 24 | 25 | 26 | // Implementation 27 | protected: 28 | HICON m_hIcon; 29 | 30 | //to store display info along with corresponding list item 31 | struct DisplayData { 32 | LUID m_adapterId; 33 | int m_targetID; 34 | int m_sourceID; 35 | //int currentDPI, minDPI, maxDPI, recommendedDPI; 36 | 37 | DisplayData() 38 | { 39 | m_adapterId = {}; 40 | m_targetID = m_sourceID = -1; 41 | //currentDPI = minDPI = maxDPI = recommendedDPI = -1; 42 | } 43 | }; 44 | std::map m_displayDataCache; 45 | 46 | // Generated message map functions 47 | virtual BOOL OnInitDialog(); 48 | bool FillDisplayInfo(LUID adapterID, int sourceID, int targetID); 49 | bool Refresh(); 50 | afx_msg void OnPaint(); 51 | afx_msg HCURSOR OnQueryDragIcon(); 52 | DECLARE_MESSAGE_MAP() 53 | public: 54 | afx_msg void OnCbnSelchangeCombo1(); 55 | // Drop down where we show displays 56 | CComboBox m_displayList; 57 | CEdit m_currentDPI; 58 | CEdit m_recommendedDPI; 59 | CListBox m_dpiList; 60 | afx_msg void OnBnClickedButton2(); 61 | afx_msg void OnBnClickedApply(); 62 | CEdit m_displayUniqueName; 63 | }; 64 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | MICROSOFT FOUNDATION CLASS LIBRARY : DPIScalingMFCApp Project Overview 3 | =============================================================================== 4 | 5 | The application wizard has created this DPIScalingMFCApp application for 6 | you. This application not only demonstrates the basics of using the Microsoft 7 | Foundation Classes but is also a starting point for writing your application. 8 | 9 | This file contains a summary of what you will find in each of the files that 10 | make up your DPIScalingMFCApp application. 11 | 12 | DPIScalingMFCApp.vcxproj 13 | This is the main project file for VC++ projects generated using an application wizard. 14 | It contains information about the version of Visual C++ that generated the file, and 15 | information about the platforms, configurations, and project features selected with the 16 | application wizard. 17 | 18 | DPIScalingMFCApp.vcxproj.filters 19 | This is the filters file for VC++ projects generated using an Application Wizard. 20 | It contains information about the assciation between the files in your project 21 | and the filters. This association is used in the IDE to show grouping of files with 22 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 23 | "Source Files" filter). 24 | 25 | DPIScalingMFCApp.h 26 | This is the main header file for the application. It includes other 27 | project specific headers (including Resource.h) and declares the 28 | CDPIScalingMFCAppApp application class. 29 | 30 | DPIScalingMFCApp.cpp 31 | This is the main application source file that contains the application 32 | class CDPIScalingMFCAppApp. 33 | 34 | DPIScalingMFCApp.rc 35 | This is a listing of all of the Microsoft Windows resources that the 36 | program uses. It includes the icons, bitmaps, and cursors that are stored 37 | in the RES subdirectory. This file can be directly edited in Microsoft 38 | Visual C++. Your project resources are in 1033. 39 | 40 | res\DPIScalingMFCApp.ico 41 | This is an icon file, which is used as the application's icon. This 42 | icon is included by the main resource file DPIScalingMFCApp.rc. 43 | 44 | res\DPIScalingMFCApp.rc2 45 | This file contains resources that are not edited by Microsoft 46 | Visual C++. You should place all resources not editable by 47 | the resource editor in this file. 48 | 49 | 50 | ///////////////////////////////////////////////////////////////////////////// 51 | 52 | The application wizard creates one dialog class: 53 | 54 | DPIScalingMFCAppDlg.h, DPIScalingMFCAppDlg.cpp - the dialog 55 | These files contain your CDPIScalingMFCAppDlg class. This class defines 56 | the behavior of your application's main dialog. The dialog's template is 57 | in DPIScalingMFCApp.rc, which can be edited in Microsoft Visual C++. 58 | 59 | ///////////////////////////////////////////////////////////////////////////// 60 | 61 | Help Support: 62 | 63 | hlp\DPIScalingMFCApp.hhp 64 | This file is a help project file. It contains the data needed to 65 | compile the help files into a .chm file. 66 | 67 | hlp\DPIScalingMFCApp.hhc 68 | This file lists the contents of the help project. 69 | 70 | hlp\DPIScalingMFCApp.hhk 71 | This file contains an index of the help topics. 72 | 73 | hlp\afxcore.htm 74 | This file contains the standard help topics for standard MFC 75 | commands and screen objects. Add your own help topics to this file. 76 | 77 | makehtmlhelp.bat 78 | This file is used by the build system to compile the help files. 79 | 80 | hlp\Images\*.gif 81 | These are bitmap files required by the standard help file topics for 82 | Microsoft Foundation Class Library standard commands. 83 | 84 | 85 | ///////////////////////////////////////////////////////////////////////////// 86 | 87 | Other standard files: 88 | 89 | StdAfx.h, StdAfx.cpp 90 | These files are used to build a precompiled header (PCH) file 91 | named DPIScalingMFCApp.pch and a precompiled types file named StdAfx.obj. 92 | 93 | Resource.h 94 | This is the standard header file, which defines new resource IDs. 95 | Microsoft Visual C++ reads and updates this file. 96 | 97 | DPIScalingMFCApp.manifest 98 | Application manifest files are used by Windows XP to describe an applications 99 | dependency on specific versions of Side-by-Side assemblies. The loader uses this 100 | information to load the appropriate assembly from the assembly cache or private 101 | from the application. The Application manifest maybe included for redistribution 102 | as an external .manifest file that is installed in the same folder as the application 103 | executable or it may be included in the executable in the form of a resource. 104 | ///////////////////////////////////////////////////////////////////////////// 105 | 106 | Other notes: 107 | 108 | The application wizard uses "TODO:" to indicate parts of the source code you 109 | should add to or customize. 110 | 111 | If your application uses MFC in a shared DLL, you will need 112 | to redistribute the MFC DLLs. If your application is in a language 113 | other than the operating system's locale, you will also have to 114 | redistribute the corresponding localized resources MFC100XXX.DLL. 115 | For more information on both of these topics, please see the section on 116 | redistributing Visual C++ applications in MSDN documentation. 117 | 118 | ///////////////////////////////////////////////////////////////////////////// 119 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/md5helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //Code from: https://stackoverflow.com/a/13259720/981766 4 | 5 | #include 6 | #include 7 | 8 | 9 | std::string GetHashText(const void* data, const size_t data_size) 10 | { 11 | HCRYPTPROV hProv = NULL; 12 | 13 | if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { 14 | return ""; 15 | } 16 | 17 | BOOL hash_ok = FALSE; 18 | HCRYPTPROV hHash = NULL; 19 | 20 | hash_ok = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash); 21 | 22 | if (!hash_ok) { 23 | CryptReleaseContext(hProv, 0); 24 | return ""; 25 | } 26 | 27 | if (!CryptHashData(hHash, static_cast(data), data_size, 0)) { 28 | CryptDestroyHash(hHash); 29 | CryptReleaseContext(hProv, 0); 30 | return ""; 31 | } 32 | 33 | DWORD cbHashSize = 0, dwCount = sizeof(DWORD); 34 | if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHashSize, &dwCount, 0)) { 35 | CryptDestroyHash(hHash); 36 | CryptReleaseContext(hProv, 0); 37 | return ""; 38 | } 39 | 40 | std::vector buffer(cbHashSize); 41 | if (!CryptGetHashParam(hHash, HP_HASHVAL, reinterpret_cast(&buffer[0]), &cbHashSize, 0)) { 42 | CryptDestroyHash(hHash); 43 | CryptReleaseContext(hProv, 0); 44 | return ""; 45 | } 46 | 47 | std::ostringstream oss; 48 | 49 | for (std::vector::const_iterator iter = buffer.begin(); iter != buffer.end(); ++iter) { 50 | oss.fill('0'); 51 | oss.width(2); 52 | oss << std::hex << static_cast(*iter); 53 | } 54 | 55 | CryptDestroyHash(hHash); 56 | CryptReleaseContext(hProv, 0); 57 | return oss.str(); 58 | } 59 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/res/DPIScalingMFCApp.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/DPIScalingMFCApp/res/DPIScalingMFCApp.ico -------------------------------------------------------------------------------- /DPIScalingMFCApp/res/DPIScalingMFCApp.rc2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/DPIScalingMFCApp/res/DPIScalingMFCApp.rc2 -------------------------------------------------------------------------------- /DPIScalingMFCApp/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/DPIScalingMFCApp/resource.h -------------------------------------------------------------------------------- /DPIScalingMFCApp/stdafx.cpp: -------------------------------------------------------------------------------- 1 | 2 | // stdafx.cpp : source file that includes just the standard includes 3 | // DPIScalingMFCApp.pch will be the pre-compiled header 4 | // stdafx.obj will contain the pre-compiled type information 5 | 6 | #include "stdafx.h" 7 | 8 | 9 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/stdafx.h: -------------------------------------------------------------------------------- 1 | 2 | // stdafx.h : include file for standard system include files, 3 | // or project specific include files that are used frequently, 4 | // but are changed infrequently 5 | 6 | #pragma once 7 | 8 | #ifndef VC_EXTRALEAN 9 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 10 | #endif 11 | 12 | #include "targetver.h" 13 | 14 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit 15 | 16 | // turns off MFC's hiding of some common and often safely ignored warning messages 17 | #define _AFX_ALL_WARNINGS 18 | 19 | #include // MFC core and standard components 20 | #include // MFC extensions 21 | 22 | 23 | 24 | 25 | 26 | #ifndef _AFX_NO_OLE_SUPPORT 27 | #include // MFC support for Internet Explorer 4 Common Controls 28 | #endif 29 | #ifndef _AFX_NO_AFXCMN_SUPPORT 30 | #include // MFC support for Windows Common Controls 31 | #endif // _AFX_NO_AFXCMN_SUPPORT 32 | 33 | #include // MFC support for ribbons and control bars 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | #ifdef _UNICODE 44 | #if defined _M_IX86 45 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 46 | #elif defined _M_X64 47 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 48 | #else 49 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 50 | #endif 51 | #endif 52 | 53 | 54 | -------------------------------------------------------------------------------- /DPIScalingMFCApp/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /DPIScalingMFCAppScreenHelp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/DPIScalingMFCAppScreenHelp.png -------------------------------------------------------------------------------- /DPIScalingMFCAppScreenHelp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 30 | 35 | 36 | 44 | 50 | 51 | 52 | 70 | 72 | 73 | 75 | image/svg+xml 76 | 78 | 79 | 80 | 81 | 82 | 86 | 90 | 799 | 804 | List of active displays on the system 815 | 821 | DPI scaling values supported by this display 832 | 833 | 834 | 835 | -------------------------------------------------------------------------------- /DPIScalingMFCAppScreenshot.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/DPIScalingMFCAppScreenshot.JPG -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The visual studio solution contains 2 projects (3 projects now. see update 1. for command line version see update 2) 2 | 3 | 1. DPIHelper (static lib) 4 | This is the helper library which contains class DpiHelper. This has functions for setting and getting DPI. 5 | 1. DPIScalingMFCApp 6 | Here is how the app looks 7 | ![DPI scaling MFC app dialog](DPIScalingMFCAppScreenHelp.png) 8 | 9 | The repo is based on the study I did by reverse engineering system settings app (immersive control panel), user32.dll (which contains the API used for DPI scaling). 10 | For getting, and setting DPI scaling of a monitor DisplayConfigGetDeviceInfo(), and DisplayConfigSetDeviceInfo() functions are used. 11 | These functions are public (their description is provided on msdn), but they use some undocumented parameters. 12 | The values of type parameter used in DISPLAYCONFIG_DEVICE_INFO_HEADER is in nagative range (-3 for get, and -4 for set). 13 | [DISPLAYCONFIG_DEVICE_INFO_TYPE](https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ne-wingdi-displayconfig_device_info_type) as it is 14 | documented by microsoft (and defined in wingdi.h) only contains non negative values. Thus the ability to get/set DPI 15 | have been made hidden my Microsoft by not making these parameters public. 16 | The structures used in DisplayConfigGet/SetDeviceInfo() are dependednt on value of type. Now since type isn't documented 17 | these structures also have not been. 18 | 19 | I used WinDbg, and ghidra (https://ghidra-sre.org/) in the reverse engineering effort. A big thanks to creators of ghidra! 20 | 21 | If you want to set\get DPI scaling from your code, just use the DpiHelper class. The main methods i the class are 22 | 1. GetDPIScalingInfo(), and 23 | 1. SetDPIScaling() 24 | 25 | ## UPDATE 3 : 27 July 2023 26 | Added support for showing monitor unique ID in the MFC app. 27 | The unique ID is used to construct the registry path where DPI value is stored. 28 | eg. Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings\LEN41410_00_07E3_24^A8DD7E34BCF1555F032E26E990ABC597 29 | I had tried to understand the algo to some extent - https://stackoverflow.com/a/57397039/981766 but it was not perfect. 30 | While working on some other project I stumbled across another undocumented API - DisplayConfigGetDeviceInfo(-7), ie. an undocumented 31 | parameter for the DisplayConfigGetDeviceInfo() API. Along with monitor unique string it gives a lot of other details - mainly related to 32 | screen brightness. 33 | https://github.com/lihas/WindowsUndocumentedLib/tree/master 34 | One thing to note is that while DPI scaling is a property of source, the display unique ID is a target property, and 35 | therefore needs adapter ID, target ID pair instead of adaper ID, source ID pair. 36 | ![DPI scaling MFC app monitorUniqueID](monitorUniqueID_MFC.png) 37 | 38 | 39 | ## Update 2 : 28 Sep 2020 40 | Command line version - https://github.com/imniko/SetDPI 41 | 42 | A user created a command line version. I have not tested it but from the description given on the repo it seems great. 43 | Do check it out. 44 | 45 | ## UPDATE 1 : 15 July 2020 46 | Added a new project which uses OS API SystemParametersInfo() to set DPI. 47 | This method is useful when trying to set DPI scaling for primary monitor 48 | in a multi monitor setup, or there is only a single display, as API is much simpler. 49 | Compared to my earlier approach this suffers from some drawbacks though. 50 | 51 | ![DPI scaling using SystemParametersInfo console App](Console_Using_SystemParametersInfo_Api.PNG) 52 | 53 | Pros wrt prev approach. 54 | 1. Very simple, thus when we are interested in setting DPI scaling for primary monitor only, or there is only a single monitor, this is quite favorable. 55 | Cons wrt prev approach. 56 | 1. Cannot be used for setting per monitor DPI scaling in multi monitor setup 57 | 1. Does not give you currently applied DPI scaling 58 | 1. Does not tell max/min values of DPI scaling allowed for a monitor. However if you try to set beyond max, it will use max value. 59 | -------------------------------------------------------------------------------- /UNLICENSE.txt: -------------------------------------------------------------------------------- 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 -------------------------------------------------------------------------------- /monitorUniqueID.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/monitorUniqueID.jpg -------------------------------------------------------------------------------- /monitorUniqueID_MFC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/monitorUniqueID_MFC.png -------------------------------------------------------------------------------- /x64/Debug/DPIHelper.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/x64/Debug/DPIHelper.lib -------------------------------------------------------------------------------- /x64/Debug/DPIScalingMFCApp.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihas/windows-DPI-scaling-sample/738ac18b7a7ce2d8fdc157eb825de9cb5eee0448/x64/Debug/DPIScalingMFCApp.exe --------------------------------------------------------------------------------