I have created a standard win32 windows application in Visual Studio 2010. The only addition I have made is a TextOut call in the WM_PAINT handler that displays the alphabet (repeated 4 times for width) at position 0, 0.
My problem is that when I resize the window, expanding to the right, there is some drawing error by the right side border. Black blocks are shown during the resizing/drawing process as if the right hand edge is being stretched. The result is a strange black “smearing” effect as I resize. It only happens during the resize; once I release the mouse, the window looks correct.
I have tried double buffering to a memory DC, but see the same effect. I am not using any windows themes code.
The only way I can remove the effect is to handle WM_NCPAINT (and return 0) – but, of course, this means the border isn’t painted which won’t be an acceptable solution! I mention it in case it helps anybody with an idea.
Thanks for any ideas or help!
@Arx – Sorry, I hadn’t made myself clear. When I say the borders smear, I meant the right hand edge of the displayed text, not the border itself.
It happens if I just add the TextOut call in the WM_PAINT handler. Handling WM_ERASEBKGRND and setting the window class background brush makes no difference.
@David – Apologies. I see the effect after adding only one line to the standard VS 2008 Win32 application created by the new project wizard – so I didn’t see the point in posting 200+ lines of code with only one line of interest :)
I added this line to the WM_PAINT handler:
TextOut (hdc, 0, 0, L"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ", 104);
Here is the full version of the code with double buffering added. The smearing of the text on the right hand side (by the window edge) as the window is expanded still occurs.
// win32_smearing_at_border.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "win32_smearing_at_border.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(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
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_SMEARING_AT_BORDER, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32_SMEARING_AT_BORDER));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage are only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
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_SMEARING_AT_BORDER));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = 0; //(HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32_SMEARING_AT_BORDER);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
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);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
static HDC memory_dc;
static HBITMAP memory_bmp;
static HBITMAP oldbmp;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_ERASEBKGND:
return 1;
case WM_CREATE:
// Create memory DC
{
HDC dc = GetDC (hWnd);
memory_dc = CreateCompatibleDC (dc);
memory_bmp = CreateCompatibleBitmap (dc, 2560, 1440); // arbitary values for testing, set to my current monitor size.
oldbmp = (HBITMAP)SelectObject(memory_dc, memory_bmp);
TextOut (memory_dc, 0, 0, L"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ",104);
ReleaseDC (hWnd, dc);
}
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right-ps.rcPaint.left, ps.rcPaint.bottom-ps.rcPaint.top, memory_dc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
// Clean up memory DC
SelectObject (memory_dc,oldbmp);
DeleteObject (memory_bmp);
DeleteDC (memory_dc);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
Hi Arx
Many thanks for trying out the code – I appreciate it. Good to know it’s working for you.
But … I am experiencing some very strange results. I have the “blurring” issue when running on a Windows 7 x64 machine. However, trying it out in a Windows 7 virtual machine (using VMWare on the SAME machine), it works perfectly.
So on the same machine, natively it blurs, but virtually it doesn’t. I even tried a Windows 8 virtual machine, and again it worked fine.
What I have found is that if I select a NON-Aero theme, all works ok (although it’s doesn’t look so good without Aero). And yet on the other machines where it IS working, they DO have Aero themes selected. I really don’t get it.
So that’s not really a solution. I don’t want to have to ask my potential customers to turn Aero off.
I have tried calls to many of the Dwm* functions trying to find a difference between the working and non-working machines but cannot find anything.
If I insert this code:
DWMNCRENDERINGPOLICY policy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hWnd,
DWMWA_NCRENDERING_POLICY,
(void*)&policy,
sizeof(DWMNCRENDERINGPOLICY));
Just after creating the main window then all works fine again (although, once again, doesn’t look as good without the Aero border).
So am I really at a loss as to how I can move forward. Any ideas gratefully received!