Справочник по рисованию детских окон - PullRequest
0 голосов
/ 21 апреля 2011

Из MSDN я понимаю, как windows обрабатывает сообщения WM_PAINT, отправленные в конкретное окно.

Одна вещь, которую MSDN, похоже, не документирует, - это фактический процесс, посредством которого оконный менеджер решает, какие окна должны получать сообщения WM_PAINT и в каком порядке.

Как я понимаю (из чтения Рэймонда Чена и MSDN), нет недопустимых областей, связанных с дочерними окнами - когда дочерние окна становятся недействительными, соответствующая область в родительском окне становится недействительной.

Меня смущает поколение WM_PAINT ... и точная точка (особенно в отношении многопоточности), что недопустимая область окон помечается как действительная - когда задействованы дочерние окна, это становится особенно интересным. Как GetMessage решает, какой из набора окон (родители + дочерние окна, которые пересекают недопустимую область) получают сообщения WM_PAINT и в каком порядке? и как это меняется в лице WS_CLIPSIBLINGS, WS_CLIPCHILDREN, WS_EX_COMPOSITED, WS_EX_TRANSPARENT и так далее? И что произойдет, если другой поток сделает недействительной часть окна верхнего уровня в середине этого процесса?

А затем, в Windows V6.0 +, как DWM подключается к этому процессу?


Это пример программы на C, демонстрирующий сбой, возникающий при использовании 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(delay);
    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;
}

1 Ответ

1 голос
/ 21 апреля 2011

Традиционная модель состояла в том, что ничего не запоминалось (потому что память была дорогой), поэтому всякий раз, когда окно было закрыто, содержимое забывалось, и оно перерисовывалось в ответ на WM_PAINT.

Но вы знали это.

С точки зрения применения, основное изменение в этой модели, которое вносит DWM, заключается не в рисовании, а в аннулировании , т. Е. Необходимости рисования. Области не являются (обязательно) недействительными, когда они закрыты другими окнами, так как DWM запоминает, как выглядит окно, и поэтому не нужно спрашивать приложение «Напомнить, что вы хотели отобразить там снова?».

Если вы объявите регион недействительным самостоятельно, явно или неявно (например, через SetWindowText), он все равно получит WM_PAINT.

При рисовании родителя дочерние элементы могут вырезаться или не вырезаться в зависимости от того, установлено ли это. Я полагаю, что рисование выполняется задом наперед, чтобы дочерние элементы управления могли (например) рисовать 3D-границы за пределами своего прямоугольника, как в Microsoft Word 6, если вы помните это далеко назад!

Насколько я знаю, это не задокументировано, и поскольку вы процитировали Рэймонда Чена, вы будете знать, что он предостерегает вас от необходимости полагаться на порядок сообщений WM_PAINT.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...