Ссылка на рисование дочернего окна

#c #windows #winapi

#c #Windows #winapi

Вопрос:

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

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

Насколько я понимаю (из чтения Raymond Chen и MSDN), нет недопустимых областей, связанных с дочерними окнами — когда дочерние окна становятся недействительными, соответствующая область в родительском окне становится недействительной.

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

И затем, в Windows версии 6.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,amp;rect);
    rect.top  = nPad;
    rect.bottom -= nPad;
    rect.left  = nPad;
    rect.right -= nPad;
    InvalidateRect(hWnd,amp;rect,TRUE);
    return 0;
  case WM_ERASEBKGND:
    DefWindowProc(hWnd, message, wParam, lParam);
    Sleep(delay);
    return 0;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, amp;ps);
    if(self = GetContext(hWnd)){
      hbr = CreateSolidBrush(self->base   ((self->index   <<5) amp; 0x7f));
      GetClientRect(hWnd,amp;rect);
      FillRect(hdc,amp;rect,hbr);
      DeleteObject(hbr);
    }
    EndPaint(hWnd, amp;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(amp;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,amp;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(amp;msg, NULL, 0, 0)>0){
    TranslateMessage(amp;msg);
    DispatchMessage(amp;msg);
  }
  return (int) msg.wParam;
}
  

Комментарии:

1. одна вещь, которую я хотел бы знать, это то, почему вы думаете, что WS_EX_COMPOSITED не имеет никакого эффекта, когда активна композиция DWM.

2. Хм, я подозреваю, что это не задокументировано, потому что это не должно быть релевантным . Они не хотят фиксировать отправку WM_PAINT сообщений в каком-либо определенном порядке, и это чрезвычайно необычный случай, когда приложению было бы все равно, в каком порядке они были отправлены. Реализация должна быть прозрачной. Вы спрашиваете исключительно из любопытства или пытаетесь решить какую-то конкретную проблему? А что касается DWM, то он вообще не подключается к процессу. Каждое окно верхнего уровня автоматически разбивается на слои и перенаправляется в буфер вне экрана. Затем этот буфер выводится на экран.

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

4. Что ж, отвечая и Дэвиду, и Хансу, мне интересно, что мое тестовое приложение, использующее WS_EX_COMPOSITED, мерцает при изменении размера с включенным DWM.

5. @Chris Я тебе верю. Просто это решило мои проблемы. Очевидно, что мы делаем разные вещи в других местах нашего кода.

Ответ №1:

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

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

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

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

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

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