List View failing to draw strings - c++

I am trying to use a list view to draw a vector of std::strings as a list. However, when I run the code, nothing happens. No error is thrown: it just simple doesn't draw, and I'm stumped. Here is a minimal reproducible example (the bulk of the code that I am questioning is in the WndProc under case WM_NOTIFY and InsertListViewItems):
//libraries
#pragma comment ("lib", "Comctl32.lib")
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <vector>
#include <string>
#include <dwrite.h>
#include <d2d1.h>
#include <commctrl.h>
#include <strsafe.h>
#define IDS_APP_TITLE 103
#define IDR_MAINFRAME 128
#define IDD_PRACTICE_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_EXIT 105
#define IDI_PRACTICE 107
#define IDI_SMALL 108
#define IDC_PRACTICE 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
#define MAX_LOADSTRING 100
#define PROJECT_LIST_VIEW 110
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
std::vector<std::string> stringsVector = { "String1", "String2", "string3" };
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
HWND listViewHandle;
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
///////////////////
//Function to insert the items into the list view
//////////////////
BOOL InsertListViewItems(HWND hWndListView, int cItems)
{
LVITEM lvI;
// Initialize LVITEM members that are common to all items.
lvI.pszText = LPSTR_TEXTCALLBACK; //This should send an LVN_GETDISPINFO message
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
lvI.stateMask = 0;
lvI.iSubItem = 0;
lvI.state = 0;
// Initialize LVITEM members that are different for each item.
for (int index = 0; index < cItems; index++)
{
lvI.iItem = index;
lvI.iImage = index;
// Insert items into the list.
if (ListView_InsertItem(hWndListView, &lvI) == -1)
return FALSE;
}
return TRUE;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
//Create the List View Control
listViewHandle = CreateWindow(WC_LISTVIEW, L"", WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS, 100, 100, 500, 500, hWnd, (HMENU)PROJECT_LIST_VIEW, hInst, NULL);
InsertListViewItems(listViewHandle, stringsVector.size());
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_NOTIFY:
{
NMLVDISPINFO* plvdi;
OutputDebugStringA("WM_NOTIFY");
switch (((LPNMHDR)lParam)->code)
{
case LVN_GETDISPINFO:
{
////////////////////
//This is the callback that should set the pszText attribute of the items
////////////////////
OutputDebugStringA("LVN_GETDISPINFO\n");
plvdi = (NMLVDISPINFO*)lParam;
const char* inString = stringsVector[plvdi->item.iItem].c_str();
size_t size = strlen(inString) + 1;
wchar_t* outString = new wchar_t[size];
size_t outSize;
mbstowcs_s(&outSize, outString, size, inString, size - 1);
LPWSTR ptr = outString;
StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, outString);
OutputDebugString(outString);
OutputDebugStringA("\n");
delete[] outString;
break;
}
}
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Thank you for your time!

As Jonathan Potter and Remy said, you need to add at least one column to ensure that the list is displayed correctly:
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
// Initialize LVITEM members that are common to all items.
// Initialize LVITEM members that are different for each item.
lvc.iSubItem = 0;
lvc.pszText = (LPWSTR)L"test";
lvc.cx = 200;
// Insert items into the list.
if (ListView_InsertColumn(hWndListView, 0, &lvc) == -1)
return FALSE;
And it works for me:
More reference: How to Add List-View Columns

Related

WM_HOVER not working with WinAPI ListView

I have a ListView drawing to the screen. Now, I am trying to have a function called when the user hovers over the items. I am trying to use WM_HOVER right now because it seems the most straight foward; however, it doesn't seem to be working. I got WM_CLICK to work fine, but not for the hovering.
Will WM_HOVER do what I need it to, or should I be looking into something else such as TrackMouseEvent()?
Here is some example code. Really the only pertinent part, I believe, are the lines in the WndProc() following case WM_HOVER:
//libraries
#pragma comment ("lib", "Comctl32.lib")
#pragma comment ("lib", "d2d1.lib")
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <vector>
#include <string>
#include <dwrite.h>
#include <d2d1.h>
#include <commctrl.h>
#include <strsafe.h>
#define IDS_APP_TITLE 103
#define IDR_MAINFRAME 128
#define IDD_PRACTICE_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_EXIT 105
#define IDI_PRACTICE 107
#define IDI_SMALL 108
#define IDC_PRACTICE 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
#define MAX_LOADSTRING 100
#define PROJECT_LIST_VIEW 110
BOOL mouseTracking = false;
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
std::vector<std::string> stringsVector = { "String1", "String2", "String3" };
//D2D1 pointers
ID2D1Factory* m_pD2DFactory;
ID2D1DCRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pBlackBrush;
//dimension variables
int w, h;
RECT rc;
HWND listViewHandle;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
HRESULT CreateDeviceResources(HWND hwnd, HDC hdc);
bool onRender(HWND hwnd, PAINTSTRUCT* ps);
///////////////////
//Function to insert the items into the list view
//////////////////
BOOL InsertListViewItems(HWND hWndListView, int cItems);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
//Create the List View Control
listViewHandle = CreateWindow(WC_LISTVIEW, L"", WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS, 100, 100, 500, 500, hWnd, (HMENU)PROJECT_LIST_VIEW, hInst, NULL);
InsertListViewItems(listViewHandle, stringsVector.size());
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
HDC hdc = BeginPaint(hWnd, &ps);
onRender(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
}
case WM_SIZE:
{
if (m_pRenderTarget)
{
m_pRenderTarget->Release();
m_pRenderTarget = nullptr;
}
break;
}
////////////////////////////////////////////
//
// HERE ARE THE MOUSE MOVE CASES
//
/////////////////////////////////////////////
case WM_MOUSEHOVER:
{
OutputDebugStringA("HOVER\n");
mouseTracking = FALSE;
break;
}
case WM_MOUSEMOVE:
if (!mouseTracking)
{
// start tracking if we aren't already
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.hwndTrack = listViewHandle; //This is the handle to the ListView window
tme.dwHoverTime = HOVER_DEFAULT;
mouseTracking = TrackMouseEvent(&tme);
}
break;
case WM_MOUSELEAVE:
mouseTracking = FALSE;
break;
case WM_NOTIFY:
{
NMLVDISPINFO* plvdi;
switch (((LPNMHDR)lParam)->code)
{
case NM_CLICK:
OutputDebugStringA("CLICK\n");
break;
case LVN_GETDISPINFO:
{
////////////////////
//This is the callback that sets the pszText attribute of the items
////////////////////
plvdi = (NMLVDISPINFO*)lParam;
const char* inString = stringsVector[plvdi->item.iItem].c_str();
size_t size = strlen(inString) + 1;
wchar_t* outString = new wchar_t[size];
size_t outSize;
mbstowcs_s(&outSize, outString, size, inString, size - 1);
LPWSTR ptr = outString;
StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, outString);
delete[] outString;
break;
}
}
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&m_pD2DFactory
);
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//////////////////
//Function to insert the items into the list view
//////////////////
BOOL InsertListViewItems(HWND hWndListView, int cItems)
{
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
// Initialize LVITEM members that are common to all items.
// Initialize LVITEM members that are different for each item.
lvc.iSubItem = 0;
lvc.pszText = (LPWSTR)L"test";
lvc.cx = 200;
// Insert items into the list.
if (ListView_InsertColumn(hWndListView, 0, &lvc) == -1)
return FALSE;
LVITEM lvI;
// Initialize LVITEM members that are common to all items.
lvI.pszText = LPSTR_TEXTCALLBACK; //This should send an LVN_GETDISPINFO message
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
lvI.stateMask = 0;
lvI.iSubItem = 0;
lvI.state = 0;
// Initialize LVITEM members that are different for each item.
for (int index = 0; index < cItems; index++)
{
lvI.iItem = index;
lvI.iImage = index;
// Insert items into the list.
if (ListView_InsertItem(hWndListView, &lvI) == -1)
return FALSE;
}
return TRUE;
}
HRESULT CreateDeviceResources(HWND hwnd, HDC hdc)
{
HRESULT hr = S_OK;
if (!m_pRenderTarget) { //If m_pRenderTarget changes size
// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pRenderTarget);
GetClientRect(hwnd, &rc);
if (SUCCEEDED(hr))
hr = m_pRenderTarget->BindDC(hdc, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
w = size.width;
h = size.height;
if (SUCCEEDED(hr))
{//position objects
// Create a black brush
hr = m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
}
}
return hr;
}
bool onRender(HWND hwnd, PAINTSTRUCT* ps)
{
HDC hdc = ps->hdc;
HRESULT hr;
hr = CreateDeviceResources(hwnd, hdc);
if (SUCCEEDED(hr))
{
m_pRenderTarget->BeginDraw();
m_pRenderTarget->SetTransform(D2D1::IdentityMatrix());
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
hr = m_pRenderTarget->EndDraw();
}
SetWindowPos(listViewHandle, HWND_TOP, w * .1, h * .1, w * .3, h * .3, SWP_SHOWWINDOW);
SendMessage(listViewHandle, LVM_SETCOLUMNWIDTH, 0, w * .2);
if(SUCCEEDED(hr))
return true;
return false;
}
As dxiv said, If you need to handle NM_HOVER message, you need to add LVS_EX_TRACKSELECT style:
ListView_SetExtendedListViewStyle(listViewHandle, LVS_EX_TRACKSELECT);
If you don't want to choose hover but only want to handle hover, you can refer to:How can I detect if the mouse is over an item/subitem in a List View control?
Edit:
According to : TrackMouseEvent tracks mouse events in your window, but only if the events belong to your window
So you need to subclass your ListView and handle its window procedure:
WNDPROC oldListViewProc; //A global variable
oldListViewProc = (WNDPROC)SetWindowLongPtr(listViewHandle, GWL_WNDPROC, (LONG_PTR)ListViewProc);
......
LRESULT CALLBACK ListViewProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_MOUSEHOVER:
{
OutputDebugStringA("HOVER\n");
return 0;
}
case WM_MOUSEMOVE:
if (!mouseTracking)
{
// start tracking if we aren't already
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.hwndTrack = hwnd; //This is the handle to the ListView window
tme.dwHoverTime = HOVER_DEFAULT;
mouseTracking = TrackMouseEvent(&tme);
}
break;
case WM_MOUSELEAVE:
mouseTracking = FALSE;
break;
}
return CallWindowProc(oldListViewProc, hwnd, msg, wParam, lParam);
}

DPI aware distorts text when screen resized with Windows API

I am writing an app in C++ with the Win32 API. I first noticed that the rendered text with DrawTextLayout() was very fuzzy, so I added the following line:
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
At first, this seemed to do the trick: the text looked much crisper, and more of what I would expect from a Windows app.
However, when I resized the window (by dragging the bottom corner), I noticed that the text I drew was distorted.
How could I keep the crispness of using DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, but also keep the text from becoming stretched?
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <dwrite.h>
#include <d2d1.h>
#define IDS_APP_TITLE 103
#define IDR_MAINFRAME 128
#define IDD_PRACTICE_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_PRACTICE 107
#define IDI_SMALL 108
#define IDC_PRACTICE 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
ID2D1Factory* m_pD2DFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pBlackBrush;
IDWriteFactory* writeFactory;
IDWriteTextFormat* writeTextFormat;
IDWriteTextLayout* writeTextLayout;
RECT rc;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
//create device independent resources
{
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
// Create a Direct2D factory.
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&m_pD2DFactory
);
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&writeFactory)
);
}
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
//create device dependent resources
{
HRESULT hr = S_OK;
if (!m_pRenderTarget) {
GetClientRect(hWnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
// Create a Direct2D render target
hr = m_pD2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hWnd, size), &m_pRenderTarget);
if (SUCCEEDED(hr))
{
// Create a black brush
hr = m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
}
}
}
//render text
{
writeFactory->CreateTextFormat(
L"Times New Roman",
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
14.0f,
L"EN-US",
&writeTextFormat
);
writeFactory->CreateTextLayout(
L"String", // The string to be laid out and formatted.
6, // The length of the string.
writeTextFormat, // The text format to apply to the string (contains font information, etc).
200, // The length of the layout box.
500, // The width of the layout box.
&writeTextLayout // The IDWriteTextLayout interface pointer.
);
}
m_pRenderTarget->BeginDraw();
m_pRenderTarget->SetTransform(D2D1::IdentityMatrix());
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pRenderTarget->DrawTextLayout(D2D1::Point2F(0, 0), writeTextLayout, m_pBlackBrush);
m_pRenderTarget->EndDraw();
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I apologize that this is a substantial amount of code; however, to be able to be completely run, I had to include this much.
Although setting dpi-awareness is a critical step to avoid bluriness on high-res monitors (when dpi is not 100%), your issue with the text getting stretched on a window resize is a separate issue.
This is simply a DirectX (DirectWrite) surface issue. I only have limited experience with DirectWrite, but the fix for this is very obvious. Like D3D, the DirectWrite surface stretches across the size of the window, but it's not directly aware of the window size change until you tell it.
Potentially two very easy fixes:
In your WndProc function, pass the new size to the m_pRenderTarget thing when the size changes. Catch WM_SIZE:
case WM_SIZE:
{
if (m_pRenderTarget)
{
GetClientRect(hWnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
m_pRenderTarget->Resize(size);
}
break;
}
Alternatively, since your WM_PAINT code would re-create the thing anyway if it's null:
case WM_SIZE:
{
if (m_pRenderTarget)
{
m_pRenderTarget->Release();
m_pRenderTarget = nullptr;
}
break;
}
One other minor fix. Invoke SetProcessDpiAwarenessContext before you call CreateWindow. Very early in WinMain before you start doing anything else. Or use the application manifest file to set it.

Win32 Inconsistent CreateWindowEx Call

I'm trying to wrap my head around the Win32 API, and when creating the window I get inconsistent results. The call to CreateWindowEx will fail and the result from GetLastError() yields the ERROR_NOT_ENOUGH_MEMORY error code. But sometimes the application successfully creates the window and runs fine. This has left me rather confused.
// Standard Includes
#include <windows.h>
#include <iostream>
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#define UNUSED_PARAM(x) (void)(x)
const char szWindowClass[] = "Program";
const char szTitle[] = "Program Desc";
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
UNUSED_PARAM(hPrevInstance);
UNUSED_PARAM(lpCmdLine);
WNDCLASSEX wcex;
HWND hWnd;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if(!RegisterClassEx(&wcex)) {
MessageBox(NULL, "Call to RegisterClassEx failed!", szWindowClass, MB_ICONEXCLAMATION | MB_OK);
return 1;
}
hWnd = CreateWindowEx( WS_EX_CLIENTEDGE, // dwExStyle
szWindowClass, // lpClassName
szTitle, // lpWindowName
WS_OVERLAPPEDWINDOW, // dwStyle
CW_USEDEFAULT, CW_USEDEFAULT, // x, y
240, 120, // width, height
NULL, NULL, // hWndParent, hMenu
hInstance, NULL); // hInstance, lpParam
std::cout << GetLastError();
if(hWnd == NULL) {
MessageBox(NULL, "Call to CreateWindow failed!", szWindowClass, MB_ICONEXCLAMATION | MB_OK);
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Main message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
You're not initializing WNDCLASSEX.cbWndExtra which defines how many extra bytes of storage your window class needs.
You should add:
wcex.cbWndExtra = 0;
Before your call to RegisterClassEx. Alternatively you could start with this:
WNDCLASSEX wcex = { sizeof(wcex) };
Which will initialize the cbSize member to the size of the structure and all other members to 0 (or NULL) for you.

c++ WinApi register second window class error

I'm Trying to create a second window on the c++ winApi and I have an error when i'm trying to register second window class. However, this classes are registered, if i'm using UnregisterClass on the first class, consequently, classes are spelled correctly.
#include "stdafx.h"
#include "rgr.h"
#include <stdio.h>
#define MAX_LOADSTRING 100
// GLOBAL:
HINSTANCE hInst;
HWND childWnd;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
WCHAR childTitle[MAX_LOADSTRING];
WCHAR childWindowClass[MAX_LOADSTRING];
ATOM MyRegisterClass(HINSTANCE hInstance);
ATOM RegisterChildClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_RGR, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
/*UnregisterClass(szWindowClass, hInst);
LoadStringW(hInstance, IDC_RGR, childWindowClass, MAX_LOADSTRING);*/
RegisterChildClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_RGR));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_RGR));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_RGR);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL, L"Error registering MAIN class", L"ERROR", MB_OK);
return 0;
}
}
ATOM RegisterChildClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = ChildProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_RGR));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = childWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL, L"Error registering CHILD class", L"ERROR", MB_OK);
return 0;
}
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_OPEN:
{
childWnd = CreateWindow(szWindowClass, L"Save", WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CHILD , 100, 100, 100, 100, hWnd, (HMENU)107, hInst, NULL);
if (!childWnd)
{
return FALSE;
}
ShowWindow(childWnd, 1);
UpdateWindow(childWnd);
break;
}
case IDM_EXIT:
DestroyWindow(childWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
What i'm doing wrong?
A Window class ...
... is a set of attributes that the system uses as a template to create a window.
With one exception (lpszClassName), all members of the WNDCLASSEX structure define properties of a window of that class. The class name is used to identify the window class. This is explained here:
Registering a window class associates a window procedure, class styles, and other class attributes with a class name.
Since it serves as an ID, the class name needs to be unique throughout the process. In your code, however, you are trying to register two window classes with the same name (the string resource referenced by IDC_RGR). The second call fails, and GetLastError returns 1410: ERROR_CLASS_ALREADY_EXISTS ("Class already exists.").
This can be verified1 using this minimal and complete test case:
#include <windows.h>
int main() {
WNDCLASSEXW wcex = { sizeof( wcex ) };
wcex.lpszClassName = L"TestWindowClass";
if ( !::RegisterClassExW( &wcex ) ) {
DWORD dwError = ::GetLastError();
return dwError;
}
if ( !::RegisterClassExW( &wcex ) ) {
DWORD dwError = ::GetLastError();
return dwError;
}
}
The solution is to use a unique window class name for each window class that a process registers. Note, that there are lots more errors in your code that I didn't address (like incompatible window styles in the CreateWindow call, mismatching character encodings, and so on).
1 By single-stepping through the code using a debugger.

'OPENFILENAME': undeclared identifier

I'm learning Windows programming (API). When I want to create Open File dialog, first I create structure declaration:
OPENFILENAME ofn;
But I get error:
error C2065: 'OPENFILENAME': undeclared identifier
Full code:
#include "stdafx.h" // <---- included windows.h
#include "Win32_Iczelion_ofn.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WIN32_ICZELION_OFN, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32_ICZELION_OFN));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32_ICZELION_OFN));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32_ICZELION_OFN);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
OPENFILENAME ofn; // <---------- ERROR HERE ----------
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
ofn.lStructSize = sizeof (ofn);
ofn.hwndOwner = g_hWindow;
ofn.hInstance = GetModuleHandle(NULL);
ofn.lpstrFile = szFile;
ofn.nFilterIndex = 0;
ofn.nMaxFile = 256;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
GetOpenFileName(&ofn);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I search on google but still not fully resolved.
Anyone please help me explain that errors , and figure out step by step to create the Open File dialog, (example for me).
I'm newbie so plz help me my stupid question !!!
Did you add
#include <Commdlg.h>
Error: undeclared identifier may because you don't include needed header file.
In Visual Studio 2017,I had meet the same problem.
If you have a header file named "stdafx.h" ,please cut #include <Commdlg.h> into "stdafx.h"