934 lines
25 KiB
C++
934 lines
25 KiB
C++
|
#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
|