终于懂了:WM_PAINT中应该用BeginPaint与EndPaint这两个api,它们的功能正是使无效区域恢复,所以WM_PAINT里即使什么都不做,也必须写上BeginPaint与EndPaint——Delphi里WM_PAINT消息的三个走向都做到了这一点 good?

程序本来是想实现鼠标单击改变背景颜色。可是,程序运行时,为什么没有任何消息触发,背景颜色就一直不断的改变了?WM_PAINT怎么被触发的

#include <windows.h>
#include <stdlib.h> 

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void DrawRectangle (HWND) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("SetBackgroundColor") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("注册失败!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("SetBackgroundColor"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while(GetMessage(&msg,NULL,0,0))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }

     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
 static RECT r;
 HDC hdc;
 HBRUSH hBrush;
    switch (iMsg)
    {
 case WM_PAINT:
  hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));
  hdc=GetDC(hwnd);
  FillRect (hdc, &r, hBrush) ;
  ReleaseDC (hwnd, hdc) ;
  DeleteObject (hBrush) ;
  return 0;

    case WM_SIZE:
         GetClientRect(hwnd,&r);
         return 0;

 case WM_LBUTTONDOWN:
  InvalidateRect(hwnd,&r,true);
  return 0;

case WM_DESTROY:          
  PostQuitMessage (0) ;
        return 0 ;
    }
     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}

回答:这个基础,看一下《windows程序设计》第三章吧

wm_paint是有无效区域的时候产生的消息,所以应首先恢复该区域,才不会一直循环下去

wm_paint中应该用beginpaint与endpaint这两个api,参数是PaintStruct类型,它们的功能正是使无效区域恢复

而不是getDC与releaseDC

invalidaterect也有使区域无效的功能,可用它手动触发wm_paint事件重绘

http://zhidao.baidu.com/question/112410452.html

--------------------------------------------------------------------------------------

对比Delphi代码:

procedure TWinControl.WMPaint(var Message: TWMPaint);
var
  DC, MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  PS: TPaintStruct;
begin
  if not FDoubleBuffered or (Message.DC <> 0) then // 消息里已经自带DC,用这个绘制就可以了
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then // 针对Windows自带控件,其包含的子控件数量为0,且没有自绘标记。
      inherited // 走向1,主要是针对Windows系统原生控件,微软定义的基本控件,总不会有错,应该内含BeginPaint了吧?
    else
      PaintHandler(Message); // 走向2,主要是针对Delphi的自绘控件,内含BeginPaint
  end
  else
  begin
    DC := GetDC(0); // 走向3,消息里没有自带DC,那么就现取,内含BeginPaint
    MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom);
    ReleaseDC(0, DC);
    MemDC := CreateCompatibleDC(0);
    OldBitmap := SelectObject(MemDC, MemBitmap);
    try
      DC := BeginPaint(Handle, PS);
      Perform(WM_ERASEBKGND, MemDC, MemDC);
      Message.DC := MemDC;
      WMPaint(Message);
      Message.DC := 0;
      BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY);
      EndPaint(Handle, PS);
    finally
      SelectObject(MemDC, OldBitmap);
      DeleteDC(MemDC);
      DeleteObject(MemBitmap);
    end;
  end;
end;
procedure TWinControl.PaintHandler(var Message: TWMPaint); var I, Clip, SaveIndex: Integer; DC: HDC; PS: TPaintStruct; begin DC := Message.DC; if DC = 0 then DC := BeginPaint(Handle, PS); try if FControls = nil then PaintWindow(DC) else begin SaveIndex := SaveDC(DC); Clip := SimpleRegion; for I := 0 to FControls.Count - 1 do with TControl(FControls[I]) do if (Visible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle)) and (csOpaque in ControlStyle) then begin Clip := ExcludeClipRect(DC, Left, Top, Left + Width, Top + Height); if Clip = NullRegion then Break; end; if Clip <> NullRegion then PaintWindow(DC); RestoreDC(DC, SaveIndex); end; PaintControls(DC, nil); finally if Message.DC = 0 then EndPaint(Handle, PS); end; end;

就是说,TWinControl无论是否双缓冲,都会调用BeginPaint和EndPaint函数,来消除自绘。整个Controls.pas单元也只有这两处调用BeginPaint。

另外,我没有找到TWinControl的DoubleBuffered被初始化的过程,说明编译器自动把它初始化成了False