我们自己如何在 属性 页面中直接 运行 OnPsnHelp 事件(或调用它)?

How can we directly run the OnPsnHelp event (or invoke it) in a property page ourselves?

我一直在通过我的应用添加 OnHelpInfo 消息处理程序。这不是因为我的应用程序将支持上下文帮助。相反,我想拦截 F1 键,然后简单地调用现有的“帮助”代码,该代码在按下对话框按钮或菜单项时触发。示例:

#pragma warning (suppress : 26434)
BOOL CBrotherExcludeDlg::OnHelpInfo(HELPINFO* pHelpInfo)
{
    OnButtonHelp();

    // return CResizingDialog::OnHelpInfo(pHelpInfo);
    return TRUE;
}

我的问题是 属性 页面。他们使用不同的机制来处理帮助主题的显示。示例:

void CCalendarSettingsGooglePage::OnPsnHelp(NMHDR* hdr, LRESULT* res)
{
    theApp.DisplayHelpTopic(_T("msa-options-calendars.html"));
}

它使用 PSN_HELP 处理程序。当我将 OnHelpInfo 添加到 属性 页面时,我现在不确定如何简单地 运行 OnPsnHelp 事件。我试图避免在两个地方调用 DisplayHelpTopic.

我们如何直接调用 OnPsnHelp(就像我调用“帮助”按钮单击处理函数)或通过消息调用它?


这是样板代码:

BOOL CPropertyPage::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    ASSERT(pResult != NULL);
    NMHDR* pNMHDR = (NMHDR*)lParam;

    // allow message map to override
    if (CDialog::OnNotify(wParam, lParam, pResult))
        return TRUE;

    // don't handle messages not from the page/sheet itself
    if (pNMHDR->hwndFrom != m_hWnd && pNMHDR->hwndFrom != ::GetParent(m_hWnd))
        return FALSE;

    // handle default
    switch (pNMHDR->code)
    {
    case PSN_SETACTIVE:
        {
            CPropertySheet* pSheet = GetParentSheet();
            if (pSheet != NULL && !(pSheet->m_nFlags & WF_CONTINUEMODAL) && !(pSheet->m_bModeless) && !(pSheet->m_psh.dwFlags & PSH_AEROWIZARD))
                *pResult = -1;
            else
                *pResult = OnSetActive() ? 0 : -1;
        }
        break;
    case PSN_KILLACTIVE:
        *pResult = !OnKillActive();
        break;
    case PSN_APPLY:
        *pResult = OnApply() ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE;
        break;
    case PSN_RESET:
        OnReset();
        break;
    case PSN_QUERYCANCEL:
        *pResult = !OnQueryCancel();
        break;
    case PSN_WIZNEXT:
        *pResult = MapWizardResult(OnWizardNext());
        break;
    case PSN_WIZBACK:
        *pResult = MapWizardResult(OnWizardBack());
        break;
    case PSN_WIZFINISH:
        *pResult = reinterpret_cast<LRESULT>(OnWizardFinishEx());       
        break;
    case PSN_HELP:
        SendMessage(WM_COMMAND, ID_HELP);
        break;

    default:
        return FALSE;   // not handled
    }

    return TRUE;    // handled
}

所以我不明白。如果我使用 SendMessage(WM_COMMAND, ID_HELP);,软件中的响应是一个弹出窗口 Failed to launch help

正如您在评论中提到的,您 可以 只需使用两个 nullptr 值调用 OnPsnHelp 函数,因为该函数实际上并不使用那些参数。但是,这是自找麻烦,如果在您的软件的未来版本中,OnPsnHelp 被修改为 使用那些。

因此,您应该构造一个适当的 PSHNOTIFY 结构,并在 [=12] 的 'proxy' 调用中传递该结构和有效 return 代码变量的地址=]. PSHNOTIFY 结构的构造非常简单;它只是一个 NMHDR 结构,后跟一个 unused lParam 项。

这是一个可能的实现:

BOOL CCalendarSettingsGooglePage::OnHelpInfo(HELPINFO*)
{
    PSHNOTIFY psns;
    psns.lParam = 0; // The docs say this contains no information.
    psns.hdr.hwndFrom = GetParent()->GetSafeHwnd(); // The parent P/Sheet
    psns.hdr.idFrom = IDHELP; // Not sure what this should REALLY be?
    #pragma warning(suppress: 26454) // For: #define PSN_HELP (PSN_FIRST-5)
    psns.hdr.code = PSN_HELP; // One would assume this is the correct code!
    LRESULT result;
    OnPsnHelp(&psns.hdr, &result); // Make the call.
    return TRUE; // Or possibly: return static_cast<BOOL>(result);
}

来自M/S docs:

lParam

Pointer to a PSHNOTIFY structure that contains information about the notification code. This structure contains an NMHDR structure as its first member, hdr. The hwndFrom member of this NMHDR structure contains the handle to the property sheet. The lParam member of the PSHNOTIFY structure does not contain any information.


下面是 PSHNOTIFYNMHDR 结构的定义:

typedef struct _PSHNOTIFY
{
    NMHDR hdr;
    LPARAM lParam;
} PSHNOTIFY, *LPPSHNOTIFY;

typedef struct tagNMHDR
{
    HWND      hwndFrom;
    UINT_PTR  idFrom;
    UINT      code;         // NM_ code
}   NMHDR;