使用 DoMessageBox 将 AfxMessageBox 转换为 CTaskDialog

Posted

技术标签:

【中文标题】使用 DoMessageBox 将 AfxMessageBox 转换为 CTaskDialog【英文标题】:Converting a AfxMessageBox into a CTaskDialog using DoMessageBox 【发布时间】:2021-02-02 16:31:30 【问题描述】:

到目前为止我已经编写了这个函数:

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();

我对此有两个问题,很高兴分成两个问题:

    使用IDI_QUESTION 会导致应用程序崩溃。 难道没有更简单的方法可以将nType 解码为所需的各种按钮和图标吗?

【问题讨论】:

【参考方案1】:

使用IDI_QUESTION 会导致应用程序崩溃

这是因为IDI_QUESTION 是standard icon 并且必须通过将NULL 实例句柄传递给::LoadIcon 来加载,但MFC 的CWinApp::LoadIcon 会传递AfxGetResourceHandle()。下面绕过MFC,直接调用Win32 API。

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

难道没有更简单的方法可以将 nType 解码为所需的各种按钮和图标吗?

不是很容易,但可以将它们分组以减少重复。

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

【讨论】:

谢谢。我现在可以工作了。我也添加了自己的答案。 很高兴它有帮助。你的也可以。问题是我不知道在两组标志之间映射的内置方式,所以必须以一种或另一种方式手动完成。 @AndrewTruckle 那应该是IDI_HAND(与IDI_ERROR 相同)。这两个函数在这里没有多大帮助,因为它们在 CTaskDialog 样式和标准按钮 ID 之间映射,它们不直接对应于 MessageBox 样式。 @AndrewTruckle 是的,两个是the same。 Doh - 我不需要更新我的代码 - 保存值。已经抓到了。【参考方案2】:

我想我会添加这个作为附加答案。

采用已接受的答案 (@dxiv) 和下面制作的 cmets (@sergiol):

int CMeetingScheduleAssistantApp::DoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)

    CString strContent = CString(lpszPrompt);
    CString strTitle = CString();

    if (!CTaskDialog::IsSupported())
        return CWinAppEx::DoMessageBox(lpszPrompt, nType, nIDPrompt);

    ENSURE(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);
    HICON hQuestionIcon = ::LoadIcon(nullptr, IDI_QUESTION);

    // Icon
    switch (nType & MB_ICONMASK)
    
    case MB_ICONERROR:
        dlgTaskMessageBox.SetMainIcon(TD_ERROR_ICON);
        break;
    case MB_ICONWARNING:
        dlgTaskMessageBox.SetMainIcon(TD_WARNING_ICON);
        break;
    case MB_ICONINFORMATION:
        dlgTaskMessageBox.SetMainIcon(TD_INFORMATION_ICON);
        break;
    case MB_ICONQUESTION:
        dlgTaskMessageBox.SetMainIcon(hQuestionIcon);
        break;
    

    // Buttons
    int nCommonButtons = 0;
    switch (nType & MB_TYPEMASK)
    
    case MB_YESNOCANCEL:
        nCommonButtons |= TDCBF_CANCEL_BUTTON;
        [[fallthrough]];
    case MB_YESNO:
        nCommonButtons |= TDCBF_YES_BUTTON | TDCBF_NO_BUTTON;
        break;

    case MB_RETRYCANCEL:
        nCommonButtons |= TDCBF_RETRY_BUTTON | TDCBF_NO_BUTTON;
        break;

    case MB_OKCANCEL:
        nCommonButtons |= TDCBF_CANCEL_BUTTON;
        [[fallthrough]];
    case MB_OK:
    default:
        nCommonButtons |= TDCBF_OK_BUTTON;
    
    dlgTaskMessageBox.SetCommonButtons(nCommonButtons);

    return static_cast<int>(dlgTaskMessageBox.DoModal());

我不知道这些:

MB_ICONMASK MB_TYPEMASK

我没有满足的唯一场景是 MB_ABORTRETRYIGNORE,因为我没有看到 AbortIgnore 的常用按钮等效项。

【讨论】:

为什么要将按钮可见性的逻辑与图标的逻辑混合在一起?对于图标,您可以执行类似switch (nType &amp; MB_ICONMASK) 和按钮switch (nType &amp; MB_TYPEMASK) 之类的操作。 @sergiol MB_ICONMASK 等到底是什么?这些对于检索按钮和图标值是特殊的吗? MFC 记录在哪里? 关于这些的文档真的很差或不存在。我记得我们之前在这里做过的一些事情的标志,但它们本质上是一个掩码,用于过滤某些MB_* 值,具体取决于目的。有时了解 MFC 的更好方法是查看提供的基本源代码,或者在其中放置一些断点。 @IInspectable 是否有任何关于MB_*MASK 实体的文档?

以上是关于使用 DoMessageBox 将 AfxMessageBox 转换为 CTaskDialog的主要内容,如果未能解决你的问题,请参考以下文章

R语言使用dplyr将特定的数据列移动到最前面使用dplyr将特定数据列移动到另一指定数据列的后面使用dplyr将特定数据列移动到另一指定数据列的前面

您将如何将 node.js 子进程与 discord.js 一起使用?

使用 ATL 编译 dll,将方法参数作为接口,但将它们作为 coclasses

使用 Tumbleweed 安全文件传输将文件发送到 S3

使用 VBA 将表单另存为报表

使用 jQuery 1.5 将请求作为 jsonp 发送,将响应解释为文本