Changing Font Size

Pages: 12
I am looking to change the size of a font, in a program. What code do I need, to change the size of the font? I already have a handle to that font (HFONT) From what I have read, I need to access the "LOGFONT" structure for that font, and change the height parameter. However, I can't find the code that I need, to do this. Any help?
CreateFont() returns a HFONT. SelectObject() installs it in a device context.
Unfortunately anachronon, you'll find that fonts are an extremely complicated subject. And to make matters worse, there is the whole issue of high resolution monitors, also known as the 'High DPI' issue (DPI - Dots Per Inch). This greatly complicates the font issue. But more about that later. Here's a snipet of code from one of my data entry apps. Whats going on here is that I have a host exe which uses a custom control 'data entry grid' in a Dll. I create the font to be used in the WM_CREATE handler of the host app, then store it using SetWindowLongPtr() in the WNDCLASSEX::cbWndExtra bytes (as you've seen me do) of the grid control and I believe the parent of the grid control in the host app....

1
2
3
4
5
6
7
8
9
10
11
dblFont=24.0;
int nHeight=0;
double dblHeight=(dblFont*(double)dpiY)/72;
nHeight=(int)dblHeight;
#ifdef DEBUG
fprintf(fp,"  dblHeight            = %6.2f\n",dblHeight);
fprintf(fp,"  nHeight              = %d\n",nHeight);
#endif
hFont=CreateFont(-nHeight,0,0,0,FW_BOLD,0,0,0,DEFAULT_CHARSET,0,0,0,0,L"Courier New");
if(!hFont) goto Terminate;
   SetWindowLongPtr(Wea.hWnd,12*sizeof(void*),(LONG_PTR)hFont);


But when executing a WM_PAINT handler and one gets an HDC, one must select the custom created HFONT into the device context with SelectObject(). Here is my WM_PAINT handler from a "Cell" object which is part of the grid control....

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
LRESULT fnCell_OnPaint(WndEventArgs& Wea, dllWndEventArgs& dllWea)
{
 TCHAR* pNum=NULL;                                          // struct GridData                     
 HFONT hFont,hTmp;                                          // { 
 double dpiY,dpiX;                                          //  unsigned int iRows;                        
 PAINTSTRUCT ps;                                            //  unsigned int iRowHt;                           
 HWND hParent;                                              //  HFONT        hFont;
 RECT rc;                                                   // };                                 
 HDC hDC;                                  

 hDC=BeginPaint(Wea.hWnd,&ps);                              // wea->hWnd is HWND grid cell  
 hParent=GetParent(Wea.hWnd);                               // grid cell is child of grid, i.e., hParent
 hFont=(HFONT)GetWindowLongPtr(hParent,8*sizeof(void*));    // HFONT created in client and sent in through 
 hTmp=(HFONT)SelectObject(hDC,hFont);                       // GridData object in lpCreateParams of grid
 rc.left=0, rc.top=0;                                       // CreateWindow() Call.
 dpiX=(double)GetDeviceCaps(hDC, LOGPIXELSX);   
 dpiY=(double)GetDeviceCaps(hDC, LOGPIXELSY);   
 rc.right=(int)SizX(80);
 rc.bottom=(int)SizY(30);
 pNum=(TCHAR*)GetWindowLongPtr(Wea.hWnd,0*sizeof(void*));
 DrawText(hDC,pNum,-1,&rc,DT_SINGLELINE);
 SelectObject(hDC,hTmp);
 EndPaint(Wea.hWnd,&ps);

 return 0;
}


What's going on there is that in the WM_PAINT handler, after painting is done, the original HFONT in the device context must be restored. The first SelectObject() call which installs your new custom created HFONT returns the old or original HFONT, which must be saved in a temporary HFONT variable. Then it is restored at termination of the procedure.

Note that rather than putting a CreateFont() call in the WM_PAINT handler, I put it in a WM_CREATE handler and save it in the WNDCLASSEX::cbWndExtra bytes. Saves constantly creating and destroying it.
Oh! On re-reading your post I see you already have a HFONT you want to use. So that leaves you with only needing SelectObject().

There's that CreateFontIndirect() function (or whatever its called) that takes a LOGFONT struct. I used to use that. Somewhere along the line I gave up on it and changed to just CreateFont(). It dispenses with the LOGFONT struct, and I didn't shed any tears over it.
This protocol with SelectObject() and returning the original HFONT back to the HDC is really important because if not done a serious memory leak can occur. I think maybe in the more recent operating systems it might not be as bad as it used to be, but back in the early days of Windows it typically took the app down in short order and the operating system with it.
Thanks Freddie1. Unfortunately, I find your code to be just as confusing, as all of the other code examples that I have read. Perhaps, if I showed the code that I am using, you can help me to proceed from there. Here is a simplified version. I am trying to place a system font in a button. I have removed all of the error checking, and replaced the variable names with ones that are more self-explanatory.

Here is the code that I have in the WM_CREATE block:

1
2
3
4
5
6
7
hBtnFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
            
hButton = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
                    100, 100, 100, 20,
                    hwnd, (HMENU)BTNNUM, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

SendMessage(hButton, WM_SETFONT, (WPARAM)hBtnFont, (LPARAM)MAKELONG(TRUE, 0));


Rather than WM_PAINT, I am handling it through WM_DRAWITEM. The "DRAWITEMSTRUCT" structure creates and destroys its own HDC. Here is that code:

1
2
3
4
5
6
7
8
9
10
case WM_DRAWITEM:
         switch (wParam)
                case BTNNUM:
                      RECT              rButton;
                      LPDRAWITEMSTRUCT  pDraw=NULL;

                      GetClientRect(hButton, &rButton);
                      pDraw = LPDRAWITEMSTRUCT(lParam);
                      DrawText(pDraw->hDC, L"Button", 6, &rButton, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                      return 0;


Perhaps, to start, you could show me how to fix this, so that I don't get memory leaks. Once I have that problem solved, we could discuss how to change the font size.


Last edited on
I only have a second now, as I'm caulking stone work around my log cabin. I'll study your situation in detail later, but just off the top of my head, when dealing with Windows Controls, one uses the SendMessage() funcrion with the WM_SETFONT message. Perhaps that's what you are missing?

Here's PowerBASIC code I was able to grab quick....

1
2
3
4
5
Sub CreateButton(Wea As WndEventArgs, szCaption As ZStr, dwStyle As Dword, x As Long, y As Long, cx As Long, cy As Long, iCtlId As Long, hFont As Dword, rxRatio As Single, ryRatio As Single)
  Local hCtl As Dword
  hCtl=CreateWindowEx(0,"button",szCaption,%WS_CHILD Or %WS_VISIBLE Or %WS_TABSTOP,SizX(x),SizY(y),SizX(cx),SizY(cy),Wea.hWnd,iCtlId,Wea.hInst,ByVal 0)
  Call SendMessage(hCtl,%WM_SETFONT,hFont,0)
End Sub
OK, I looked at it again, and see you are using SendMessage(WM_SETFONT). That's the correct way to do it. After I eat I'll try to figure out what you are doing with thst WM_DRAWITEM code. I don't follow that.
I'm confused too, Anachronon. By using GetStockObject() you'll be getting a particular font of, I believe, an unalterable size. I believe using SendMessage(WM_SETFONT) on a control where you have retrieved the DEFAULT_GUI_FONT will accomplish nothing because the DEFAULT_GUI_FONT will be painted there before your SendMessage() call, and nothing will change after the call.

Everything I said about GDI memory leaks doesn't apply in this case. Don't worry about it.

To change the size of the font you'll have to use either CreateFontIndirect() or CreateFont(). Once you've created the font then you're SendMessage() call to your button will work fine. I don't understand why you need to handle a WM_DRAWITEM message.

The CreateFont/CreateFontIndirect functions look daunting with all the fields/parameters of the call, many of which are quite confusing. However, in practice, the call is unbelievably forgiving. In my experience, almost nothing will cause the call to fail. You might not get exactly what you want, but the font mapper will give you something or other.
Years ago I posted quite a lot of code in this forum concerning the High DPI aware situation. I located the code here....

http://cplusplus.com/forum/windows/192541/

In there you'll find examples of use of the CreateFont() function where the correct modifications are made to translate the font size parameter to font size numbers one typically uses such as 8, 10, 12, 14, etc. It involves the muldiv() macro.

Here's some of the applicable code, for example, if you want the Tahoma font of size 8.0...

1
2
3
4
5
6
7
double dblFont=8.0; 
double dpiX,dpiY; 

dpiY=(double)GetDeviceCaps(hDC, LOGPIXELSY);   
hFont=CreateFont(-MulDiv(dblFont,dpiY,72),0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,0,0,0,0,L"Tahoma");
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); 
Last edited on
Hey Freddie, Actually, the default font used by VS2109 on buttons, is similar to the text on the "Reply" button, below. The font that I am calling, is that of the menu text on the title and menu bars.

From what I have read, WM_DRAWITEM is a special, self-contained version of WM_PAINT, used with "owner draw" buttons (and maybe some other controls). This way, such child buttons do not need their own callback functions. "LPDRAWITEMSTRUCT" is a pointer to a DRAWITEMSTRUCT structure. When declared, this structure creates its own GetDC() and BeginPaint() functions. When destroyed (falls out of scope) it initiates its own EndPaint() and ReleaseDC() functions. Or, so I have read.

At this point, I haven't messed with the colors of the button. I'm still trying to work the bugs of painting the text. Should I move my WM_SETFONT message, to the WM_DRAWITEM block (instead of WM_CREATE)? Plus, are you sure that I don't need to restore the old font, to prevent memory leaks? I seem to have a memory leak somewhere. But then, VS2019 is a very leaky program, itself.
This is something that I'm afraid I'm not going to be able to help you with anachronon. I'm not in the least bit familiar with that message.

About the only advice I can give you concerning situations where I had to become familiar with some new and complex message or programming concept, is to create a very simple app that illustrates that concept alone, and put debug output statements everywhere to find out what values variables are taking on, and illustrating the sequence of events as they occur.

How do you debug? I seem to recall some post of yours where you mentioned MessageBoxes? I thought of replying but didn't. I'll mention it now though. Don't use message boxes to debug. It is a very, very bad idea. In my mind there are really only two ways to do it. Most folks like the stepping debuggers. I personally don't care for them. I prefer output to a log file. In that way there is no interference with messaging in the Window Procedure. You can determine what values variables are taking on, and track the sequence of events. Further, you can study the log file at your leisure and 'ruminate' over it to gain an understanding of what's going on.

I read the MS docs on WM_DRAWITEM and the documentation struct me as leaving something to be desired. That's why I said you'll have to sink a bit of time into really exploring the situation to find out what is really going on.

You mentioned about changing the color of a button. That is something I may be able to help you with. Although you could likely figure it out yourself. I don't believe I've ever changed the color of a button, but I have changed the color of labels and check boxes. Actually, something just dawned on me. I guess I have changed the color of buttons because a checkbox is a button with a special style! Anyway, its done by processing the WM_CTLCOLORSTATIC message. It is a bit involved, and actually seems something like that WM_DRAWITEM message. Not exactly, but close.
@OP,
is this maybe sth. you want accomplish?
https://pasteboard.co/JqgpmrV.png
I re-read your posts anachronon and I believe the advice I gave you was correct. Somehow I was mistakeingly thinking you were wanting to change the font used by menus. It looks like you just want to change the font - maybe just the size, of the font in a button, just like in the *.png Thomas1965 posted. I'm wondering if you aren't making this more complicated than it is. Is this button you are wanting to change the font an "owner draw button"? I really don't know anything about that, as I've never created an owner draw control. I routinely make "custom controls" and ActiveX Controls, but never "owner drawn" controls.

If its just a regular button and you want to make the text of the button smaller or larger, or change the typeface name, then the HFONT returned by GetStockObject() isn't what you want to use. When you get an HFONT returned by GetStockObject(), that font has a specific name, height, width, etc., etc., which can't be changed. In other words, you can't start with that HFONT and modify it somehow to make it a bit smalleer, larger, more bold, less bold, etc., than it alread is. In still other words, that specific HFONT is unalterable.

However, if you have the 'Face Name' of some specific font, you can create that font of any size using one of the HFONT creation functions. Like I said I'm partial to CreateFont(). But CreateFontIndirect() works just the same. Once you've created the HFONT you simply use SendMessage to set the new font in the control. Here is the code that more or less creates Thomas1965's *.png image...

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
// Form11.cpp
// cl Form11.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib gdi32.lib
// cl Form11.cpp /O1 /Os kernel32.lib user32.lib gdi32.lib
// Size: 56,320 Bytes; LIBC, LIBCMT Linkage; VC15 (VStudio 2008)
// Size:  5,632 Bytes; TCLib Linkage; VC15 (VStudio 2008)
//#define TCLib
//#define Debug
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE   
   #define _UNICODE
#endif   
#include <windows.h>
#ifdef TCLib
   #include "stdio.h"
   #include "Form11.h"
   extern "C" int _fltused = 1;
#else
   #include <cstdio>
   #include "Form11.h"
#endif 
#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 CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
 HWND   hCtl        = NULL;
 HDC    hDC         = NULL;
 HFONT  hFont       = NULL;
 BOOL   blnDpiAware = FALSE;
 double dblFont     = 14.0;
 double dpiX        = 0.0;
 double dpiY        = 0.0;
 int    nHeight     = 0; 
 
 #ifdef Debug
 fp=fopen("Output.txt","w");
 fprintf(fp,"Entering fnWndProc_OnCreate()\n");
 #endif
 Wea.hIns  = ((LPCREATESTRUCT)Wea.lParam)->hInstance; 
 hDC       = GetDC(NULL);                      
 dpiX      = (double)GetDeviceCaps(hDC, LOGPIXELSX);   
 dpiY      = (double)GetDeviceCaps(hDC, LOGPIXELSY);   
 nHeight   = -MulDiv(dblFont, dpiY, 72);
 blnDpiAware = IsProcessDPIAware();
 #ifdef Debug
 fprintf(fp,"  blnDpiAware       = %d\n",blnDpiAware);
 fprintf(fp,"  nHeight           = %d\n",nHeight);
 fprintf(fp,"  dpiX              = %6.3f\n",dpiX);
 fprintf(fp,"  dpiY              = %6.3f\n",dpiY);
 #endif
 MoveWindow(Wea.hWnd,SizX(200),SizY(200),SizX(400),SizY(400),FALSE);
 ReleaseDC(Wea.hWnd,hDC);
 hCtl  = CreateWindow(L"button",L"Default Button",WS_CHILD|WS_VISIBLE,SizX(40),SizY(40),SizX(150),SizX(40),Wea.hWnd,(HMENU)IDC_DEFAULT_BUTTON,Wea.hIns,0); 
 hCtl  = CreateWindow(L"button",L"User Button",WS_CHILD|WS_VISIBLE,SizX(40),SizX(200),SizX(150),SizX(40),Wea.hWnd,(HMENU)IDC_CUSTOM_FONT_BUTTON,Wea.hIns,0); 
 hFont = CreateFont(nHeight,0,0,0,FW_BOLD,0,0,0,0,0,0,2,0,L"DEFAULT_GUI_FONT");
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");
 #endif
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 #ifdef Debug
 fprintf(fp,"Entering fnWndProc_OnDestroy()\n");
 fprintf(fp,"  Wea.hWnd = %u\n",Wea.hWnd);
 #endif
 PostQuitMessage(0);
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnDestroy()\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 WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 wchar_t szClassName[]=L"Form10";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;
 
 SetMyProcessDpiAware();
 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=0;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=NULL;                             wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,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 (int)messages.wParam;
}


Here's the header....

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
//Form11.h
#ifndef Form11_h
#define Form11_h

#define IDC_DEFAULT_BUTTON                1500
#define IDC_CUSTOM_FONT_BUTTON            1505
#define dim(x)                            (sizeof(x) / sizeof(x[0]))
#define SizX(x)                           (x * dpiX/96)
#define SizY(y)                           (y * dpiY/96)

struct                                    WndEventArgs
{
 HWND                                     hWnd;
 WPARAM                                   wParam;
 LPARAM                                   lParam;
 HINSTANCE                                hIns;
};

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

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

const EVENTHANDLER                        EventHandler[]=
{
 {WM_CREATE,                              fnWndProc_OnCreate},
 {WM_DESTROY,                             fnWndProc_OnDestroy}
};
#endif 


I developed the above code on a Dell M6500 laptop running Windows 7 Professional with the 64 bit OS. If Debug is defined in the above program, and running the default display configuration, I get the following statistics in the Output.txt log file....

1
2
3
4
5
6
7
8
9
10
Entering fnWndProc_OnCreate()
  blnDpiAware       = 1
  nHeight           = -19
  dpiX              = 96.000
  dpiY              = 96.000
Leaving fnWndProc_OnCreate()

Entering fnWndProc_OnDestroy()
  Wea.hWnd = 2163426
Leaving fnWndProc_OnDestroy()


I had to log off to change the display configuration to 'Medium Fonts', and here is that log file data....

1
2
3
4
5
6
7
8
9
10
Entering fnWndProc_OnCreate()
  blnDpiAware       = 1
  nHeight           = -23
  dpiX              = 120.000
  dpiY              = 120.000
Leaving fnWndProc_OnCreate()

Entering fnWndProc_OnDestroy()
  Wea.hWnd = 393804
Leaving fnWndProc_OnDestroy()


Just dawned on me! You are concerned about memory leaks, no doubt because I ominously warned you about them in coding GDI routines! And what did I just do? I posted code above with a memory leak!

In fnWndProc_OnCreate() above I used CreateFont() to - guess what? Create a font. An HFONT is a GDI object that needs to be deleted with DeleteObject(). Something I don't know is whether one could delete it in fnWndProc_OnCreate() after the SendMessage() call. I could test that of course, but what I always do, except when I forget to do it as above, is store the HFONT somehow and somewhere or other until the app closes out. As you know from studying other code I posted, I typically store values that need to persist in the WNDCLASSEX::cbWndExtra Bytes. Then in a WM_DESTROY handler I'd retrieve it and use DeleteObject() on it. I guess I'll repost it in a bit or fix the above.
I'll repost the *.cpp file. But first I'll mention something that occurred to me. I got to wondering what would happen if I called DeleteObject(hFont) on the font I just created in my WM_CREATE handler after I used SendMessage() to send it into the button control. My past history of coding these things had always been to assume that Windows would be relying upon the HFONT I just created, and relying upon its continued existence until the termination of the app. But then I thought, "You know, Windows might internally make a copy of it and use that copy instead of the one I created". Windows does do that at times. So I performed an experiment. After the SendMessage() call to send my HFONT to the button, I deleted it. Guess what happened! I started the app and was fully expecting it to work properly. I guess I was actually fearing it would work correctly which would make me a fool for all the extra work I had done over the years saving and retrieving HFONTs from WNDCLASSEX::cbWndExtra bytes.

When the app started I knew something was amiss. It took an extra second or two to start, and when it finally became visible it was like only about 25% there. There was a hesitation, and then finally the app came up and presented a normal appearance, except that the lower button had the same font as the 'default' button. Its kind of clear what happened. Microsoft naturally has all kinds of error handling code. It obviously looked for the HFONT I gave it - and it was gone. I had deleted it. No doubt all kinds or error handling code had to be loaded and 'kicked in'. That would explain the abnormal instantiation. But it worked. Microsoft saved the app and ran code to use the default font for the button. I find that interesting.

So anyway, here's the fixed *.cpp file. Just to do something different I used Window Properties instead of WNDCLASSEX::cbWndExtra bytes.

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
// Form11.cpp
// cl Form11.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib gdi32.lib
// cl Form11.cpp /O1 /Os kernel32.lib user32.lib gdi32.lib
// Size: 56,320 Bytes; LIBC, LIBCMT Linkage; VC15 (VStudio 2008)
// Size:  6,144 Bytes; TCLib Linkage; VC15 (VStudio 2008)
//#define TCLib
//#define Debug
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE   
   #define _UNICODE
#endif   
#include <windows.h>
#ifdef TCLib
   #include "stdio.h"
   #include "Form11.h"
   extern "C" int _fltused = 1;
#else
   #include <cstdio>
   #include "Form11.h"
#endif 
#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 CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
 HWND   hCtl        = NULL;
 HDC    hDC         = NULL;
 HFONT  hFont       = NULL;
 BOOL   blnDpiAware = FALSE;
 double dblFont     = 14.0;
 double dpiX        = 0.0;
 double dpiY        = 0.0;
 int    nHeight     = 0; 
 
 #ifdef Debug
 fp=fopen("Output.txt","w");
 fprintf(fp,"Entering fnWndProc_OnCreate()\n");
 #endif
 Wea.hIns  = ((LPCREATESTRUCT)Wea.lParam)->hInstance; 
 hDC       = GetDC(NULL);                      
 dpiX      = (double)GetDeviceCaps(hDC, LOGPIXELSX);   
 dpiY      = (double)GetDeviceCaps(hDC, LOGPIXELSY);   
 nHeight   = -MulDiv(dblFont, dpiY, 72);
 blnDpiAware = IsProcessDPIAware();
 #ifdef Debug
 fprintf(fp,"  blnDpiAware       = %d\n",blnDpiAware);
 fprintf(fp,"  nHeight           = %d\n",nHeight);
 fprintf(fp,"  dpiX              = %6.3f\n",dpiX);
 fprintf(fp,"  dpiY              = %6.3f\n",dpiY);
 #endif
 MoveWindow(Wea.hWnd,SizX(200),SizY(200),SizX(400),SizY(400),FALSE);
 ReleaseDC(Wea.hWnd,hDC);
 hCtl  = CreateWindow(L"button",L"Default Button",WS_CHILD|WS_VISIBLE,SizX(40),SizY(40),SizX(150),SizX(40),Wea.hWnd,(HMENU)IDC_DEFAULT_BUTTON,Wea.hIns,0); 
 hCtl  = CreateWindow(L"button",L"User Button",WS_CHILD|WS_VISIBLE,SizX(40),SizX(200),SizX(150),SizX(40),Wea.hWnd,(HMENU)IDC_CUSTOM_FONT_BUTTON,Wea.hIns,0); 
 hFont = CreateFont(nHeight,0,0,0,FW_BOLD,0,0,0,0,0,0,2,0,L"DEFAULT_GUI_FONT");
 SendMessage(hCtl,WM_SETFONT,(WPARAM)hFont,0);
 SetProp(Wea.hWnd,L"Font",hFont);
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");
 #endif
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 HFONT hFnt = NULL;
 
 #ifdef Debug
 fprintf(fp,"Entering fnWndProc_OnDestroy()\n");
 fprintf(fp,"  Wea.hWnd = %u\n",Wea.hWnd);
 #endif
 hFnt=(HFONT)GetProp(Wea.hWnd,L"Font");
 DeleteObject(hFnt);
 RemoveProp(Wea.hWnd,L"Font");
 PostQuitMessage(0);
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnDestroy()\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 WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 wchar_t szClassName[]=L"Form10";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;
 
 SetMyProcessDpiAware();
 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=0;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=NULL;                             wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,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 (int)messages.wParam;
}
So in terms of memory leaks, I'd say the only thing to be cautious of when changing the font of a control with SendMessage(HFONT) is that one must at some point call DeleteObject() on the font created with CreateFont() or CreateFontIndirect(). In such cases one is not dealing with HDCs because those are encapsulated within the control, and in terms of Standard or Common Controls, HDCs are Microsoft's responsibility.

With a window though such as a top level window which one has registered and instantiated within one's app, and one in which one has a Window Procedure with a WM_PAINT handler, one must always restore the HDC to its initial state before calling EndPaint() and exiting the WM_PAINT handler. Here is an example of that....

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
// Form10.cpp
// cl Form10.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib gdi32.lib
// cl Form10.cpp /O1 /Os kernel32.lib user32.lib gdi32.lib
// Size:40,448 Bytes; LIBC, LIBCMT Linkage; VC15 (VStudio 2008)
// Size: 4,608 Bytes; TCLib Linkage; VC15 (VStudio 2008)
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE   
   #define _UNICODE
#endif   
#include <windows.h>
#include "Form10.h"


LRESULT CALLBACK fnWndProc_OnPaint(WndEventArgs& Wea)
{
  HDC         hDC         = NULL;
  HBRUSH      hBrush      = NULL;
  HBRUSH      hBrushTmp   = NULL;
  HFONT       hFont       = NULL;
  HFONT       hFntTmp     = NULL;
  wchar_t     szText[16];
  int         iBkMode;
  PAINTSTRUCT ps;
  RECT        rc;
  
  hDC       = BeginPaint(Wea.hWnd, &ps);
  hBrush    = CreateSolidBrush(RGB(0xFF,0x00,0x00));
  hBrushTmp = (HBRUSH)SelectObject(hDC,hBrush);
  GetClientRect(Wea.hWnd,&rc);
  FillRect(hDC,&rc,hBrush);
  DeleteObject(SelectObject(hDC,hBrushTmp));
  SetTextColor(hDC,RGB(0xFF,0xFF,0xFF));
  hFont     = CreateFont(40,0,0,0,FW_BOLD,0,0,0,0,0,0,2,0,L"SYSTEM_FIXED_FONT");
  hFntTmp   = (HFONT)SelectObject(hDC,hFont);
  iBkMode   = SetBkMode(hDC,TRANSPARENT);
  wcscpy(szText,L"Hello, World!");
  TextOut(hDC,52,90,szText,wcslen(szText));
  DeleteObject(SelectObject(hDC,hFntTmp));
  SetBkMode(hDC,iBkMode);
  EndPaint(Wea.hWnd,&ps);
                                         
 return 0;
}


LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 PostQuitMessage(0);
 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 WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 wchar_t szClassName[]=L"Form10";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=0;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=NULL;                             wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,210,100,330,300,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }
  
 return (int)messages.wParam;
}


Here's Form10.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
//Form10.h
#ifndef Form10_h
#define Form10_h

#define dim(x)                            (sizeof(x) / sizeof(x[0]))

struct                                    WndEventArgs
{
 HWND                                     hWnd;
 WPARAM                                   wParam;
 LPARAM                                   lParam;
 HINSTANCE                                hIns;
};

LRESULT CALLBACK fnWndProc_OnPaint        (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnDestroy      (WndEventArgs& Wea);

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

const EVENTHANDLER                        EventHandler[]=
{
 {WM_PAINT,                               fnWndProc_OnPaint},
 {WM_DESTROY,                             fnWndProc_OnDestroy}
};
#endif 


Got about six inches of snow today here in Colorado where I'm at. So I stayed in all day playing with code! :)
Hey Freddie1, Thanks for the heads-up on this. I was away from the computer for a few days, thanks to doctor appointments and the crazy weather.

After studying your various codes, i found the one line that I needed to add to mine, in the WM_DESTROY block:

 
DeleteObject(hBtnFont);


I also noticed another useful way of passing object handles between functions, using "SetProp()", "GetProp()" and "RemoveProp()".

Thanks again. I too, had to deal with the crazy weather, during what was supposed to be Summer. I'm not too far southeast of you---down by Amarillo. There was no real snow here. Just a couple wet flakes mixed-in with the cold rain.
thank you so much! This helped me lot.
https://10tipsfortentcamping.splashthat.com and visit my https://the-mathematics-of-the-pop-up-tent.manifo.com homepage
Last edited on
Pages: 12