#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.