代码之家  ›  专栏  ›  技术社区  ›  rotoglup

使用Win32 API平滑动画-无需控制消息泵

  •  0
  • rotoglup  · 技术社区  · 16 年前

    我目前正试图以外部插件的形式将我的一些动画绘图代码集成到第三方应用程序中。

    在我的基础应用程序中,我是世界之王,我控制应用程序消息泵,以便尽可能地进行绘图。像这样:

    for (;;)
    {
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
      {
        do
        {
          if (msg.message == WM_QUIT) break;
          TranslateMessage(&msg);
          DispatchMessage(&msg);
        } 
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
      }
    
      draw();
    }
    

    现在我不再是世界之王,我必须好好处理应用程序消息,这样它才能保持响应。据我所知,由于我是一个插件,我无法劫持整个应用程序消息泵;所以我尝试了各种各样的事情,在里面画画 WM_PAINT 消息处理程序:

  • 使用 WM_TIMER ,这不起作用:我事先不知道我需要哪个时间步长(通常不是固定的),时间也不准确。
  • 呼叫 InvalidateRect 一旦我完成绘图,它就不起作用了:完全阻止了应用程序的其余部分响应并自行刷新。
  • 无效Rect there ).

    到目前为止,我的最后一次尝试是更好的,有时效果很好。
    DWORD WINAPI PaintCommandThreadProc(LPVOID lpParameter)
    {
      Plugin* plugin = static_cast<Plugin*>(lpParameter);
      HANDLE updateEvent = plugin->updateEvent();
    
      while (updateEvent == plugin->updateEvent())
      {
        ::WaitForSingleObject(updateEvent, 100);
        ::Sleep(0);
        if (updateEvent == plugin->updateEvent())
        {
          ::PostMessage(plugin->hwnd(), WM_USER+0x10, 0, 0);
        }
      }
      return 0;
    }
    
    ...
    
    LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
      bool processDefault = true;
      LRESULT result = 0;
    
      Plugin* plugin = reinterpret_cast<Plugin*>( GetWindowLong(hWnd, GWL_USERDATA) );
    
      switch (msg) {
        ...
        case WM_GL_MESSAGE:
          {
            ::InvalidateRect( hWnd, NULL, FALSE );
            processDefault = false;
            result = TRUE;
          }
          break;
    
        case WM_PAINT:
          {
            draw(hWnd);
            ::SetEvent( plugin->updateEvent() );
            processDefault = false;
            result = TRUE;
          }
          break;
        ...
      }
    
      if (processDefault && plugin && plugin->m_wndOldProc)
        result = ::CallWindowProc(plugin->m_wndOldProc, hWnd, msg, wParam, lParam);
    
      return result;
    }
    



    对于这种尽可能频繁的动画重绘问题,是否有任何“行业标准”的解决方案?

  • 1 回复  |  直到 16 年前
        1
  •  1
  •   Doub    16 年前

    每个线程都有自己的消息队列,发送到窗口的消息到达创建窗口的线程的队列。如果你自己创建插件窗口,你可以在一个单独的线程中创建它,这样你就可以完全控制它的消息泵。

    第二种解决方案的主要问题是插件WindowProc和渲染循环之间的通信必须考虑线程(即在访问共享内存时使用锁)。然而,由于消息泵与渲染是分开的,因此它可以是同时的,并且您的消息处理尽可能地响应。