I am trying to set tooltips on an MFC dialog. I have the code for reading the tooltips from a string resource and am trying to modify it to not read from the resource and instead make up a tooltip.
My implementation is causing garbage to be displayed as the tooltip instead of teh string I want to. Also, it is causing a crash when run from the debugger but not when the executable is run directly (I am sure there is a buffer corruption or something similar)
Here is the code which is relevant:
BOOL CPreviewDlg::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
{
ASSERT(pNMHDR->code == TTN_NEEDTEXT);
TOOLTIPTEXT* pTTT = (TOOLTIPTEXT*)pNMHDR;
if (!(pTTT->uFlags & TTF_IDISHWND))
return FALSE;
UINT_PTR hWnd = pNMHDR->idFrom;
// idFrom is actually the HWND of the tool
UINT nID = (UINT)(WORD)::GetDlgCtrlID((HWND)hWnd);
CString sDlgItemText;
(UINT)(WORD)::GetDlgItemText(this->GetSafeHwnd(), nID, sDlgItemText.GetBufferSetLength(50), 50);
sDlgItemText.ReleaseBuffer();
if(sDlgItemText.IsEmpty())
sDlgItemText = _T("Unnamed");
CString sToolTip = _T("");
sToolTip.Format(_T("%s \n This is the %s control. Here we can put its description."), sDlgItemText, sDlgItemText);
pTTT->lpszText = sToolTip.GetBufferSetLength(sToolTip.GetLength());/* MAKEINTRESOURCE(nID);*/
pTTT->hinst = AfxGetInstanceHandle();
sToolTip.ReleaseBuffer();
*pResult = 0;
// bring the tooltip window above other popup windows
::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
return TRUE; // message was handled
}
I am pretty sure the error is where I am trying to set the pTTT->lpszText
(instead of MAKEINTRESOURCE) I think I am not getting the correct way of setting a LPSTR from a CString
CString sDlgItemText;
(UINT)(WORD)::GetDlgItemText(this->GetSafeHwnd(), nID, sDlgItemText.GetBufferSetLength(50), 50);
sDlgItemText.ReleaseBuffer();
if(sDlgItemText.IsEmpty())
sDlgItemText = _T("Unnamed");
CString sToolTip = _T("");
sToolTip.Format(_T("%s \n This is the %s control. Here we can put its description."), sDlgItemText, sDlgItemText);
pTTT->lpszText = sToolTip.GetBufferSetLength(sToolTip.GetLength());/* MAKEINTRESOURCE(nID);*/
The root problem is that you are returning the address of a local variable (sToolTip.m_pData)
from your OnToolTipText
handler. When control leaves the notification handler, sToolTip
goes out of scope and its destructor is run, leaving behind garbage.
To solve the issue you have 2 options:
TOOLTIPTEXT::szText[]
array.Unrelated to your question: When assigning a pointer to TOOLTIPTEXT::lpszText
member you should be using a const_cast
instead of calling CString::GetBuffer[SetLength]()
. The TOOLTIPTEXT
structure is used in both directions, to set and retrieve tooltip information. Consequently, the members cannot be declared const
, even if they are. It may look awkward but you would rather want to do the following:
pTTT->lpszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(sToolTip));