调整窗口大小会导致右边框附近出现涂抹

Resizing Window causes smearing near the right border

本文关键字:边框 窗口大小 右边 调整      更新时间:2023-10-16

我在Visual Studio 2010中创建了一个标准的win32 windows应用程序。我所做的唯一增加的是在WM_PAINT处理程序中显示字母(重复4次宽度)在位置0,0的TextOut调用。

我的问题是,当我调整窗口的大小,向右扩展,有一些绘制错误的右侧边界。在调整大小/绘图过程中显示黑色块,好像右手边正在拉伸。当我调整大小时,结果是一个奇怪的黑色"涂抹"效果。它只在调整大小时发生;一旦我松开鼠标,窗口看起来是正确的。

我已经尝试了双缓冲到内存DC,但看到相同的效果。我不使用任何windows主题代码。

我可以删除效果的唯一方法是处理WM_NCPAINT(并返回0)-但是,当然,这意味着边界不被绘制,这将不是一个可接受的解决方案!我提到它,也许它能帮助任何人有一个想法。

感谢您的任何建议或帮助!

@Arx -对不起,我没有说清楚。当我说边框涂抹时,我指的是显示文本的右手边,而不是边框本身。

如果我只是在WM_PAINT处理程序中添加TextOut调用,它就会发生。处理WM_ERASEBKGRND和设置窗口类背景刷没有区别。

@David -抱歉在新项目向导创建的标准VS 2008 Win32应用程序中只添加一行后,我就看到了效果-所以我不认为只有一行感兴趣的代码发布200多行代码有什么意义:)

我在WM_PAINT处理程序中添加了这一行:

TextOut (hdc, 0, 0, L"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ", 104);

下面是添加了双缓冲的代码的完整版本。当窗口展开时,右侧(窗口边缘)的文本涂抹仍然会发生。

// 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;
}

嗨Arx

非常感谢您尝试代码-我很感激。很高兴知道这对你有用。

但是…我正在经历一些非常奇怪的结果。我在Windows 7 x64机器上运行时遇到了"模糊"问题。然而,在Windows 7虚拟机(在同一台机器上使用VMWare)中试用它,效果非常好。

所以在同一台机器上,它本来是模糊的,但实际上它没有。我甚至尝试了Windows 8虚拟机,同样运行良好。

我发现的是,如果我选择一个非Aero主题,所有的工作都很好(虽然没有Aero看起来不太好)。然而在其他机器上,它是工作的,他们有Aero主题选择。我真不明白。

所以这不是一个真正的解决方案。我不想让我的潜在客户关掉Aero。

我已经尝试调用许多Dwm*函数,试图找到工作和非工作机器之间的差异,但找不到任何东西。

如果我插入以下代码:

DWMNCRENDERINGPOLICY policy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hWnd, 
                      DWMWA_NCRENDERING_POLICY, 
                      (void*)&policy, 
                      sizeof(DWMNCRENDERINGPOLICY));

就在创建主窗口之后,然后一切都可以正常工作(尽管,再一次,没有Aero边框看起来不那么好)。

所以我真的不知道该如何前进。任何想法,感谢收到!

我想我可以提供一些更多的见解,关于涂抹/模糊是从哪里来的,为什么你看到它更多(或不同)在Windows 8/10 Aero。

你的代码有:

wcex.style = CS_HREDRAW | CS_VREDRAW;

是你在Win7上看不到涂抹/模糊的原因。这会导致窗口用纯色填充尚未由WM_PAINT绘制的窗口新暴露区域,这不是完美的,但不会分散注意力。

但是在Windows 8/10 Aero下,情况就不同了。应用程序不直接绘制到屏幕上,而是绘制到屏幕外的缓冲区,然后由邪恶的DWM.exe窗口管理器合成。事实证明,DWM实际上在现有的受CS_HREDRAW | CS_VREDRAW影响的遗留XP/Vista/7 BitBlt行为之上增加了另一层BitBlt类型的行为。

DWM blit的行为更疯狂,因为他们不只是复制客户端区域,但他们实际上复制像素的边缘,你的旧客户端区域,使新的。

不幸的是,让DWM不做blit比仅仅传递一些额外的标志要困难得多。

我没有100%的解决方案,但请参阅这个问题& a的一种定时技巧,可以用来大大减少DWM与您的窗口客户端区域混淆的频率,这将减少涂抹/模糊:

如何平滑丑陋的抖动/闪烁/跳跃时调整窗口的大小,特别是拖动左/上边框(Win 7-10;bg、bitbit和DWM)?

享受吧!