/////////////////////////////////////////////////////////////////////////////////////////
// Project: SP ATL Extensions 1.3
//
// File: spDetailedMessageBox.h interface for
// SP::CDetailedMessageWindow class,
// SP::CDetailedMessageBoxImpl and
// SP::CAxDetailedMessageBoxImpl
// class templates.
//
// Developer(s): Sergei Pavlovsky
// sergei_vp@hotmail.com
// Copyright (c) 2003-2005
//
// Description: In addition to message string extended message box provids an
// option to show/hide additional information or "details".
// CDetailedMessageWindow - implements common operations needed for
// message box functionality.
// CDetailedMessageBoxImpl - handles standard window messages.
// CAxDetailedMessageBoxImpl - handles standard window messages
// and also provides support for AcviveX controls.
//
// Platforms: Win32, ATL
//
// This code may be used in compiled form in any way you desire. This file may be
// redistributed unmodified by any means PROVIDING it is not sold for profit without
// the authors written consent, and providing that this notice and the authors name
// is included. If the source code in this file is used in any commercial application
// then acknowledgement must be made to the author of this file (in whatever form
// you wish).
//
// This file is provided "as is" with no expressed or implied warranty. The author
// accepts no liability for any damage/loss of business that this product may cause.
/////////////////////////////////////////////////////////////////////////////////////////
#if !defined(__SP_SPDETAILEDMESSAGEBOX_H__INCLUDED__)
#define __SP_SPDETAILEDMESSAGEBOX_H__INCLUDED__
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#include <vector>
#include <string>
namespace SP
{
/////////////////////////////////////////////////////////////////////////////////////////
// Structures
enum DTLMSGBOXSTYLES
{
DMB_TOOLWINDOW = 0x0001,
DMB_CONTEXTHELP = 0x0002,
#if (_WIN32_WINNT >= 0x0500)
DMB_RTLREADING = 0x0004,
#endif // (_WIN32_WINNT >= 0x0500)
DMB_HASDETAIL = 0x0008,
DMB_HASHELP = 0x0010
};
typedef struct
{
UINT unID;
LPCTSTR lpcszText;
DWORD dwCtxHelpID;
} DTLMSGBOXBUTTONPARAMS, *LPDTLMSGBOXBUTTONPARAMS;
typedef struct
{
UINT cbSize;
HWND hwndOwner;
DWORD fStyle;
HFONT hFont;
DWORD dwCtxHelpID;
LPCTSTR lpcszCaption;
HICON hIcon;
LPCTSTR lpcszText;
int cButtons;
const DTLMSGBOXBUTTONPARAMS* pacButtons;
int iDefaultButton;
UINT unHelpID;
DWORD dwHelpCtxHelpID;
LPCTSTR lpcszHelp;
UINT unDetailID;
DWORD dwDetailCtxHelpID;
LPCTSTR lpcszDetailOn;
LPCTSTR lpcszDetailOff;
} DTLMSGBOXPARAMS, *LPDTLMSGBOXPARAMS;
/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageBoxBase class
class CDetailedMessageWindow : public CWindow
{
// Types
protected:
typedef std::vector<HWND> CHwndVector;
typedef std::basic_string<TCHAR> tstring;
// Helper classes
private:
// CDialogTempalte class
friend class CDialogTempalte;
class CDialogTempalte
{
// Types
private:
union MultyTypePtr
{
BYTE* lpb;
WORD* lpw;
DWORD* lpdw;
};
// Construction & destruction
public:
CDialogTempalte() throw()
: m_pcDMBP(NULL)
{
}
CDialogTempalte(const DTLMSGBOXPARAMS* pcDMBP)
: m_pcDMBP(NULL)
{
if ( !Initialize(pcDMBP) )
throw false; // ???
}
// Initialization & uninitialization
public:
BOOL Initialize(const DTLMSGBOXPARAMS* pcDMBP);
void Uninitialize();
// Helpers: Content
protected:
#if (_WIN32_WINNT >= 0x0500)
BOOL IsRTLLayout() const
{
ATLASSERT( m_pcDMBP );
return m_pcDMBP->fStyle & DMB_RTLREADING;
};
#endif // (_WIN32_WINNT >= 0x0500)
BOOL IsToolWindowNeeded() const
{
ATLASSERT( m_pcDMBP );
return m_pcDMBP->fStyle & DMB_TOOLWINDOW;
};
BOOL IsContextHelpNeeded() const
{
ATLASSERT( m_pcDMBP );
return m_pcDMBP->fStyle & DMB_CONTEXTHELP;
};
BOOL IsIconNeeded() const
{
ATLASSERT( m_pcDMBP );
return m_pcDMBP->hIcon != NULL;
};
BOOL IsTextNeeded() const
{
ATLASSERT( m_pcDMBP );
return m_pcDMBP->lpcszText != NULL;
};
BOOL IsDetailButtonNeeded() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( (m_pcDMBP->fStyle & DMB_HASDETAIL) == 0 ||
(m_pcDMBP->lpcszDetailOn && m_pcDMBP->lpcszDetailOff) );
return m_pcDMBP->fStyle & DMB_HASDETAIL;
};
BOOL IsHelpButtonNeeded() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( (m_pcDMBP->fStyle & DMB_HASHELP) == 0 || m_pcDMBP->lpcszHelp );
return m_pcDMBP->fStyle & DMB_HASHELP;
};
// Helpers: Calculation of dimensions
private:
void CalculateAreaSize(const SIZE* pcSize, BOOL bNotEmpty, SIZE* pAreaSize) const;
BOOL CalculateCaptionBarWidth(HDC hdc, int* pcxCaptionBar) const;
int CalculateButtonsWidth(int cButtons) const;
int CalculateButtonsHeight(int cButtons) const;
// Initialization: GUI
private:
void InitializeFontParameters(HDC hdc);
BOOL InitializeBaseUnits(HDC hdc);
BOOL InitializeIconSize(HDC hdc);
BOOL InitializeButtonSize(HDC hdc, const SIZE* pcClientMaxSize);
BOOL InitializeTextSize(HDC hdc, const SIZE& sizeBounds);
BOOL Initialize(HDC hdc);
// Helpers: Dialog template
private:
LONG XDU2Pix(LONG x) const
{
ATLASSERT( m_sizeBaseUnit.cx > 0 );
return MulDiv(x, m_sizeBaseUnit.cx, 4);
}
LONG YDU2Pix(LONG y) const
{
ATLASSERT( m_sizeBaseUnit.cy > 0 );
return MulDiv(y, m_sizeBaseUnit.cy, 8);
}
LONG XPix2DU(LONG x) const
{
ATLASSERT( m_sizeBaseUnit.cx > 0 );
return MulDiv(x, 4, m_sizeBaseUnit.cx);
}
LONG YPix2DU(LONG y) const
{
ATLASSERT( m_sizeBaseUnit.cy > 0 );
return MulDiv(y, 8, m_sizeBaseUnit.cy);
}
void RECTPix2DU(RECT* prc) const
{
ATLASSERT( !::IsBadWritePtr(prc, sizeof(RECT)) );
ATLASSERT( m_sizeBaseUnit.cx > 0 );
ATLASSERT( m_sizeBaseUnit.cy > 0 );
prc->left = XPix2DU(prc->left);
prc->right = XPix2DU(prc->right);
prc->top = YPix2DU(prc->top);
prc->bottom = YPix2DU(prc->bottom);
}
static void* AlignByDWord(void* lpSrc)
{
return (void*)(((reinterpret_cast<ULONG_PTR>(lpSrc) + 3) >> 2) << 2);
}
int GetControlsNumber() const;
SIZE_T CalculateDlgHdrSize() const;
void* AddDlgHdr(void* pBuffer, const RECT* pcrc) const;
SIZE_T CalculateDlgIconSize() const;
void* AddDlgIcon(void* pBuffer, const RECT* pcrc) const;
SIZE_T CalculateDlgTextSize() const;
void* AddDlgText(void* pBuffer, const RECT* pcrc) const;
SIZE_T CalculateDlgButtonSize(LPCTSTR lpcszText) const;
void* AddDlgButton(void* pBuffer, const DTLMSGBOXBUTTONPARAMS* pcInfo,
BOOL bDefault, const RECT* pcrc) const;
// Operations
public:
SIZE_T CalculateDlgTmplSize() const;
BOOL GenerateDlgTmpl(void* lpBuffer) const;
// Accessors
public:
LONG GetPadding() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( m_sizeBaseUnit.cx >= 0 );
return GetSmallPadding() * 2;
}
LONG GetSmallPadding() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( m_sizeBaseUnit.cy >= 0 );
return m_sizeBaseUnit.cx;
}
const SIZE* GetIconSize() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( m_sizeIcon.cy >= 0 && m_sizeIcon.cy >= 0 );
return &m_sizeIcon;
}
const SIZE* GetTextSize() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( m_sizeText.cy >= 0 && m_sizeText.cy >= 0 );
return &m_sizeText;
}
LONG GetIconAndTextAreaHeight() const
{
ATLASSERT( m_pcDMBP );
ATLASSERT( m_sizeIcon.cy >= 0 && m_sizeText.cy >= 0 );
return ( IsIconNeeded() || IsTextNeeded() )
? max(m_sizeIcon.cy, m_sizeText.cy) + 2 * GetPadding()
: 0;
}
int GetStdButtonsNumber() const
{
ATLASSERT( m_pcDMBP );
int cStdBtns = 0;
if ( IsDetailButtonNeeded() )
cStdBtns++;
if ( IsHelpButtonNeeded() )
cStdBtns++;
return cStdBtns;
}
int GetUsrButtonsNumber() const
{
ATLASSERT( m_pcDMBP );
return m_cUsrBtns;
}
const SIZE* GetButtonSize() const
{
ATLASSERT( m_pcDMBP );
return &m_sizeButton;
}
LONG GetAllButtonsWidth() const
{
ATLASSERT( m_pcDMBP );
return CalculateButtonsWidth(GetStdButtonsNumber() + GetUsrButtonsNumber());
}
LONG GetAllButtonsHeight() const
{
ATLASSERT( m_pcDMBP );
return ( IsDetailButtonNeeded() || IsHelpButtonNeeded() || m_cUsrBtns )
? m_sizeButton.cy : 0;
}
const SIZE* GetClientSize() const
{
ATLASSERT( m_pcDMBP );
return &m_sizeClient;
}
// Attributes
private:
const DTLMSGBOXPARAMS* m_pcDMBP;
// Font parameters
WORD m_cyFontPointSize;
WORD m_wFontWeight;
BOOL m_bFontItalic;
BYTE m_btFontCharset;
WCHAR m_szFontFaceName[LF_FACESIZE];
// Dialog base units
SIZE m_sizeBaseUnit;
// Child element dimensions
SIZE m_sizeIcon;
SIZE m_sizeText;
BOOL m_bScrollText;
SIZE m_sizeButton;
int m_cUsrBtns;
SIZE m_sizeClient;
};
// Utilities
protected:
static HWND GetActualOwnerWindow(HWND hwndOwner)
{
return hwndOwner ? hwndOwner : ::GetDesktopWindow();
}
static BOOL CalcualteTextSize(HDC hdc, LPCTSTR lpcszText, const SIZE& sizeBounds,
SIZE* pSize);
// Construction & destruction
public:
CDetailedMessageWindow()
: m_unHelpID(0),
m_unDetailID(0),
m_hIcon(NULL),
m_lPadding(0),
m_lSmallPadding(0),
m_hwndIcon(NULL),
m_hwndText(NULL),
m_hwndDetail(NULL),
m_cyMaxText(0)
{
m_sizeButton.cx = m_sizeButton.cy = 0;
m_sizeMinClient.cx = m_sizeMinClient.cy = 0;
}
// Initialization & uninitialization
protected:
void InitializeParams(const DTLMSGBOXPARAMS* pcDMBP);
DLGTEMPLATE* CreateDialogTemplate(const DTLMSGBOXPARAMS* pcDMBP);
void FreeDialogTemplate(DLGTEMPLATE* lpdt);
// Helpers: Child controls
public:
BOOL InitializeChildControls();
void AlignChildControls(LONG cx, LONG cy);
void CalculateMinWindowSize(SIZE* pSize) const;
// Operations: User Commads
public:
BOOL IsDetailShown() const
{
DWORD_PTR fStyle = GetWindowLongPtr(GWL_STYLE);
return (fStyle & WS_THICKFRAME) != NULL;
}
void ShowDetail(BOOL bShow);
void QueryHelp();
void QueryHelp(const HELPINFO* pcHI);
// Overridables
protected:
virtual HWND CreateDetailControl(const RECT* pcrc) = 0;
// Accessors
protected:
UINT _GetHelpCmdID() const
{
return m_unHelpID;
}
UINT _GetDetailCmdID() const
{
return m_unDetailID;
}
LPCTSTR _GetDetailOnText() const
{
return m_csDetailOn.c_str();
}
LPCTSTR _GetDetailOffText() const
{
return m_csDetailOff.c_str();
}
HWND _GetIconHwnd() const
{
return m_hwndIcon;
}
HWND _GetTextHwnd() const
{
return m_hwndText;
}
const int _GetButtonsCount() const
{
return static_cast<int>(m_vecButtons.size());
}
HWND _GetButtonAt(int index) const
{
return m_vecButtons[index];
}
HWND _GetGripperHwnd() const
{
return m_hwndGripper;
}
HWND _GetDetailHwnd() const
{
return m_hwndDetail;
}
const SIZE& _GetMinClientSize() const
{
return m_sizeMinClient;
}
LONG _GetMaxTextHeight() const
{
return m_cyMaxText;
}
LONG _GetMinPadding() const
{
return m_lPadding;
}
LONG _GetSmallPadding() const
{
return m_lSmallPadding;
}
const SIZE& _GetButtonSize() const
{
return m_sizeButton;
}
// Attributes
private:
UINT m_unHelpID;
UINT m_unDetailID;
HICON m_hIcon;
tstring m_csDetailOn;
tstring m_csDetailOff;
HWND m_hwndIcon;
HWND m_hwndText;
CHwndVector m_vecButtons;
HWND m_hwndGripper;
HWND m_hwndDetail;
SIZE m_sizeMinClient;
LONG m_cyMaxText;
LONG m_lPadding;
LONG m_lSmallPadding;
SIZE m_sizeButton;
};
/////////////////////////////////////////////////////////////////////////////////////////
// CDetailedMessageBoxImpl class template
template <class T, class TBase = CDetailedMessageWindow>
class ATL_NO_VTABLE CDetailedMessageBoxImpl
: public CDialogImpl< CDetailedMessageBoxImpl, TBase >
{
// Types
private:
typedef CDialogImpl< CDetailedMessageBoxImpl, TBase > CBase;
// Message handling
public:
BEGIN_MSG_MAP(CBase)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo);
MESSAGE_HANDLER(WM_HELP, OnHelp)
MESSAGE_HANDLER(WM_COMMAND, OnCommand)
END_MSG_MAP()
// Supressed inherited operations
private:
INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), LPARAM dwInitParam = NULL)
{
return -1;
}
HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL)
{
return NULL;
}
HWND Create(HWND hWndParent, RECT&, LPARAM dwInitParam = NULL)
{
return NULL;
}
// Operations: Modal dialogs
public:
INT_PTR DoModal(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
{
ATLASSERT( !IsWindow() );
InitializeParams(pcDMBP);
// Genrate dialog template
DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
ATLASSERT( lpdt );
if ( !lpdt ) return -1;
INT_PTR nResult;
__try
{
HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);
_AtlWinModule.AddCreateWndData(&m_thunk.cd,
static_cast<CDialogImplBaseT<TBase>*>(this));
nResult = ::DialogBoxIndirectParam(_AtlBaseModule.GetResourceInstance(),
lpdt, hwndOwner,
T::StartDialogProc, dwInitParam);
}
__finally
{
FreeDialogTemplate(lpdt);
}
return nResult;
}
BOOL EndDialog(int nRetCode)
{
ATLASSERT(::IsWindow(m_hWnd));
return ::EndDialog(m_hWnd, nRetCode);
}
// Operations: Modeless dialogs
public:
HWND Create(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
{
ATLASSERT( !IsWindow() );
InitializeParams(pcDMBP);
// Genrate dialog template
DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
ATLASSERT( lpdt );
if ( !lpdt ) return -1;
__try
{
HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);
_AtlWinModule.AddCreateWndData(&m_thunk.cd,
static_cast<CDialogImplBaseT<TBase>*>(this));
HWND hwnd = ::CreateDialogParam(_AtlBaseModule.GetResourceInstance(), lpdt,
hwndOwner, T::StartDialogProc, dwInitParam);
ATLASSERT(m_hWnd == hwnd);
}
__finally
{
FreeDialogTemplate(lpdt);
}
return m_hWnd;
}
BOOL DestroyWindow()
{
ATLASSERT(::IsWindow(m_hWnd));
return ::DestroyWindow(m_hWnd);
}
// Message handlers
protected:
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = FALSE;
if ( !InitializeChildControls() )
{
EndDialog(-1);
return FALSE;
}
ShowDetail(FALSE);
return TRUE;
}
LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = FALSE;
AlignChildControls(LOWORD(lParam), HIWORD(lParam));
return 0;
}
LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
MINMAXINFO* pMaxMinInfo = reinterpret_cast<MINMAXINFO*>(lParam);
SIZE size;
CalculateMinWindowSize(&size);
pMaxMinInfo->ptMinTrackSize.x = size.cx;
pMaxMinInfo->ptMinTrackSize.y = size.cy;
return 0;
}
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
WORD wCmdID = LOWORD(wParam);
if ( wCmdID == _GetHelpCmdID() && _GetHelpCmdID() )
QueryHelp();
else if ( wCmdID == _GetDetailCmdID() && _GetDetailCmdID() )
ShowDetail(!IsDetailShown());
else
bHandled = FALSE;
return 0;
}
LRESULT OnHelp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
QueryHelp(reinterpret_cast<const HELPINFO*>(lParam));
return TRUE;
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// CAxDetailedMessageBoxImpl class template
template <class T, class TBase = CDetailedMessageWindow>
class ATL_NO_VTABLE CAxDetailedMessageBoxImpl
: public CAxDialogImpl< CAxDetailedMessageBoxImpl, TBase >
{
// Types
private:
typedef CAxDialogImpl< CAxDetailedMessageBoxImpl, TBase > CBase;
// Message handling
public:
BEGIN_MSG_MAP(CBase)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo);
MESSAGE_HANDLER(WM_HELP, OnHelp)
MESSAGE_HANDLER(WM_COMMAND, OnCommand)
END_MSG_MAP()
// Supressed inherited operations
private:
INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), LPARAM dwInitParam = NULL)
{
return -1;
}
HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL)
{
return NULL;
}
HWND Create(HWND hWndParent, RECT&, LPARAM dwInitParam = NULL)
{
return NULL;
}
// Operations: Modal dialogs
public:
INT_PTR DoModal(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
{
ATLASSERT( !IsWindow() );
InitializeParams(pcDMBP);
// Genrate dialog template
DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
ATLASSERT( lpdt );
if ( !lpdt ) return -1;
INT_PTR nResult;
__try
{
HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);
_AtlWinModule.AddCreateWndData(&m_thunk.cd,
static_cast<CDialogImplBaseT<TBase>*>(this));
nResult = ::DialogBoxIndirectParam(_AtlBaseModule.GetResourceInstance(),
lpdt, hwndOwner,
T::StartDialogProc, dwInitParam);
}
__finally
{
FreeDialogTemplate(lpdt);
}
return nResult;
}
BOOL EndDialog(int nRetCode)
{
ATLASSERT(::IsWindow(m_hWnd));
return ::EndDialog(m_hWnd, nRetCode);
}
// Operations: Modeless dialogs
public:
HWND Create(const DTLMSGBOXPARAMS* pcDMBP, LPARAM dwInitParam = NULL)
{
ATLASSERT( !IsWindow() );
InitializeParams(pcDMBP);
// Genrate dialog template
DLGTEMPLATE* lpdt = CreateDialogTemplate(pcDMBP);
ATLASSERT( lpdt );
if ( !lpdt ) return -1;
__try
{
HWND hwndOwner = GetActualOwnerWindow(pcDMBP->hwndOwner);
_AtlWinModule.AddCreateWndData(&m_thunk.cd,
static_cast<CDialogImplBaseT<TBase>*>(this));
HWND hwnd = ::CreateDialogParam(_AtlBaseModule.GetResourceInstance(), lpdt,
hwndOwner, T::StartDialogProc, dwInitParam);
ATLASSERT(m_hWnd == hwnd);
}
__finally
{
FreeDialogTemplate(lpdt);
}
return m_hWnd;
}
BOOL DestroyWindow()
{
ATLASSERT(::IsWindow(m_hWnd));
return ::DestroyWindow(m_hWnd);
}
// Message handlers
protected:
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = FALSE;
if ( !InitializeChildControls() )
{
EndDialog(-1);
return FALSE;
}
ShowDetail(FALSE);
return TRUE;
}
LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = FALSE;
AlignChildControls(LOWORD(lParam), HIWORD(lParam));
return 0;
}
LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
MINMAXINFO* pMaxMinInfo = reinterpret_cast<MINMAXINFO*>(lParam);
SIZE size;
CalculateMinWindowSize(&size);
pMaxMinInfo->ptMinTrackSize.x = size.cx;
pMaxMinInfo->ptMinTrackSize.y = size.cy;
return 0;
}
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
WORD wCmdID = LOWORD(wParam);
if ( wCmdID == _GetHelpCmdID() && _GetHelpCmdID() )
QueryHelp();
else if ( wCmdID == _GetDetailCmdID() && _GetDetailCmdID() )
ShowDetail(!IsDetailShown());
else
bHandled = FALSE;
return 0;
}
LRESULT OnHelp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
QueryHelp(reinterpret_cast<const HELPINFO*>(lParam));
return TRUE;
}
};
}; // SP namespace
#endif // __SP_SPDETAILEDMESSAGEBOX_H__INCLUDED__