/*++ Copyright (c) 1999-2001 Microsoft Corporation Module Name: cmnwin.cpp Abstract: This module contains the code for the common window architecture. --*/ #include "precomp.hxx" #pragma hdrstop ULONG g_WinOptions = WOPT_AUTO_ARRANGE | WOPT_AUTO_DISASM; LIST_ENTRY g_ActiveWin; PCOMMONWIN_DATA g_IndexedWin[MAXVAL_WINDOW]; HWND g_IndexedHwnd[MAXVAL_WINDOW]; INDEXED_FONT g_Fonts[FONT_COUNT]; BOOL g_LineMarkers = FALSE; #define CW_WSP_SIG3 '3WCW' // // // COMMONWIN_DATA::COMMONWIN_DATA(ULONG ChangeBy) : StateBuffer(ChangeBy) { m_Size.cx = 0; m_Size.cy = 0; m_CausedArrange = FALSE; // Creation is an automatic operation so // InAutoOp is initialized to a non-zero value. // After CreateWindow returns it is decremented. m_InAutoOp = 1; m_enumType = MINVAL_WINDOW; m_Font = &g_Fonts[FONT_FIXED]; m_FontHeight = 0; m_LineHeight = 0; m_Toolbar = NULL; m_ShowToolbar = FALSE; m_ToolbarHeight = 0; m_MinToolbarWidth = 0; m_ToolbarEdit = NULL; } void COMMONWIN_DATA::Validate() { Assert(MINVAL_WINDOW < m_enumType); Assert(m_enumType < MAXVAL_WINDOW); } void COMMONWIN_DATA::SetFont(ULONG FontIndex) { m_Font = &g_Fonts[FontIndex]; m_FontHeight = m_Font->Metrics.tmHeight; m_LineHeight = m_Size.cy / m_FontHeight; } BOOL COMMONWIN_DATA::CanCopy() { if (GetFocus() == m_ToolbarEdit) { DWORD Start, End; SendMessage(m_ToolbarEdit, EM_GETSEL, (WPARAM)&Start, (WPARAM)&End); return Start != End; } else { return FALSE; } } BOOL COMMONWIN_DATA::CanCut() { if (GetFocus() == m_ToolbarEdit) { DWORD Start, End; SendMessage(m_ToolbarEdit, EM_GETSEL, (WPARAM)&Start, (WPARAM)&End); return Start != End; } else { return FALSE; } } BOOL COMMONWIN_DATA::CanPaste() { if (GetFocus() == m_ToolbarEdit) { return TRUE; } else { return FALSE; } } void COMMONWIN_DATA::Copy() { if (GetFocus() == m_ToolbarEdit) { SendMessage(m_ToolbarEdit, WM_COPY, 0, 0); } } void COMMONWIN_DATA::Cut() { if (GetFocus() == m_ToolbarEdit) { SendMessage(m_ToolbarEdit, WM_CUT, 0, 0); } } void COMMONWIN_DATA::Paste() { if (GetFocus() == m_ToolbarEdit) { SendMessage(m_ToolbarEdit, WM_PASTE, 0, 0); } } BOOL COMMONWIN_DATA::CanSelectAll() { return FALSE; } void COMMONWIN_DATA::SelectAll() { } BOOL COMMONWIN_DATA::HasEditableProperties() { return FALSE; } BOOL COMMONWIN_DATA::EditProperties() /*++ Returns TRUE - If properties were edited FALSE - If nothing was changed --*/ { return FALSE; } HMENU COMMONWIN_DATA::GetContextMenu(void) { return NULL; } void COMMONWIN_DATA::OnContextMenuSelection(UINT Item) { // Nothing to do. } BOOL COMMONWIN_DATA::CanGotoLine(void) { return FALSE; } void COMMONWIN_DATA::GotoLine(ULONG Line) { // Do nothing. } void COMMONWIN_DATA::Find(PTSTR Text, ULONG Flags) { // Do nothing. } BOOL COMMONWIN_DATA::CodeExprAtCaret(PSTR Expr, PULONG64 Offset) { return FALSE; } void COMMONWIN_DATA::ToggleBpAtCaret(void) { char CodeExpr[MAX_OFFSET_EXPR]; ULONG64 Offset; if (!CodeExprAtCaret(CodeExpr, &Offset) && Offset != DEBUG_INVALID_OFFSET) { MessageBeep(0); ErrorBox(NULL, 0, ERR_No_Code_For_File_Line); return; } ULONG CurBpId = DEBUG_ANY_ID; // This doesn't work too well with duplicate // breakpoints, but that should be a minor problem. if (IsBpAtOffset(NULL, Offset, &CurBpId) != BP_NONE) { PrintStringCommand(UIC_SILENT_EXECUTE, "bc %d", CurBpId); } else { PrintStringCommand(UIC_SILENT_EXECUTE, "bp %s", CodeExpr); } } BOOL COMMONWIN_DATA::OnCreate(void) { return TRUE; } LRESULT COMMONWIN_DATA::OnCommand(WPARAM wParam, LPARAM lParam) { return 1; } void COMMONWIN_DATA::OnSetFocus(void) { } void COMMONWIN_DATA::OnSize(void) { RECT Rect; // Resize the toolbar. if (m_Toolbar != NULL && m_ShowToolbar) { // If the toolbar gets too small sometimes it's better // to just let it get clipped rather than have it // try to fit into a narrow column. if (m_Size.cx >= m_MinToolbarWidth) { MoveWindow(m_Toolbar, 0, 0, m_Size.cx, m_ToolbarHeight, TRUE); } // Record what size it ended up. GetClientRect(m_Toolbar, &Rect); m_ToolbarHeight = Rect.bottom - Rect.top; if (m_FontHeight != 0) { if (m_ToolbarHeight >= m_Size.cy) { m_LineHeight = 0; } else { m_LineHeight = (m_Size.cy - m_ToolbarHeight) / m_FontHeight; } } } else { Assert(m_ToolbarHeight == 0); } } void COMMONWIN_DATA::OnButtonDown(ULONG Button) { } void COMMONWIN_DATA::OnButtonUp(ULONG Button) { } void COMMONWIN_DATA::OnMouseMove(ULONG Modifiers, ULONG X, ULONG Y) { } void COMMONWIN_DATA::OnTimer(WPARAM TimerId) { } LRESULT COMMONWIN_DATA::OnGetMinMaxInfo(LPMINMAXINFO Info) { return 1; } LRESULT COMMONWIN_DATA::OnVKeyToItem(WPARAM wParam, LPARAM lParam) { return -1; } LRESULT COMMONWIN_DATA::OnNotify(WPARAM wParam, LPARAM lParam) { return 0; } void COMMONWIN_DATA::OnUpdate(UpdateType Type) { } void COMMONWIN_DATA::OnDestroy(void) { } LRESULT COMMONWIN_DATA::OnOwnerDraw(UINT uMsg, WPARAM wParam, LPARAM lParam) { return 0; } ULONG COMMONWIN_DATA::GetWorkspaceSize(void) { return 3 * sizeof(ULONG) + sizeof(WINDOWPLACEMENT); } PUCHAR COMMONWIN_DATA::SetWorkspace(PUCHAR Data) { // First store the special signature that marks // this version of the workspace data. *(PULONG)Data = CW_WSP_SIG3; Data += sizeof(ULONG); // Store the size saved by this layer. *(PULONG)Data = COMMONWIN_DATA::GetWorkspaceSize(); Data += sizeof(ULONG); // // Store the actual data. // *(PULONG)Data = m_ShowToolbar; Data += sizeof(ULONG); LPWINDOWPLACEMENT Place = (LPWINDOWPLACEMENT)Data; Place->length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(m_Win, Place); Data += sizeof(WINDOWPLACEMENT); return Data; } PUCHAR COMMONWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End) { ULONG_PTR Size = End - Data; // There are three versions of the base COMMONWIN data. // 1. RECT. // 2. WINDOWPLACEMENT. // 3. CW_WSP_SIG3 sized block. // All three cases can be easily distinguished. if (Size > 2 * sizeof(ULONG) && *(PULONG)Data == CW_WSP_SIG3 && Size >= *(PULONG)(Data + sizeof(ULONG))) { Size = *(PULONG)(Data + sizeof(ULONG)) - 2 * sizeof(ULONG); Data += 2 * sizeof(ULONG); if (Size >= sizeof(ULONG)) { SetShowToolbar(*(PULONG)Data); Size -= sizeof(ULONG); Data += sizeof(ULONG); } } if (Size >= sizeof(WINDOWPLACEMENT) && ((LPWINDOWPLACEMENT)Data)->length == sizeof(WINDOWPLACEMENT)) { LPWINDOWPLACEMENT Place = (LPWINDOWPLACEMENT)Data; if (!IsAutoArranged(m_enumType)) { SetWindowPlacement(m_Win, Place); } return (PUCHAR)(Place + 1); } else { LPRECT Rect = (LPRECT)Data; Assert((PUCHAR)(Rect + 1) <= End); if (!IsAutoArranged(m_enumType)) { MoveWindow(m_Win, Rect->left, Rect->top, (Rect->right - Rect->left), (Rect->bottom - Rect->top), TRUE); } return (PUCHAR)(Rect + 1); } } void COMMONWIN_DATA::UpdateColors(void) { // Nothing to do. } void COMMONWIN_DATA::UpdateSize(ULONG Width, ULONG Height) { m_Size.cx = Width; m_Size.cy = Height; if (m_FontHeight != 0) { m_LineHeight = m_Size.cy / m_FontHeight; } } void COMMONWIN_DATA::SetShowToolbar(BOOL Show) { if (!m_Toolbar) { return; } m_ShowToolbar = Show; if (m_ShowToolbar) { ShowWindow(m_Toolbar, SW_SHOW); } else { ShowWindow(m_Toolbar, SW_HIDE); m_ToolbarHeight = 0; } OnSize(); if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } } PCOMMONWIN_DATA NewWinData(WIN_TYPES Type) { switch(Type) { case DOC_WINDOW: return new DOCWIN_DATA; case WATCH_WINDOW: return new WATCHWIN_DATA; case LOCALS_WINDOW: return new LOCALSWIN_DATA; case CPU_WINDOW: return new CPUWIN_DATA; case DISASM_WINDOW: return new DISASMWIN_DATA; case CMD_WINDOW: return new CMDWIN_DATA; case SCRATCH_PAD_WINDOW: return new SCRATCH_PAD_DATA; case MEM_WINDOW: return new MEMWIN_DATA; #if 0 case QUICKW_WINDOW: // XXX drewb - Unimplemented. return new QUICKWWIN_DATA; #endif case CALLS_WINDOW: return new CALLSWIN_DATA; case PROCESS_THREAD_WINDOW: return new PROCESS_THREAD_DATA; default: Assert(FALSE); return NULL; } } LRESULT CALLBACK COMMONWIN_DATA::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { PCOMMONWIN_DATA pWinData = GetCommonWinData(hwnd); #if 0 { DebugPrint("CommonWin msg %X for %p, args %X %X\n", uMsg, pWinData, wParam, lParam); } #endif if (uMsg != WM_CREATE && pWinData == NULL) { return DefMDIChildProc(hwnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_CREATE: RECT rc; COMMONWIN_CREATE_DATA* Data; Assert(NULL == pWinData); Data = (COMMONWIN_CREATE_DATA*) ((LPMDICREATESTRUCT) (((CREATESTRUCT *)lParam)->lpCreateParams))->lParam; pWinData = NewWinData(Data->Type); if (!pWinData) { return -1; // Fail window creation } Assert(pWinData->m_enumType == Data->Type); pWinData->m_Win = hwnd; GetClientRect(hwnd, &rc); pWinData->m_Size.cx = rc.right; pWinData->m_Size.cy = rc.bottom; if ( !pWinData->OnCreate() ) { delete pWinData; return -1; // Fail window creation } // store this in the window SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pWinData); #if DBG pWinData->Validate(); #endif g_IndexedWin[Data->Type] = pWinData; g_IndexedHwnd[Data->Type] = hwnd; InsertHeadList(&g_ActiveWin, &pWinData->m_ActiveWin); if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } SendMessage(hwnd, WM_SETICON, 0, (LPARAM) LoadIcon(g_hInst, MAKEINTRESOURCE(pWinData->m_enumType + MINVAL_WINDOW_ICON))); // A new buffer has been created so put it in the list // then wake up the engine to fill it. Dbg_EnterCriticalSection(&g_QuickLock); InsertHeadList(&g_StateList, pWinData); Dbg_LeaveCriticalSection(&g_QuickLock); UpdateEngine(); // Force initial updates so that the window starts // out with a state which matches the current debug // session's state. PostMessage(hwnd, WU_UPDATE, UPDATE_BUFFER, 0); PostMessage(hwnd, WU_UPDATE, UPDATE_EXEC, 0); if (g_WinOptions & WOPT_AUTO_ARRANGE) { Arrange(); } return 0; case WM_COMMAND: if (pWinData->OnCommand(wParam, lParam) == 0) { return 0; } break; case WM_SETFOCUS: pWinData->OnSetFocus(); break; case WM_MOVE: // When the frame window is minimized or restored // a move to 0,0 comes through. Ignore this so // as to not trigger the warning. if (!IsIconic(g_hwndFrame) && lParam != 0) { DisplayAutoArrangeWarning(pWinData); } if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } return DefWindowProc(hwnd, uMsg, wParam, lParam); case WM_SIZE: if (wParam == SIZE_MAXHIDE || wParam == SIZE_MAXSHOW) { // We don't care about cover/uncover events. break; } DisplayAutoArrangeWarning(pWinData); if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } pWinData->UpdateSize(LOWORD(lParam), HIWORD(lParam)); // No need to run sizing code for minimize. if (wParam == SIZE_MINIMIZED) { // The minimized window will leave a hole so // arrange to fill it and leave space for the // minimized window. if (g_WinOptions & WOPT_AUTO_ARRANGE) { pWinData->m_CausedArrange = TRUE; Arrange(); } break; } if (wParam == SIZE_RESTORED && pWinData->m_CausedArrange) { // If we're restoring a window that caused // a rearrange when it was minimized we // need to update things to account for it. pWinData->m_CausedArrange = FALSE; if (g_WinOptions & WOPT_AUTO_ARRANGE) { Arrange(); } } else if (wParam == SIZE_MAXIMIZED) { // Ask for a rearrange on restore just // for consistency with minimize. pWinData->m_CausedArrange = TRUE; } pWinData->OnSize(); break; case WM_LBUTTONDOWN: pWinData->OnButtonDown(MK_LBUTTON); return 0; case WM_LBUTTONUP: pWinData->OnButtonUp(MK_LBUTTON); return 0; case WM_MBUTTONDOWN: pWinData->OnButtonDown(MK_MBUTTON); return 0; case WM_MBUTTONUP: pWinData->OnButtonUp(MK_MBUTTON); return 0; case WM_RBUTTONDOWN: pWinData->OnButtonDown(MK_RBUTTON); return 0; case WM_RBUTTONUP: pWinData->OnButtonUp(MK_RBUTTON); return 0; case WM_MOUSEMOVE: pWinData->OnMouseMove((ULONG)wParam, LOWORD(lParam), HIWORD(lParam)); return 0; case WM_TIMER: pWinData->OnTimer(wParam); return 0; case WM_GETMINMAXINFO: if (pWinData->OnGetMinMaxInfo((LPMINMAXINFO)lParam) == 0) { return 0; } break; case WM_VKEYTOITEM: return pWinData->OnVKeyToItem(wParam, lParam); case WM_NOTIFY: return pWinData->OnNotify(wParam, lParam); case WU_UPDATE: pWinData->OnUpdate((UpdateType)wParam); return 0; case WU_RECONFIGURE: pWinData->OnSize(); break; case WM_DESTROY: pWinData->OnDestroy(); SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL); g_IndexedWin[pWinData->m_enumType] = NULL; g_IndexedHwnd[pWinData->m_enumType] = NULL; RemoveEntryList(&pWinData->m_ActiveWin); if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } // Mark this buffer as ready for cleanup by the // engine when it gets around to it. pWinData->m_Win = NULL; if (pWinData == g_FindLast) { g_FindLast = NULL; } UpdateEngine(); if (g_WinOptions & WOPT_AUTO_ARRANGE) { Arrange(); } break; case WM_MEASUREITEM: case WM_DRAWITEM: // // Both these messages must be handled by owner drawn windows // return pWinData->OnOwnerDraw(uMsg, wParam, lParam); } return DefMDIChildProc(hwnd, uMsg, wParam, lParam); } // // // SINGLE_CHILDWIN_DATA::SINGLE_CHILDWIN_DATA(ULONG ChangeBy) : COMMONWIN_DATA(ChangeBy) { m_hwndChild = NULL; } void SINGLE_CHILDWIN_DATA::Validate() { COMMONWIN_DATA::Validate(); Assert(m_hwndChild); } void SINGLE_CHILDWIN_DATA::SetFont(ULONG FontIndex) { COMMONWIN_DATA::SetFont(FontIndex); SendMessage(m_hwndChild, WM_SETFONT, (WPARAM) m_Font->Font, (LPARAM) TRUE ); } BOOL SINGLE_CHILDWIN_DATA::CanCopy() { if (GetFocus() != m_hwndChild) { return COMMONWIN_DATA::CanCopy(); } switch (m_enumType) { default: Assert(!"Unknown type"); return FALSE; case CMD_WINDOW: Assert(!"Should not be handled here since this is only for windows" " with only one child window."); return FALSE; case WATCH_WINDOW: case LOCALS_WINDOW: case CPU_WINDOW: case QUICKW_WINDOW: return -1 != ListView_GetNextItem(m_hwndChild, -1, // Find the first match LVNI_FOCUSED ); case CALLS_WINDOW: return LB_ERR != SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0); case DOC_WINDOW: case DISASM_WINDOW: case MEM_WINDOW: case SCRATCH_PAD_WINDOW: CHARRANGE chrg; SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&chrg); return chrg.cpMin != chrg.cpMax; case PROCESS_THREAD_WINDOW: return NULL != TreeView_GetSelection(m_hwndChild); } } BOOL SINGLE_CHILDWIN_DATA::CanCut() { if (GetFocus() != m_hwndChild) { return COMMONWIN_DATA::CanCut(); } switch (m_enumType) { default: Assert(!"Unknown type"); return FALSE; case CMD_WINDOW: Assert(!"Should not be handled here since this is only for windows" " with only one child window."); return FALSE; case WATCH_WINDOW: case LOCALS_WINDOW: case CPU_WINDOW: case QUICKW_WINDOW: case CALLS_WINDOW: case DOC_WINDOW: case DISASM_WINDOW: case MEM_WINDOW: case PROCESS_THREAD_WINDOW: return FALSE; case SCRATCH_PAD_WINDOW: CHARRANGE chrg; SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&chrg); return chrg.cpMin != chrg.cpMax; } } BOOL SINGLE_CHILDWIN_DATA::CanPaste() { if (GetFocus() != m_hwndChild) { return COMMONWIN_DATA::CanPaste(); } switch (m_enumType) { default: Assert(!"Unknown type"); return FALSE; case CMD_WINDOW: Assert(!"Should not be handled here since this is only for windows" " with only one child window."); return FALSE; case WATCH_WINDOW: case LOCALS_WINDOW: case CPU_WINDOW: case QUICKW_WINDOW: case CALLS_WINDOW: case DOC_WINDOW: case DISASM_WINDOW: case MEM_WINDOW: case PROCESS_THREAD_WINDOW: return FALSE; case SCRATCH_PAD_WINDOW: return TRUE; } } void SINGLE_CHILDWIN_DATA::Copy() { if (GetFocus() != m_hwndChild) { COMMONWIN_DATA::Copy(); } else { SendMessage(m_hwndChild, WM_COPY, 0, 0); } } void SINGLE_CHILDWIN_DATA::Cut() { if (GetFocus() != m_hwndChild) { COMMONWIN_DATA::Paste(); } } void SINGLE_CHILDWIN_DATA::Paste() { if (GetFocus() != m_hwndChild) { COMMONWIN_DATA::Paste(); } } void SINGLE_CHILDWIN_DATA::OnSetFocus() { ::SetFocus(m_hwndChild); } void SINGLE_CHILDWIN_DATA::OnSize(void) { COMMONWIN_DATA::OnSize(); MoveWindow(m_hwndChild, 0, m_ToolbarHeight, m_Size.cx, m_Size.cy - m_ToolbarHeight, TRUE); } // // // PROCESS_THREAD_DATA::PROCESS_THREAD_DATA() : SINGLE_CHILDWIN_DATA(512) { m_enumType = PROCESS_THREAD_WINDOW; m_NumProcesses = 0; } void PROCESS_THREAD_DATA::Validate() { SINGLE_CHILDWIN_DATA::Validate(); Assert(PROCESS_THREAD_WINDOW == m_enumType); } HRESULT PROCESS_THREAD_DATA::ReadState(void) { HRESULT Status; ULONG CurProc, CurThread; ULONG NumProc, NumThread; ULONG TotalThread, MaxThread; ULONG Proc, Thread; PULONG ProcIds, ProcSysIds, ProcThreads, ProcNames; PULONG ThreadIds, ThreadSysIds; ULONG ThreadsDone; char ExeName[MAX_PATH]; if ((Status = g_pDbgSystem->GetCurrentProcessId(&CurProc)) != S_OK || (Status = g_pDbgSystem->GetCurrentThreadId(&CurThread)) != S_OK || (Status = g_pDbgSystem->GetNumberProcesses(&NumProc)) != S_OK || (Status = g_pDbgSystem-> GetTotalNumberThreads(&TotalThread, &MaxThread)) != S_OK) { return Status; } Empty(); ProcIds = (PULONG)AddData((NumProc * 4 + TotalThread * 2) * sizeof(ULONG)); if (ProcIds == NULL) { return E_OUTOFMEMORY; } ProcSysIds = ProcIds + NumProc; if ((Status = g_pDbgSystem-> GetProcessIdsByIndex(0, NumProc, ProcIds, ProcSysIds)) != S_OK) { return Status; } ThreadsDone = 0; for (Proc = 0; Proc < NumProc; Proc++) { PSTR ExeStore; // Refresh pointers on every loop in case a resize // caused buffer movement. ProcIds = (PULONG)GetDataBuffer(); ProcSysIds = ProcIds + NumProc; ProcThreads = ProcSysIds + NumProc; ProcNames = ProcThreads + NumProc; ThreadIds = ProcNames + NumProc + ThreadsDone; ThreadSysIds = ThreadIds + TotalThread; if ((Status = g_pDbgSystem-> SetCurrentProcessId(ProcIds[Proc])) != S_OK || FAILED(Status = g_pDbgSystem-> GetCurrentProcessExecutableName(ExeName, sizeof(ExeName), NULL)) || (Status = g_pDbgSystem->GetNumberThreads(&NumThread)) != S_OK || (Status = g_pDbgSystem-> GetThreadIdsByIndex(0, NumThread, ThreadIds, ThreadSysIds)) != S_OK) { goto CurProc; } ProcThreads[Proc] = NumThread; ThreadsDone += NumThread; ProcNames[Proc] = strlen(ExeName) + 1; if (ProcNames[Proc] > 1) { ExeStore = (PSTR)AddData(ProcNames[Proc]); if (ExeStore == NULL) { Status = E_OUTOFMEMORY; goto CurProc; } strcpy(ExeStore, ExeName); } } m_NumProcesses = NumProc; m_TotalThreads = TotalThread; Status = S_OK; CurProc: g_pDbgSystem->SetCurrentProcessId(CurProc); return Status; } BOOL PROCESS_THREAD_DATA::OnCreate(void) { if (!SINGLE_CHILDWIN_DATA::OnCreate()) { return FALSE; } m_hwndChild = CreateWindow( WC_TREEVIEW, // class name NULL, // title WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES, // style 0, // x 0, // y m_Size.cx, // width m_Size.cy, // height m_Win, // parent (HMENU) IDC_PROCESS_TREE, // control id g_hInst, // hInstance NULL); // user defined data if (!m_hwndChild) { return FALSE; } SetFont(FONT_FIXED); return TRUE; } LRESULT PROCESS_THREAD_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm) { LPNMTREEVIEW Tvn; HTREEITEM Sel; Tvn = (LPNMTREEVIEW)Lpm; if (Tvn->hdr.idFrom != IDC_PROCESS_TREE) { return FALSE; } switch(Tvn->hdr.code) { case NM_DBLCLK: TVHITTESTINFO HitTest; if (!GetCursorPos(&HitTest.pt)) { break; } ScreenToClient(m_hwndChild, &HitTest.pt); Sel = TreeView_HitTest(m_hwndChild, &HitTest); if (Sel != NULL && (HitTest.flags & TVHT_ONITEMLABEL)) { SetCurThreadFromProcessTreeItem(m_hwndChild, Sel); } break; } return FALSE; } void PROCESS_THREAD_DATA::OnUpdate(UpdateType Type) { if (Type != UPDATE_BUFFER && Type != UPDATE_EXEC) { return; } HRESULT Status; Status = UiLockForRead(); if (Status != S_OK) { return; } ULONG NumThread; ULONG Proc, Thread; PULONG ProcIds, ProcSysIds, ProcThreads, ProcNames; PULONG ThreadIds, ThreadSysIds; char Text[MAX_PATH + 64]; PSTR Names; HTREEITEM CurThreadItem = NULL; ProcIds = (PULONG)GetDataBuffer(); ProcSysIds = ProcIds + m_NumProcesses; ProcThreads = ProcSysIds + m_NumProcesses; ProcNames = ProcThreads + m_NumProcesses; ThreadIds = ProcNames + m_NumProcesses; ThreadSysIds = ThreadIds + m_TotalThreads; Names = (PSTR)(ThreadSysIds + m_TotalThreads); TreeView_DeleteAllItems(m_hwndChild); for (Proc = 0; Proc < m_NumProcesses; Proc++) { HTREEITEM ProcItem; TVINSERTSTRUCT Insert; sprintf(Text, "%03d:%x ", ProcIds[Proc], ProcSysIds[Proc]); if (ProcNames[Proc] > 1) { strcpy(Text + strlen(Text), Names); Names += strlen(Names) + 1; } Insert.hParent = TVI_ROOT; Insert.hInsertAfter = TVI_LAST; Insert.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM; Insert.item.pszText = Text; Insert.item.state = ProcIds[Proc] == g_CurProcessId ? TVIS_EXPANDED | TVIS_BOLD: 0; Insert.item.stateMask = TVIS_EXPANDED | TVIS_BOLD; // Parameter is the thread ID to set to select the given thread. Insert.item.lParam = (LPARAM)ThreadIds[0]; ProcItem = TreeView_InsertItem(m_hwndChild, &Insert); for (Thread = 0; Thread < ProcThreads[Proc]; Thread++) { HTREEITEM ThreadItem; sprintf(Text, "%03d:%x", ThreadIds[Thread], ThreadSysIds[Thread]); Insert.hParent = ProcItem; Insert.hInsertAfter = TVI_LAST; Insert.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM; Insert.item.pszText = Text; Insert.item.state = ProcIds[Proc] == g_CurProcessId && ThreadIds[Thread] == g_CurThreadId ? TVIS_BOLD : 0; Insert.item.stateMask = TVIS_BOLD; Insert.item.lParam = (LPARAM)ThreadIds[Thread]; ThreadItem = TreeView_InsertItem(m_hwndChild, &Insert); if (Insert.item.state & TVIS_BOLD) { CurThreadItem = ThreadItem; } } ThreadIds += ProcThreads[Proc]; ThreadSysIds += ProcThreads[Proc]; } if (CurThreadItem) { TreeView_Select(m_hwndChild, CurThreadItem, TVGN_CARET); } UnlockStateBuffer(this); } void PROCESS_THREAD_DATA::SetCurThreadFromProcessTreeItem(HWND Tree, HTREEITEM Sel) { TVITEM Item; Item.hItem = Sel; Item.mask = TVIF_CHILDREN | TVIF_PARAM; TreeView_GetItem(Tree, &Item); g_pUiSystem->SetCurrentThreadId((ULONG)Item.lParam); } // // // EDITWIN_DATA::EDITWIN_DATA(ULONG ChangeBy) : SINGLE_CHILDWIN_DATA(ChangeBy) { m_TextLines = 0; m_Highlights = NULL; } void EDITWIN_DATA::Validate() { SINGLE_CHILDWIN_DATA::Validate(); } void EDITWIN_DATA::SetFont(ULONG FontIndex) { SINGLE_CHILDWIN_DATA::SetFont(FontIndex); // Force the tabstop size to be recomputed // with the new font. SendMessage(m_hwndChild, EM_SETTABSTOPS, 1, (LPARAM)&g_TabWidth); } BOOL EDITWIN_DATA::CanSelectAll() { return TRUE; } void EDITWIN_DATA::SelectAll() { CHARRANGE Sel; Sel.cpMin = 0; Sel.cpMax = INT_MAX; SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel); } BOOL EDITWIN_DATA::OnCreate(void) { m_hwndChild = CreateWindowEx( WS_EX_CLIENTEDGE, // Extended style RICHEDIT_CLASS, // class name NULL, // title WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL | WS_HSCROLL | ES_AUTOHSCROLL | ES_READONLY | ES_MULTILINE, // style 0, // x m_ToolbarHeight, // y m_Size.cx, // width m_Size.cy - m_ToolbarHeight, // height m_Win, // parent (HMENU) 0, // control id g_hInst, // hInstance NULL); // user defined data if (m_hwndChild) { SetFont( FONT_FIXED ); SendMessage(m_hwndChild, EM_SETBKGNDCOLOR, FALSE, g_Colors[COL_PLAIN].Color); } return m_hwndChild != NULL; } LRESULT EDITWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm) { NMHDR* Hdr = (NMHDR*)Lpm; if (Hdr->code == EN_SAVECLIPBOARD) { // Indicate that the clipboard contents should // be kept alive. return 0; } else if (Hdr->code == EN_MSGFILTER) { MSGFILTER* Filter = (MSGFILTER*)Lpm; if (WM_SYSKEYDOWN == Filter->msg || WM_SYSKEYUP == Filter->msg || WM_SYSCHAR == Filter->msg) { // Force default processing for menu operations // so that the Alt-minus menu comes up. return 1; } } return 0; } void EDITWIN_DATA::OnDestroy(void) { EDIT_HIGHLIGHT* Next; while (m_Highlights != NULL) { Next = m_Highlights->Next; delete m_Highlights; m_Highlights = Next; } SINGLE_CHILDWIN_DATA::OnDestroy(); } void EDITWIN_DATA::UpdateColors(void) { SendMessage(m_hwndChild, EM_SETBKGNDCOLOR, FALSE, g_Colors[COL_PLAIN].Color); UpdateBpMarks(); } void EDITWIN_DATA::SetCurrentLineHighlight(ULONG Line) { // // Clear any other current line highlight in this window. // Also, oOnly one doc window can have a current IP highlight so if // this is a doc window getting a current IP highlight make // sure no other doc windows have a current IP highlight. // if (m_enumType == DOC_WINDOW && ULONG_MAX != Line) { RemoveActiveWinHighlights(1 << DOC_WINDOW, EHL_CURRENT_LINE); } else { RemoveAllHighlights(EHL_CURRENT_LINE); } if (ULONG_MAX != Line) { AddHighlight(Line, EHL_CURRENT_LINE); CHARRANGE crSet; // Set the caret on the current line. This automatically // scrolls the line into view if necessary and prevents // the view from snapping back to whatever old selection // there was. HWND OldFocus = ::SetFocus(m_hwndChild); crSet.cpMax = crSet.cpMin = (LONG) SendMessage(m_hwndChild, EM_LINEINDEX, Line, 0); SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&crSet); ::SetFocus(OldFocus); } } EDIT_HIGHLIGHT* EDITWIN_DATA::GetLineHighlighting(ULONG Line) { EDIT_HIGHLIGHT* Hl; for (Hl = m_Highlights; Hl != NULL; Hl = Hl->Next) { if (Hl->Line == Line) { return Hl; } } return NULL; } void EDITWIN_DATA::ApplyHighlight(EDIT_HIGHLIGHT* Hl) { CHARRANGE crOld; // Get the old selection SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM) &crOld); // // Compute the highlight information. // char Markers[LINE_MARKERS + 1]; CHARFORMAT2 cf; ULONG TextCol, BgCol; Markers[2] = 0; ZeroMemory(&cf, sizeof(cf)); cf.cbSize = sizeof(cf); cf.dwMask = CFM_COLOR | CFM_BACKCOLOR; if (Hl->Flags & EHL_CURRENT_LINE) { Markers[1] = '>'; switch(Hl->Flags & EHL_ANY_BP) { case EHL_ENABLED_BP: Markers[0] = 'B'; TextCol = COL_BP_CURRENT_LINE_TEXT; BgCol = COL_BP_CURRENT_LINE; break; case EHL_DISABLED_BP: Markers[0] = 'D'; TextCol = COL_BP_CURRENT_LINE_TEXT; BgCol = COL_BP_CURRENT_LINE; break; default: Markers[0] = ' '; TextCol = COL_CURRENT_LINE_TEXT; BgCol = COL_CURRENT_LINE; break; } } else { Markers[1] = ' '; switch(Hl->Flags & EHL_ANY_BP) { case EHL_ENABLED_BP: Markers[0] = 'B'; TextCol = COL_ENABLED_BP_TEXT; BgCol = COL_ENABLED_BP; break; case EHL_DISABLED_BP: Markers[0] = 'D'; TextCol = COL_DISABLED_BP_TEXT; BgCol = COL_DISABLED_BP; break; default: Markers[0] = ' '; TextCol = COL_PLAIN_TEXT; BgCol = COL_PLAIN; break; } } cf.crTextColor = g_Colors[TextCol].Color; cf.crBackColor = g_Colors[BgCol].Color; // // Select the line to be highlighted // CHARRANGE crNew; crNew.cpMin = (LONG)SendMessage(m_hwndChild, EM_LINEINDEX, Hl->Line, 0); if (g_LineMarkers) { // Replace the markers at the beginning of the line. crNew.cpMax = crNew.cpMin + 2; SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&crNew); SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)Markers); } // Color the line. crNew.cpMax = crNew.cpMin + (LONG) SendMessage(m_hwndChild, EM_LINELENGTH, crNew.cpMin, 0) + 1; if (g_LineMarkers) { crNew.cpMin += 2; } SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM) &crNew); SendMessage(m_hwndChild, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); // Restore the old selection SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM) &crOld); } EDIT_HIGHLIGHT* EDITWIN_DATA::AddHighlight(ULONG Line, ULONG Flags) { EDIT_HIGHLIGHT* Hl; // Search for an existing highlight record for the line. Hl = GetLineHighlighting(Line); if (Hl == NULL) { Hl = new EDIT_HIGHLIGHT; if (Hl == NULL) { return NULL; } Hl->Data = 0; Hl->Line = Line; Hl->Flags = 0; Hl->Next = m_Highlights; m_Highlights = Hl; } Hl->Flags |= Flags; ApplyHighlight(Hl); return Hl; } void EDITWIN_DATA::RemoveHighlight(ULONG Line, ULONG Flags) { EDIT_HIGHLIGHT* Hl; EDIT_HIGHLIGHT* Prev; // Search for an existing highlight record for the line. Prev = NULL; for (Hl = m_Highlights; Hl != NULL; Hl = Hl->Next) { if (Hl->Line == Line) { break; } Prev = Hl; } if (Hl == NULL) { return; } Hl->Flags &= ~Flags; ApplyHighlight(Hl); if (Hl->Flags == 0) { if (Prev == NULL) { m_Highlights = Hl->Next; } else { Prev->Next = Hl->Next; } delete Hl; } } void EDITWIN_DATA::RemoveAllHighlights(ULONG Flags) { EDIT_HIGHLIGHT* Hl; EDIT_HIGHLIGHT* Next; EDIT_HIGHLIGHT* Prev; Prev = NULL; for (Hl = m_Highlights; Hl != NULL; Hl = Next) { Next = Hl->Next; if (Hl->Flags & Flags) { Hl->Flags &= ~Flags; ApplyHighlight(Hl); if (Hl->Flags == 0) { if (Prev == NULL) { m_Highlights = Hl->Next; } else { Prev->Next = Hl->Next; } delete Hl; } else { Prev = Hl; } } else { Prev = Hl; } } } void EDITWIN_DATA::RemoveActiveWinHighlights(ULONG Types, ULONG Flags) { PLIST_ENTRY Entry = g_ActiveWin.Flink; while (Entry != &g_ActiveWin) { PEDITWIN_DATA WinData = (PEDITWIN_DATA) ACTIVE_WIN_ENTRY(Entry); if (Types & (1 << WinData->m_enumType)) { WinData->RemoveAllHighlights(Flags); } Entry = Entry->Flink; } } void EDITWIN_DATA::UpdateBpMarks(void) { // Empty implementation for derived classes // that do not show BP marks. } int EDITWIN_DATA::CheckForFileChanges(PCSTR File, FILETIME* LastWrite) { HANDLE Handle; Handle = CreateFile(File, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (Handle == INVALID_HANDLE_VALUE) { goto Changed; } FILETIME NewWrite; if (!GetFileTime(Handle, NULL, NULL, &NewWrite)) { if (!GetFileTime(Handle, &NewWrite, NULL, NULL)) { ZeroMemory(&NewWrite, sizeof(NewWrite)); } } CloseHandle(Handle); if (CompareFileTime(LastWrite, &NewWrite) == 0) { // No change. return IDCANCEL; } Changed: return QuestionBox(ERR_File_Has_Changed, MB_YESNO, File); } // // // SCRATCH_PAD_DATA::SCRATCH_PAD_DATA() : EDITWIN_DATA(16) { m_enumType = SCRATCH_PAD_WINDOW; } void SCRATCH_PAD_DATA::Validate() { EDITWIN_DATA::Validate(); Assert(SCRATCH_PAD_WINDOW == m_enumType); } void SCRATCH_PAD_DATA::Cut() { SendMessage(m_hwndChild, WM_CUT, 0, 0); } void SCRATCH_PAD_DATA::Paste() { SendMessage(m_hwndChild, WM_PASTE, 0, 0); } BOOL SCRATCH_PAD_DATA::OnCreate(void) { if (!EDITWIN_DATA::OnCreate()) { return FALSE; } SendMessage(m_hwndChild, EM_SETOPTIONS, ECOOP_AND, ~ECO_READONLY); return TRUE; } // // // DISASMWIN_DATA::DISASMWIN_DATA() : EDITWIN_DATA(2048) { m_enumType = DISASM_WINDOW; sprintf(m_OffsetExpr, "0x%I64x", g_EventIp); m_UpdateExpr = FALSE; m_FirstInstr = 0; m_LastInstr = 0; } void DISASMWIN_DATA::Validate() { EDITWIN_DATA::Validate(); Assert(DISASM_WINDOW == m_enumType); } HRESULT DISASMWIN_DATA::ReadState(void) { HRESULT Status; // Sample these values right away in case the UI changes them. ULONG LinesTotal = m_LineHeight; ULONG LinesBefore = LinesTotal / 2; ULONG Line; DEBUG_VALUE Value; if ((Status = g_pDbgControl->Evaluate(m_OffsetExpr, DEBUG_VALUE_INT64, &Value, NULL)) != S_OK) { return Status; } m_PrimaryInstr = Value.I64; // Reserve space at the beginning of the buffer to // store the line to offset mapping table. PULONG64 LineMap; Empty(); LineMap = (PULONG64)AddData(sizeof(ULONG64) * LinesTotal); if (LineMap == NULL) { return E_OUTOFMEMORY; } // We also need to allocate a temporary line map to // pass to the engine for filling. This can't be // the state buffer data since that may move as // output is generated. LineMap = new ULONG64[LinesTotal]; if (LineMap == NULL) { return E_OUTOFMEMORY; } g_OutStateBuf.SetBuffer(this); if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK) { delete LineMap; return Status; } Status = g_pOutCapControl-> OutputDisassemblyLines(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_OVERRIDE_MASK | DEBUG_OUTCTL_NOT_LOGGED, LinesBefore, LinesTotal, m_PrimaryInstr, DEBUG_DISASM_EFFECTIVE_ADDRESS | DEBUG_DISASM_MATCHING_SYMBOLS, &m_PrimaryLine, &m_FirstInstr, &m_LastInstr, LineMap); memcpy(m_Data, LineMap, sizeof(ULONG64) * LinesTotal); delete LineMap; if (Status != S_OK) { g_OutStateBuf.End(FALSE); return Status; } m_TextLines = LinesTotal; m_TextOffset = LinesTotal * sizeof(ULONG64); // The line map is generated with offsets followed by // invalid offsets for continuation lines. We want // the offsets to be on the last line of the disassembly // for a continuation set so move them down. // We don't want to move the offsets down to blank lines, // though, such as the blank lines that separate bundles // in IA64 disassembly. LineMap = (PULONG64)m_Data; PULONG64 LineMapEnd = LineMap + m_TextLines; PULONG64 SetStart; PSTR Text = (PSTR)m_Data + m_TextOffset; PSTR PrevText; while (LineMap < LineMapEnd) { if (*LineMap != DEBUG_INVALID_OFFSET) { SetStart = LineMap; for (;;) { PrevText = Text; Text = strchr(Text, '\n') + 1; LineMap++; if (LineMap >= LineMapEnd || *LineMap != DEBUG_INVALID_OFFSET || *Text == '\n') { break; } } LineMap--; Text = PrevText; if (LineMap > SetStart) { *LineMap = *SetStart; *SetStart = DEBUG_INVALID_OFFSET; } } LineMap++; Text = strchr(Text, '\n') + 1; } #ifdef DEBUG_DISASM LineMap = (PULONG64)m_Data; for (Line = 0; Line < m_TextLines; Line++) { DebugPrint("%d: %I64x\n", Line, LineMap[Line]); } #endif return g_OutStateBuf.End(TRUE); } BOOL DISASMWIN_DATA::CodeExprAtCaret(PSTR Expr, PULONG64 Offset) { BOOL Succ = FALSE; LRESULT LineChar; LONG Line; PULONG64 LineMap; if (UiLockForRead() != S_OK) { goto NoCode; } LineChar = SendMessage(m_hwndChild, EM_LINEINDEX, -1, 0); Line = (LONG)SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, LineChar); if (Line < 0 || (ULONG)Line >= m_TextLines) { goto UnlockNoCode; } ULONG64 LineOff; // Look up the offset in the line map. If it's part of // a multiline group move forward to the offset. LineMap = (PULONG64)m_Data; LineOff = LineMap[Line]; while ((ULONG)(Line + 1) < m_TextLines && LineOff == DEBUG_INVALID_OFFSET) { Line++; LineOff = LineMap[Line]; } if (Expr != NULL) { sprintf(Expr, "0x%I64x", LineOff); } if (Offset != NULL) { *Offset = LineOff; } Succ = TRUE; UnlockNoCode: UnlockStateBuffer(this); NoCode: return Succ; } BOOL DISASMWIN_DATA::OnCreate(void) { RECT Rect; int i; ULONG Height; Height = GetSystemMetrics(SM_CYVSCROLL) + 4 * GetSystemMetrics(SM_CYEDGE); m_Toolbar = CreateWindowEx(0, REBARCLASSNAME, NULL, WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN | RBS_VARHEIGHT | RBS_BANDBORDERS, 0, 0, m_Size.cx, Height, m_Win, (HMENU)ID_TOOLBAR, g_hInst, NULL); if (m_Toolbar == NULL) { return FALSE; } REBARINFO BarInfo; BarInfo.cbSize = sizeof(BarInfo); BarInfo.fMask = 0; BarInfo.himl = NULL; SendMessage(m_Toolbar, RB_SETBARINFO, 0, (LPARAM)&BarInfo); m_ToolbarEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL, 0, 0, 18 * m_Font->Metrics.tmAveCharWidth, Height, m_Toolbar, (HMENU)IDC_EDIT_OFFSET, g_hInst, NULL); if (m_ToolbarEdit == NULL) { return FALSE; } SendMessage(m_ToolbarEdit, WM_SETFONT, (WPARAM)m_Font->Font, 0); SendMessage(m_ToolbarEdit, EM_LIMITTEXT, sizeof(m_OffsetExpr) - 1, 0); GetClientRect(m_ToolbarEdit, &Rect); REBARBANDINFO BandInfo; BandInfo.cbSize = sizeof(BandInfo); BandInfo.fMask = RBBIM_STYLE | RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE; BandInfo.fStyle = RBBS_FIXEDSIZE; BandInfo.lpText = "Offset:"; BandInfo.hwndChild = m_ToolbarEdit; BandInfo.cxMinChild = Rect.right - Rect.left; BandInfo.cyMinChild = Rect.bottom - Rect.top; SendMessage(m_Toolbar, RB_INSERTBAND, -1, (LPARAM)&BandInfo); // If the toolbar is allowed to shrink too small it hangs // while resizing. Just let it clip off below a certain width. m_MinToolbarWidth = BandInfo.cxMinChild * 2; PSTR PrevText = "Previous"; m_PreviousButton = AddButtonBand(m_Toolbar, PrevText, PrevText, IDC_DISASM_PREVIOUS); m_NextButton = AddButtonBand(m_Toolbar, "Next", PrevText, IDC_DISASM_NEXT); if (m_PreviousButton == NULL || m_NextButton == NULL) { return FALSE; } // Maximize the space for the offset expression. SendMessage(m_Toolbar, RB_MAXIMIZEBAND, 0, FALSE); GetClientRect(m_Toolbar, &Rect); m_ToolbarHeight = Rect.bottom - Rect.top; m_ShowToolbar = TRUE; if (!EDITWIN_DATA::OnCreate()) { return FALSE; } // Suppress the scroll bar as the text is always // fitted to the window size. SendMessage(m_hwndChild, EM_SHOWSCROLLBAR, SB_VERT, FALSE); SendMessage(m_hwndChild, EM_SETEVENTMASK, 0, ENM_KEYEVENTS); return TRUE; } LRESULT DISASMWIN_DATA::OnCommand(WPARAM Wpm, LPARAM Lpm) { switch(LOWORD(Wpm)) { case IDC_EDIT_OFFSET: if (HIWORD(Wpm) == EN_CHANGE) { // This message is sent on every keystroke // which causes a bit too much updating. // Set up a timer to trigger the actual // update in half a second. SetTimer(m_Win, IDC_EDIT_OFFSET, EDIT_DELAY, NULL); m_UpdateExpr = TRUE; } break; case IDC_DISASM_PREVIOUS: ScrollLower(); break; case IDC_DISASM_NEXT: ScrollHigher(); break; } return 0; } void DISASMWIN_DATA::OnSize(void) { EDITWIN_DATA::OnSize(); // Force buffer to refill for new line count. UiRequestRead(); } void DISASMWIN_DATA::OnTimer(WPARAM TimerId) { if (TimerId == IDC_EDIT_OFFSET && m_UpdateExpr) { m_UpdateExpr = FALSE; GetWindowText(m_ToolbarEdit, m_OffsetExpr, sizeof(m_OffsetExpr)); UiRequestRead(); } } LRESULT DISASMWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm) { MSGFILTER* Filter = (MSGFILTER*)Lpm; if (Filter->nmhdr.code != EN_MSGFILTER) { return EDITWIN_DATA::OnNotify(Wpm, Lpm); } if (Filter->msg == WM_KEYDOWN) { switch(Filter->wParam) { case VK_UP: { CHARRANGE range; SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM) &range); if (!SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0)) { // up arrow on top line, scroll ScrollLower(); return 1; } break; } case VK_DOWN: { CHARRANGE range; int MaxLine; SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM) &range); MaxLine = (int) SendMessage(m_hwndChild, EM_GETLINECOUNT, 0, 0); if (MaxLine == (1+SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0))) { // down arrow on bottom line, scroll ScrollHigher(); return 1; } break; } case VK_PRIOR: ScrollLower(); return 1; case VK_NEXT: ScrollHigher(); return 1; } } else if (WM_SYSKEYDOWN == Filter->msg || WM_SYSKEYUP == Filter->msg || WM_SYSCHAR == Filter->msg) { // Force default processing for menu operations // so that the Alt-minus menu comes up. return 1; } return 0; } void DISASMWIN_DATA::OnUpdate(UpdateType Type) { if (Type == UPDATE_BP || Type == UPDATE_END_SESSION) { UpdateBpMarks(); return; } else if (Type != UPDATE_BUFFER) { return; } HRESULT Status; Status = UiLockForRead(); if (Status == S_OK) { PULONG64 LineMap; ULONG Line; if (!g_LineMarkers) { SendMessage(m_hwndChild, WM_SETTEXT, 0, (LPARAM)m_Data + m_TextOffset); } else { SendMessage(m_hwndChild, WM_SETTEXT, 0, (LPARAM)""); PSTR Text = (PSTR)m_Data + m_TextOffset; for (;;) { SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)" "); PSTR NewLine = strchr(Text, '\n'); if (NewLine != NULL) { *NewLine = 0; } SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)Text); if (NewLine == NULL) { break; } SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)"\n"); *NewLine = '\n'; Text = NewLine + 1; } } // Highlight the last line of multiline disassembly. LineMap = (PULONG64)m_Data; Line = m_PrimaryLine; while (Line + 1 < m_TextLines && LineMap[Line] == DEBUG_INVALID_OFFSET) { Line++; } SetCurrentLineHighlight(Line); UnlockStateBuffer(this); UpdateBpMarks(); EnableWindow(m_PreviousButton, m_FirstInstr != m_PrimaryInstr); EnableWindow(m_NextButton, m_LastInstr != m_PrimaryInstr); } else { SendLockStatusMessage(m_hwndChild, WM_SETTEXT, Status); RemoveCurrentLineHighlight(); } } void DISASMWIN_DATA::UpdateBpMarks(void) { if (m_TextLines == 0 || UiLockForRead() != S_OK) { return; } if (g_BpBuffer->UiLockForRead() != S_OK) { UnlockStateBuffer(this); return; } SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0); // Remove existing BP highlights. RemoveAllHighlights(EHL_ANY_BP); // // Highlight every line that matches a breakpoint. // PULONG64 LineMap = (PULONG64)m_Data; BpBufferData* BpData = (BpBufferData*)g_BpBuffer->GetDataBuffer(); ULONG Line; BpStateType State; for (Line = 0; Line < m_TextLines; Line++) { if (*LineMap != DEBUG_INVALID_OFFSET) { State = IsBpAtOffset(BpData, *LineMap, NULL); if (State != BP_NONE) { AddHighlight(Line, State == BP_ENABLED ? EHL_ENABLED_BP : EHL_DISABLED_BP); } } LineMap++; } SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndChild, NULL, TRUE); UnlockStateBuffer(g_BpBuffer); UnlockStateBuffer(this); } void DISASMWIN_DATA::SetCurInstr(ULONG64 Offset) { // Any pending user update is now irrelevant. m_UpdateExpr = FALSE; sprintf(m_OffsetExpr, "0x%I64x", Offset); // Force engine to update buffer. UiRequestRead(); } void RicheditFind(HWND Edit, PTSTR Text, ULONG Flags, CHARRANGE* SaveSel, PULONG SaveFlags, BOOL HideSel) { if (Text == NULL) { // Clear last find. if (SaveSel->cpMax >= SaveSel->cpMin) { if (*SaveFlags & FR_DOWN) { SaveSel->cpMin = SaveSel->cpMax; } else { SaveSel->cpMax = SaveSel->cpMin; } if (HideSel) { SendMessage(Edit, EM_SETOPTIONS, ECOOP_AND, ~ECO_NOHIDESEL); } SendMessage(Edit, EM_EXSETSEL, 0, (LPARAM)SaveSel); SendMessage(Edit, EM_SCROLLCARET, 0, 0); SaveSel->cpMin = 1; SaveSel->cpMax = 0; } } else { LRESULT Match; FINDTEXTEX Find; SendMessage(Edit, EM_EXGETSEL, 0, (LPARAM)&Find.chrg); if (Flags & FR_DOWN) { Find.chrg.cpMax = LONG_MAX; } else { Find.chrg.cpMax = 0; } Find.lpstrText = Text; Match = SendMessage(Edit, EM_FINDTEXTEX, Flags, (LPARAM)&Find); if (Match != -1) { *SaveSel = Find.chrgText; *SaveFlags = Flags; if (HideSel) { SendMessage(Edit, EM_SETOPTIONS, ECOOP_OR, ECO_NOHIDESEL); } SendMessage(Edit, EM_EXSETSEL, 0, (LPARAM)SaveSel); SendMessage(Edit, EM_SCROLLCARET, 0, 0); } else { InformationBox(ERR_No_More_Matches, Text); SetFocus(g_FindDialog); } } } #undef DEFINE_GET_WINDATA #undef ASSERT_CLASS_TYPE #ifndef DBG #define ASSERT_CLASS_TYPE(p, ct) ((VOID)0) #else #define ASSERT_CLASS_TYPE(p, ct) if (p) { AssertType(*p, ct); } #endif #define DEFINE_GET_WINDATA(ClassType, FuncName) \ ClassType * \ Get##FuncName##WinData( \ HWND hwnd \ ) \ { \ ClassType *p = (ClassType *) \ GetWindowLongPtr(hwnd, GWLP_USERDATA); \ \ ASSERT_CLASS_TYPE(p, ClassType); \ \ return p; \ } #include "fncdefs.h" #undef DEFINE_GET_WINDATA #undef ASSERT_CLASS_TYPE