|
|
#include "stdafx.h"
#include "Lava.h"
#include "Spy.h"
#include "..\DUser\resource.h"
#if DBG
const UINT IDC_GADGETTREE = 1; const int cxValue = 80; const int cxBorder = 5; const int cyBorder = 5;
/***************************************************************************\
***************************************************************************** * * class Spy * ***************************************************************************** \***************************************************************************/
PRID Spy::s_pridLink = 0; ATOM Spy::s_atom = NULL; HFONT Spy::s_hfntDesc = NULL; HFONT Spy::s_hfntDescBold = NULL; HBRUSH Spy::s_hbrOutline = NULL; int Spy::s_cyLinePxl = 0; DWORD Spy::g_tlsSpy = (DWORD) -1; CritLock Spy::s_lockList; GList<Spy> Spy::s_lstSpys;
static const GUID guidLink = { 0xd5818900, 0xaf18, 0x4c98, { 0x87, 0x20, 0x5a, 0x32, 0x47, 0xa3, 0x1, 0x78 } }; // {D5818900-AF18-4c98-8720-5A3247A30178}
//------------------------------------------------------------------------------
Spy::Spy() {
}
//------------------------------------------------------------------------------
Spy::~Spy() { s_lockList.Enter(); s_lstSpys.Unlink(this); s_lockList.Leave(); }
//------------------------------------------------------------------------------
BOOL Spy::BuildSpy(HWND hwndParent, HGADGET hgadRoot, HGADGET hgadSelect) { BOOL fSuccess = FALSE; Spy * pSpy, * pSpyCur;
s_lockList.Enter();
//
// Perform first-time initialization for Spy
//
if (g_tlsSpy == -1) { //
// Allocate a TLS slot for Spy. This is DEBUG only, so we don't worry about
// the extra cost. However, if this ever becomes on in RETAIL, we need to
// create a SubTread for Lava and add a Spy slot.
//
g_tlsSpy = TlsAlloc(); if (g_tlsSpy == -1) { goto Exit; }
//
// Initialize CommCtrl.
//
INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); icc.dwICC = ICC_TREEVIEW_CLASSES;
if (!InitCommonControlsEx(&icc)) { goto Exit; } }
AssertMsg(::GetGadget(hgadRoot, GG_PARENT) == NULL, "Ensure Root Gadget");
//
// Each Gadget subtree can only be spied on once because there are
// back-pointers from each Gadget to the corresponding HTREEITEM's. Need to
// check if this Gadget subtree is already is being spied on.
//
pSpyCur = s_lstSpys.GetHead(); while (pSpyCur != NULL) { if (pSpyCur->m_hgadRoot == hgadRoot) { //
// Already exists, so don't open another Spy.
//
SetForegroundWindow(pSpyCur->m_hwnd); goto Exit; }
pSpyCur = pSpyCur->GetNext(); }
//
// Register a WNDCLASS to use
//
if (s_atom == NULL) { WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex)); wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = RawSpyWndProc; wcex.hInstance = g_hDll; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wcex.lpszClassName = "GadgetSpy (Inside)";
s_atom = RegisterClassEx(&wcex); if (s_atom == NULL) { goto Exit; } }
//
// Create GDI objects used in painting
//
if (s_hfntDesc == NULL) { s_hfntDesc = UtilBuildFont(L"Tahoma", 85, FS_NORMAL, NULL);
HDC hdc = GetGdiCache()->GetCompatibleDC(); HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc);
TEXTMETRIC tm; GetTextMetrics(hdc, &tm); s_cyLinePxl = tm.tmHeight;
SelectObject(hdc, hfntOld); GetGdiCache()->ReleaseCompatibleDC(hdc); }
if (s_hfntDescBold == NULL) { s_hfntDescBold = UtilBuildFont(L"Tahoma", 85, FS_BOLD, NULL); }
if (s_hbrOutline == NULL) { s_hbrOutline = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW)); }
if (s_pridLink == 0) { s_pridLink = RegisterGadgetProperty(&guidLink); }
//
// Create a new Spy instance and HWND
//
pSpy = ProcessNew(Spy); if (pSpy == NULL) { goto Exit; }
pSpy->m_hgadMsg = CreateGadget(NULL, GC_MESSAGE, RawEventProc, pSpy); pSpy->m_hgadRoot = hgadRoot; Verify(TlsSetValue(g_tlsSpy, pSpy));
{ RECT rcParentPxl; GetWindowRect(hwndParent, &rcParentPxl);
HWND hwndSpy = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE, (LPCTSTR) s_atom, NULL, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, rcParentPxl.left + 20, rcParentPxl.top + 20, 300, 500, hwndParent, NULL, g_hDll, NULL); if (hwndSpy == NULL) { ProcessDelete(Spy, pSpy); goto Exit; }
pSpy->UpdateTitle();
ShowWindow(hwndSpy, SW_SHOW); UpdateWindow(hwndSpy); }
//
// Select the specified Gadget as a starting point. We want to check
// if this HGADGET is actually a valid child since it may have been
// "grabbed" at an earlier time and no longer be valid.
//
if (hgadSelect) { CheckIsChildData cicd; cicd.hgadCheck = hgadSelect; cicd.fChild = FALSE;
Verify(EnumGadgets(hgadRoot, EnumCheckIsChild, &cicd, GENUM_DEEPCHILD));
if (cicd.fChild) { HTREEITEM htiSelect; if (GetGadgetProperty(hgadSelect, s_pridLink, (void **) &htiSelect)) { AssertMsg(htiSelect != NULL, "Must have valid HTREEITEM"); if (!TreeView_SelectItem(pSpy->m_hwndTree, htiSelect)) { Trace("SPY: Unable to select default Gadget\n"); } } } }
s_lstSpys.Add(pSpy);
fSuccess = TRUE; Exit: s_lockList.Leave();
return fSuccess; }
//------------------------------------------------------------------------------
BOOL CALLBACK Spy::EnumCheckIsChild(HGADGET hgad, void * pvData) { CheckIsChildData * pcicd = (CheckIsChildData *) pvData;
if (hgad == pcicd->hgadCheck) { pcicd->fChild = TRUE; return FALSE; // No longer need to enumerate
}
return TRUE; }
//------------------------------------------------------------------------------
void Spy::UpdateTitle() { TCHAR szTitle[256]; wsprintf(szTitle, "Root HGADGET = 0x%p, %d Gadgets", m_hgadRoot, m_cItems);
SetWindowText(m_hwnd, szTitle); }
//------------------------------------------------------------------------------
LRESULT CALLBACK Spy::RawSpyWndProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { Spy * pSpy = (Spy *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pSpy == NULL) { //
// Creating a new Spy HWND, so hook up to the Spy object that was
// previously created.
//
pSpy = reinterpret_cast<Spy *> (TlsGetValue(g_tlsSpy)); AssertMsg(pSpy != NULL, "Ensure already created new Spy instance"); TlsSetValue(g_tlsSpy, NULL); pSpy->m_hwnd = hwnd; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pSpy); }
AssertMsg(pSpy->m_hwnd == hwnd, "Ensure HWND's match"); return pSpy->SpyWndProc(nMsg, wParam, lParam); }
//------------------------------------------------------------------------------
LRESULT Spy::SpyWndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { switch (nMsg) { case WM_CREATE: if (DefWindowProc(m_hwnd, nMsg, wParam, lParam) == -1) { return -1; }
//
// Setup the window
//
AssertMsg(m_hgadRoot != NULL, "Must already have specified Gadget");
RECT rcClient; GetClientRect(m_hwnd, &rcClient);
m_hwndTree = CreateWindowEx(0, _T("SysTreeView32"), NULL, WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_HASLINES | TVS_SHOWSELALWAYS, 0, 0, rcClient.right, rcClient.bottom, m_hwnd, (HMENU)((UINT_PTR)IDC_GADGETTREE), g_hDll, NULL);
m_hilc = ImageList_LoadImage(g_hDll, MAKEINTRESOURCE(IDB_SPYICON), 16, 1, RGB(128, 0, 128), IMAGE_BITMAP, LR_SHARED); if ((m_hwndTree == NULL) || (m_hilc == NULL)) { return -1; }
TreeView_SetImageList(m_hwndTree, m_hilc, TVSIL_NORMAL);
EnumData ed; ed.pspy = this; ed.htiParent = InsertTreeItem(TVI_ROOT, m_hgadRoot); ed.nLevel = 1; Verify(EnumGadgets(m_hgadRoot, EnumAddList, &ed, GENUM_SHALLOWCHILD)); m_fValid = TRUE;
TreeView_Expand(m_hwndTree, ed.htiParent, TVE_EXPAND); m_hgadDetails = m_hgadRoot; UpdateDetails();
break;
case WM_DESTROY: SelectGadget(NULL); ::DeleteHandle(m_hgadMsg); break;
case WM_NCDESTROY: ProcessDelete(Spy, this); goto CallDWP;
case WM_SIZE: if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) { UpdateLayout(); } break;
case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(m_hwnd, &ps); OnPaint(hdc); EndPaint(m_hwnd, &ps); } break;
case WM_NOTIFY: if (wParam == IDC_GADGETTREE) { NMHDR * pnm = (NMHDR *) lParam; if (pnm->code == TVN_SELCHANGED) { NMTREEVIEW * pnmtv = (NMTREEVIEW *) lParam; if (m_fValid) { SelectGadget(GetGadget(pnmtv->itemNew.hItem)); } break; } else if (pnm->code == TVN_KEYDOWN) { NMTVKEYDOWN * pnmtvkd = (NMTVKEYDOWN *) lParam; if (pnmtvkd->wVKey == VK_APPS) { DisplayContextMenu(TRUE); } } else if (pnm->code == NM_RCLICK) { DisplayContextMenu(FALSE); } } goto CallDWP;
default: CallDWP: return DefWindowProc(m_hwnd, nMsg, wParam, lParam); }
return 0; }
//------------------------------------------------------------------------------
HRESULT CALLBACK Spy::RawEventProc(HGADGET hgadCur, void * pvCur, EventMsg * pmsg) { UNREFERENCED_PARAMETER(hgadCur);
Spy * pSpy = (Spy *) pvCur; return pSpy->EventProc(pmsg); }
//------------------------------------------------------------------------------
BOOL IsDescendant( HGADGET hgadParent, HGADGET hgadChild) { AssertMsg(hgadParent != NULL, "Must have valid parent");
if (hgadChild == hgadParent) { return TRUE; } if (hgadChild == NULL) { return FALSE; } else { return IsDescendant(hgadParent, ::GetGadget(hgadChild, GG_PARENT)); } }
//------------------------------------------------------------------------------
HRESULT Spy::EventProc(EventMsg * pmsg) { switch (GET_EVENT_DEST(pmsg)) { case GMF_DIRECT: //
// Our Listener is being destroyed. We need to detach from everything.
//
if (m_hgadRoot != NULL) { Trace("SPY: Destroying Spy MsgGadget\n"); Verify(EnumGadgets(m_hgadRoot, EnumRemoveLink, NULL, GENUM_DEEPCHILD)); m_hgadRoot = NULL; } break;
case GMF_EVENT: switch (pmsg->nMsg) { case GM_DESTROY: { GMSG_DESTROY * pmsgD = (GMSG_DESTROY *) pmsg; if (pmsgD->nCode == GDESTROY_START) { //
// Gadget is being destroyed
//
Trace("SPY: Destroying Gadget 0x%p\n", pmsg->hgadMsg); HTREEITEM hti; if (GetGadgetProperty(pmsg->hgadMsg, s_pridLink, (void **) &hti)) { AssertMsg(hti != NULL, "Must have valid HTREEITEM"); Verify(EnumGadgets(pmsg->hgadMsg, EnumRemoveLink, NULL, GENUM_DEEPCHILD)); TreeView_DeleteItem(m_hwndTree, hti); } } } break;
case GM_CHANGERECT: if (IsDescendant(pmsg->hgadMsg, m_hgadDetails)) { UpdateDetails(); } break; } }
return DU_S_NOTHANDLED; }
//------------------------------------------------------------------------------
BOOL CALLBACK Spy::EnumAddList(HGADGET hgad, void * pvData) { EnumData * ped = (EnumData *) pvData; Spy * pSpy = ped->pspy; HTREEITEM htiNew = pSpy->InsertTreeItem(ped->htiParent, hgad);
pSpy->m_cItems++;
if (::GetGadget(hgad, GG_TOPCHILD) != NULL) { EnumData ed; ed.pspy = pSpy; ed.htiParent = htiNew; ed.nLevel = ped->nLevel + 1; Verify(EnumGadgets(hgad, EnumAddList, &ed, GENUM_SHALLOWCHILD));
if (ped->nLevel <= 2) { TreeView_Expand(pSpy->m_hwndTree, htiNew, TVE_EXPAND); } }
return TRUE; }
//------------------------------------------------------------------------------
BOOL CALLBACK Spy::EnumRemoveLink(HGADGET hgad, void * pvData) { UNREFERENCED_PARAMETER(pvData);
RemoveGadgetProperty(hgad, s_pridLink); return TRUE; }
//------------------------------------------------------------------------------
void Spy::SelectGadget(HGADGET hgad) { m_hgadDetails = hgad;
{ //
// We are bypassinging the normal API's to directly call a
// DEBUG-only function. Need to lock the Context and do FULL handle
// validation.
//
ContextLock cl; if (!cl.LockNL(ContextLock::edDefer)) { return; } DuVisual * pgadTree = ValidateVisual(hgad); DuVisual::DEBUG_SetOutline(pgadTree); }
UpdateDetails(); }
//------------------------------------------------------------------------------
HTREEITEM Spy::InsertTreeItem(HTREEITEM htiParent, HGADGET hgad) { TCHAR szName[1024];
GMSG_QUERYDESC msg; msg.cbSize = sizeof(msg); msg.hgadMsg = hgad; msg.nMsg = GM_QUERY; msg.nCode = GQUERY_DESCRIPTION; msg.szName[0] = '\0'; msg.szType[0] = '\0';
if (DUserSendEvent(&msg, 0) == DU_S_COMPLETE) { if (msg.szName[0] != '\0') { wsprintf(szName, "0x%p %S: \"%S\"", hgad, msg.szType, msg.szName); } else { wsprintf(szName, "0x%p %S", hgad, msg.szType); } } else { wsprintf(szName, "0x%p", hgad); }
TVINSERTSTRUCT tvis; tvis.hParent = htiParent; tvis.hInsertAfter = TVI_LAST; tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE; tvis.item.pszText = szName; tvis.item.iImage = iGadget; tvis.item.lParam = (LPARAM) hgad;
HTREEITEM htiNew = TreeView_InsertItem(m_hwndTree, &tvis); if (htiNew != NULL) { if (AddGadgetMessageHandler(hgad, 0, m_hgadMsg)) { Verify(SetGadgetProperty(hgad, s_pridLink, htiNew)); } else { Trace("WARNING: Spy unable to attach handler on 0x%p\n", hgad); } } return htiNew; }
//------------------------------------------------------------------------------
HGADGET Spy::GetGadget(HTREEITEM hti) { TVITEM tvi; tvi.hItem = hti; tvi.mask = TVIF_PARAM | TVIF_HANDLE;
if (TreeView_GetItem(m_hwndTree, &tvi)) { HGADGET hgadItem = (HGADGET) tvi.lParam; AssertMsg(hgadItem != NULL, "All items in the tree should have a Gadget");
return hgadItem; }
return NULL; }
//------------------------------------------------------------------------------
void Spy::DisplayContextMenu(BOOL fViaKbd) { //
// Locate TreeView item
//
POINT ptPopup; ZeroMemory(&ptPopup, sizeof(ptPopup));
HTREEITEM hti;
if (fViaKbd) { //
// Keyboard driven
//
hti = TreeView_GetSelection(m_hwndTree); if (hti != NULL) { RECT rc;
TreeView_GetItemRect(m_hwndTree, hti, &rc, TRUE);
ptPopup.x = rc.left; ptPopup.y = rc.bottom; ClientToScreen(m_hwndTree, &ptPopup); } } else { //
// Mouse driven
//
TVHITTESTINFO tvht;
DWORD dwPos = GetMessagePos();
ptPopup.x = GET_X_LPARAM(dwPos); ptPopup.y = GET_Y_LPARAM(dwPos);
tvht.pt = ptPopup; ScreenToClient(m_hwndTree, &tvht.pt);
hti = TreeView_HitTest(m_hwndTree, &tvht); }
//
// Now have tree item and popup position
//
if (hti != NULL) { //
// Get Gadget associated with this item
//
HGADGET hgad = GetGadget(hti); //
// Create popup menu template
//
HMENU hMenu = CreatePopupMenu(); if (hMenu != NULL) {
BOOL fRes;
const int cmdDetails = 10;
fRes = AppendMenu(hMenu, MF_STRING, cmdDetails, "Details..."); if (fRes) {
UINT nCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTBUTTON, ptPopup.x, ptPopup.y, 0, m_hwndTree, NULL);
DestroyMenu(hMenu);
//
// Invoke commands
//
switch (nCmd) { case cmdDetails: { GMSG_QUERYDETAILS msg; msg.cbSize = sizeof(msg); msg.hgadMsg = hgad; msg.nMsg = GM_QUERY; msg.nCode = GQUERY_DETAILS; msg.nType = GQDT_HWND; msg.hOwner = m_hwndTree;
DUserSendEvent(&msg, 0); } break; } } } } }
//------------------------------------------------------------------------------
int Spy::NumLines(int cyPxl) const { return (cyPxl - 1) / s_cyLinePxl + 1; }
//------------------------------------------------------------------------------
void Spy::UpdateDetails() { if (m_hgadDetails == NULL) { return; }
RECT rcPxl; GetGadgetRect(m_hgadDetails, &rcPxl, SGR_CONTAINER);
wsprintf(m_szRect, "(%d, %d)-(%d, %d) %d� %d", rcPxl.left, rcPxl.top, rcPxl.right, rcPxl.bottom, rcPxl.right - rcPxl.left, rcPxl.bottom - rcPxl.top);
GMSG_QUERYDESC msg; msg.cbSize = sizeof(msg); msg.hgadMsg = m_hgadDetails; msg.nMsg = GM_QUERY; msg.nCode = GQUERY_DESCRIPTION; msg.szName[0] = '\0'; msg.szType[0] = '\0';
if (DUserSendEvent(&msg, 0) == DU_S_COMPLETE) { CopyString(m_szName, msg.szName, _countof(m_szName)); CopyString(m_szType, msg.szType, _countof(m_szType)); } else { m_szName[0] = '\0'; m_szType[0] = '\0'; }
//
// We are bypassinging the normal API's to directly call a
// DEBUG-only function. Need to lock the Context and do FULL handle
// validation.
//
ContextLock cl; if (cl.LockNL(ContextLock::edNone)) { DuVisual * pgadTree = ValidateVisual(m_hgadDetails); AssertMsg(pgadTree != NULL, "Should be a valid DuVisual for Spy"); pgadTree->DEBUG_GetStyleDesc(m_szStyle, _countof(m_szStyle));
UpdateLayoutDesc(FALSE); InvalidateRect(m_hwnd, NULL, TRUE); } }
//------------------------------------------------------------------------------
void Spy::UpdateLayout() { RECT rcClient; GetClientRect(m_hwnd, &rcClient); m_sizeWndPxl.cx = rcClient.right; m_sizeWndPxl.cy = rcClient.bottom;
UpdateLayoutDesc(TRUE); }
//------------------------------------------------------------------------------
void Spy::UpdateLayoutDesc(BOOL fForceLayoutDesc) { //
// Compute the number of needed lines
//
int cOldLines = m_cLines; m_cLines = 4;
RECT rcStyle; rcStyle.left = cxBorder + cxValue; rcStyle.top = 0; rcStyle.right = m_sizeWndPxl.cx - cxBorder; rcStyle.bottom = 10000;
HDC hdc = GetGdiCache()->GetTempDC(); HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc); int nHeight = OS()->DrawText(hdc, m_szStyle, (int) wcslen(m_szStyle), &rcStyle, DT_CALCRECT | DT_LEFT | DT_TOP | DT_WORDBREAK); SelectObject(hdc, hfntOld); GetGdiCache()->ReleaseTempDC(hdc);
m_cLines += NumLines(nHeight);
//
// Move the Tree to provide space for the description
//
if ((cOldLines != m_cLines) || fForceLayoutDesc) { m_cyDescPxl = s_cyLinePxl * m_cLines + 10; m_fShowDesc = m_sizeWndPxl.cy > m_cyDescPxl;
SIZE sizeTree; sizeTree.cx = m_sizeWndPxl.cx; sizeTree.cy = m_fShowDesc ? (m_sizeWndPxl.cy - m_cyDescPxl) : m_sizeWndPxl.cy;
MoveWindow(m_hwndTree, 0, 0, sizeTree.cx, sizeTree.cy, TRUE); } }
//------------------------------------------------------------------------------
void Spy::OnPaint(HDC hdc) { HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc); int nOldMode = SetBkMode(hdc, TRANSPARENT);
RECT rcOutline; rcOutline.left = 2; rcOutline.top = m_sizeWndPxl.cy - m_cyDescPxl + 2; rcOutline.right = m_sizeWndPxl.cx - 1; rcOutline.bottom = m_sizeWndPxl.cy - 1; GdDrawOutlineRect(hdc, &rcOutline, s_hbrOutline);
POINT pt; pt.x = cxBorder; pt.y = m_sizeWndPxl.cy - m_cyDescPxl + cyBorder;
// NOTE: m_cLines should equal the number of lines displayed here
PaintLine(hdc, &pt, "HGADGET: ", m_hgadDetails); PaintLine(hdc, &pt, "Name: ", m_szName, FALSE, s_hfntDescBold); PaintLine(hdc, &pt, "Type: ", m_szType); PaintLine(hdc, &pt, "Rectangle: ", m_szRect); PaintLine(hdc, &pt, "Style: ", m_szStyle, TRUE);
SetBkMode(hdc, nOldMode); SelectObject(hdc, hfntOld); }
class CTempSelectFont { public: CTempSelectFont(HDC hdc, HFONT hfnt) { m_hdc = hdc; m_fSelect = (hfnt != NULL); if (m_fSelect) { m_hfntOld = (HFONT) SelectObject(m_hdc, hfnt); } }
~CTempSelectFont() { if (m_fSelect) { SelectObject(m_hdc, m_hfntOld); } }
BOOL m_fSelect; HDC m_hdc; HFONT m_hfntOld; };
//------------------------------------------------------------------------------
void Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, LPCTSTR pszText, HFONT hfnt) { TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt); TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, pszText, (int) _tcslen(pszText));
pptOffset->y += s_cyLinePxl; }
//------------------------------------------------------------------------------
void Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, LPCWSTR pszText, BOOL fMultiline, HFONT hfnt) { TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt);
if (fMultiline) { RECT rcStyle; rcStyle.left = pptOffset->x + cxValue; rcStyle.top = pptOffset->y; rcStyle.right = m_sizeWndPxl.cx - cxBorder; rcStyle.bottom = 10000;
int nHeight = OS()->DrawText(hdc, pszText, (int) wcslen(pszText), &rcStyle, DT_LEFT | DT_TOP | DT_WORDBREAK); pptOffset->y += NumLines(nHeight) * s_cyLinePxl; } else { OS()->TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, pszText, (int) wcslen(pszText)); pptOffset->y += s_cyLinePxl; } }
//------------------------------------------------------------------------------
void Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, int nValue, HFONT hfnt) { TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt); TCHAR szValue[20]; _itot(nValue, szValue, 10); TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, szValue, (int) _tcslen(szValue));
pptOffset->y += s_cyLinePxl; }
//------------------------------------------------------------------------------
void Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, void * pvValue, HFONT hfnt) { TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt); TCHAR szValue[20]; wsprintf(szValue, "0x%p", pvValue); TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, szValue, (int) _tcslen(szValue));
pptOffset->y += s_cyLinePxl; }
#endif // DBG
|