/*++ Copyright (c) 1992-2001 Microsoft Corporation Module Name: callswin.cpp Abstract: This module contains the main line code for display of calls window. Environment: Win32, User Mode --*/ #include "precomp.hxx" #pragma hdrstop #define MIN_FRAMES 10 #define MORE_LESS 10 #define CALLS_CONTEXT_ID_BASE 0x100 #define TBB_MORE 9 #define TBB_LESS 10 // The IDs of the buttons are the bit shift of the // corresponding flag. TBBUTTON g_CallsTbButtons[] = { TEXT_TB_BTN(0, "Args", BTNS_CHECK), TEXT_TB_BTN(1, "Func info", BTNS_CHECK), TEXT_TB_BTN(2, "Source", BTNS_CHECK), TEXT_TB_BTN(3, "Addrs", BTNS_CHECK), TEXT_TB_BTN(4, "Headings", BTNS_CHECK), TEXT_TB_BTN(5, "Nonvolatile regs", BTNS_CHECK), TEXT_TB_BTN(6, "Frame nums", BTNS_CHECK), TEXT_TB_BTN(7, "Arg types", BTNS_CHECK), SEP_TB_BTN(), TEXT_TB_BTN(TBB_MORE, "More", 0), TEXT_TB_BTN(TBB_LESS, "Less", 0), SEP_TB_BTN(), TEXT_TB_BTN(ID_SHOW_TOOLBAR, "Toolbar", 0), }; #define NUM_CALLS_MENU_BUTTONS \ (sizeof(g_CallsTbButtons) / sizeof(g_CallsTbButtons[0])) #define NUM_CALLS_TB_BUTTONS \ (NUM_CALLS_MENU_BUTTONS - 2) HMENU CALLSWIN_DATA::s_ContextMenu; // // // CALLSWIN_DATA::CALLSWIN_DATA() : SINGLE_CHILDWIN_DATA(1024) { m_enumType = CALLS_WINDOW; m_Flags = 0; m_Frames = 20; m_FramesFound = 0; m_TextOffset = 0; } void CALLSWIN_DATA::Validate() { SINGLE_CHILDWIN_DATA::Validate(); Assert(CALLS_WINDOW == m_enumType); } HRESULT CALLSWIN_DATA::ReadState(void) { HRESULT Status; Empty(); // // Record the raw frame data first. // // Preallocate space to record the raw frames. if (AddData(sizeof(DEBUG_STACK_FRAME) * m_Frames) == NULL) { return E_OUTOFMEMORY; } // Allocate a separate buffer to hold the frames while // calling OutputStackTrace on them. We can't just pass // in the state buffer pointer as resizing of the state // buffer may cause the data pointer to change. PDEBUG_STACK_FRAME RawFrames = (PDEBUG_STACK_FRAME)malloc(m_DataUsed); if (RawFrames == NULL) { return E_OUTOFMEMORY; } Status = g_pDbgControl->GetStackTrace(0, 0, 0, RawFrames, m_Frames, &m_FramesFound); if (Status != S_OK) { free(RawFrames); return Status; } m_TextOffset = m_DataUsed; g_OutStateBuf.SetBuffer(this); if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK) { return Status; } // If nonvolatile registers were requested we can't use just // our saved frames as they require full context information. Status = g_pOutCapControl-> OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_OVERRIDE_MASK | DEBUG_OUTCTL_NOT_LOGGED, (m_Flags & DEBUG_STACK_NONVOLATILE_REGISTERS) ? NULL : RawFrames, m_FramesFound, m_Flags); if (Status == S_OK) { Status = g_OutStateBuf.End(TRUE); if (Status == S_OK) { g_OutStateBuf.ReplaceChar('\n', 0); } } else { g_OutStateBuf.End(TRUE); } // Now that the state buffer is stable put the raw frame // data in. memcpy(m_Data, RawFrames, m_TextOffset); free(RawFrames); return Status; } void CALLSWIN_DATA::Copy() { LRESULT Line = SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0); Assert(Line != LB_ERR); LRESULT Len = SendMessage(m_hwndChild, LB_GETTEXTLEN, Line, 0); if (Len <= 0) { return; } Len++; PSTR Text = (PSTR)malloc(Len); if (!Text) { return; } SendMessage(m_hwndChild, LB_GETTEXT, Line, (LPARAM)Text); Text[Len - 1] = 0; CopyToClipboard(Text); free (Text); return; } HMENU CALLSWIN_DATA::GetContextMenu(void) { ULONG i; // // We only keep one menu around for all call windows // so apply the menu check state for this particular // window. // In reality there's only one calls window anyway, // but this is a good example of how to handle // multi-instance windows. // for (i = 0; i < NUM_CALLS_TB_BUTTONS; i++) { CheckMenuItem(s_ContextMenu, i + CALLS_CONTEXT_ID_BASE, MF_BYCOMMAND | ((m_Flags & (1 << i)) ? MF_CHECKED : 0)); } CheckMenuItem(s_ContextMenu, ID_SHOW_TOOLBAR + CALLS_CONTEXT_ID_BASE, MF_BYCOMMAND | (m_ShowToolbar ? MF_CHECKED : 0)); return s_ContextMenu; } void CALLSWIN_DATA::OnContextMenuSelection(UINT Item) { Item -= CALLS_CONTEXT_ID_BASE; switch(Item) { case TBB_MORE: m_Frames += MORE_LESS; break; case TBB_LESS: if (m_Frames >= MIN_FRAMES + MORE_LESS) { m_Frames -= MORE_LESS; } break; case ID_SHOW_TOOLBAR: SetShowToolbar(!m_ShowToolbar); break; default: m_Flags ^= 1 << Item; SyncUiWithFlags(1 << Item); break; } if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } UiRequestRead(); } BOOL CALLSWIN_DATA::CodeExprAtCaret(PSTR Expr, PULONG64 Offset) { BOOL Succ = FALSE; LRESULT Line = SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0); // If the column headers are on ignore that line. if (m_Flags & DEBUG_STACK_COLUMN_NAMES) { if (Line == 0) { return Succ; } else { Line--; } } if (Line >= 0 && (ULONG)Line < m_FramesFound) { if (UiLockForRead() == S_OK) { PDEBUG_STACK_FRAME RawFrames = (PDEBUG_STACK_FRAME)m_Data; if (Expr != NULL) { sprintf(Expr, "0x%I64x", RawFrames[Line].InstructionOffset); } if (Offset != NULL) { *Offset = RawFrames[Line].InstructionOffset; } Succ = TRUE; UnlockStateBuffer(this); } } return Succ; } HRESULT CALLSWIN_DATA:: StackFrameAtCaret(PDEBUG_STACK_FRAME pFrame) { LRESULT Line = SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0); // If the column headers are on ignore that line. if (m_Flags & DEBUG_STACK_COLUMN_NAMES) { if (Line == 0) { return E_INVALIDARG; } else { Line--; } } if (Line >= 0 && (ULONG)Line < m_FramesFound && pFrame) { if (UiLockForRead() == S_OK) { PDEBUG_STACK_FRAME RawFrames = (PDEBUG_STACK_FRAME)m_Data; *pFrame = RawFrames[Line]; UnlockStateBuffer(this); } } return S_OK; } BOOL CALLSWIN_DATA::OnCreate(void) { if (s_ContextMenu == NULL) { s_ContextMenu = CreateContextMenuFromToolbarButtons (NUM_CALLS_MENU_BUTTONS, g_CallsTbButtons, CALLS_CONTEXT_ID_BASE); if (s_ContextMenu == NULL) { return FALSE; } } m_Toolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_WRAPABLE | TBSTYLE_LIST | CCS_TOP, 0, 0, m_Size.cx, 0, m_Win, (HMENU)ID_TOOLBAR, g_hInst, NULL); if (m_Toolbar == NULL) { return FALSE; } SendMessage(m_Toolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SendMessage(m_Toolbar, TB_ADDBUTTONS, NUM_CALLS_TB_BUTTONS, (LPARAM)&g_CallsTbButtons); SendMessage(m_Toolbar, TB_AUTOSIZE, 0, 0); RECT Rect; GetClientRect(m_Toolbar, &Rect); m_ToolbarHeight = Rect.bottom - Rect.top; m_ShowToolbar = TRUE; m_hwndChild = CreateWindowEx( WS_EX_CLIENTEDGE, // Extended style _T("LISTBOX"), // class name NULL, // title WS_CHILD | WS_VISIBLE | WS_MAXIMIZE | WS_HSCROLL | WS_VSCROLL | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT | LBS_NOINTEGRALHEIGHT, // style 0, // x m_ToolbarHeight, // y m_Size.cx, // width m_Size.cy - m_ToolbarHeight, // height m_Win, // parent 0, // control id g_hInst, // hInstance NULL // user defined data ); if (m_hwndChild != NULL) { SetFont( FONT_FIXED ); return TRUE; } else { return FALSE; } } LRESULT CALLSWIN_DATA::OnCommand( WPARAM wParam, LPARAM lParam ) { if (HIWORD(wParam) == LBN_DBLCLK) { ULONG64 Offset; if (CodeExprAtCaret(NULL, &Offset) && Offset != DEBUG_INVALID_OFFSET) { UIC_DISPLAY_CODE_DATA* DispCode = StartStructCommand(UIC_DISPLAY_CODE); if (DispCode != NULL) { DispCode->Offset = Offset; FinishCommand(); } } DEBUG_STACK_FRAME StkFrame; if (StackFrameAtCaret(&StkFrame) == S_OK) { UIC_SET_SCOPE_DATA* SetScope = StartStructCommand(UIC_SET_SCOPE); if (SetScope != NULL) { SetScope->StackFrame = StkFrame; FinishCommand(); } } return 0; } if ((HWND)lParam == m_Toolbar) { OnContextMenuSelection(LOWORD(wParam) + CALLS_CONTEXT_ID_BASE); return 0; } return 0; } LRESULT CALLSWIN_DATA::OnVKeyToItem( WPARAM wParam, LPARAM lParam ) { if (LOWORD(wParam) == VK_RETURN) { ULONG64 Offset; if (CodeExprAtCaret(NULL, &Offset) && Offset != DEBUG_INVALID_OFFSET) { UIC_DISPLAY_CODE_DATA* DispCode = StartStructCommand(UIC_DISPLAY_CODE); if (DispCode != NULL) { DispCode->Offset = Offset; FinishCommand(); } } DEBUG_STACK_FRAME StkFrame; if (StackFrameAtCaret(&StkFrame) == S_OK) { UIC_SET_SCOPE_DATA* SetScope = StartStructCommand(UIC_SET_SCOPE); if (SetScope != NULL) { SetScope->StackFrame = StkFrame; FinishCommand(); } } } else if (_T('G') == LOWORD(wParam)) { ULONG64 Offset; if (CodeExprAtCaret(NULL, &Offset) && Offset != DEBUG_INVALID_OFFSET) { PrintStringCommand(UIC_EXECUTE, "g 0x%I64x", Offset); } } else if (_T('R') == LOWORD(wParam)) { OnUpdate(UPDATE_BUFFER); } else { // Default behavior. return -1; } // Keystroke processed. return -2; } void CALLSWIN_DATA::OnUpdate( UpdateType Type ) { if (Type != UPDATE_BUFFER) { return; } LRESULT lbItem; int nFrameCount; HRESULT Status; lbItem = SendMessage( m_hwndChild, LB_GETCURSEL, 0, 0 ); SendMessage( m_hwndChild, WM_SETREDRAW, FALSE, 0L ); SendMessage( m_hwndChild, LB_RESETCONTENT, 0, 0 ); Status = UiLockForRead(); if (Status == S_OK) { PSTR Buf = (PSTR)m_Data + m_TextOffset; // Ignore final terminator. PSTR End = (PSTR)m_Data + m_DataUsed - 1; ULONG Width = 0; nFrameCount = 0; while (Buf < End) { ULONG Len = strlen(Buf) + 1; ULONG StrWidth = (Len - 1) * m_Font->Metrics.tmAveCharWidth; SendMessage(m_hwndChild, LB_ADDSTRING, 0, (LPARAM)Buf); Buf += Len; if (StrWidth > Width) { Width = StrWidth; } nFrameCount++; } SendMessage(m_hwndChild, LB_SETHORIZONTALEXTENT, Width, 0); UnlockStateBuffer(this); } else { SendLockStatusMessage(m_hwndChild, LB_ADDSTRING, Status); nFrameCount = 1; } SendMessage( m_hwndChild, LB_SETCURSEL, (lbItem > nFrameCount) ? 0 : lbItem, 0 ); SendMessage( m_hwndChild, WM_SETREDRAW, TRUE, 0L ); } ULONG CALLSWIN_DATA::GetWorkspaceSize(void) { return SINGLE_CHILDWIN_DATA::GetWorkspaceSize() + 2 * sizeof(ULONG); } PUCHAR CALLSWIN_DATA::SetWorkspace(PUCHAR Data) { Data = SINGLE_CHILDWIN_DATA::SetWorkspace(Data); *(PULONG)Data = m_Flags; Data += sizeof(ULONG); *(PULONG)Data = m_Frames; Data += sizeof(ULONG); return Data; } PUCHAR CALLSWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End) { Data = SINGLE_CHILDWIN_DATA::ApplyWorkspace1(Data, End); if (End - Data >= 2 * sizeof(ULONG)) { m_Flags = *(PULONG)Data; Data += sizeof(ULONG); m_Frames = *(PULONG)Data; Data += sizeof(ULONG); SyncUiWithFlags(0xffffffff); UiRequestRead(); } return Data; } void CALLSWIN_DATA::SyncUiWithFlags(ULONG Changed) { ULONG i; // // Set toolbar button state from flags. // for (i = 0; i < NUM_CALLS_TB_BUTTONS; i++) { if (Changed & (1 << i)) { SendMessage(m_Toolbar, TB_SETSTATE, g_CallsTbButtons[i].idCommand, TBSTATE_ENABLED | ((m_Flags & (1 << i)) ? TBSTATE_CHECKED : 0)); } } }