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

75 views Asked by At

I have been working through my app adding the OnHelpInfo message handler. This is not because my app will support context help. Rather, I wanted to intercept the F1 key and then simply call the existing "Help" code that is triggered when then press the dialog button or menu item. Example:

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

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

My problem is for the property pages. They handle the displaying on the help topics by using a different mechanism. Example:

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

It uses the PSN_HELP handler. When I add the OnHelpInfo to the property page, I am now not sure how to simply run the OnPsnHelp event. I am trying to avoid having two places where I call DisplayHelpTopic.

How can we either directly call OnPsnHelp (like I called the Help button click handler function) or invoke it via a message?


This is the boiler plate code:

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
}

So I don't get that. If I use SendMessage(WM_COMMAND, ID_HELP); the response in the software is a popup Failed to launch help.

1

There are 1 answers

8
Adrian Mole On BEST ANSWER

As you mentioned in the comments, you could just call your OnPsnHelp function with two nullptr values, because that function doesn't actually use those parameters. However, this is asking for trouble if, in a future version of your software, the OnPsnHelp is modified so that it does use those.

So, you should construct an appropriate PSHNOTIFY structure and pass the address of that, and of a valid return-code variable, in your 'proxy' call of OnPsnHelp. The construction of that PSHNOTIFY structure is quite trivial; it is simply an NMHDR structure followed by an unused lParam item.

Here's a possible implementation:

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

From the 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.


Here are the definitions of the PSHNOTIFY and NMHDR structures:

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

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