Add tooltip to a CStatic - mfc

I haven't been able to find a concise chunk of code that allows me to add/display tooltips to a CStatic (and CLed) control. Obviously, the standard code to do so does not apply for this type of control. Can someone post code snippets?

I hope this code will solve your problem .One important thing make NOTIFY property of CStatic =TRUE.
if( !m_ToolTip.Create(this))
{
TRACE0("Unable to create the ToolTip!");
}
else
{
CWnd* pWnd = GetDlgItem(IDC_STATIC_MASTER_PWD);
m_ToolTip.AddTool(pWnd,"Ok");
m_ToolTip.Activate(TRUE);
}
Let me know if any problem.

I don't know if this is still needed, but here's what I used to solve the problem:
just add SS_NOTIFY to dwStyle when creating the static label. (or simply set "Nofity" "True" in the properties). That worked fine for me.

When I add CStatic on Dialog based autocreated mfc application, tooltips don't show until I add RelayEvent in pretranslate dialog message
BOOL CTooltipStaticDlg::PreTranslateMessage(MSG* pMsg)
{
m_ToolTip.RelayEvent(pMsg);
return CDialog::PreTranslateMessage(pMsg);
}

I've had success with multiline tooltips using this simple class:
Create a class for ToolTips:
class ToolTip
{
public:
static HWND CreateToolTip(int toolID, HWND hDlg, UINT id);
};
Next, implement a tooltip creation function:
HWND ToolTip::CreateToolTip(int toolID, HWND hDlg, UINT id)
{
if (!toolID || !hDlg || !id)
{
return FALSE;
}
CString strTTText;
strTTText.LoadString( id );
// Get the window handle of the control to attach the TT to.
HWND hwndTool = ::GetDlgItem(hDlg, toolID);
// Create the tooltip window
HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP |TTS_ALWAYSTIP,// | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hDlg, NULL,
AfxGetInstanceHandle() , NULL);
if (!hwndTool || !hwndTip)
{
return (HWND)NULL;
}
// Associate the tooltip with the tool.
TOOLINFO toolInfo = { 0 };
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hwnd = hDlg;
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
toolInfo.uId = (UINT_PTR)hwndTool;
toolInfo.lpszText = (char*)(LPCTSTR)strTTText;
::SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
::SendMessageA(hwndTip, TTM_SETMAXTIPWIDTH, 0, 40); // force multi-line
return hwndTip;
}
Call it somewhere in your InitDialog:
CMyDialog::InitDialog()
{
ToolTip::CreateToolTip( PickAUniqueNumber, m_hWnd, IDS_MY_RESOURCE_STRING );
}

I've had on my dialog label with assigned custom ID IDC_PATH. I needed to turn on Notify flag (SS_NOTIFY) of the label and I needed to overload CWnd method OnToolHitTest and handle tooltip hit test like this:
INT_PTR CPath::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
INT_PTR r = CWnd::OnToolHitTest(point,pTI);
this->ClientToScreen(&point);
CRect rcLbl;
GetDlgItem(IDC_PATH)->GetWindowRect(&rcLbl);
if( rcLbl.PtInRect(point) )
{
pTI->uFlags |= TTF_IDISHWND;
pTI->uFlags &= ~TTF_NOTBUTTON;
pTI->uId = (UINT_PTR)GetDlgItem(IDC_PATH)->m_hWnd;
return IDC_PATH;
}
return r;
}
Then my dialog started to receive TTN_NEEDTEXT notification, which I handled and dynamicaly set text for tooltip.
BOOL CPath::OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
UNREFERENCED_PARAMETER(id);
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if (pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID == IDC_PATH)
{
pTTT->lpszText = (LPSTR)(LPCTSTR)m_FullDestPath;
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
}

Related

How to made JAWS readout ListView Item use Server Annotation

I have write a windows client, which UI framework is ATL/WTL. And There is a ListView (CListViewCtrl in ATL) with LVS_OWNERDRAWFIXED style.
For support Accessibility tool JAWS.I Want Jaws read ListView item.
I follow the doc:https://learn.microsoft.com/en-us/windows/win32/winauto/server-annotation-sample
0.Impl of IAccPropServer for support GetPropValue .
class ListViewAccServer : public IAccPropServer
{
ULONG m_Ref;
IAccPropServices * m_pAccPropSvc;
public:
/* skip over ListViewAccServer/ ~ListViewAccServer
*/
static ListViewAccServer * CreateProvider(HWND hControl)
{
ATL::CComPtr<IAccPropServices> pAccPropSvc;
HRESULT hr = pAccPropSvc.CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER);
if (hr == S_OK && pAccPropSvc)
{
ListViewAccServer * pLVServer = new (std::nothrow) ListViewAccServer(pAccPropSvc);
if (pLVServer)
{
MSAAPROPID propid = PROPID_ACC_NAME;
//pAccPropSvc->SetHwndPropServer(hControl, (DWORD)OBJID_CLIENT, CHILDID_SELF, &propid, 1, pLVServer, ANNO_CONTAINER);
pAccPropSvc->SetHwndPropServer(hControl, (DWORD)OBJID_CLIENT, CHILDID_SELF, &propid, 1, pLVServer, ANNO_CONTAINER);
pLVServer->Release();
}
return pLVServer;
}
return NULL;
}
/* skip over: Addref/Release/QI
*/
HRESULT STDMETHODCALLTYPE GetPropValue(const BYTE * pIDString,
DWORD dwIDStringLen, MSAAPROPID idProp, VARIANT * pvarValue,
BOOL * pfGotProp)
{
if (!pfGotProp)
return E_POINTER;
pvarValue->vt = VT_EMPTY;
*pfGotProp = FALSE;
//HWND hwnd;
DWORD idObject;
DWORD idChild;
HWND dwHcontrol;
if (S_OK != m_pAccPropSvc->DecomposeHwndIdentityString(pIDString,
dwIDStringLen, &dwHcontrol, &idObject, &idChild))
{
return S_OK;
}
HWND Hwnd = dwHcontrol;
// Only supply name string for child elements, not the listview itself
if (idChild != CHILDID_SELF)
{
if (idProp == PROPID_ACC_NAME)//Jaws should read acc name?
{
CString str;
str.Format(L"Line index %d", idChild);
OutputDebugPrintfW(_T("GetPropValue str =%s\n"), str);
BSTR bstr = ::SysAllocString((LPCTSTR)str.GetString());
pvarValue->vt = VT_BSTR;
pvarValue->bstrVal = bstr;
*pfGotProp = TRUE;
}
}
return S_OK;
}
};
1.initUI
//if Set 'LVS_OWNERDRAWFIXED',JAWS will read onDraw Item "Dummy List Item"
m_lvMain.Create(m_hWnd, rc, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
LVS_REPORT | LVS_AUTOARRANGE | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | WS_TABSTOP|LVS_OWNERDRAWFIXED,
WS_EX_CLIENTEDGE, LIST_ID);
/*
m_lvMain.Create(m_hWnd, rc, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
LVS_REPORT | LVS_AUTOARRANGE | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | WS_TABSTOP ,
WS_EX_CLIENTEDGE, LIST_ID);
*/
//otherwise JAWS will read Hello,Hello1,Hello2...
m_lvMain.InsertColumn(0, _T("Column"), LVCFMT_LEFT, 200);
m_lvMain.InsertItem(0, _T("Hello"));
m_lvMain.InsertItem(0, _T("Hello1"));
m_lvMain.InsertItem(0, _T("Hello2"));
m_lvMain.InsertItem(0, _T("Hello3"));
2.Bind Listview m_hwnd with ListViewAccServer
m_pAccServer = ListViewAccServer::CreateProvider(m_lvMain.m_hWnd);
3
LRESULT OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
//LRESULT onDraw(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)(lParam);
//OutputDebugPrintfW(_T("OnDrawItem i = %d, %d, %08x \n"), pDIS->itemID, pDIS->itemData, pDIS->hwndItem);
BOOL bSelected = ((pDIS->itemState & ODS_SELECTED) == ODS_SELECTED);
BOOL bFocus = ((pDIS->itemState & ODS_FOCUS) == ODS_FOCUS);
HDC hDC = pDIS->hDC;
RECT rc = pDIS->rcItem;
HBRUSH bg = (HBRUSH)(::GetStockObject(WHITE_BRUSH));
if (bSelected)
{
bg = (HBRUSH)(::GetStockObject(LTGRAY_BRUSH));
}
if (bFocus)
{
bg = (HBRUSH)(::GetStockObject(LTGRAY_BRUSH));
}
HPEN pn = (HPEN)(::GetStockObject(BLACK_PEN));
::SelectObject(hDC, bg);
::SelectObject(hDC, pn);
::SetTextColor(hDC, RGB(0, 0, 0));
const wchar_t *text = L"Dummy List Item";
::Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
::DrawText(hDC, text, wcslen(text), &rc, DT_SINGLELINE | DT_VCENTER);
return S_OK;
}
When use UP and down array By Keyboard alert in ListView items.I want Jaws read out "Line index i" in GetPropValue.
But
1: If I set ListView with style:LVS_OWNERDRAWFIXED.On some PC, Jaws will read out "Dummy List Item". On Some PC, It read nothing.
2: If remove style:LVS_OWNERDRAWFIXED. Jaws will read out 'Hello','Hello1'...
both 1 or 2 do not read out GetPropValue give string"Line index i".
The log shows GetPropValue is be called:
GetPropValue str =Line index 1
GetPropValue str =Line index 2
My test PC machine OSs are Win10,Win7
So what's the problem?
Thanks a lot.
PS:At last , I found this:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=73496
I can use Insert+6 to bring up the JAWS Configuration Manager.
- click OK to Add a New Configuration (for your application)
- in the Configuration Manager dialog, type "listv" in the filter to see the page containing the "Rely on MSAA for ListViews" checkbox.
- check "Rely on MSAA for ListViews"
- click OK to save and exit the Configuration Manager.
So Jaws default do not use MSAA to get information?

Tracking tooltip in CScrollView?

In a standard C++/MFC MDI doc/view project, I want to implement a tracking tooltip in the view (the tabbed view windows which generally occupy most of the main frame window). So, in class MyAppView, I have a member CToolTipCtrl tooltip. Function MyAppView::OnInitialUpdate() contains the initialization
BOOL ok0 = tooltip.Create(this, TTS_ALWAYSTIP);
CRect clientRect; GetClientRect(&clientRect);
BOOL ok2 = tooltip.AddTool(this, LPSTR_TEXTCALLBACK, &clientRect, 1234/*tool ID*/);
tooltip.Activate(TRUE);
to make the entire client area of the view be the "tool". The message map contains an entry
ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
and the function OnNeedToolTipText is defined as
BOOL MyAppView::OnNeedToolTipText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
UNREFERENCED_PARAMETER(id);
NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if(nID == 1234)
{
// Come here when text is needed for tracking tooltip
}
if(pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID)
{
_stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
_T("Control ID = %d"), nID);
pTTT->hinst = AfxGetResourceHandle();
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
}
What happens is that only placing the mouse on the menu items (File, Edit, View, Window, Help) causes the code to enter OnNeedToolTipText, with an ID of 0-5. Moving the mouse into the client area (the view) does nothing.
How can I get the tooltip to appear in the client area of the view only?
Visual Studio 2017; C++; 64-bit Windows 7
In order to solve the problem you need to do the following:
BOOL CMyAppView::PreTranslateMessage(MSG* pMsg)
{
switch (pMsg->message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
if (m_pToolTip->GetSafeHwnd () != NULL)
{
m_pToolTip->RelayEvent(pMsg);
}
break;
}
return CScrollView::PreTranslateMessage(pMsg);
}
If you want a tracking tooltip in a view, these are the steps to follow:
Create tooltip and add the tool.
void CToolTipDemoView::OnInitialUpdate()
{
// ...
m_toolTip.Create(this, TTS_ALWAYSTIP | TTS_NOANIMATE);
m_toolTip.AddTool(this, _T("Doesn't matter"));
}
Handle WM_MOUSEMOVE message. First, call _TrackMouseEvent in order to further receive WM_MOUSELEAVE and activate the tooltip. Second, update the tooltip text, and show it at mouse pointer coordinates.
void CToolTipDemoView::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bTrackingMouseLeave)
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = m_hWnd;
::_TrackMouseEvent(&tme);
m_toolTip.Activate(TRUE);
m_bTrackingMouseLeave = TRUE;
}
if (m_pointLastMousePos != point)
{
CString strText;
strText.Format(_T("x = %d y = %d"), point.x, point.y);
m_toolTip.UpdateTipText(strText, this);
m_toolTip.Popup();
m_pointLastMousePos = point;
}
CScrollView::OnMouseMove(nFlags, point);
}
Handle WM_MOUSELEAVE and deactivate the tooltip.
void CCToolTipDemoView::OnMouseLeave()
{
m_bTrackingMouseLeave = FALSE;
// mouse pointer leaves the window so deactivate the tooltip
m_toolTip.Activate(FALSE);
CScrollView::OnMouseLeave();
}
Notes:
there is no more necessary to handle TTN_NEEDTEXT.
also, there is no more necessary to override PreTranslateMessage
So I went back to see what I could be missing. I wrote this stuff over 10 years ago. I had also overridden a CWnd member
virtual INT_PTR OnToolHitTest( CPoint point, TOOLINFO* pTI ) const;
With:
INT_PTR HERichView::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const
{
pTI->hwnd = m_hWnd;
pTI->uId = point.x + ( point.y << 16 );
CRect rect;
GetClientRect( rect );
pTI->rect= rect;
pTI->lpszText= LPSTR_TEXTCALLBACK;
return pTI->uId;
}
And I checked, it won't work without this. So your:
ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTip )
Should get called if you add the above. And only EnableToolTips( ); Should be needed.
I have not succeeded in getting the tracking tooltip to work within MFC. The closest I have come is
In message map: ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
In OnInitialUpdate: BOOL ok1 = EnableTrackingToolTips(TRUE);
In override of virtual function OnToolHitTest:
pTI->hwnd = m_hWnd;
pTI->uId = (UINT_PTR)m_hWnd;
pTI->uFlags = TTF_IDISHWND | TTF_ALWAYSTIP | TTF_TRACK | TTF_NOTBUTTON | TTF_ABSOLUTE | TTF_SUBCLASS;
pTI->lpszText = LPSTR_TEXTCALLBACK;
return pTI->uId;
In OnNeedToolTipText:
NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if(pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID)
{
CURSORINFO ci; ci.cbSize = sizeof(CURSORINFO); // get something interesting to display
GetCursorInfo(&ci);
_stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
_T("Control ID = %lld at (%d, %d)"), nID, ci.ptScreenPos.x, ci.ptScreenPos.y);
pTTT->hinst = AfxGetResourceHandle();
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
This produces the following peculiar behavior. When I start the app and move the mouse cursor into the client area of the CScrollView, a tooltip appears right next to the cursor.
If I move the mouse carefully (smoothly) the tooltip tracks properly. After a while, though, it disappears, and no further mouse motions, including leaving the CScrollView window and returning, make it re-appear.
I think what is happening is that when the mouse cursor moves over the tooltip window, the tooltip is turned off, permanently. This disappearance does not seem to be time-related (e g, due to auto-pop); if the mouse is left untouched, the tooltip remains indefinitely.

WinApi - How to obtain SHELLDLL_DefView

I am trying to obtain handle to SHELLDLL_DefView.
So, I have this code.
HWND hProgman = FindWindow(L"Progman", NULL);
HWND hWnd = FindWindowEx(hProgman, 0, L"SHELLDLL_DefView", NULL);
Eveyrtihing works OK, until I change in Windows desktop brackground to slideshow. Then when I search with spy++ hierarchy of the windows, than SHELLDLL_DefView has another parent. Now it is #32769 (Desktop) -> WorkerW -> SHELLDLL_DefView. So I can't find it. Problem is that when I try
HWND desktop = GetDesktopWindow();
HWND hWnd = FindWindowEx(desktop , 0, L"WorkerW", NULL);
HWND hWnd = FindWindowEx(hWnd, 0, L"SHELLDLL_DefView", NULL);
Than SHELLDLL_DefView is not found. WorkerW yes.
Can anybody help?
Your code only works on some Windows versions as "SHELLDLL_DefView" can be found under "WorkerW" or "Progman" and as you discovered there can be many windows under the "WorkerW" class (normal in Win7).
Microsoft Docs report EnumWindows() is more reliable than calling GetWindow()/FindWindowEx() functions in loops, so more universal code (tested on Windows 98/Windows 7) would look like this (say you want to refresh the desktop):
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
HWND hNextWin;
hNextWin = FindWindowExA(hwnd, 0, "SHELLDLL_DefView", 0);
if ( hNextWin ) {
// The correct desktop shell window under Progman/WorkerW will have only 1 child window!
if ( GetNextWindow(hNextWin, GW_HWNDNEXT) || GetNextWindow(hNextWin, GW_HWNDPREV) )
return true;
// We found correct handle
PostMessageA(hNextWin, WM_KEYDOWN, VK_F5, 0);
return false;
}
return true;
}
void main() {
EnumWindows(&EnumWindowsProc, 0);
}
I found the answer. Need to iterate through all WorkerW.
HWND destop = GetDesktopWindow();
HWND hWorkerW = NULL;
HWND hShellViewWin = NULL;
do
{
hWorkerW = FindWindowEx(destop, hWorkerW, L"WorkerW", NULL);
hShellViewWin = FindWindowEx(hWorkerW, 0, L"SHELLDLL_DefView", 0);
} while (hShellViewWin == NULL && hWorkerW != NULL);

How to glow the minimum. maximum and close button?

I followed below guide to create a custom Aero Frame using DWM API.
Custom Window Frame Using DWM
My work:
void CMainFrame::OnActivate(UINT nState,CWnd* pWndOther,BOOL bMinimized )
{
CFrameWnd::OnActivate(nState,pWndOther,bMinimized);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
{
if(nState == WA_ACTIVE )
{
MARGINS margins ={-1};
HRESULT hr = DwmExtendFrameIntoClientArea(m_hWnd, &margins);
if (!SUCCEEDED(hr));
}
}
}
void CMainFrame::OnNcPaint(){
RECT rcClient;
GetWindowRect(&rcClient);
// Inform the application of the frame change.
SetWindowPos(
NULL,
rcClient.left, rcClient.top,
RECTWIDTH(rcClient), RECTHEIGHT(rcClient),
SWP_FRAMECHANGED);
CFrameWnd::OnNcPaint();
CDC* dc = GetWindowDC();
dc->FillSolidRect(0,0,RECTWIDTH(rcClient),RECTHEIGHT(rcClient),RGB(0,0,0));
}
LRESULT CMainFrame::OnNcHitTest(CPoint p)
{
LRESULT r ;
r = CFrameWnd::OnNcHitTest( p);
if(r == HTMINBUTTON || r == HTMAXBUTTON || r == HTCLOSE)
return r;
else
r = HitTestNCA(m_hWnd,p); // this function is direct copied from above link.
return r;
}
Result:
I found out the minimum, maximum and close button that will not be glowed when I move the mouse on these buttons.
General situation:
How to fix this problem?
Best Regards,
DwmDefWindowProc is required to handle caption buttons. From msdn:
For caption button hit testing, DWM provides the DwmDefWindowProc
function. To properly hit test the caption buttons in custom frame
scenarios, messages should first be passed to DwmDefWindowProc for
handling. DwmDefWindowProc returns TRUE if a message is handled and
FALSE if it is not. If the message is not handled by DwmDefWindowProc,
your application should handle the message itself or pass the message
onto DefWindowProc.
In MFC it can work out as follows:
LRESULT cframeWnd::OnNcHitTest(CPoint p)
{
BOOL dwm_enabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled)))
{
LRESULT result = 0;
if (!DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y), &result))
result = HitTestNCA(m_hWnd, p);
if (result == HTNOWHERE && GetForegroundWindow() != this)
{
return HTCAPTION;
}
return result;
}
return CWnd::OnNcHitTest(p);
}
I added a fix with GetForegroundWindow(), because the HitTestNCA function from MSDN example is wrong, it doesn't return HTCLIENT when it should. So when another window has focus, it won't switch windows upon mouse click in client area.
Also, there is a leak in OnNcPaint:
CDC* dc = GetWindowDC();
Whenever GetWindowDC() is called it should be followed by ReleaseDC. Or just use CWindowDC which has automatic cleanup. You don't actually need to override OnNcPaint because frame has been extended to "client area".
Here is a full example:
class cglassWnd : public CWnd
{
void OnNcCalcSize(BOOL, NCCALCSIZE_PARAMS FAR*);
LRESULT OnNcHitTest(CPoint p);
void OnNcMouseLeave();
int OnCreate(LPCREATESTRUCT lpCreateStruct);
void OnActivate(UINT state, CWnd* otherWnd, BOOL minimized);
void OnPaint();
CRect borders;
int titlebar_height;
DECLARE_MESSAGE_MAP()
public:
cglassWnd();
};
BEGIN_MESSAGE_MAP(cglassWnd, CWnd)
ON_WM_NCHITTEST()
ON_WM_NCCALCSIZE()
ON_WM_NCMOUSELEAVE()
ON_WM_ACTIVATE()
ON_WM_CREATE()
ON_WM_PAINT()
END_MESSAGE_MAP()
cglassWnd::cglassWnd()
{
BOOL dwm_enabled = FALSE;
DwmIsCompositionEnabled(&dwm_enabled);
if (!dwm_enabled)
TRACE("Error: don't use this class, add error handling...");
//modified height for the new title bar
titlebar_height = 60;
}
int cglassWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int res = CWnd::OnCreate(lpCreateStruct);
//find border thickness
borders = { 0,0,0,0 };
if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&borders,
GetWindowLongPtr(m_hWnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
borders.left = abs(borders.left);
borders.top = abs(borders.top);
}
else if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_BORDER)
{
borders = { 1,1,1,1 };
}
//Extend caption in to client area
MARGINS margins = { 0 };
margins.cyTopHeight = titlebar_height;
DwmExtendFrameIntoClientArea(m_hWnd, &margins);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
return res;
}
void cglassWnd::OnPaint()
{
CPaintDC dc(this);
//paint titlebar area (this used to be the non-client area)
CRect rc;
GetClientRect(&rc);
rc.bottom = titlebar_height;
//see MSDN reference for explanation of this code
//upside-down bitmap is for the sake of DrawThemeTextEx
CDC memdc;
memdc.CreateCompatibleDC(&dc);
BITMAPINFOHEADER infhdr = { sizeof(infhdr), rc.right, -rc.bottom, 1, 32 };
HBITMAP hbitmap = CreateDIBSection(dc,(BITMAPINFO*)(&infhdr),DIB_RGB_COLORS,0,0,0);
auto oldbitmap = memdc.SelectObject(hbitmap);
//do extra titlebar painting here
//for example put DrawThemeTextEx for window's name
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbitmap);
DeleteObject(hbitmap);
//begin normal paint
//The new client area begins below titlebar_height which we define earlier
GetClientRect(&rc);
rc.top = titlebar_height;
dc.FillSolidRect(&rc, RGB(128, 128, 255));
}
void cglassWnd::OnNcCalcSize(BOOL validate, NCCALCSIZE_PARAMS FAR* sz)
{
if (validate)
{
sz->rgrc[0].left += borders.left;
sz->rgrc[0].right -= borders.right;
sz->rgrc[0].bottom -= borders.bottom;
}
else
{
CWnd::OnNcCalcSize(validate, sz);
}
}
LRESULT cglassWnd::OnNcHitTest(CPoint pt)
{
LRESULT result = 0;
//handle close/minimize/maximize button
if (DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y), &result))
return result;
//cursor is over the frame or client area:
result = CWnd::OnNcHitTest(pt);
if (result == HTCLIENT)
{
ScreenToClient(&pt);
if (pt.y < borders.top) return HTTOP;
if (pt.y < titlebar_height) return HTCAPTION;
}
return result;
}
void cglassWnd::OnNcMouseLeave()
{
//This is for close/minimize/maximize/help buttons
LRESULT result;
DwmDefWindowProc(m_hWnd, WM_NCMOUSELEAVE, 0, 0, &result);
CWnd::OnNcMouseLeave();
}
void cglassWnd::OnActivate(UINT state, CWnd* otherWnd, BOOL minimized)
{
CWnd::OnActivate(state, otherWnd, minimized);
Invalidate(FALSE);
}

Trying to get text from tooltips not working

I've been trying for a couple of hours to interrogate tooltips to give up the text they contain to no avail. I've found How to get tooltip text for a given HWND? and tried that without success.
This shouldn't be that hard. I'm just not sure what I'm doing wrong. Here's a section of my code:
BOOL CALLBACK EnumWindowsProc(
_In_ HWND hwnd,
_In_ LPARAM lParam
)
{
TCHAR className[200];
GetClassName(hwnd, className, _countof(className));
ASSERT(IsWindow(hwnd));
if (_tcscmp(className, _T("tooltips_class32")) == 0)
{
TOOLINFO ti = { 0 };
ti.cbSize = sizeof(TOOLINFO);
TCHAR text[500] = { 0 };
ti.lpszText = text;
ti.hwnd = GetParent(hwnd);
IsWindow(ti.hwnd);
ti.uId = GetDlgCtrlID(hwnd);
int result = SendMessage(hwnd, TTM_GETTEXT, _countof(text), (LPARAM)&ti);
CString info;
info.Format(_T("%p: %s \"%s\"\r\n"), hwnd, className, ti.lpszText);
CString& output = *(CString*)lParam;
output += info;
}
return 1;
}
void CTooltipVerifyDlg::OnTimer(UINT_PTR nIDEvent)
{
m_output = "";
VERIFY(EnumWindows(EnumWindowsProc, (LPARAM)&m_output));
SYSTEMTIME systemTime;
GetLocalTime(&systemTime);
CString text;
text.Format(_T("%02u:%02u:%02u.%03u\r\n"), systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
m_output = text + m_output;
this->UpdateData(FALSE);
CDialogEx::OnTimer(nIDEvent);
}
CTooltipVerifyDlg is a dialogue with a text box which I communicate to with m_output, a CString that is bound to the text box.
When the SendMessage call is done, something on my desktop (or even the desktop manager) crashes. Any ideas why it would be crashing and not giving me the text that I desire?