├── .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