Scaling the Window down (window.h)

Hello there, i have a program with 5 buttons:
Abyssal Scepter -> Writes something into ChampionName-SummonersRift.txt
Aegis of the Legion -> Same thing, different id
Beep -> Send a Beep Sound
Show File Content -> Show What is currently in ChampionName-SummonersRift.txt
AutoBuddy.... -> Simple MessageBox with a Static text
Quit -> Terminates the program and removes that last character of the .txt file
Bami's Cinder - Does nothing.
Code : http://pastebin.com/TcV3LNfN
The "Bami's Cinder" button is located at the top right part of the Window.
Oddly enought when i use any resolution lower than 1920x1080p the Window is CUT from right to left.
How do i tell the program to not cut the Window, to just scale it down according to what resolution is the user using.
I.E to work the same in 800x600 or higher as it works in FHD
1920x1080 = http://i.imgur.com/U7gmSD8.png
1366x768 = http://i.imgur.com/DcujLgO.png
You have to code High DPI Aware.
@freddie1, i took a look at https://msdn.microsoft.com/en-us/library/windows/desktop/dd371771(v=vs.85).aspx | https://msdn.microsoft.com/en-us/library/windows/desktop/dd464659(v=vs.85).aspx
I only understand only really basic c++ stuff, i don't know how i made my program even work...
I really don't know where to start, could you help me a little bit more?
Its really not to terribly hard, but if you are a beginner you'll likely find it quite difficult. Most of the simple examples I have are in PowerBASIC, but I do have one C++ SDK, API example. A friend of mine was having trouble with this, so I used his example. It could be a bit simpler, but it is still fairly basic and simple. There is a main form with two edit controls on it. If I recall the problem he was having was that the text in the edit controls was being clipped in some instances. Its coming back now. It was OK when compiled with MS VC, but was clipped when compiled with GCC. Or vice versa. I forget.

But anyway, the context in which I wrote this code was that I had been working (and still am) on my replacements for the C and C++ Standard Libraries. I wrote my own versions of these because I couldn't tolerate the bloated sizes of the executables produced by the standard linkages.

So the code I'm going to supply has a couple #defines you should be aware of. There is a #define Debug that if not commented out, produces voluminous debug info to an "Output.txt" file which for you will be the only way you'll be able to figure out how the app works or how to code High DPI Aware.

The other #define is #define TCLib. You won't be able to use that and therefore must leave it commented out. That is my substitute version of the C Standard Libraries wich I use to minimize executable bloat. You wouldn't have that unless you got it from me, but we would need a way to get it to you. So what you'll have to do is simply compile with MS VC.

And as far as I know, you are going to have to use MS VC if you want High DPI Aware apps. At least with my TDM-GCC versions 4.7, 4.8, and 4.9 the critical function...

SetProcessDPIAware()

....isn't present. I didn't ask you what compiler you were using, but it had better be MS VC - like I said.

So there are two files - Form1.h and ec02.cpp. The latter is the main cpp file. But a word of warning, if you are a beginning C++ Windows Api GUI coder you almost certainly use the typical switch() logic construct to map windows messages to the code that handles them. I hardly ever do that except for the very simplest apps, as I prefer to use a somewhat sophisticated function pointer setup where I use a for loop to map messages to unique message handling functions - one for each message. I've written tutorials on that if you care to study it.

But the first thing I do to code a 'High DPI Aware' app is call SetProcessDPIAware(). And my calling of that function is a bit complex as I call it through a function pointer setup using LoadLibrary() and GetProcAddress(), simply because I always use both MS VC and GCC compilers to test my code. And like I said, with the GCC compilers I have their build libraries don't include thatr function. So I need a way to see that it isn't called on GCC compiles. You'll note my setup in the function SetMyProcessDPIAware().

Anyway, here is DpiTest.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//DpiTest.h
#ifndef DpiTest_h
#define DpiTest_h

#define IDC_EDIT1                 2000
#define IDC_EDIT2                 2005
#define dim(x)                    (sizeof(x) / sizeof(x[0]))
#define SizX(x)                   (int)(x * pDpiData->rx)       // For DPI Scaling Calculations And Purposes
#define SizY(y)                   (int)(y * pDpiData->ry)       // For DPI Scaling Calculations And Purposes
#ifdef TCLib
   extern "C" int                 _fltused=1;
#endif
   
struct WndEventArgs
{
 HWND                             hWnd;
 WPARAM                           wParam;
 LPARAM                           lParam;
 HINSTANCE                        hIns;
};

LRESULT fnWndProc_OnCreate        (WndEventArgs& Wea);
LRESULT fnWndProc_OnCtlColorEdit  (WndEventArgs& Wea);
LRESULT fnWndProc_OnDestroy       (WndEventArgs& Wea);

struct EVENTHANDLER
{
 unsigned int                     iMsg;
 LRESULT                          (*fnPtr)(WndEventArgs&);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,                      fnWndProc_OnCreate},
 {WM_CTLCOLOREDIT,                fnWndProc_OnCtlColorEdit},
 {WM_DESTROY,                     fnWndProc_OnDestroy}
};

struct DpiData                    
{                                 
 double                           rx; 
 double                           ry;
};

#endif 


You'll note my SizX() and SizY() #define macros. Those are used to scale all pixel coordinates to provide for resolution independent code. For example, if you use CreateWindow() to create a button, and you want a width of 100, instead of putting 100 in the CreateWindow() call to create the button, you would use SizX(100). Its as simple as that.

Next is DpiTest.cpp. I think it'll need two posts to get it all...
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Uses/links with LIBCMT:    cl DpiTest.cpp /O1 /Os /MT kernel32.lib user32.lib gdi32.lib
// Uses/links with TCLib:     cl DpiTest.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib gdi32.lib
//  5,632 Bytes               TCLib, x86, VC19, UNICODE
//  6,144 Bytes               TCLib, x64, VC15, UNICODE
//  7,168 Bytes               TCLib, x64, VC19, UNICODE
// 19,968 Bytes               GCC 4.8, x64, UNICODE
// 88,064 Bytes               VC15, x64 Std. UNICODE, Build With C Runtime
// #define TCLib
#ifndef UNICODE
   #define UNICODE            // App demonstrates Dpi Aware coding.  Note, you can't build with both
#endif                        // #define TCLib and #define Debug.  As of yet I have not provided
#ifndef _UNICODE              // FILE* i/o capabilities in TCLib.  If TCLib is defined, TCLib specific
   #define _UNICODE           // versions of stdio.h and tchar.h must be used.  Hence, those files
#endif                        // need to be placed in the app's directory, or pathed specifically. 
#include <windows.h>
#ifdef TCLib                  // I'm explicitely calling SetProcessDPIAware() in SetMyProcessDpiAware()
   #include "stdio.h"         // because that function isn't available in all build environments
   #include "tchar.h"         // out there.
#else   
   #include <stdio.h>
   #include <tchar.h>
#endif   
#include "DpiTest.h"          // If Debug is defined an Output.txt Log File is created, and debug
#define Debug                 // information logged to there.
#ifdef Debug
   FILE* fp=NULL;
#endif 


void SetMyProcessDpiAware()   // This function isn't available on the older GCC versions I have.
{                             // Calling this function tells Windows the app is High DPI Aware,
 BOOL (__stdcall* pFn)(void); // and so Windows won't virtualize fonts and sizes of things and
                              // wreck them.
 HINSTANCE hInstance=LoadLibrary(_T("user32.dll"));
 if(hInstance)
 {
    pFn=(BOOL (__stdcall*)(void))GetProcAddress(hInstance,"SetProcessDPIAware");
    if(pFn)
       pFn();
    FreeLibrary(hInstance);
 }
}


LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)
{
 BOOL blnDpiAware=FALSE;      // This is the creation/instantiation function, i.e., Constructor 
 DpiData* pDpiData=NULL;      // for the "Form1" Class registered in WinMain, i.e., the main
 TCHAR szBuffer[6];           // and only app window or start up form/dialog/window.  In WinMain
 HANDLE hHeap=NULL;           // I allocate three 'spots' for app specific data in the 
 HFONT hFont1=NULL;           // WNDCLASS::cbWndExtra bytes.  That would be 3 * sizeof(void*) or
 HFONT hFont2=NULL;           // 24 bytes in x64 and 12 bytes in x86.  I store the DpiData* in
 int dpiX,dpiY;               // offset #1, hFont1 in offset #2, and hFont2 in offset #3.
 HDC hDC=NULL;                
 HWND hCtl;
 
 #ifdef Debug
 fp=_tfopen(_T("Output.txt"),_T("w"));
 _ftprintf(fp,_T("Entering fnWndProc_OnCreate()\r\n"));
 _ftprintf(fp,_T("  Wea.hWnd         = %p\r\n"),Wea.hWnd);
 _ftprintf(fp,_T("  sizeof(LRESULT)  = %Iu\r\n"),sizeof(LRESULT));
 #endif 
 Wea.hIns    = ((LPCREATESTRUCT)Wea.lParam)->hInstance;
 hHeap       = GetProcessHeap();
 blnDpiAware = IsProcessDPIAware();
 #ifdef Debug
 _ftprintf(fp,_T("  blnDpiAware      = %d\r\n"),blnDpiAware);
 #endif
 
 // Deal With DPI                                 // This code will normalize, fix, whatever you want 
 hDC          = GetDC(NULL);                      // to call it, the bad things that happen on modern
 dpiX         = GetDeviceCaps(hDC, LOGPIXELSX);   // displays when the user alters DPI settings through
 dpiY         = GetDeviceCaps(hDC, LOGPIXELSY);   // Control Panel.  If code like this isn't implemented,
 pDpiData     = (DpiData*)HeapAlloc               // and the user alters DPI settings from those used
 (                                                // by the coder (you) when the app was developed, text
  hHeap,                                          // and controls will almost certainly be 'clipped', and
  HEAP_ZERO_MEMORY,                               // fonts will be 'virtualized' such that they'll become
  sizeof(DpiData)                                 // fuzzy.  Note I have a DpiData struct defined in 
 );                                               // Form1.h.  The purpose of that is to persist DPI 
 if(!pDpiData)                                    // factors across function calls.  Not needed here, but
    return -1;                                    // useful in more complex apps.
 SetWindowLongPtr(Wea.hWnd,0*sizeof(void*),(LONG_PTR)pDpiData);
 pDpiData->rx = dpiX/96.0;
 pDpiData->ry = dpiY/96.0;
 ReleaseDC(Wea.hWnd,hDC);
 #ifdef Debug
    _ftprintf(fp,_T("  pDpiData         = %p\r\n"),pDpiData);
    _ftprintf(fp,_T("  dpiX             = %d\r\n"),dpiX);
    _ftprintf(fp,_T("  dpiY             = %d\r\n"),dpiY);
    #ifdef TCLib
       FltToTch(szBuffer, pDpiData->rx, 6, 3, _T('.'),true);
       _ftprintf(fp,_T("  pDpiData->rx     = %s\r\n"),szBuffer);
       FltToTch(szBuffer, pDpiData->ry, 6, 3, _T('.'),true);
       _ftprintf(fp,_T("  pDpiData->ry     = %s\r\n"),szBuffer);
    #else
       _ftprintf(fp,_T("  pDpiData->rx     = %6.3f\n"),pDpiData->rx);
       _ftprintf(fp,_T("  pDpiData->ry     = %6.3f\n"),pDpiData->ry);      
    #endif
 #endif
 
 // Position/Center Main App Window
 int iCxScreen=GetSystemMetrics(SM_CXSCREEN);
 int iCyScreen=GetSystemMetrics(SM_CYSCREEN);
 int x = (iCxScreen/2 - 338/2)/pDpiData->rx;
 int y = (iCyScreen/2 - 320/2)/pDpiData->ry;
 MoveWindow(Wea.hWnd,SizX(x),SizY(y),SizX(338),SizY(320),FALSE);
 #ifdef Debug
 _ftprintf(fp,_T("  iCxScreen        = %d\r\n"),iCxScreen);
 _ftprintf(fp,_T("  iCyScreen        = %d\r\n"),iCyScreen);
 _ftprintf(fp,_T("  x                = %d\r\n"),x);
 _ftprintf(fp,_T("  y                = %d\r\n"),y);
 #endif 
  
 // Instantiate Child Window Objects
 double dblFont1 = 10.0;
 double dblFont2 = 10.0; 
 DWORD  dwStyles = WS_CHILD|WS_VISIBLE|WS_BORDER|ES_AUTOVSCROLL|ES_WANTRETURN|ES_AUTOHSCROLL|ES_MULTILINE;
 
 hCtl=CreateWindow(_T("edit"),NULL,dwStyles,SizX(15),SizY(15),SizX(295),SizY(112),Wea.hWnd,(HMENU)IDC_EDIT1,Wea.hIns,NULL);
 hFont1=CreateFont(-MulDiv(dblFont1,dpiY,72),0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,_T("Arial"));
 SetWindowLongPtr(Wea.hWnd,1*sizeof(void*),(LONG_PTR)hFont1);
 SendMessage(hCtl, (UINT)WM_SETFONT, (WPARAM)hFont1, (LPARAM)TRUE);
 SetWindowText(hCtl, _T(" Text in this control set to RED ON WHITE"));
 
 hCtl=CreateWindow(_T("edit"),NULL,dwStyles,SizX(15),SizY(160),SizX(295),SizY(112),Wea.hWnd,(HMENU)IDC_EDIT2,Wea.hIns,NULL);
 hFont2=CreateFont(-MulDiv(dblFont2,dpiY,72),0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,_T("Garamond"));
 SetWindowLongPtr(Wea.hWnd,2*sizeof(void*),(LONG_PTR)hFont2);
 SendMessage(hCtl, (UINT)WM_SETFONT, (WPARAM)hFont2, (LPARAM)TRUE);
 SetWindowText(hCtl, _T(" Text in this control set to Green ON BLACK"));
 #ifdef Debug
 _ftprintf(fp,_T("Leaving fnWndProc_OnCreate()\r\n\r\n"));
 #endif 
 
 return 0;
}


LRESULT fnWndProc_OnCtlColorEdit(WndEventArgs& Wea)
{
 int iCtlId=GetDlgCtrlID((HWND)Wea.lParam);
 
 if(iCtlId==IDC_EDIT1)
 {
    SetTextColor((HDC)Wea.wParam, RGB(255, 0, 0));
	   SetBkMode((HDC)Wea.wParam,TRANSPARENT);
	   return (LRESULT)GetStockObject(WHITE_BRUSH);
 }
 if(iCtlId==IDC_EDIT2)
 {
    SetTextColor((HDC)Wea.wParam, RGB(0, 255, 0));
	   SetBkMode((HDC)Wea.wParam,TRANSPARENT);
	   return (LRESULT)GetStockObject(BLACK_BRUSH);
 }	  
 
 return DefWindowProc(Wea.hWnd, WM_CTLCOLOREDIT, Wea.wParam, Wea.lParam); 
}	


continued...
Last edited on
...last part of DpiTest.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
LRESULT fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 DpiData* pDpiData=NULL;
 BOOL blnFree=FALSE;
 HFONT hFont1=NULL;
 HFONT hFont2=NULL;
 HFONT hFont=NULL;
 
 #ifdef Debug
 _ftprintf(fp,_T("Entering fnWndProc_OnDestroy()\r\n"));
 _ftprintf(fp,_T("  Wea.hWnd         = %p\r\n"),Wea.hWnd);
 #endif 
 pDpiData=(DpiData*)GetWindowLongPtr(Wea.hWnd,0*sizeof(void*)); 
 blnFree=HeapFree(GetProcessHeap(),0,pDpiData);
 #ifdef Debug
 _ftprintf(fp,_T("  pDpiData         = %p\r\n"),pDpiData);
 _ftprintf(fp,_T("  blnFree          = %d\r\n"),blnFree);
 #endif 
 hFont=(HFONT)GetWindowLongPtr(Wea.hWnd,1*sizeof(void*));
 blnFree=DeleteObject(hFont);
 #ifdef Debug
 _ftprintf(fp,_T("  hFont1           = %p\r\n"),hFont1);
 _ftprintf(fp,_T("  blnFree(hFont1)  = %d\r\n"),blnFree);
 #endif
 hFont=(HFONT)GetWindowLongPtr(Wea.hWnd,2*sizeof(void*));
 blnFree=DeleteObject(hFont);
 #ifdef Debug
 _ftprintf(fp,_T("  hFont2           = %p\r\n"),hFont2);
 _ftprintf(fp,_T("  blnFree(hFont2)  = %d\r\n"),blnFree);
 #endif
 PostQuitMessage(0);
 #ifdef Debug
 _ftprintf(fp,_T("Leaving fnWndProc_OnDestroy()\r\n"));
 CloseHandle(fp);
 #endif
 
 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI _tWinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("Form1");
 MSG messages;
 WNDCLASS wc;
 HWND hWnd;

 memset(&wc,0,sizeof(WNDCLASS));
 SetMyProcessDpiAware();
 wc.lpszClassName = szClassName,                     wc.lpfnWndProc   = fnWndProc;
 wc.hIcon         = LoadIcon(NULL,IDI_APPLICATION),  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 
 wc.hInstance     = hIns,                            wc.hCursor       = LoadCursor(NULL,IDC_ARROW),          
 wc.cbWndExtra    = 3*sizeof(void*);
 RegisterClass(&wc);
 hWnd=CreateWindow(szClassName,_T("Edit Ctrl Colors and Fonts"),WS_OVERLAPPEDWINDOW,0,0,0,0,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}

Last edited on
Note above my Window Procedure (fnWndProc) is only 15 lines. Using my function pointer setup that will never grow any larger, even if I have an app with 100,000 lines of code. All my message handling code is in separate message handling procedures. Some of the stuff in Form1.h is the necessary plumbing to make that all work.

This should compile and run for you no matter which compiler you are using. Its just that if you are using GCC you won't end up with any proper scaling as monitors or screen resolutions change.

In my WinMain() function I call SetMyProcessDpiAware(). That function is a wrapper around LoadLibrary()/GetProcAddress() calls to see if SetProcessDPIAware() was found in user32.dll. If it is that function is called. If it isn't - nothing bad happens.

Note in the CreateWindow() call for the main program window in WinMain() I use...

0,0,0,0,

...for the x, y, cx, and cy parameters. This is necessary. ASt the point of the CreateWindow() call in WinMain() fnWndProc_OnCreate() will be called, which is the handler for the WM_CREATE message, and that message can be best thought of as a C based constructor call on the main window class.

Drats!!!! I just looked at what I posted and I see that in the #define Debug code in fnWndProc_OnCreate() I'm calling my FltToTch() function, and you won't have that! Its in my TCLib.lib. I needed to code my own routine to convert floating point numbers to strings, because Windows uses the C Standard Library for that, and my TCLib.lib is a substitute for the C Standard Library (LIBCMT.LIB). So I'll have to fix that for you so you can compile it with VC. Give me a little time for that. But I can still continue with the explanation.

But study the code where I have the comment...

// Deal With DPI

That's really the gist of how to set up those scaling factors (SizX() and SizY() I mentioned earlier. And its not thast hard. So once those are set up, they are used wherever user interface objects are created and sized. More later as I fix the code to produce debug output for you with MS VC....





I fixed the problems mentioned above, and changed the names of the files to DpiTest.h and DpiTest.cpp.

Also, its setup to compile with MS VC. Here is what my Output.txt debug log file looks like after a run...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Entering fnWndProc_OnCreate()
  Wea.hWnd         = 00000000004407DE
  sizeof(LRESULT)  = 8
  blnDpiAware      = 1
  pDpiData         = 00000294293C5A30
  dpiX             = 96
  dpiY             = 96
  pDpiData->rx     =  1.000
  pDpiData->ry     =  1.000
  iCxScreen        = 1920
  iCyScreen        = 1080
  x                = 791
  y                = 380
Leaving fnWndProc_OnCreate()

Entering fnWndProc_OnDestroy()
  Wea.hWnd         = 00000000004407DE
  pDpiData         = 00000294293C5A30
  blnFree          = 1
  hFont1           = 0000000000000000
  blnFree(hFont1)  = 1
  hFont2           = 0000000000000000
  blnFree(hFont2)  = 1
Leaving fnWndProc_OnDestroy()


I ran that on an HP Envy Win10 laptop running 1920 x 1080 resolution. Then I changed the resolution to some bizarre numbers (and wrecked up my shortcuts but good), but still the output.txt files showed rx/ry scaling factors of 1.0, which surprised me. On my older Win7 Dell M6500 with native resolution of 1920 x 1200 I get quite different results when I change the resolution to other bizarre numbers. The Dpi scaling factors, which are floating point numbers, change to such numbers as 1.25, 1.50, etc. Not sure what's going on there. Possibly a difference in operating systems. But in any case, the relationship between the text in the text boxes and the size of the edit controls didn't change. If the Dpi code wasn't working there would be a highly visibe change in the size and placement relationships.
I really don't know what to say... I feel like you said more than enought but at the same time i feel like i will disappoint you & waste your precious time.
I hope i didn't waste your time & i will try my best to make my program work.
1.Don't know why, my code as it is doesn't work in VS 2015
2.Currently using codeblocks and the standard minGW compiler that comes with it.
Currently i have some finals at school , after i am done with them i will try my best.
Thank you!
Well, I guess it really is some fairly advanced stuff nothing17. I believe the various Class Frameworks take care of all this transparently, but if you code SDK Api style as I do, you have to take care of it yourself if you want your apps to scale correctly for users who have changed the default Windows Display resolutions.

I realize there are a lot of things in my code that could confuse you. I already had that app done so it didn't cost me too much time to provide it. It would have taken me another hour or so to recode it using the standard Windows style of the switch construct in the Window Procedure. But the crux of the matter are those several lines of code I mentioned under the comment...

//Deal With DPI

It is there that the scaling factors are determined that are used in my SizX and SizY macros, and those macros have to be used everywhere in the app where size or coordinates are used in Api calls to create or locate windows or controls. In fact, they need to be used in CreateFont() too to size fonts. If not, text will get clipped in controls. So yes, you've asked a very hard question, and its difficult to do.

What isn't working in VS 2015? My app I posted or your app?

I don't do much coding over the weekends anymore, but on Monday or Tuesday I'll provide a simpler app to demonstrate the techniques involved, and I'll code it straight SDK style (with the switch construct in WndProc). I'd also like to test it a bit more with Windows 10. Most of my work on this was a couple years ago on Windows 7. So check back.
Last edited on
Alright, my dream is to get a window that scales with all the windows resolutions with one button that i can copy and paste over and over again and modify the coordinates & what the buttons do . This is how i made the so called "program".
If it matters i have sent you the latest version of the program...not much changed.
One option is to use a dialog as the main window. Dialog boxes use device independent units and scale quite well. For simple apps it might be worth trying. Try it different screen sizes and resolutions.

Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <windows.h>
#include "resource.h"

BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg,WPARAM wParam,LPARAM lParam)
{
  switch(uMsg)
  {
    case WM_INITDIALOG:
      return TRUE;
    case WM_COMMAND:
			if (wParam == ID_OK || wParam == ID_CANCEL)
			{
				EndDialog(hwndDlg, 0);
				return TRUE;
			}
			break;
    case WM_CLOSE:
      PostQuitMessage(0);
      break;
  }
  return FALSE;
}

//---------------------------------------------------------------------------

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  return DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), 0, (DLGPROC)DialogProc);
	
}

Resource.h
1
2
3
#define IDD_MAINDIALOG                          101
#define ID_OK                                   1000
#define ID_CANCEL                               1001 


Resource.rc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Generated by ResEdit 1.6.6
// Copyright (C) 2006-2015
// http://www.resedit.net

#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"

//
// Dialog resources
//
IDD_MAINDIALOG DIALOG 0, 0, 186, 95
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Dialog"
FONT 12, "Microsoft Sans Serif"
{
    LTEXT           "Static", 0, 17, 67, 17, 9, SS_LEFT, WS_EX_LEFT
    LTEXT           "Static", 0, 17, 52, 17, 9, SS_LEFT, WS_EX_LEFT
    GROUPBOX        "Static", 0, 12, 42, 80, 40, 0, WS_EX_LEFT
    AUTORADIOBUTTON "Radio", 0, 47, 12, 33, 8, 0, WS_EX_LEFT
    LISTBOX         0, 112, 12, 60, 40, WS_TABSTOP | WS_VSCROLL | LBS_NOINTEGRALHEIGHT | LBS_SORT | LBS_NOTIFY, WS_EX_LEFT
    EDITTEXT        0, 42, 52, 45, 10, ES_AUTOHSCROLL, WS_EX_LEFT
    AUTOCHECKBOX    "Check", 0, 12, 12, 35, 8, 0, WS_EX_LEFT
    DEFPUSHBUTTON   "OK", ID_OK, 102, 77, 36, 10, 0, WS_EX_LEFT
    PUSHBUTTON      "Cancel", ID_CANCEL, 142, 77, 36, 10, 0, WS_EX_LEFT
    EDITTEXT        0, 42, 67, 45, 10, ES_AUTOHSCROLL, WS_EX_LEFT
}
That is a possible answer Thomas. I had thought of mentioning it but I didn't. My feelings on the point are that everyone abuses resource script created Dialogs because they are so simple to create. And next thing you know they are trying to create major apps out of them, and problems show up.
I’ll start from scratch in presenting my approach at resolution independent High Dpi Aware coding. Here will be the starting point, which is my interpretation of the most basic Win32 Api SDK style GUI program…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// cl HiDpiEx01.cpp  /O1 /Os /MT kernel32.lib user32.lib gdi32.lib                  >>> Stand Alone Binary >>> 39,912 bytes
// cl HiDpiEx01.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib gdi32.lib  >>> Stand Alone Binary >>>  2,560 bytes
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <windows.h>

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 if(msg==WM_DESTROY)
 {
    PostQuitMessage(0);
    return 0;
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 WNDCLASSEX wc={};
 MSG messages;
 HWND hWnd;

 wc.lpszClassName = L"Form1";
 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX);
 wc.hInstance     = hInstance;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,L"Form1",L"Form1",WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


I seldom use IDEs and my command line compilation strings for MS VC are as listed above. With VC 9 from Visual Studio 2008 I’m seeing 39,912 bytes as a statically linked (/MT) LIBCMT binary. I’ve written my own versions of the C and C++ Standard Libraries optimized for producing small binaries, and with that linkage I’m at 2,560 bytes for a stand alone executable. Note these examples were compiled wide character and 64 bit.

To make the above program High DPI Aware and resolution independent one must start by either calling SetProcessDPIAware() or placing some lines which you can find in the documentation in a manifest. I’ll be using the first technique in these posts. The next program – HiDpiEx02, shows the above program augmented so as to call that function, and it also opens a debug log file to show the results of various function calls…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// cl HiDpiEx02.cpp  /O1 /Os /MT kernel32.lib user32.lib gdi32.lib                  : Stand Alone Binary  >>> 64,000 bytes
// cl HiDpiEx02.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib gdi32.lib  : Stand Alone Binary  >>>  4,096 bytes
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <windows.h>
//#define TCLib
#define MSVC
#ifdef TCLib
   #include "stdio.h"
#else
   #include <stdio.h>
#endif  
#define MyDebug
#ifdef MyDebug
   FILE* fp=NULL;
#endif

void SetMyProcessDpiAware()
{
 BOOL (__stdcall* pFn)(void);

 HINSTANCE hInstance=LoadLibrary(L"user32.dll");
 if(hInstance)
 {
    pFn=(BOOL (__stdcall*)(void))GetProcAddress(hInstance,"SetProcessDPIAware");
    if(pFn)
       pFn();
    FreeLibrary(hInstance);
 }
}


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_CREATE:
     {
        BOOL      blnHiDpiAware = FALSE;
        HINSTANCE hIns          = NULL;
     
        #ifdef MSVC 
           blnHiDpiAware=IsProcessDPIAware();
        #endif           
        #ifdef MyDebug
        fp=fopen("Output.txt","w");
        fprintf(fp,"Entering fnWndProc() : Message WM_CREATE\r\n");
        fprintf(fp,"  hWnd          = %p\r\n",hWnd);
        fprintf(fp,"  blnHiDpiAware = %d\r\n",blnHiDpiAware);
        #endif
        hIns=((LPCREATESTRUCT)lParam)->hInstance;
        #ifdef MyDebug
        fprintf(fp,"Leaving fnWndProc()  : Message WM_CREATE\r\n\r\n");
        #endif
        return 0;
     }
   case WM_DESTROY:
     {
        #ifdef MyDebug
        fprintf(fp,"Entering fnWndProc()  : Message WM_DESTROY\r\n");
        fprintf(fp,"  hWnd = %p\r\n",hWnd);
        #endif
        PostQuitMessage(0);
        #ifdef MyDebug
        fprintf(fp,"Leaving fnWndProc()   : Message WM_DESTROY\r\n");
        #endif
        fclose(fp);
        return 0;
     }
 }
 
 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 WNDCLASSEX wc={};
 MSG messages;
 HWND hWnd;

 SetMyProcessDpiAware();
 wc.lpszClassName = L"Form1";
 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX);
 wc.hInstance     = hInstance;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,L"Form1",L"Form1",WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}

#if 0
Entering fnWndProc() : Message WM_CREATE
  hWnd          = 0000000000250356
  blnHiDpiAware = 1
Leaving fnWndProc()  : Message WM_CREATE

Entering fnWndProc()  : Message WM_DESTROY
  hWnd = 0000000000250356
Leaving fnWndProc()   : Message WM_DESTROY
#endif 


In the above program I call SetProcessDPIAware() from Main(), and in the WM_CREATE handler code I call IsProcessDPIAware() to test whether the call was successful. From the debug output following the code you can see that it was. Note I did a LoadLibrary() / GetProcAddress() sequence on SetProcessDPIAware() because all versions of Microsoft’s compilers don’t support that functionality, and as far as I’m aware, none of the MinGW versions do. Let’s move on to HiDpiEx03.cpp where we actually set up the scaling factors necessary to achieve resolution independence. Here is that…

continued...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// cl HiDpiEx03.cpp  /O1 /Os /MT kernel32.lib user32.lib gdi32.lib                  : Stand Alone Binary  >>> 79,872 bytes
// cl HiDpiEx03.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib gdi32.lib  : Stand Alone Binary  >>>  6,656 bytes
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <windows.h>
//#define TCLib
#define MSVC
#ifdef TCLib
   #include "stdio.h"
   extern "C" int _fltused=1.0;
#else
   #include <stdio.h>
#endif  
#define MyDebug
#ifdef MyDebug
   FILE* fp=NULL;
#endif

struct DpiData                    
{                                 
 double  rx; 
 double  ry;
};
   

void SetMyProcessDpiAware()
{
 BOOL (__stdcall* pFn)(void);

 HINSTANCE hInstance=LoadLibrary(L"user32.dll");
 if(hInstance)
 {
    pFn=(BOOL (__stdcall*)(void))GetProcAddress(hInstance,"SetProcessDPIAware");
    if(pFn)
       pFn();
    FreeLibrary(hInstance);
 }
}


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_CREATE:
     {
        BOOL      blnHiDpiAware = FALSE;
        HINSTANCE hIns          = NULL;
        double    dpiX          = 0.0;
        double    dpiY          = 0.0;
        HDC       hDC           = NULL;
        DpiData*  pDpiData      = NULL;
        HANDLE    hHeap         = NULL;
        char      szBuffer[32];
        
        #ifdef MSVC 
           blnHiDpiAware=IsProcessDPIAware();
        #endif           
        #ifdef MyDebug
        fp=fopen("Output.txt","w");
        fprintf(fp,"Entering fnWndProc() : Message WM_CREATE\r\n");
        fprintf(fp,"  hWnd             = %p\r\n",hWnd);
        fprintf(fp,"  blnHiDpiAware    = %d\r\n",blnHiDpiAware);
        #endif
        hIns=((LPCREATESTRUCT)lParam)->hInstance;
        
        // Deal With DPI               
        hDC          = GetDC(hWnd);        
        dpiX         = GetDeviceCaps(hDC, LOGPIXELSX);   
        dpiY         = GetDeviceCaps(hDC, LOGPIXELSY);
        hHeap        = GetProcessHeap();        
        pDpiData     = (DpiData*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(DpiData));       
        ReleaseDC(hWnd,hDC);        
        if(!pDpiData)                                    
           return -1;                                    
        SetWindowLongPtr(hWnd,0*sizeof(void*),(LONG_PTR)pDpiData);
        pDpiData->rx = dpiX/96.0;
        pDpiData->ry = dpiY/96.0;
        #ifdef MyDebug
           fprintf(fp,"  pDpiData         = %p\r\n",pDpiData);
           #ifdef TCLib
              FltToCh(szBuffer,dpiX,8,3,'.',true);
              fprintf(fp,"  dpiX             = %s\r\n",szBuffer);
              FltToCh(szBuffer,dpiY,8,3,'.',true);
              fprintf(fp,"  dpiY             = %s\r\n",szBuffer);
              FltToCh(szBuffer, pDpiData->rx, 6, 3, '.',true);
              fprintf(fp,"  pDpiData->rx     = %s\r\n",szBuffer);
              FltToCh(szBuffer, pDpiData->ry, 6, 3, '.',true);
              fprintf(fp,"  pDpiData->ry     = %s\r\n",szBuffer);
           #else
              fprintf(fp,"  dpiX             = %6.3f\n",dpiX);
              fprintf(fp,"  dpiY             = %6.3f\n",dpiY);
              fprintf(fp,"  pDpiData->rx     = %5.3f\n",pDpiData->rx);
              fprintf(fp,"  pDpiData->ry     = %5.3f\n",pDpiData->ry);
           #endif            
        #endif
        #ifdef MyDebug
        fprintf(fp,"Leaving fnWndProc()  : Message WM_CREATE\r\n\r\n");
        #endif
        return 0;
     }
   case WM_DESTROY:
     {
        DpiData* pDpiData=NULL;
        BOOL blnFree=FALSE;
 
        #ifdef MyDebug
        fprintf(fp,"Entering fnWndProc()  : Message WM_DESTROY\r\n");
        fprintf(fp,"  hWnd             = %p\r\n",hWnd);
        #endif
        pDpiData=(DpiData*)GetWindowLongPtr(hWnd,0*sizeof(void*)); 
        blnFree=HeapFree(GetProcessHeap(),0,pDpiData);
        #ifdef MyDebug
        fprintf(fp,"  pDpiData         = %p\r\n",pDpiData);
        fprintf(fp,"  blnFree          = %d\r\n",blnFree);
        #endif 
        PostQuitMessage(0);
        #ifdef MyDebug
        fprintf(fp,"Leaving fnWndProc()   : Message WM_DESTROY\r\n");
        #endif
        fclose(fp);
        return 0;
     }
 }
 
 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


The critical code above is this…

1
2
3
4
5
6
7
8
9
10
hDC          = GetDC(hWnd);        
dpiX         = GetDeviceCaps(hDC, LOGPIXELSX);   
dpiY         = GetDeviceCaps(hDC, LOGPIXELSY);
…
…
ReleaseDC(hWnd,hDC);        
…
…
pDpiData->rx = dpiX/96.0;
pDpiData->ry = dpiY/96.0;


…where pDpiData is an object like so…

1
2
3
4
5
struct DpiData                    
{                                 
 double  rx; 
 double  ry;
};


Typically I allocate an object like that from the heap, and I store a pointer to it within each instantiated window object through the WNDCLASS::cbWndExtra bytes. That’s the C way of doing OOP in case you didn’t know. And that’s what’s going on with the HeapAlloc(), SetWindowLongPtr(), and GetWindowLongPtr() stuff above. You will find no global variables in any of my code.

Now, when run in standard and recommended resolutions dpiX and dpiY will typically end up as 96 dots per inch. Here is an output run on my Dell M6500 17” Windows 7 laptop whose standard resolution is 1920 X 1200…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#if 0
At 1920 X 1200 Resolution Small Fonts Windows 7 Dell M6500 Laptop With 17" Screen...

Entering fnWndProc() : Message WM_CREATE
  hWnd             = 0000000000B30468
  blnHiDpiAware    = 1
  pDpiData         = 0000000000231C10
  dpiX             =  96.000
  dpiY             =  96.000
  pDpiData->rx     = 1.000
  pDpiData->ry     = 1.000
Leaving fnWndProc()  : Message WM_CREATE

Entering fnWndProc()  : Message WM_DESTROY
  hWnd             = 0000000000B30468
  pDpiData         = 0000000000231C10
  blnFree          = 1
Leaving fnWndProc()   : Message WM_DESTROY
#endif 


As can be seen, we’ve got 96 dpi (dots per inch) and the rx and ry ratios come out to 1.0, i.e., no scaling. Where the problem comes in is that a coder will typically design an app at such standard resolutions, but a user who is old or with poor eyesight might go into Control Panel >>> Display and change the resolution or increase the text/font size. When that happens all h*** breaks loose. The application visuals go totally to pot. User interface elements such as buttons, edit controls, combo boxes, etc., end up off the application somewhere, and text gets clipped within controls. Here is an example of what the above metrics can look like on that same box when the resolution is set to 1280 X 800 with Medium Fonts…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#if 0
At 1280 X 800 Resolution Medium Fonts Windows 7 Dell M6500 Laptop With 17" Screen...

Entering fnWndProc() : Message WM_CREATE
  hWnd             = 0000000000010182
  blnHiDpiAware    = 1
  pDpiData         = 0000000000311C10
  dpiX             = 120.000
  dpiY             = 120.000
  pDpiData->rx     = 1.250
  pDpiData->ry     = 1.250
Leaving fnWndProc()  : Message WM_CREATE

Entering fnWndProc()  : Message WM_DESTROY
  hWnd             = 0000000000010182
  pDpiData         = 0000000000311C10
  blnFree          = 1
Leaving fnWndProc()   : Message WM_DESTROY
#endif 

And here are some even more radicle results from my Windows 10 HP Envy 17” laptop when custom scaling factors are used…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Entering fnWndProc() : Message WM_CREATE
  hWnd             = 00000000000501FE
  blnHiDpiAware    = 1
  pDpiData         = 0000000000570810
  dpiX             = 192.000
  dpiY             = 192.000
  pDpiData->rx     = 2.000
  pDpiData->ry     = 2.000
Leaving fnWndProc()  : Message WM_CREATE

Entering fnWndProc()  : Message WM_DESTROY
  hWnd             = 00000000000501FE
  pDpiData         = 0000000000570810
  blnFree          = 1
Leaving fnWndProc()   : Message WM_DESTROY


As you can see, what will soon become our ‘scaling factor’ is now at 2.0 instead of 1.0. The way this information is used to correct the application is to multiply every pixel placement location or size by this scaling factor. For example, if you want to create a button control like so…

 
hButton = CreateWindow("button", "Execute", WS_CHILD|WS_VISIBLE, 70, 35, 100, 30, hWnd, (HMENU)IDC_BUTTON, hIns, 0);


…you need to multiply the 70, 35, 100, 30 each by the appropriate rx/ry scaling factor. At standard resolutions such as you likely design by, these pixel coordinates and sizes will end up being multiplied by 1.0 yielding no change. But if your user modified Display Properties, the multiplication could very well end up being by 1.25, 1.44, 1.50, or some other number. I have personally seen all the numbers I just listed. So as not to trash the code by doing these multiplications right in the function calls, I created simple #define macros to make it a bit cleaner….

1
2
#define SizX(x)   (int)(x * pDpiData->rx)       // For DPI Scaling Calculations And Purposes
#define SizY(y)   (int)(y * pDpiData->ry)    


So, using those macros the above CreateWindow() function call for our button would simply be this…

 
hButton = CreateWindow("button", "Execute", WS_CHILD|WS_VISIBLE, SizX(70), SizY(35), SizX(100), SizY(30), hWnd, (HMENU)IDC_BUTTON, hIns, 0);


Not really too much worse, in my opinion, and far better than having an unusable app. So lets create a very simple ‘Log On’ type app with two labels, two edit controls, and a Log On and Cancel button. But lets use my function pointer setup for message handlers instead of the typical switch construct for mapping Windows messages to the code which handles the message. From my exposition of the required code above related to DPI independence you should be able to understand this app even if you don’t understand my function pointer message handling logic. Here is HiDpiEx04.h and HiDpiEx04.cpp…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//HiDpiEx04.h
#ifndef HiDpiEx04_h
#define HiDpiEx04_h

#define IDC_USERNAME              2000
#define IDC_PASSWORD              2005
#define IDC_SUBMIT                2010
#define IDC_CANCEL                2015
#define dim(x)                    (sizeof(x) / sizeof(x[0]))
#define SizX(x)                   (int)(x * pDpiData->rx)       // For DPI Scaling Calculations And Purposes
#define SizY(y)                   (int)(y * pDpiData->ry)       // For DPI Scaling Calculations And Purposes
#ifdef TCLib
   extern "C" int                 _fltused=1;
#endif
   
struct WndEventArgs
{
 HWND                             hWnd;
 WPARAM                           wParam;
 LPARAM                           lParam;
 HINSTANCE                        hIns;
};

LRESULT fnWndProc_OnCreate        (WndEventArgs& Wea);
LRESULT fnWndProc_OnCommand       (WndEventArgs& Wea);
LRESULT fnWndProc_OnDestroy       (WndEventArgs& Wea);

LRESULT btnSubmit_Click           (WndEventArgs& Wea);
LRESULT btn_Cancel_Click          (WndEventArgs& Wea);

struct EVENTHANDLER
{
 unsigned int                     iMsg;
 LRESULT                          (*fnPtr)(WndEventArgs&);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,                      fnWndProc_OnCreate},
 {WM_COMMAND,                     fnWndProc_OnCommand},
 {WM_DESTROY,                     fnWndProc_OnDestroy}
};

struct DpiData                    
{                                 
 double                           rx; 
 double                           ry;
};

#endif 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#ifndef UNICODE
   #define UNICODE            
#endif                        
#ifndef _UNICODE              
   #define _UNICODE           
#endif  
//#define TCLib                       
#include <windows.h>
#ifdef TCLib                  
   #include "stdio.h"         
#else   
   #include <stdio.h>
#endif   
#include "HiDpiEx04.h"          
#define Debug                 
#ifdef Debug
   FILE* fp=NULL;
#endif 


void SetMyProcessDpiAware()   
{                             
 BOOL (__stdcall* pFn)(void); 

 HINSTANCE hInstance=LoadLibrary(L"user32.dll");
 if(hInstance)
 {
    pFn=(BOOL (__stdcall*)(void))GetProcAddress(hInstance,"SetProcessDPIAware");
    if(pFn)
       pFn();
    FreeLibrary(hInstance);
 }
}


#ifdef Debug
BOOL IsMyProcessDpiAware()    
{                             
 BOOL (__stdcall* pFn)(void); 
 BOOL blnReturn=FALSE;
                              
 HINSTANCE hInstance=LoadLibrary(L"user32.dll");
 if(hInstance)
 {
    pFn=(BOOL (__stdcall*)(void))GetProcAddress(hInstance,"IsProcessDPIAware");
    if(pFn)
       blnReturn=pFn();
    FreeLibrary(hInstance);
 }
 
 return blnReturn;
}
#endif


LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)
{
 BOOL blnDpiAware=FALSE;       
 DpiData* pDpiData=NULL; 
 double dblFont=8.0; 
 char szBuffer[16];   
 HANDLE hHeap=NULL;            
 HFONT hFont=NULL;  
 double dpiX,dpiY;               
 HDC hDC=NULL;                
 HWND hCtl;
 
 #ifdef Debug
 fp=fopen("Output.txt","w");
 fprintf(fp,"Entering fnWndProc_OnCreate()\r\n");
 fprintf(fp,"  Wea.hWnd         = %p\r\n",Wea.hWnd);
 #endif 
 Wea.hIns    = ((LPCREATESTRUCT)Wea.lParam)->hInstance;
 hHeap       = GetProcessHeap();
 blnDpiAware = IsProcessDPIAware();
 #ifdef Debug
 fprintf(fp,"  blnDpiAware      = %d\r\n",blnDpiAware);
 #endif
 hDC          = GetDC(NULL);                      
 dpiX         = (double)GetDeviceCaps(hDC, LOGPIXELSX);   
 dpiY         = (double)GetDeviceCaps(hDC, LOGPIXELSY);   
 pDpiData     = (DpiData*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(DpiData));                                                
 if(!pDpiData)                                    
    return -1;                                    
 SetWindowLongPtr(Wea.hWnd,0*sizeof(void*),(LONG_PTR)pDpiData);
 pDpiData->rx = dpiX/96.0;
 pDpiData->ry = dpiY/96.0;
 #ifdef Debug
    fprintf(fp,"  pDpiData         = %p\r\n",pDpiData);
    #ifdef TCLib
       FltToCh(szBuffer,dpiX,8,3,'.',true);
       fprintf(fp,"  dpiX             = %s\r\n",szBuffer);
       FltToCh(szBuffer,dpiY,8,3,'.',true);
       fprintf(fp,"  dpiY             = %s\r\n",szBuffer);
       FltToCh(szBuffer, pDpiData->rx, 6, 3, '.',true);
       fprintf(fp,"  pDpiData->rx     = %s\r\n",szBuffer);
       FltToCh(szBuffer, pDpiData->ry, 6, 3, '.',true);
       fprintf(fp,"  pDpiData->ry     = %s\r\n",szBuffer);
    #else
       fprintf(fp,"  dpiX             = %f\r\n",dpiX);
       fprintf(fp,"  dpiY             = %f\r\n",dpiY);
       fprintf(fp,"  pDpiData->rx     = %6.3f\n",pDpiData->rx);
       fprintf(fp,"  pDpiData->ry     = %6.3f\n",pDpiData->ry);      
    #endif
 #endif
 MoveWindow(Wea.hWnd,SizX(200),SizY(100),SizX(300),SizY(124),FALSE);
 hFont=CreateFont(-MulDiv(dblFont,dpiY,72),0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,0,0,0,0,L"Tahoma"); 
 SetWindowLongPtr(Wea.hWnd,GWLP_USERDATA,(LONG_PTR)hFont);
 hCtl=CreateWindowEx(0,L"static",L"User Name",WS_CHILD|WS_VISIBLE|SS_RIGHT,SizX(10),SizY(12),SizX(90),SizY(16),Wea.hWnd,(HMENU)-1,Wea.hIns,0);
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 hCtl=CreateWindowEx(WS_EX_CLIENTEDGE,L"edit",L"",WS_CHILD|WS_VISIBLE|WS_TABSTOP,SizX(110),SizY(10),SizX(160),SizY(20),Wea.hWnd,(HMENU)IDC_USERNAME,Wea.hIns,0);
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 hCtl=CreateWindowEx(0,L"static",L"Password",WS_CHILD|WS_VISIBLE|SS_RIGHT,SizX(10),SizY(36),SizX(90),SizY(16),Wea.hWnd,(HMENU)-1,Wea.hIns,0);
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 hCtl=CreateWindowEx(WS_EX_CLIENTEDGE,L"edit",L"",WS_CHILD|WS_VISIBLE|WS_TABSTOP,SizX(110),SizY(34),SizX(160),SizY(20),Wea.hWnd,(HMENU)IDC_PASSWORD,Wea.hIns,0);
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 hCtl=CreateWindowEx(0,L"button",L"Submit",WS_CHILD|WS_VISIBLE|WS_TABSTOP,SizX(120),SizY(60),SizX(60),SizY(20),Wea.hWnd,(HMENU)IDC_SUBMIT,Wea.hIns,0);
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 hCtl=CreateWindowEx(0,L"button",L"Cancel",WS_CHILD|WS_VISIBLE|WS_TABSTOP,SizX(200),SizY(60),SizX(60),SizY(20),Wea.hWnd,(HMENU)IDC_CANCEL,Wea.hIns,0);
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 ReleaseDC(Wea.hWnd,hDC);
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnCreate()\r\n\r\n");
 #endif 
 
 return 0;
}


LRESULT btnSubmit_Click(WndEventArgs& Wea)
{
 MessageBox(Wea.hWnd,L"You Clicked The Submit Button",L"Button Click Report",MB_OK);
 return 0;
}


LRESULT btnCancel_Click(WndEventArgs& Wea)
{
 MessageBox(Wea.hWnd,L"You Clicked The Cancel Button",L"Button Click Report",MB_OK);
 return 0;
} 


LRESULT fnWndProc_OnCommand(WndEventArgs& Wea)
{
 switch(LOWORD(Wea.wParam))
 {
   case IDC_SUBMIT:
     return btnSubmit_Click(Wea);
   case IDC_CANCEL:
     return btnCancel_Click(Wea);
 } 
 
 return DefWindowProc(Wea.hWnd, WM_CTLCOLOREDIT, Wea.wParam, Wea.lParam); 
}	


LRESULT fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 DpiData* pDpiData=NULL;
 BOOL blnFree=FALSE;
 HFONT hFont=NULL;
 
 #ifdef Debug
 fprintf(fp,"Entering fnWndProc_OnDestroy()\r\n");
 fprintf(fp,"  Wea.hWnd         = %p\r\n",Wea.hWnd);
 #endif 
 pDpiData=(DpiData*)GetWindowLongPtr(Wea.hWnd,0*sizeof(void*)); 
 blnFree=HeapFree(GetProcessHeap(),0,pDpiData);
 #ifdef Debug
 fprintf(fp,"  pDpiData         = %p\r\n",pDpiData);
 fprintf(fp,"  blnFree          = %d\r\n",blnFree);
 #endif 
 hFont=(HFONT)GetWindowLongPtr(Wea.hWnd,GWLP_USERDATA);
 blnFree=DeleteObject(hFont);
 #ifdef Debug
 fprintf(fp,"  hFont            = %p\r\n",hFont);
 fprintf(fp,"  blnFree(hFont1)  = %d\r\n",blnFree);
 #endif
 PostQuitMessage(0);
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnDestroy()\r\n");
 fclose(fp);
 #endif
 
 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI wWinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
{
 wchar_t szClassName[]=L"HiDpiEx04";
 MSG messages;
 WNDCLASS wc;
 HWND hWnd;

 memset(&wc,0,sizeof(WNDCLASS));
 SetMyProcessDpiAware();
 wc.lpszClassName = szClassName,                     wc.lpfnWndProc   = fnWndProc;
 wc.hIcon         = LoadIcon(NULL,IDI_APPLICATION),  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 
 wc.hInstance     = hIns,                            wc.hCursor       = LoadCursor(NULL,IDC_ARROW),          
 wc.cbWndExtra    = sizeof(void*);
 RegisterClass(&wc);
 hWnd=CreateWindow(szClassName,L"Dpi Aware",WS_OVERLAPPEDWINDOW,0,0,0,0,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    if(!IsDialogMessage(hWnd,&messages))
    { 
       TranslateMessage(&messages);
       DispatchMessage(&messages);
    }   
 }

 return messages.wParam;
}


continued...
The command line compilation strings and binary sizes for standard C linkages with the C Standard Library, and for my own TCLib (Tiny C Library), are as follows…

1
2
3
4
5
6
7
// Uses/links with TCLib:     cl HiDpiEx04.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib gdi32.lib
//   7,680 Bytes              TCLib, x64, VC19, RELEASE, UNICODE
//   9,728 Bytes              TCLib, x64, VC19, DEBUG,   UNICODE

// Uses/links with LIBCMT:    cl HiDpiEx04.cpp /O1 /Os /MT kernel32.lib user32.lib gdi32.lib
//  88,576 Bytes              LIBCMT, x64, VC19, RELEASE, UNICODE
// 125,440 Bytes              LIBCMT, x64, VC19, DEBUG,   UNICODE 


What might be extremely useful for you to do to see clearly the effects of all this is to compile both a Hi DPI Aware version of the program, and one with the call to SetProcessDPIAware remmed out (Rem out my SetMyProcessDpiAware() in WinMain()). Then go to…

Settings >>> Control Panel >>> System >>> Display

…and change the Font/Text Scaling to some large number like 200% or 250% or something like that. Then run both versions of the program side by side to see the effects. One thing I might add – on one of the versions do a Release build by commenting out the Debug define. Recall that they both would open that output.txt debug file, and that wouldn’t be good if both versions are in the same directory.

When I did that on my HP Envy the relative sizes of the controls, though different, were OK. Nothing got clipped off the app’s client area. But the noteworthy degradation that did occur and which you’ll immediately see if you do this, is that the text of the version with the call to SetProcessDPIAware remmed out is very blurry, and drastically inferior to the version actually running High DPI Aware. The reason for this is that the operating system virtualizes fonts on older apps not running High Dpi Aware through some sort of pixel sampling algorithm, and that results in blurry text. You’ll likely only ever notice it if you perform my experiment as described above where you run the same app side by side with one High Dpi Aware and the other not. So in closing I’d like to say that if you don’t code High Dpi Aware, all kinds of degradations can occur with your apps ranging from controls that don’t fit on the client area, to degraded and blurry text.
I have no words to describe how sad i am.
I let you guys down and i have abandoned the project (not because of being busy)
When i regain my interest in continuing the project i am sure i will read every word you just said again and again until i make it work!
In the mean time i hope the community and the random people that search for help will find this topic.
You can call me a dick, i deserve it, i just feel awfull because i literally have wasted your precious time.
My sincere apologies, Rapan Cristian.
Topic archived. No new replies allowed.