2011-04-21 159 views
0

我從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; 
} 
+0

我想知道的一件事就是爲什麼當DWM組合處於活動狀態時,您認爲WS_EX_COMPOSITED不起作用。 – 2011-04-21 11:13:36

+2

嗯,我懷疑它沒有記錄,因爲它不應該*相關*。他們不想承諾以任何特定的順序發送WM_PAINT消息,這是一個非常不尋常的情況,應用程序會關心它們被髮送的順序。實現應該是透明的。你是純粹出於好奇,還是想解決一些特殊問題?就DWM而言,它根本沒有涉及到這個過程。每個頂層窗口都會自動分層並重定向到屏幕外緩衝區。這個緩衝區隨後被傳送到屏幕上。 – 2011-04-21 11:13:59

+1

同意。這是由窗口管理器完成的,這是一個特別沒有文檔的代碼塊。有很多關於它如何工作的軼事證據,但這不是你要求的。把這個問題帶到你想要解決的具體問題的牆上,所以我們不必寫一本關於無證代碼的書。 – 2011-04-21 11:47:22

回答

1

傳統模式是什麼也沒有記住(因爲存儲器是昂貴的),因此,每當一個窗口是被覆蓋,內容被遺忘,並且響應WM_PAINT而重新繪製。

但你知道這一點。

從應用角度來看,DWM對此模型的主要變化不是繪畫而是無效,即導致需要繪畫。由於DWM記得窗口看起來像什麼,所以區域不會(必然)無效,因此不需要詢問應用程序「提醒我你想在那裏再次顯示什麼?」。

如果你自己無效的區域或隱或顯(通過SetWindowText函數例如),那麼它仍然會得到WM_PAINT。

在繪製父項時,根據是否設置子項,可能會或不會裁剪子項。我相信繪畫是背對背進行的,以允許子控件(例如)在他們自己的矩形外繪製3D邊框,就像在Microsoft Word 6中一樣,如果你還記得那麼遠!

據我知道這是不是證明了,既然你引述雷蒙德陳,你就會知道他會警告你不要依靠WM_PAINT消息的順序上。