So, I want to display a CDialog to the user:
void CMeetingScheduleAssistantDlg::OnOptionsOutlookCalendarOptions()
{
    COutlookCalendarSettingsDlg dlgSettings(this);
    dlgSettings.DoModal();
}
Now, the popup dialogue (in OnInitDialog) runs a console application behind the scenes. This console application is communicating with Microsoft Graph.
As a result, it can take a few seconds for the dialog to display.
I execute the console application with this method:
bool CMeetingScheduleAssistantApp::ExecuteProgram(CString strCommand, DWORD& rExitCode)
{
    PROCESS_INFORMATION processInformation = { nullptr };
    STARTUPINFO         startupInfo = { 0 };
    int                 nStrBuffer;
    BOOL                bProcessResult, bExitCodeProcess;
    bool                bOK = false;
    CWaitCursor         wait;
    SetProgramExecuting(true);
    rExitCode = -1;
    startupInfo.cb = sizeof(startupInfo);
    nStrBuffer = strCommand.GetLength() + 50;
    bProcessResult = CreateProcess(nullptr, strCommand.GetBuffer(nStrBuffer),
        nullptr, nullptr, FALSE,
        NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
        nullptr, nullptr, &startupInfo, &processInformation);
    strCommand.ReleaseBuffer();
    if (!bProcessResult)
    {
        // CreateProcess() failed
        // Get the error from the system
        LPVOID lpMsgBuf;
        DWORD dw = GetLastError();
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr);
        // Display the error
        CString strError = (LPTSTR)lpMsgBuf;
        TRACE(_T("Authenticate failed at CreateProcess()\nCommand=%s\nMessage=%s\n\n"), strCommand, strError);
        // Free resources created by the system
        LocalFree(lpMsgBuf);
        SetProgramExecuting(false);
        // We failed.
        return false;
    }
    else
    {
        // Successfully created the process.  Wait for it to finish.
        DWORD WaitResult;
        do
        {
            WaitResult = MsgWaitForMultipleObjects(1,
                // only 1 wait object
                &processInformation.hProcess, // worker thread
                FALSE,   // stop if any
                INFINITE,  // no timeout
                QS_ALLINPUT);
            if (WaitResult == WAIT_OBJECT_0 + 1)
            {
                // Handle windows message
                MSG Msg;
                while (PeekMessage(&Msg, nullptr, 0, (UINT)-1, PM_REMOVE))
                {
                    TRACE3("%d %d %d\n", Msg.message, Msg.wParam, Msg.lParam);
                    TranslateMessage(&Msg);
                    DispatchMessage(&Msg);
                }
            }
        } while (WaitResult != WAIT_OBJECT_0);
        ASSERT(WaitResult == WAIT_OBJECT_0);
        // Get the exit code.
        bExitCodeProcess = GetExitCodeProcess(processInformation.hProcess, &rExitCode);
        // Close the handles.
        CloseHandle(processInformation.hProcess);
        CloseHandle(processInformation.hThread);
        if (!bExitCodeProcess)
        {
            // Could not get exit code.
            TRACE(_T("Executed command but couldn't get exit code.\nCommand=%s\n"), strCommand);
            SetProgramExecuting(false);
            return false;
        }
        SetProgramExecuting(false);
        return true;
    }
}
Inside OnInitDialog, just before the ExecuteProgram is called, I tried using:
CWaitCursor wait;
But it makes no difference. So how can I show a wait cursor from the moment I invoke the popup dialog until the dialog is visible to the user?
One solution could be to use Modeless Dialog. You can create a dialog which looks similar to wait cursor dialog. 
You show that Modeless Dialog just before dlgSettings.DoModal(); statement in your code. Please use TOP_MOST while showing Modeless Dialog. 
Finally, hide/close Modeless Dialog from OnInitDialog() once processing is over. 
Another approach could be:
Add a public member of asCWaitCursor* m_pWaitCursor in COutlookCalendarSettingsDlg class. Now modify code as
void CMeetingScheduleAssistantDlg::OnOptionsOutlookCalendarOptions()
{
    COutlookCalendarSettingsDlg dlgSettings(this);
    dlgSettings->m_pWaitCursor = new CWaitCursor();
    dlgSettings.DoModal();
}
Then modify OnInitDialog of COutlookCalendarSettingsDlg to delete instance of CWaitCursor before returning from it.
delete m_pWaitCursor;
I thought I would add an update to this answer that applies in other situations. What you do is use a CPersistantWaitCursor instead. The article provides a little example:
#include "PersistentWaitCursor.h"
void CMyWnd::DoSomeLengthyOperation()
{
    // Create and show the wait cursor
    CPersistentWaitCursor waitCursor;
    // Do some lengthy operation
    ...
    // waitCursor goes out of scope and cursor is restored
}
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    if (CPersistentWaitCursor::WaitCursorShown())
    {
        // We are showing the wait cursor
        RestoreWaitCursor();
        return TRUE;
    }
    // Let the base class deal with this one
    return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
Look at the article for full details about how it works. But I can confirm that for some of my other lengthy actions this enhanced CPersistantWaitCursor did the trick.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With