C++ - окно Win32 зависает после первого розыгрыша (directx 11)

У меня есть стандартное окно win32, на котором я рисую D2D1, Все отзывчиво и работает без сбоев. Моя проблема заключается в следующем: после того, как окно создано, оно вызывает WM_PAINT один раз, а затем застревает в ожидании любого пользовательского ввода (например, движение мыши или щелчок в области окна). Получив ввод, он работает без каких-либо проблем. Хотя это не обязательно делает программу нефункциональной, она кажется крайне непрофессиональной и... некрасивой. О, и что я подразумеваю под "зависанием", так это то, что фоновые процессы по-прежнему выполняются без проблем, поэтому цикл выполняется так, как задумано - однако WM_PAINT не вызывается по какой-то причине.

Вот текущий код:

заголовочный файл:

#define PROGRAM_FPS 30
#define GWINDOW_STYLE (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)

class Application
{
public:
    Application();
    ~Application();

    // Register the window class and call methods for instantiating drawing resources
    HRESULT Initialize();

    // Process and dispatch messages
    void RunMessageLoop();

    static inline void SafeRelease(IUnknown * _X)
    {
        if(_X != NULL)
            _X->Release();
    };

    HRESULT SetFullscreen(bool);

private:

    // Time (ms) between frames of the application
    // Initiation: m_AppFPS_Div( (DWORD)1000.0 / (DWORD)PROGRAM_FPS )
    const DWORD m_AppFPS_Div;

    // Initialize device-independent resources.
    HRESULT CreateDeviceIndependentResources();

    // Initialize device-dependent resources.
    HRESULT CreateDeviceResources();

    // Release device-dependent resource.
    void DiscardDeviceResources();

    // Draw content.
    HRESULT OnRender();

    HRESULT LoadBitmapFromFile(
        ID2D1RenderTarget*,
        PCWSTR,
        UINT,
        UINT,
        ID2D1Bitmap **
        );

    // The windows procedure.
    static LRESULT CALLBACK WndProc(
        HWND hWnd,
        UINT message,
        WPARAM wParam,
        LPARAM lParam
        );
    HWND m_hwnd;
    ID2D1Factory * m_pDirect2dFactory;
    ID2D1HwndRenderTarget * m_pRenderTarget;
    IWICImagingFactory *m_pIWICFactory;
    ID2D1SolidColorBrush * m_pWhiteBrush;
    ID2D1SolidColorBrush * m_pGrayBrush;
    ID2D1LinearGradientBrush * m_pLinearGradientBrush;
    ID2D1Bitmap *m_LoadingPicture;
};

части файла.cpp

void Application::RunMessageLoop()
{
    HACCEL hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_WIN32APP));
    DWORD screen_last_refresh = 0;
    MSG msg;
    for(bool applicationRunning = true; applicationRunning;)
    {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT)
            {
                applicationRunning = false;
            }

            if(!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }

        /*** HERE I RUN VARIOUS BACKGROUND PROCESSES ***/

        while((GetTickCount() - screen_last_refresh) < m_AppFPS_Div)
            Sleep(2);
        InvalidateRect(msg.hwnd, NULL, false);
        screen_last_refresh = GetTickCount();
    }
}

LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result = 0;
    if (message == WM_CREATE)
    {
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
        Application *pApp = (Application *)pcs->lpCreateParams;

        ::SetWindowLongPtrW(hwnd, GWLP_USERDATA, PtrToUlong(pApp) );
        InvalidateRect(hwnd, NULL, false);
        result = 1;
    }
    else
    {
        Application *pApp = reinterpret_cast<Application *>(static_cast<LONG_PTR>(
            ::GetWindowLongPtrW(
                hwnd,
                GWLP_USERDATA
                )));

        bool wasHandled = false;

        if (pApp)
        {
            switch (message)
            {
            case WM_SIZE:
                {
                    UINT width = LOWORD(lParam);
                    UINT height = HIWORD(lParam);
                    pApp->OnResize(width, height);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_DISPLAYCHANGE:
                {
                    InvalidateRect(hwnd, NULL, FALSE);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_PAINT:
                {
                    pApp->OnRender();
                    ValidateRect(hwnd, NULL);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_DESTROY:
                {
                    PostQuitMessage(0);
                }
                result = 1;
                wasHandled = true;
                break;
            }
        }

        if (!wasHandled)
        {
            result = DefWindowProc(hwnd, message, wParam, lParam);
        }
    }
    return result;
}

HRESULT Application::OnRender()
{
    HRESULT hr = S_OK;
    hr = CreateDeviceResources();
    if (SUCCEEDED(hr))
    {
        m_pRenderTarget->BeginDraw();
        m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());

        /** HERE I HANDLE THE OPENING ANIMATION **/

        hr = m_pRenderTarget->EndDraw();
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    return hr;
}

Итак, я протестировал его на 2 компьютерах (оба с Win 8.1) с VS 2012 Ultimate и VS 2013 Professional, запускал отладочные тесты назад и вперед, разбирал определенные части программы, искал MDSN, Google и StackExchange - безрезультатно. Это замораживание также происходит, когда я справляюсь WM_COMMAND детского окна. Когда я пытался реализовать GDI+, такой проблемы также не было, но она оказалась крайне неэффективной, и поэтому я переключился на DirectX. Кроме того, программа работает без нареканий. Я надеюсь, что предоставил достаточно информации.

1 ответ

Вам не ясно, что вы делаете для запуска "различных фоновых процессов". Вероятно, отсюда и возникает "застрявшая" проблема. Предложение состоит в том, чтобы убрать это из цикла сообщений. Вместо этого вызывайте SetTimer во время инициализации окна, а затем выполняйте небольшую часть фонового процесса каждый раз, когда приходит сообщение WM_TIMER.

И, когда вы хотите, чтобы WM_PAINT пришел, вызовите InvalidateRect().

Другие вопросы по тегам