【问题标题】:(Windows API) WM_PAINT Mouse problems(Windows API) WM_PAINT 鼠标问题
【发布时间】:2011-01-19 01:19:22
【问题描述】:

我创建了一个带有以下标志的窗口来覆盖 d3d 应用程序: WS_EX_TOPMOST | WS_EX_COMPOSITED | WS_EX_TRANSPARENT | WS_EX_LAYERED 我继续对窗口进行颜色键控以实现透明化,一切都运行良好。 然而,一旦我开始使用 GDI 绘制它,就会出现无法预料的问题:

由于某种原因,当 WM_PAINT 正在进行时,鼠标事件(尤其是移动)没有正确地通过窗口,因此看起来鼠标和键盘似乎滞后了。 FPS 很好,这是一些 API 问题,我怀疑由于某种原因,在 WM_PAINT 正在进行时键盘/鼠标消息没有按应有的方式处理,因为计时器设置得越慢,抽搐就越少。

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

 switch(msg)
 {
  case WM_DESTROY:
  {
          KillTimer(hwnd, ID_TIMER);
          PostQuitMessage(0);
          break;
         }
  case WM_CREATE:
  {
   SetTimer(hwnd, ID_TIMER, 10, NULL);
   break;
  }
  case WM_TIMER:
  {
   InvalidateRect(hwnd, 0, 1);
   break;
         }
  case WM_PAINT:
  {
   paint(hwnd);
   break;
  }
 }
 return DefWindowProc(hwnd, msg, wParam, lParam);
}

void paint (HWND hwnd)
{
 PAINTSTRUCT Ps;
 HDC hdc = BeginPaint(hwnd, &Ps);

 SetBkColor(hdc, RGB(0,0,0));
 SetBkMode(hdc, TRANSPARENT);



 LOGBRUSH log_brush;
 log_brush.lbStyle = BS_NULL;
 HBRUSH handle_brush = CreateBrushIndirect(&log_brush);
 SelectObject(hdc, handle_brush);


..........................................


 DeleteObject(font);
 DeleteObject(pen);
 DeleteObject(handle_brush);

 EndPaint(hwnd, &Ps);
}

感谢您提供的任何帮助。

【问题讨论】:

    标签: windows window gdi overlay createwindow


    【解决方案1】:

    WM_PAINT 消息永远不会传递到您的窗口,除非有人调用 UpdateWindow 或者您的输入队列中没有键盘或鼠标消息。

    一旦您开始处理 WM_PAINT,如果键盘或鼠标消息到达,它只会留在您的队列中,直到您完成 WM_PAINT。所以你描述的是不可能的。

    如果您的 WM_PAINT 代码需要很长时间才能执行,这可能会导致抖动,但您说这不是问题,所以也许是您对 WM_ERASEBKGND 的处理?我没有看到该代码,但我确实看到当您 InvalidateRect 时,您将 TRUE 作为最后一个参数传递,这意味着您希望删除背景。

    如果您不处理 WM_ERASEBKGND,那么 DefWindowProc 将为您使用窗口类中的画笔擦除整个窗口。这可能会导致窗口认为窗口的任何部分都不是透明的。

    如果您希望鼠标消息通过您的窗口,更可靠的方法是处理 WM_NCHITTEST 消息并在您希望鼠标通过的位置返回 HTTRANSPARENT。
    这基本上就是 WS_EX_TRANSPARENT 样式的工作原理。像这样

    case WM_NCHITTEST:
       {
       lRet = DefWindowProc(hwnd, uMsg, wParam, lParam);
       if (HTCLIENT == lRet)
          lRet = HTTRANSPARENT;
       }
    

    如果您的窗口没有非客户区,那么您可以跳过对 DefWindowProc 的调用。

    【讨论】:

    • 通过将 InvalidateRect 第三个参数更改为 FALSE,抖动消失了,但是之前的帧不再被删除,这能告诉你什么吗?这个 Windows API 令人困惑。谢谢。
    • 尝试使用 Fillrect 来擦除前一帧,这确实有助于大大减少抖动。
    • @Nick:你没有在你的绘制代码中展示你实际做了什么,所以我看不出你需要做什么来代替erasebkgnd,抱歉。 Fillrect 听起来可能会奏效。或者您可以使用 PatBlt 用出售的颜色填充矩形区域。
    • 好吧,似乎用纯色填充几百万像素会在处理过程中产生轻微的延迟,而这种延迟是问题所在,因为在该延迟期间,鼠标事件不会通过覆盖窗口导致鼠标滞后或更确切地说是抽搐。我该怎么办?感谢您的帮助。
    • @Nick:不要那样做 ;) 只需擦除您可以逃脱的最小矩形。您可以尝试将 ExtTextOut(.. ETO_OPAQUE, ...) 与空字符串一起使用,这一次是绘制纯色矩形的最快方法,但没有什么比 not 擦除你不删除的地方'不需要删除
    【解决方案2】:

    WndProc() 并不总是可重入的。我相信使用主消息泵,鼠标和键盘消息排队等待您完成之前的 WM_PAINT 消息。相反,如果您要从 WndProc() 调用 SendMessage(),那么您正在查看重新进入。另一种情况是 PostMessage() ,它将消息添加到队列中。如果这是一个问题,也许可以考虑使用 DirectInput 进行鼠标和键盘输入。否则,请寻找加快绘图速度的方法。

    【讨论】:

    • 您还应该将 SelectObject() 调用更改为 DeleteObject(SelectObject(hdc, handle_brush));
    • @John 的回答 - 他的计时器调用 InvalidateRect(),因此 WM_PAINT 将由 WM_TIMER 产生。对吗?
    猜你喜欢
    • 2018-12-04
    • 2014-03-29
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多