开发者

Child Window Painting Reference

开发者 https://www.devze.com 2023-02-28 00:57 出处:网络
I understand, from MSDN, how windows handles WM_PAINT messages sent to a particular window. One thing MSDN doesn\'t seem to document is the actual process by which the window manager decides which wi

I understand, from MSDN, how windows handles WM_PAINT messages sent to a particular window.

One thing MSDN doesn't seem to document is the actual process by which the window manager decides which windows should receive WM_PAINT messages, and in what order.

As I understand things (from reading Raymond Chen and MSDN) there are no invalid regions associated with child windows - when child windows are invalidated the corresponding area on the parent window gets invalidated.

Its the WM_PAINT generation that confuses me... and the exact point (especially wrt multithreading) that a windows invalid region gets marked as valid - when child windows are involved it gets particularly interesting. How does GetMessage decide which of the set of windows (parents + child windows that intersect the invalid region) get WM_PAINT messages, and in what order? and how does that change in the face of WS_CLIPSIBLINGS, WS_CLIPCHILDREN, WS_EX_COMPOSITED, WS_EX_TRANSPARENT and so on? And what happens if another thread invalidates part of the top level window halfway through this process?

And then, On Windows V6.0+, how does the DWM hook into this process?


This is a sample C program demonstrating the glitching that occurs when using WS_EX_COMPOSITED :-

#include <windows.h>
#include <windowsx.h>
INT delay = 50;
INT nPad = 32;

struct wnd_ctx {
  COLORREF base;
  char index;
};

HMODULE GetWindowModuleHandle(HWND hwnd){
  return (HMODULE)GetWindowLongPtr(hwnd,GWLP_HINSTANCE);
}

struct wnd_ctx* GetContext(HWND hWnd){
  struct wnd_ctx* pctx = (LPVOID)GetWindowLongPtr(hWnd,GWLP_USERDATA);
  if(!pctx)
  {
    pctx = malloc(sizeof(struct wnd_ctx));
    SetWindowLongPtr(hWnd,GWLP_USERDATA,(LONG_PTR)pctx);
  }
  return pctx;
}

LRESULT CALLBACK wnd_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
  PAINTSTRUCT ps;
  HDC hdc;
  RECT rect;
  HBRUSH hbr;
  struct wnd_ctx* self;
  switch (message){
  case WM_LBUTTONUP:
    GetClientRect(hWnd,&rect);
    rect.top += nPad;
    rect.bottom -= nPad;
    rect.left += nPad;
    rect.right -= nPad;
    InvalidateRect(hWnd,&rect,TRUE);
    return 0;
  case WM_ERASEBKGND:
    DefWindowProc(hWnd, message, wParam, lParam);
    Sleep(d开发者_如何学编程elay);
    return 0;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    if(self = GetContext(hWnd)){
      hbr = CreateSolidBrush(self->base + ((self->index++ <<5) & 0x7f));
      GetClientRect(hWnd,&rect);
      FillRect(hdc,&rect,hbr);
      DeleteObject(hbr);
    }
    EndPaint(hWnd, &ps);
    Sleep(delay);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

ATOM CreateClass(HINSTANCE hInstance,LPCTSTR strClass,COLORREF brush,HICON hIcon){
  WNDCLASSEX wcex;
  wcex.cbSize = sizeof(WNDCLASSEX);
  wcex.style            = CS_HREDRAW|CS_VREDRAW;
  wcex.lpfnWndProc  = wnd_WndProc;
  wcex.cbClsExtra       = 0;
  wcex.cbWndExtra       = 0;
  wcex.hInstance        = hInstance;
  wcex.hIcon            = hIcon;
  wcex.hCursor      = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground    = CreateSolidBrush(brush);
  wcex.lpszMenuName = 0;
  wcex.lpszClassName    = strClass;
  wcex.hIconSm      = hIcon;
  return RegisterClassEx(&wcex);
}

BOOL CreateWindows(HINSTANCE hInstance, INT nCmdShow, LPCTSTR strTitle){
  HWND hWnd, hChild;
  ATOM atm;
  RECT rect;
  DWORD dwStyleEx, dwStyle, dwChildStyle, dwChildStyleEx;
  struct  wnd_ctx* pctx;
  dwStyleEx = WS_EX_COMPOSITED;
  dwStyle = WS_OVERLAPPEDWINDOW;//|WS_CLIPCHILDREN;
  dwChildStyle = WS_CHILD|WS_VISIBLE;//|WS_CLIPSIBLINGS;
  atm = CreateClass( hInstance, TEXT("APPWINDOW"), RGB(0x80,0x80,0x80), LoadIcon(NULL,IDI_APPLICATION));
  hWnd = CreateWindowEx(dwStyleEx,(LPCTSTR)atm, strTitle, dwStyle,
      CW_USEDEFAULT, 0, 256, 256, NULL, NULL, hInstance, NULL);
   pctx = GetContext(hWnd);
   pctx->base = RGB(0x00,0xff,0xff);
   pctx->index=0;
   atm = CreateClass(hInstance,TEXT("CONTROL1"),RGB(0x00,0x80,0x40),0);
   GetClientRect(hWnd,&rect);
   hChild = CreateWindowEx(0L,(LPCTSTR)atm, TEXT("Top"), dwChildStyle,
     rect.right/2, rect.top, rect.right/2, rect.bottom, hWnd, NULL, hInstance, NULL);
   pctx = GetContext(hChild);
   pctx->base = RGB(0x00,0xff,0x80);
   pctx->index=0;
   atm = CreateClass(hInstance,TEXT("CONTROL2"),RGB(0x00,0x40,0x80),0);
   hChild = CreateWindowEx(0L,(LPCTSTR)atm, TEXT("Bottom"), dwChildStyle,
     rect.left, rect.bottom/2, rect.right , rect.bottom/2, hWnd, NULL, hInstance, NULL);
   pctx = GetContext(hChild);
   pctx->base = RGB(0x00,0x80,0xff);
   pctx->index=0;
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   return TRUE;
 }

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow){
  MSG msg;
  CreateWindows(hInstance,nCmdShow,TEXT("Test Child Painting"));
  while(GetMessage(&msg, NULL, 0, 0)>0){
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int) msg.wParam;
}


The traditional model was that nothing was remembered (because memory was expensive), hence whenever a window was covered, the contents was forgotten, and it was redrawn in response to WM_PAINT.

But you knew this.

From the application point of view, the main change to this model the DWM makes is not to painting but to invalidation, i.e. causing painting to be required. Regions are not (necessarily) invalidated when covered by other windows, since the DWM remembers what the window looks like, and so doesn't need to ask the application "Remind me what you wanted to display there again?".

If you invalidate a region yourself either explicitly or implicitly (via SetWindowText for example) then it will still get WM_PAINT.

When painting the parent, children may or may not be clipped depending on whether that is set. I believe painting is done back-to-front, to allow child controls to (for example) draw 3D borders outside their own rectangle, as in Microsoft Word 6, if you remember that far back!

As far as I know this is not documented, and since you quoted Raymond Chen, you will know he would caution you against relying on the order of WM_PAINT messages.

0

精彩评论

暂无评论...
验证码 换一张
取 消