Search code examples
visual-c++mfcmessageboxtaskdialog

Converting a AfxMessageBox into a CTaskDialog using DoMessageBox


I have written this function so far:

int CMFCApplication3App::DoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)
{
    CString strContent = CString(lpszPrompt);
    CString strTitle; strTitle.LoadString(AFX_IDS_APP_TITLE);
    CTaskDialog dlgTaskMessageBox(strContent, _T(""), strTitle);
    int iPixelWidth = (::GetSystemMetrics(SM_CXSCREEN) / 100) * 30;
    int iDialogUnitsWidth = MulDiv(iPixelWidth, 4, LOWORD(GetDialogBaseUnits()));
    dlgTaskMessageBox.SetDialogWidth(iDialogUnitsWidth);

    /*
    if (nType & MB_ICONINFORMATION)
        dlgTaskMessageBox.SetMainIcon(TD_INFORMATION_ICON);
    if (nType & MB_ICONERROR)
        dlgTaskMessageBox.SetMainIcon(TD_ERROR_ICON);
    if (nType & MB_ICONWARNING)
        dlgTaskMessageBox.SetMainIcon(TD_WARNING_ICON);
    if (nType & MB_ICONQUESTION)
    {
        HICON hIcon = LoadIcon(IDI_QUESTION);
        dlgTaskMessageBox.SetMainIcon(hIcon);
    }

    int iButtons = 0;
    if (nType & IDYES)
        iButtons |= TDCBF_YES_BUTTON;
    if (nType & IDNO)
        iButtons |= TDCBF_NO_BUTTON;
    if (nType & IDCANCEL)
        iButtons |= TDCBF_CANCEL_BUTTON;
    if (nType & IDOK)
        iButtons |= TDCBF_OK_BUTTON;
    if (nType & IDRETRY)
        iButtons |= TDCBF_RETRY_BUTTON;
    dlgTaskMessageBox.SetCommonButtons(iButtons);
    */

    if (nType == (MB_YESNOCANCEL | MB_ICONERROR))
    {
        dlgTaskMessageBox.SetCommonButtons(TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON);
        dlgTaskMessageBox.SetMainIcon(TD_ERROR_ICON);
    }
    if (nType == (MB_YESNOCANCEL | MB_ICONWARNING))
    {
        dlgTaskMessageBox.SetCommonButtons(TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON);
        dlgTaskMessageBox.SetMainIcon(TD_WARNING_ICON);
    }
    if (nType == (MB_YESNOCANCEL | MB_ICONINFORMATION))
    {
        dlgTaskMessageBox.SetCommonButtons(TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON);
        dlgTaskMessageBox.SetMainIcon(TD_INFORMATION_ICON);
    }
    /*
    if (nType == (MB_YESNOCANCEL | MB_ICONQUESTION))
    {
        dlgTaskMessageBox.SetCommonButtons(TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON);
        HICON hIcon = LoadIcon(IDI_QUESTION);
        dlgTaskMessageBox.SetMainIcon(hIcon);
    }
    */
    return dlgTaskMessageBox.DoModal();
}

I have two issues with this and am happy to split as two questions:

  1. Using IDI_QUESTION is causing the application to crash.
  2. Isn't there an easier way to decode nType into the various buttons and icon required?

Solution

  • Using IDI_QUESTION is causing the application to crash

    That's because IDI_QUESTION is a standard icon and must be loaded by passing a NULL instance handle to ::LoadIcon, but MFC's CWinApp::LoadIcon passes AfxGetResourceHandle() instead. The following bypasses MFC and calls the Win32 API directly.

    HICON hIcon = ::LoadIcon(NULL, IDI_QUESTION);
    

    Isn't there an easier way to decode nType into the various buttons and icon required?

    Not a lot easier, but they could be grouped to lessen the duplication.

    int nCommonButtons = 0;
    
    switch(nType)
    {
    case MB_YESNOCANCEL:
        nCommonButtons |= TDCBF_CANCEL_BUTTON;
    case MB_YESNO:
        nCommonButtons |= TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; 
        break;
    
    case MB_OKCANCELRETRY:
        nCommonButtons |= TDCBF_RETRY_BUTTON;
    case MB_OKCANCEL:
        nCommonButtons |= TDCBF_CANCEL_BUTTON;
    case MB_OK:
        nCommonButtons |= TDCBF_OK_BUTTON; 
        break;
    
    //... etc
    }