A strange problem with modeless dialog boxes - V2

Alex Sokolek

Member
Joined
Apr 5, 2024
I have solved the problem! See the original post, "A strange problem with modeless dialog boxes", for details.

This is a repost, incorporating the latest simplifications of the problem statement, and a minimal reproducible example of the code...

I'm having a problem with modeless dialog boxes. I have one in a loop that scans files in a directory, adding the files to a linked list class for later processing, using the modeless dialog box as a progress box. When I run with the debugger, F5, it works fine. When I run without the debugger, Ctrl-F5, the box stops updating after five seconds, the mouse cursor changes to an hourglass, and the "X" button in the upper right hand corner of the windows turns dim red. This condition persists until the loop is done, at which point the program continues normally.

I have consolidated the code into a minimalist example of what is going on, using only the dialog box and the loop, which simply counts to 100, with 100 millisecond delays between iterations.

Here are my Global Variables...

Code:
HWND hwndDialog1;
HDC dc;
UINT_PTR upTimer;
int wmId;
HDC hdc;

and here is the Windows Proc...

Code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
        hwndDialog1 = CreateDialog(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, NULL);
        break;
   
    case WM_COMMAND:
        wmId = LOWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        case ID_TEST_FIRE:
            dc = GetDC(hwndDialog1);
            ShowWindow(hwndDialog1, SW_SHOW);
            TCHAR sz[5];
            for (int i = 1; i <= 100; ++i)
            {
                StringCchPrintf(sz, 5, _T("%d"), i);
                SetBkColor(dc, RGB(240, 240, 240));
                TextOut(dc, 50, 50, sz, lstrlen(sz));
                Sleep(100);
            }
            upTimer = SetTimer(hWnd, 1, 2000, NULL);
            break;
           
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
       
        break;

    case WM_TIMER:
        KillTimer(hWnd, upTimer);
        ShowWindow(hwndDialog1, SW_HIDE);
        break;

    case WM_DESTROY:
        DestroyWindow(hwndDialog1);
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

I've scoured the documentation for modeless dialog boxes, and I can't find an explanation. The strange part of this is that the code works correctly when I run with the debugger, and it freezes after five seconds when I run without the debugger. Please take a look and see if I have missed something. Thank you.
 
Last edited:
This appears to be a potential example of blocking Windows message queue. Key parts of Windows GUI interact through an event-driven mechanism, using a message queue. When you call the Sleep function in a loop, it blocks the queue and the Windows messages are unable to get through, leading to the GUI becoming non-responsive.

Specifically, the loop where you sleep for 100ms per iteration is run directly in response to a WM_COMMAND message; during this time Windows messages posted to the queue aren't processed, freezing the UI.

I suggest using a different approach that doesn't block the window's message pump.

One simple alternative is to use a separate thread to do the job. But you should be careful as UI function calls must be made from the thread that created the window.

A better approach is to use a timer to send messages periodically. Instead of using `Sleep(100)`, you could create a window timer using `SetTimer`:

Code:
// Declare a global counter variable
int i = 0;

// ...

case ID_TEST_FIRE:
    dc = GetDC(hwndDialog1);
    ShowWindow(hwndDialog1, SW_SHOW);
    // Setup timer
    upTimer = SetTimer(hWnd, 1, 100, NULL); // 100ms interval
    break;

case WM_TIMER:
    // WM_TIMER handling
    TCHAR sz[5];
    if (i < 100)
    {
        i++;
        StringCchPrintf(sz, 5, _T("%d"), i);
        SetBkColor(dc, RGB(240, 240, 240));
        TextOut(dc, 50, 50, sz, lstrlen(sz));
    }
    else
    {
        KillTimer(hWnd, upTimer);
        ShowWindow(hwndDialog1, SW_HIDE);
    }
    break;

This will fire a WM_TIMER message every 100ms without blocking the queue, allowing your dialog to handle all messages and stay responsive. Also check if it's necessary to get your dialog's DC every time ID_TEST_FIRE is commanded. If the DC does not change, you may consider getting it once and reuse it.

The Hourglass cursor and red X indicate that your GUI is not responsive because it's not processing messages. When you don't block the message queue, the hourGlass cursor and red X should disappear, and your GUI should remain responsive.
 
I don't think that's the answer. This is because the program works fine if I run with the debugger, F5, but it does not work fine if I run without the debugger, Ctrl-F5. I would expect the function to be the same for each case, but it's not.

Besides, the dialog box procedure does not process messages. It is an empty dialog box with no controls. I am simply drawing in the dialog box with a DC that I get from GetDC(). Also, I tried to provide a dialog box procedure that processesd messages - that did not change the symptomology.

Last, the real program does not have Sleep() calls inside the loop. I provided the Sleep() call in the example because I wanted to emulate the process of processing the directory without cluttering up the posting.
 
Last edited:
Back
Top Bottom