C++ Win32 API - CreateFont(): the documentation example is confusing - winapi

The Win32 API function CreateFont() documentation has a confusing example:
As you can see below, they made 3 calls to CreateFont() using the same HANDLE (hFont).. and at the end they have called DeleteObject() for the last one only... but before EndPaint() (while the font is being select to the DC).
I've found this example so misleading about the way DeleteObject() must be used.
I hope if someone could confirm or justify this code.
The example:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
{
RECT rect;
HBRUSH hBrush;
HFONT hFont;
hdc = BeginPaint(hWnd, &ps);
//Logical units are device dependent pixels, so this will create a handle to a logical font that is 48 pixels in height.
//The width, when set to 0, will cause the font mapper to choose the closest matching value.
//The font face name will be Impact.
hFont = CreateFont(48,0,0,0,FW_DONTCARE,FALSE,TRUE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Impact"));
SelectObject(hdc, hFont);
//Sets the coordinates for the rectangle in which the text is to be formatted.
SetRect(&rect, 100,100,700,200);
SetTextColor(hdc, RGB(255,0,0));
DrawText(hdc, TEXT("Drawing Text with Impact"), -1,&rect, DT_NOCLIP);
//Logical units are device dependent pixels, so this will create a handle to a logical font that is 36 pixels in height.
//The width, when set to 20, will cause the font mapper to choose a font which, in this case, is stretched.
//The font face name will be Times New Roman. This time nEscapement is at -300 tenths of a degree (-30 degrees)
hFont = CreateFont(36,20,-300,0,FW_DONTCARE,FALSE,TRUE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Times New Roman"));
SelectObject(hdc,hFont);
//Sets the coordinates for the rectangle in which the text is to be formatted.
SetRect(&rect, 100, 200, 900, 800);
SetTextColor(hdc, RGB(0,128,0));
DrawText(hdc, TEXT("Drawing Text with Times New Roman"), -1,&rect, DT_NOCLIP);
//Logical units are device dependent pixels, so this will create a handle to a logical font that is 36 pixels in height.
//The width, when set to 10, will cause the font mapper to choose a font which, in this case, is compressed.
//The font face name will be Arial. This time nEscapement is at 250 tenths of a degree (25 degrees)
hFont = CreateFont(36,10,250,0,FW_DONTCARE,FALSE,TRUE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY, VARIABLE_PITCH,TEXT("Arial"));
SelectObject(hdc,hFont);
//Sets the coordinates for the rectangle in which the text is to be formatted.
SetRect(&rect, 500, 200, 1400, 600);
SetTextColor(hdc, RGB(0,0,255));
DrawText(hdc, TEXT("Drawing Text with Arial"), -1,&rect, DT_NOCLIP);
DeleteObject(hFont);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Related

Smooth WM_NCPAINT in Win32

I am wanting to handle WM_NCPAINT messages to draw my own window frame. I wrote some simple code to draw just a rectangle, which should give a black border around it. Here was the code:
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch(msg){
case WM_NCPAINT:
{
RECT rc;
GetWindowRect(hWnd, &rc);
HDC hDC = GetWindowDC(hWnd);
Rectangle(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top);
ReleaseDC(hWnd, hDC);
return TRUE;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lparam);
}
However, there were a couple issues with this. When resizing the window on the left or top edge, it causes the window to flicker heavily on the bottom and right edges. The second problem is that on the upper corners of the window, there are rounded corners, where my painting does not seem to take place. (This is Windows 10).
To my understanding, there should not be any flicker, as I am painting the window immediately after receiving a WM_NCPAINT message, which does not seem to be the case.
Can someone tell me what I am doing wrong and how to avoid these problems?
By the way, this is what the rounded corners look like, on the top-left and top-right corners.
After some more searching, I found the solution. I was a bit skeptical at first. I added the following code to my WM_NCCREATE:
HMODULE uxtheme = LoadLibrary("uxtheme.dll");
HRESULT __stdcall (* SetWindowTheme) (HWND, LPWSTR, LPWSTR) = GetProcAddress("SetWindowTheme");
SetWindowTheme(uxtheme, L" ", L" ");
FreeLibrary(uxtheme);
This fixes both the flickering and the annoying rounded corners.

How to set the scroll bar for a listbox at runtime properly?

I created a lsitbox this way:
HWND hLstBx = CreateWindowEx(WS_EX_CLIENTEDGE, "Listbox", NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL, 10, 10, 300, 500, hWnd, (HMENU)ID_LSTBX, (HINSTANCE)GetWindowLong(GWL_HINSTANCE, hWnd), NULL;
//SendMessage(hLstBx, LB_SETHORIZONTALEXTENT, (WPARAM)1000, 0);
Above It creates successfully a horizontal scroll bar but what I want:
How to set the value at runtime depending on the item with the longest length?
Please don't say something like in MFC but only win32.
Thank you in advance.
There is no free lunch here, if you want to call LB_SETHORIZONTALEXTENT or LB_SETCOLUMNWIDTH you have to measure the text of each item yourself to figure out the correct pixel size.
Each time an item is added/deleted or the controls font is changed you need to measure each item. Select the controls font (WM_GETFONT) into a HDC and use GetTextExtentPoint32 or DrawText(..., DT_CALCRECT) to measure the text. You probably also want to add some padding, (2 * GetSystemMetrics(SM_CXEDGE)) perhaps and account for the vertical scrollbar (if it is visible). You can optimize this by using LB_SETITEMDATA/LB_GETITEMDATA to cache the widths so you only have to calculate the size of a new item when it is added.
Once you know the widths of all items you can set the extent to the largest item.
UINT CalcLBItemWidth(HWND hLB, LPCTSTR Text)
{
RECT r;
HDC hLBDC = GetDC(hLB);
HDC hDC = CreateCompatibleDC(hLBDC);
HFONT hFont = (HFONT) SendMessage(hLB, WM_GETFONT, 0, 0);
HGDIOBJ hOrgFont = SelectObject(hDC, hFont);
ZeroMemory(&r, sizeof(r));
DrawText(hDC, Text, -1, &r, DT_CALCRECT|DT_SINGLELINE|DT_NOCLIP);
SelectObject(hDC, hOrgFont);
DeleteDC(hDC);
ReleaseDC(hLB, hLBDC);
return (r.right - r.left) + (2 * GetSystemMetrics(SM_CXEDGE));
}
static LRESULT CALLBACK MainWndProc(HWND hWnd, UINT Msg, WPARAM wp, LPARAM lp)
{
switch(Msg)
{
case WM_CREATE:
{
HWND hLB = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTBOX, NULL, WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|LBS_NOINTEGRALHEIGHT|LBS_DISABLENOSCROLL, 10, 10, 200, 100, hWnd, (HMENU)666, g_hInst, NULL);
static const LPCTSTR strings[] = { TEXT("Foo"), TEXT("Foo bar"), TEXT("Foo bar baaaaaaaaaaaaaaaz") };
UINT largest = 0;
for (UINT i = 0; i < 33; ++i)
{
UINT temp = CalcLBItemWidth(hLB, strings[i%3]);
if (temp > largest) largest = temp;
SendMessage(hLB, LB_ADDSTRING, 0, (LPARAM) strings[i%3]);
}
SendMessage(hLB, LB_SETHORIZONTALEXTENT, largest, 0);
}
break;
...
There is a MFC example of this here that subclasses the control and does all of it for you but there is nothing MFC specific that prevents you from doing the same in plain win32...

AdjustWindowRectEx() and GetWindowRect() give wrong size with WS_OVERLAPPED

I want to calculate the full size of a window before opening it. I'm using AdjustWindowRectEx() to achieve this. My code looks like this for a window with a client size of 640x480:
wrect.left = 0;
wrect.top = 0;
wrect.right = 640;
wrect.bottom = 480;
AdjustWindowRectEx(&wrect, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, FALSE, 0);
This returns the following values:
left: -3
top: -22
right: 643
bottom: 483
However, when opening the window using CreateWindowEx() and passing
wrect.right - wrect.left
wrect.bottom - wrect.top
as the window size, the window's size physical size actually turns out to be 656x515 pixels. Still, GetWindowRect() returns 646x505, i.e. the same dimensions as returned by AdjustWindowRectEx() but as I said, when I take a screenshot of the desktop and measure the window's size using a paint program, its physical size is actually 656x515 pixels. Does anybody have an explanation for this?
The client size is alright, it's 640x480 but it looks like the border size is calculated wrongly because the border uses more pixels than calculated by AdjustWindowRectEx() and GetWindowRect().
I'm on Windows 7.
EDIT:
Is this downvoted because the title of the question is misleading? As stated in the MSDN autodocs, WS_OVERLAPPED is not supported by AdjustWindowRectEx(). So is there any other way to calculate the dimensions of a WS_OVERLAPPED window? Using WS_OVERLAPPEDWINDOW instead is not a solution because it sets the WS_THICKFRAME and WS_MAXIMIZEBOX which I don't want.
Here is some test code now which shows the problem. You can see that the client size is alright but the window's physical size is larger than what is returned by GetWindowRect().
#include <stdio.h>
#include <windows.h>
#define CLASSNAME "Test"
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcx;
RECT wrect;
HWND hWnd;
char tmpstr[256];
memset(&wcx, 0, sizeof(WNDCLASSEX));
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_HREDRAW|CS_VREDRAW;
wcx.lpfnWndProc = WindowProc;
wcx.hInstance = hInstance;
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = GetStockObject(BLACK_BRUSH); // important! otherwise a borderless window resize will not be drawn correctly
wcx.lpszClassName = CLASSNAME;
RegisterClassEx(&wcx);
wrect.left = 0;
wrect.top = 0;
wrect.right = 640;
wrect.bottom = 480;
AdjustWindowRectEx(&wrect, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, FALSE, 0);
hWnd = CreateWindowEx(0, CLASSNAME, "Test", WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, 0, 0, wrect.right - wrect.left, wrect.bottom - wrect.top, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, SW_SHOWNORMAL);
GetWindowRect(hWnd, &wrect);
sprintf(tmpstr, "%d %d %d %d\n", wrect.left, wrect.top, wrect.right, wrect.bottom);
AllocConsole();
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), tmpstr, strlen(tmpstr), NULL, NULL);
GetClientRect(hWnd, &wrect);
sprintf(tmpstr, "%d %d %d %d\n", wrect.left, wrect.top, wrect.right, wrect.bottom);
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), tmpstr, strlen(tmpstr), NULL, NULL);
Sleep(10000);
DestroyWindow(hWnd);
UnregisterClass(CLASSNAME, hInstance);
return 0;
}
GetWindowRect api doesn't give the correct size:
Due to compatability requirements, certain metrics are reported in such a way that they're not consistent with what is actually drawn on the screen when Aero Glass (more accurately, "Windows Vista Aero") is enabled. This sort of approach is needed in order to change the look of the system for the vast majority of apps for which this isn't an issue.However, there's been a recent change in the system which will be coming out in Vista RC1 that will return the correct rendered value from GetWindowRect() for executables that are linked with "winver = 6.0". This allows new and newly-linked applications to get the "correct" values from GetWindowRect().
GetWindowRect on non-resizable windows under Aero Glass
Use instead DwmGetWindowAttribute to get the correct size:
DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &wrect, sizeof(wrect));
If AdjustWindowRect doesn't work, just try to create the Window and adjust the size after the window is created.
// Create the window with a nearly correct size
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 640;
rect.bottom = 480;
AdjustWindowRectEx(&rect, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, FALSE, 0);
// try
hWnd = CreateWindowEx(0, CLASSNAME, "Test", WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL);
// Get the size that is really required and adjust
RECT rectCreated;
GetWindowRect(hWnd, &rectCreated);
rect.right += rectCreated.right-rectcreated.left;
rect.bottom += rectCreated.bottom-rectcreated.top;
// Resize to let the window fir the inner client aea
SetWindowPos(hWnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOZORDER);
// Show it.
ShowWindow(hWnd, SW_SHOWNORMAL);
Code is not tested. Hopefully I have no bugs or syntax errors in it.

Win32 can't get SetBkMode Transparency to work

I am writing a simple program, that prints some text on the screen, overlaying the other windows.
#include "stdafx.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
HWND hwnd = GetDesktopWindow();
HDC hdc;
RECT rect;
//LPRECT rect = new RECT;
wchar_t text[] = L"test";
GetClientRect(hwnd, &rect);
do{
hdc = GetWindowDC(hwnd);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(100, 100, 100));
DrawText(hdc, text, -1, &rect, DT_NOCLIP);
ReleaseDC(hwnd, hdc);
Sleep(15);
} while (1);
return 0;
}
The problem is that I would like the background of the printed text to be transparent, but SetBkMode does not seem to work (it actually makes no difference if I set it to OPAQUE or TRANSPARENT) so I get a solid background. Any ideas? What am I missing?
edit: Changed LPRECT to RECT, as suggested.
edit: using transparent window:
creating window:
CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED, // extended style
(LPCWSTR)WINDOW_CLASS_NAME, // class
L"test", // title
NULL,
0, 0, // initial x,y
400, 300, // initial width, height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance of this application
NULL)
globals:
wchar_t tst_Str[] = L"TEST";
WM_PAINT:
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
SetTextColor(hdc, RGB(255, 0, 0));
DrawText(hdc, tst_Str, -1, &rc,NULL);
EndPaint(hwnd, &ps);
return 0;
Windows are responsible for painting themselves. The system is not designed to allow external parties to draw on other windows in the manner you are attempting. You will have to recalibrate your expectations.
If you wish to draw something you should create a window and draw on it. You are at liberty to make the window a transparent layered window and position your drawing so that it appears on top of another window. And you also gain the benefit that you don't need to run a busy loop. You can paint in the standard way, in response to WM_PAINT messages.
To make a layered window appear on the desktop, you have to call SetLayeredWindowAttributes function, for example
SetLayeredWindowAttributes(hWnd,
RGB(255, 255, 255), // The transparent color
128, // Opacity of the window
LWA_ALPHA | LWA_COLORKEY); // Enable both features
This example will make portions of the window with white color completely transparent. Parameter bAlpha specifies the opacity of the window (the pixels not affected with crKey).
Here's a screen shot of such window with Test text floating over browser window with this question:
If you create your window without border, using these window styles: WS_POPUP | WS_SYSMENU, then it will look like on the screen shot. I included WS_SYSMENU to have the standard window menu in the taskbar. To force the taskbar to display a button for popup window, include WS_EX_APPWINDOW extended style.

how to use DrawText() to write text in a given window whose handle is known?

I want to know how to write text on a particular window starting at a given location in the window using the Windows API.
For example if the coordinates within the window where the text is to be written are (x,y) = (40,10) then what do I need to do to write a line of text to a particular window at that location in the window?
Suppose your window name is "hwnd" and the text which u want to write on that window at x,y coordinate is say stored in "message" where
LPCWSTR message=L"My First Window"; then
RECT rect;
HDC wdc = GetWindowDC(hwnd);
GetClientRect (bgHandle, &rect) ;
SetTextColor(wdc, 0x00000000);
SetBkMode(wdc,TRANSPARENT);
rect.left=40;
rect.top=10;
DrawText( wdc, message, -1, &rect, DT_SINGLELINE | DT_NOCLIP ) ;
DeleteDC(wdc);
Thats it.. remember this is just one example.
I am hoping this a more complete answer...
void OnPaint(HWND hWnd)
{
RECT rect;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
SetTextColor(hdc, RGB(0xFF, 0x00, 0x00));
SetBkMode(hdc, TRANSPARENT);
rect.left = 40;
rect.top = 10;
DrawText(hdc, L"Hello World!", -1, &rect, DT_SINGLELINE | DT_NOCLIP);
SelectObject(hdc, oldPen);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
}
This would then be called from the WM_PAINT message in the WndProc.

Resources