Detect window restore operation is about to begin

2019-07-30 16:23发布

WM_GETMINMAXINFO is generated when maximize operation is about to begin and WM_SIZE when maximize operation is finished.

WM_SIZE is also generated when restore operation is finished.

But how to detect window restore operation is about to begin?


I need detect exact moment when window is about to restore , but not the moment when already restored. I am developing multithread DirectX application. I render in dedicated secondary thread. When window is about to begin maximize or restore I need to change render back buffer size (DirectX Device Reset). I can resize back buffer only from main thread so I use Critical Sections to sync with render thread. The problem is that I cant interrupt Present operation in render thread and when maximize or restore operation is about to begin I wait until current Present operation is finished and only then begin to resize (maximize / restore). If you change back buffer size too late (when maximize / restore operation is finished (WM_SIZE message) you can notice old frame is drawn with wrong size (image is stretched).

1条回答
太酷不给撩
2楼-- · 2019-07-30 16:40

Well, it is nice to see that someone is still ponder over such issues. This small things, like one-second image stretching when resizing, distinguish a professional looking application from bedroom coded one.

In my old code I found tricks that probably will be interesting to you. A first thing is that you can check WM_SYSCOMMAND:

        case WM_SYSCOMMAND:
           {
               switch (wParam)
               {
               case SC_MAXIMIZE:
                   std::cout << "Going to MAXIMIZE: " << std::endl;
                   break;

               case SC_MINIMIZE:
                   std::cout << "Going to MINIMIZE: " << std::endl;

                   break;

               case SC_RESTORE:
                   std::cout << "Going to RESTORE: " << std::endl;
                   break;


               default:
                   break;
               }
               return DefWindowProc(m_hWnd, msg, wParam, lParam);
           }

But the problem is that it don't catch maximize / restore events when user double-clicks titlebar.

So I found a little trick, when parse WM_WINDOWPOSCHANGING.

First let's do a little fun research. We read docs here and here where we found that:

WM_WINDOWPOSCHANGING message sent to a window whose size, position, or place in the Z order is ABOUT to change

Because it is nearly impossible to debug events in common debugger (how would you test if debugger often changes z-order of window?), so for testing purposes we will make a little console app with int main(), where we initialize a window as usual (HINSTANCE we can get from GetModuleHandle(0);). So we have a console and a window at same time. In window procedure we catch WM_WINDOWPOSCHANGING and print info, it will tell us, to console:

        case WM_WINDOWPOSCHANGING:
        {
            WINDOWPOS* wp = ((WINDOWPOS*)lParam);

            // We checking current state which is saved in member (or global) bools
            // Set them checking WM_SIZE before
            if (m_bMaximized)
            {
                std::cout << "Currently MAXIMIZED: ";
            }
            else if (m_bMinimized)
            {
                std::cout << "Currently MINIMIZED: ";
            }
            else
            {
                std::cout << "Currently NORMAL: ";
            }

            dbgPrintPositionCurrent();

            std::cout << "Going to change to: ";
            dbgPrintPosition(wp->x, wp->y, wp->cx, wp->cy, wp->flags);
            std::cout << std::endl << std::endl;

            return DefWindowProc(m_hWnd, msg, wParam, lParam);
        }

See utility functions here.

When we played enough we can now make it useful:

                bool bFrameChanged = ((wp->flags) & SWP_FRAMECHANGED) > 0;
            bool bNoCopyBits = ((wp->flags) & SWP_NOCOPYBITS) > 0;
            bool bNormal = (!m_bMaximized) && (!m_bMinimized);

            // from maximized
            if(m_bMaximized && bFrameChanged && !bNoCopyBits)
            {
                std::cout << " MAXIMIZED -> NORMAL " << std::endl;
            }
            if (m_bMaximized && bFrameChanged && bNoCopyBits)
            {
                std::cout << " MAXIMIZED -> MINIMIZED " << std::endl;
            }

            // from normal states
            if (bNormal && bFrameChanged && !bNoCopyBits)
            {
                std::cout << " NORMAL -> MAXIMIZED " << std::endl;
            }
            if (bNormal && bFrameChanged && bNoCopyBits)
            {
                std::cout << " NORMAL -> MINIMIZED"  << std::endl;
            }

            // from minimized
            if(m_bMinimized && bFrameChanged)
            {
                std::cout << " MINIMIZED -> MAXIMIZED " << std::endl;
            }
            if(m_bMinimized && m_bMaximized && bFrameChanged)
            {
                std::cout << " MINIMIZED -> MAXIMIZED " << std::endl;
            }


            return DefWindowProc(m_hWnd, msg, wParam, lParam);

I'm really not sure that is it a simplest or even is it right way to do this. But it works for now =) Also, I think your app dont'care what exactly happened: maximizing, restoration or resizing. It just cares if size has changed, so you need to resize your buffers / recreate swap chain etc...

Hope it helps! Happy coding!

查看更多
登录 后发表回答