为什么不手动发送WM_PAINT

why not to send WM_PAINT manually

本文关键字:WM PAINT 为什么不      更新时间:2023-10-16

我读到过,我不应该手动发送WM_PAINT,而应该调用InvalidateRect,但是没有发现任何原因。为什么不呢?

updateInvalidateRect有效,但对SendMessage(WM_PAINT)无效

LRESULT CALLBACK window_proc(HWND wnd, UINT msg, WPARAM w_param, LPARAM l_param)
{
  switch (msg)
  {
    case WM_PAINT:
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(wnd, &ps);
        Polyline(..);
        EndPaint(wnd, &ps);
        return 0;
    case WM_USER:           
        // SendMessage(wnd, WM_PAINT, NULL, NULL);
        // InvalidateRect(wnd, NULL, FALSE);
        return 0;
  }
}

WM_PAINT的官方文档在备注部分的第一句话就指出不应该这样做。说真的,这应该是一个足够的理由不。

至于技术原因,我想这是其中之一,摘自BeginPaint评论部分:

更新区域由InvalidateRectInvalidateRgn函数设置,并由系统在调整大小、移动、创建、滚动或任何其他影响客户端区域的操作后设置。

因此,如果您手动发送WM_PAINT, BeginPaint可能无法正常工作。

可能有更多的原因/惊喜

如果您想立即触发重新绘制,正确的方法是:

  1. 使用InvalidateRect() + UpdateWindow()

  2. 使用RedrawWindow()

这些将触发新的WM_PAINT消息生成

您没有任何关于其他程序的窗口的信息。只有操作系统有这个信息。所以你真的不知道你的窗户何时何地需要重新粉刷。WM_PAINT和BeginPaint提供了这些缺失的信息

因为WM_PAINT不是真正的消息

想象一下,每个窗口都有一个存储"无效区域"的结构,也就是说,"屏幕上窗口的这一部分不再是最新的,需要重新绘制"。

无效区域由窗口管理器自己修改(窗口大小调整,覆盖等),或通过调用InvalidateRect, ValidateRect, EndPaint等。

现在,这里是GetMessage如何处理这个的粗略模型:

... GetMessage(MSG* msg, ...)
{
  while(true) {
    if(ThereIsAnyMessageInTheMessageQueue()) {
      *msg = GetFirstMessageOfTheMessageQueue();
      return ...;
    } else if(TheInvalidRegionIsNotEmpty()) {
      *msg = CreateWMPaintMessage();
      return ...;
    } else {
      WaitUntilSomethingHappend();
    }
  }
}

tl;dr: WM_PAINT表示接收,而不是发送。