2

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!

Anthony Wild
  • 137
  • 5
  • If you start again with a fresh application, does the border smear? If not, drawing some text should make no difference. Are you sure you haven't changed something else? Like the window background brush, or handled the WM_ERASEBKGRND message or somesuch? – arx Mar 02 '13 at 20:03
  • Yes, still smears with a new project and the one TextOut call. Interestingly, if I copy the application to Windows XP - it works perfectly! So I guessed it might be something to do with themes. I added this call just after the main window is created: `SetWindowTheme (hWnd, L" ", L" ");` And it now works without the text smearing! But the window looks horrible - grey borders, old style caption bar buttons. So I'm trying to find a way to make this work but keep the look and feel of Windows 7... – Anthony Wild Mar 04 '13 at 14:47
  • I tried your code. It works for me without smearing on Windows 7. Do other applications (e.g. notepad) have the same problem? Are you using a standard Windows theme? – arx Mar 04 '13 at 15:40
  • Hi I have tried other apps - but I can't find any with text up to the edge of the window, so I can't tell. I have tried several different themes - the Aero themes are giving me trouble. – Anthony Wild Mar 12 '13 at 19:05
  • If it only fails on certain computers and then only with aero enabled I would suspect the graphics card driver. You could check for an update. – arx Mar 13 '13 at 09:14
  • Thanks @arx - we are thinking along the same lines :) I have just updated to the latest video drivers (ATI Radeon), but unfortunately the problem persists. Oddly enough though, Windows 7 VMs on this machine display correctly! – Anthony Wild Mar 13 '13 at 10:56

1 Answers1

0

I think I can provide some more insight about where that smearing/blurring is coming from and why you see it more (or differently) in Windows 8/10 Aero.

The fact that your code has:

wcex.style = CS_HREDRAW | CS_VREDRAW;

is the reason why you didn't see the smearing/blurring on Win7. This causes Windows to fill in newly exposed areas of the window that have not yet been drawn by your WM_PAINT with a solid color, which is not perfect but not that distracting.

But under Windows 8/10 Aero, things are different. Apps don't draw directly to the screen, but rather draw to offscreen buffers that are then composited by the evil DWM.exe window manager. It turns out that DWM actually adds another layer of BitBlt-type behavior on top of the existing legacy XP/Vista/7 BitBlt behavior that is affected by CS_HREDRAW | CS_VREDRAW.

And the DWM blit behavior is even more crazy because they don't just copy the client area, but they actually replicate pixels at the edges of your old client area to make the new one.

Unfortunately, making DWM not do its blit is much harder than just passing some extra flags.

I don't have a 100% solution, but please see this Q&A for a sort of timing trick that can be used to greatly reduce the frequency with which DWM messes with your window client area, which will reduce the smearing/blurring:

How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

Enjoy!

Louis Semprini
  • 3,515
  • 2
  • 30
  • 31