/*++ Copyright (c) 1992-2002 Microsoft Corporation Module Name: Memwin.cpp Abstract: This module contains the main line code for display of multiple memory windows and the subclassed win proc to handle editing, display, etc. --*/ #include "precomp.hxx" #pragma hdrstop _INTERFACE_TYPE_NAMES rgInterfaceTypeNames[MaximumInterfaceType] = { { Internal, "Internal" }, { Isa, "Isa" }, { Eisa, "Eisa" }, { MicroChannel, "MicroChannel" }, { TurboChannel, "TurboChannel" }, { PCIBus, "PCIBus" }, { VMEBus, "VMEBus" }, { NuBus, "NuBus" }, { PCMCIABus, "PCMCIABus" }, { CBus, "CBus" }, { MPIBus, "MPIBus" }, { MPSABus, "MPSABus" }, { ProcessorInternal, "ProcessorInternal" }, { InternalPowerBus, "InternalPowerBus" }, { PNPISABus, "PNPISABus" }, { PNPBus, "PNPBus" } }; _BUS_TYPE_NAMES rgBusTypeNames[MaximumBusDataType] = { { Cmos, "Cmos" }, { EisaConfiguration, "EisaConfiguration" }, { Pos, "Pos" }, { CbusConfiguration, "CbusConfiguration" }, { PCIConfiguration, "PCIConfiguration" }, { VMEConfiguration, "VMEConfiguration" }, { NuBusConfiguration, "NuBusConfiguration" }, { PCMCIAConfiguration, "PCMCIAConfiguration" }, { MPIConfiguration, "MPIConfiguration" }, { MPSAConfiguration, "MPSAConfiguration" }, { PNPISAConfiguration, "PNPISAConfiguration" }, { SgiInternalConfiguration, "SgiInternalConfiguration" } }; PSTR g_MemTypeNames[] = { "Virtual:", "Physical:", "Control:", "I/O:", "MSR:", "Bus data:" }; // // // MEMWIN_DATA::MEMWIN_DATA() : EDITWIN_DATA(512) { m_enumType = MEM_WINDOW; ZeroMemory(&m_GenMemData, sizeof(m_GenMemData)); strcpy(m_OffsetExpr, FormatAddr64(g_EventIp)); m_GenMemData.memtype = VIRTUAL_MEM_TYPE; m_GenMemData.nDisplayFormat = 3; m_Columns = 4; m_WindowDataSize = 0; } void MEMWIN_DATA::Validate() { EDITWIN_DATA::Validate(); Assert(MEM_WINDOW == m_enumType); } HRESULT MEMWIN_DATA::ReadState(void) { HRESULT Status; ULONG DataNeeded; PVOID Data; ULONG64 Offset; DEBUG_VALUE Value; ULONG i; // Evaluate offset expression. if ((Status = g_pDbgControl->Evaluate(m_OffsetExpr, DEBUG_VALUE_INT64, &Value, NULL)) != S_OK) { return Status; } Offset = Value.I64; // Compute how much data to retrieve. We don't want to // create a big matrix of memtype/display format so just // ask for a chunk of data big enough for any display format. DataNeeded = m_LineHeight * m_Columns * 2 * sizeof(ULONG64); Empty(); Data = AddData(DataNeeded); if (Data == NULL) { return E_OUTOFMEMORY; } ULONG Read; switch(m_GenMemData.memtype) { default: Assert(!"Unhandled condition"); Status = E_FAIL; break; case PHYSICAL_MEM_TYPE: Status = g_pDbgData->ReadPhysical(Offset, Data, DataNeeded, &Read ); break; case VIRTUAL_MEM_TYPE: Status = g_pDbgData->ReadVirtual(Offset, Data, DataNeeded, &Read ); break; case CONTROL_MEM_TYPE: Status = g_pDbgData->ReadControl(m_GenMemData.any.control.Processor, Offset, Data, DataNeeded, &Read ); break; case IO_MEM_TYPE: Status = g_pDbgData->ReadIo(m_GenMemData.any.io.interface_type, m_GenMemData.any.io.BusNumber, m_GenMemData.any.io.AddressSpace, Offset, Data, DataNeeded, &Read ); break; case MSR_MEM_TYPE: Read = 0; for (i = 0; i < DataNeeded / sizeof(ULONG64); i++) { if ((Status = g_pDbgData->ReadMsr((ULONG)Offset + i, (PULONG64)Data + i )) != S_OK) { // Assume an error means we've run out of MSRs to // read. If some were read, don't consider it an error. if (Read > 0) { Status = S_OK; } break; } Read += sizeof(ULONG64); } break; case BUS_MEM_TYPE: Status = g_pDbgData->ReadBusData(m_GenMemData.any.bus.bus_type, m_GenMemData.any.bus.BusNumber, m_GenMemData.any.bus.SlotNumber, (ULONG)Offset, Data, DataNeeded, &Read ); break; } if (Status == S_OK) { // Trim data back if read didn't get everything. RemoveTail(DataNeeded - Read); m_OffsetRead = Offset; } return Status; } BOOL MEMWIN_DATA::HasEditableProperties() { return TRUE; } BOOL MEMWIN_DATA::EditProperties() /*++ Returns TRUE - If properties were edited FALSE - If nothing was changed --*/ { if (g_TargetClass != DEBUG_CLASS_UNINITIALIZED) { INT_PTR Res = DisplayOptionsPropSheet(GetParent(m_hwndChild), g_hInst, m_GenMemData.memtype ); if (IDOK == Res) { UpdateOptions(); return TRUE; // Properties have been changed } } MessageBeep(0); return FALSE; // No Debuggee or User Cancel out. } BOOL MEMWIN_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); REBARBANDINFO BandInfo; BandInfo.cbSize = sizeof(BandInfo); BandInfo.fMask = RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE; 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); 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); m_FormatCombo = CreateWindowEx(0, "COMBOBOX", NULL, WS_VISIBLE | WS_CHILD | WS_VSCROLL | CBS_SORT | CBS_DROPDOWNLIST, 0, 0, 15 * m_Font->Metrics.tmAveCharWidth, (g_nMaxNumFormatsMemWin * m_Font->Metrics.tmHeight / 2), m_Toolbar, (HMENU)IDC_COMBO_DISPLAY_FORMAT, g_hInst, NULL); if (m_FormatCombo == NULL) { return FALSE; } SendMessage(m_FormatCombo, WM_SETFONT, (WPARAM)m_Font->Font, 0); for (i = 0; i < g_nMaxNumFormatsMemWin; i++) { LRESULT Idx; // The format strings will be sorted so mark them with // their true index for retrieval when selected. Idx = SendMessage(m_FormatCombo, CB_ADDSTRING, 0, (LPARAM)g_FormatsMemWin[i].lpszDescription); SendMessage(m_FormatCombo, CB_SETITEMDATA, (WPARAM)Idx, i); } GetClientRect(m_FormatCombo, &Rect); BandInfo.lpText = "Display format:"; BandInfo.hwndChild = m_FormatCombo; BandInfo.cxMinChild = Rect.right - Rect.left; BandInfo.cyMinChild = Rect.bottom - Rect.top; SendMessage(m_Toolbar, RB_INSERTBAND, -1, (LPARAM)&BandInfo); PSTR PrevText = "Previous"; m_PreviousButton = AddButtonBand(m_Toolbar, PrevText, PrevText, IDC_MEM_PREVIOUS); m_NextButton = AddButtonBand(m_Toolbar, "Next", PrevText, IDC_MEM_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 + GetSystemMetrics(SM_CYEDGE); m_ShowToolbar = TRUE; if (!EDITWIN_DATA::OnCreate()) { return FALSE; } SendMessage(m_hwndChild, EM_SETBKGNDCOLOR, FALSE, g_Colors[COL_PLAIN].Color); SendMessage(m_hwndChild, EM_SETEVENTMASK, 0, ENM_KEYEVENTS); UpdateOptions(); return TRUE; } LRESULT MEMWIN_DATA::OnCommand( WPARAM wParam, LPARAM lParam ) { switch(LOWORD(wParam)) { case IDC_EDIT_OFFSET: if (HIWORD(wParam) == 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_COMBO_DISPLAY_FORMAT: if (HIWORD(wParam) == CBN_SELCHANGE) { LRESULT Sel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); if (Sel != CB_ERR) { m_GenMemData.nDisplayFormat = (int) SendMessage((HWND)lParam, CB_GETITEMDATA, (WPARAM)Sel, 0); UpdateOptions(); UiRequestRead(); } } break; case IDC_MEM_PREVIOUS: ScrollLower(); break; case IDC_MEM_NEXT: ScrollHigher(); break; } return 0; } void MEMWIN_DATA::OnSize(void) { EDITWIN_DATA::OnSize(); // Force buffer to refill for new line count. UiRequestRead(); } void MEMWIN_DATA::OnTimer(WPARAM TimerId) { if (TimerId == IDC_EDIT_OFFSET && m_UpdateExpr) { m_UpdateExpr = FALSE; if (SendMessage(m_ToolbarEdit, EM_GETMODIFY, 0,0)) { GetWindowText(m_ToolbarEdit, m_OffsetExpr, sizeof(m_OffsetExpr)); SendMessage(m_ToolbarEdit, EM_SETMODIFY, 0,0); UiRequestRead(); } // KillTimer(m_Win, IDC_EDIT_OFFSET); } } LRESULT MEMWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm) { LPNMHDR Hdr = (LPNMHDR)Lpm; switch(Hdr->code) { case RBN_HEIGHTCHANGE: PostMessage(m_Win, WU_RECONFIGURE, 0, 0); break; case EN_MSGFILTER: MSGFILTER* Filter = (MSGFILTER*)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 TRUE; } 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 TRUE; } break; } case VK_PRIOR: ScrollLower(); return TRUE; case VK_NEXT: ScrollHigher(); return TRUE; case VK_LEFT: case VK_RIGHT: break; case VK_DELETE: MessageBeep(0); return TRUE; default: // Allow default processing of everything else return TRUE; } } else if (Filter->msg == WM_KEYUP) { return TRUE; } if (ES_READONLY & GetWindowLongPtr(m_hwndChild, GWL_STYLE)) { break; } if (Filter->msg == WM_CHAR) { switch(Filter->wParam) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': { CHARRANGE value; ULONG charIndex; ULONG64 Address; CHAR writeval[2] = {0}; writeval[0] = (CHAR) tolower((CHAR) Filter->wParam); Address = GetAddressOfCurValue(&charIndex, &value); if (Address) { TEXTRANGE textRange; SendMessage(m_hwndChild, EM_SETSEL, charIndex, charIndex+1); SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM) writeval); textRange.chrg = value; textRange.lpstrText = &m_ValueExpr[0]; if (SendMessage(m_hwndChild, EM_GETTEXTRANGE, 0, (LPARAM) &textRange)) { m_ValueExpr[charIndex - value.cpMin] = writeval[0]; WriteValue(Address); SendMessage(m_hwndChild, EM_SETSEL, charIndex+1, charIndex+1); return TRUE; } } } default: MessageBeep(0); return TRUE; } } break; } return EDITWIN_DATA::OnNotify(Wpm, Lpm); } void MEMWIN_DATA::OnUpdate( UpdateType Type ) { if (Type != UPDATE_BUFFER) { return; } HRESULT Status; Status = UiLockForRead(); if (Status == S_OK) { ULONG charIndex; SendMessage(m_hwndChild, EM_GETSEL, (WPARAM) &charIndex, NULL); SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0); CHARRANGE Sel; // Select everything so it's all replaced. Sel.cpMin = 0; Sel.cpMax = -1; SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel); TCHAR Buf[64]; TCHAR CharBuf[64]; TCHAR* ColChar; ULONG Row, Col; ULONG64 Offset; ULONG64 DataEnd; PUCHAR Data; ULONG Bytes; _FORMATS_MEM_WIN* Fmt = g_FormatsMemWin + m_GenMemData.nDisplayFormat; Offset = m_OffsetRead; Data = (PUCHAR)m_Data; DataEnd = Offset + m_DataUsed; Bytes = (Fmt->cBits + 7) / 8; for (Row = 0; Row < m_LineHeight; Row++) { SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)FormatAddr64(Offset)); ColChar = CharBuf; *ColChar++ = ' '; *ColChar++ = ' '; for (Col = 0; Col < m_Columns; Col++) { if (Offset < DataEnd) { _tcscpy(Buf, _T(" ")); // If the formatting succeeds, // Buf contains the formatted data. if (!CPFormatMemory(Buf + 1, (DWORD)min(_tsizeof(Buf) - 1, Fmt->cchMax + 1), Data, Fmt->cBits, Fmt->fmtType, Fmt->radix)) { // Else we don't know what to format for (UINT uTmp = 0; uTmp < Bytes; uTmp++) { m_AllowWrite = FALSE; _tcscat(Buf + 1, _T("??")); } } if (Fmt->fTwoFields) { if (!CPFormatMemory(ColChar, 1, Data, 8, fmtAscii, 0)) { *ColChar = '?'; } ColChar++; } } else { m_AllowWrite = FALSE; _tcscpy(Buf, _T(" ????????")); *ColChar++ = '?'; } SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)Buf); Data += Bytes; Offset += Bytes; } if (Fmt->fTwoFields) { *ColChar = 0; SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)CharBuf); } // Don't complete the last line to avoid leaving // a blank line at the bottom. if (Row < m_LineHeight - 1) { SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)"\n"); } } m_WindowDataSize = (ULONG)(Offset - m_OffsetRead); UnlockStateBuffer(this); SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndChild, NULL, TRUE); SendMessage(m_hwndChild, EM_SETSEL, charIndex, charIndex); } else { SendLockStatusMessage(m_hwndChild, WM_SETTEXT, Status); } } void MEMWIN_DATA::ScrollLower(void) { ULONG64 Offs = m_OffsetRead; if (Offs >= m_WindowDataSize) { Offs -= m_WindowDataSize; } else { Offs = 0; } sprintf(m_OffsetExpr, "0x%I64x", Offs); UiRequestRead(); } void MEMWIN_DATA::ScrollHigher(void) { ULONG64 Offs = m_OffsetRead; if (Offs + m_WindowDataSize > Offs) { Offs += m_WindowDataSize; } else { Offs = (ULONG64)-1 - m_WindowDataSize; } sprintf(m_OffsetExpr, "0x%I64x", Offs); UiRequestRead(); } void MEMWIN_DATA::UpdateOptions(void) { REBARBANDINFO BandInfo; BandInfo.cbSize = sizeof(BandInfo); BandInfo.fMask = RBBIM_TEXT; BandInfo.lpText = g_MemTypeNames[m_GenMemData.memtype]; SendMessage(m_Toolbar, RB_SETBANDINFO, 0, (LPARAM)&BandInfo); SetWindowText(m_ToolbarEdit, m_OffsetExpr); m_AllowWrite = (m_GenMemData.memtype == PHYSICAL_MEM_TYPE || m_GenMemData.memtype == VIRTUAL_MEM_TYPE) && ((g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtUInt || (g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtInt || (g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtAddress ) && g_FormatsMemWin[m_GenMemData.nDisplayFormat].radix == 16; SendMessage(m_hwndChild, EM_SETREADONLY, !m_AllowWrite, 0); for (LONG Idx = 0; Idx < g_nMaxNumFormatsMemWin; Idx++) { if ((LONG)SendMessage(m_FormatCombo, CB_GETITEMDATA, Idx, 0) == m_GenMemData.nDisplayFormat) { SendMessage(m_FormatCombo, CB_SETCURSEL, Idx, 0); break; } } switch(m_GenMemData.memtype) { case MSR_MEM_TYPE: m_Columns = 1; break; default: if ((g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtBit) { m_Columns = 8; } else if ((g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtAscii || (g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtUnicode || g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits == 8) { m_Columns = 16; } else if (g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits == 16) { m_Columns = 8; } else if (g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits > 64) { m_Columns = 2; } else { m_Columns = 4; } break; } } void MEMWIN_DATA::WriteValue( ULONG64 Offset ) { if (!m_AllowWrite) { return; } ULONG64 Data; ULONG Size; DEBUG_VALUE Value; // Evaluate value expression. if (g_pDbgControl->Evaluate(m_ValueExpr, DEBUG_VALUE_INT64, &Value, NULL) != S_OK) { return; } Data = Value.I64; Size = g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits / 8; UIC_WRITE_DATA_DATA* WriteData; WriteData = StartStructCommand(UIC_WRITE_DATA); if (WriteData == NULL) { return; } // Fill in WriteData members. memcpy(WriteData->Data, &Data, Size); WriteData->Length = Size; WriteData->Offset = Offset; WriteData->Type = m_GenMemData.memtype; WriteData->Any = m_GenMemData.any; FinishCommand(); } ULONG64 MEMWIN_DATA::GetAddressOfCurValue( PULONG pCharIndex, CHARRANGE *pCRange ) { CHARRANGE range; ULONG CurLine, FirstLineChar, CurCol; SendMessage(m_hwndChild, EM_EXGETSEL, NULL, (LPARAM) &range); CurLine = (ULONG)SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0); FirstLineChar = (ULONG)SendMessage(m_hwndChild, EM_LINEINDEX, CurLine, 0); CurCol = range.cpMin - FirstLineChar; ULONG Length; PCHAR pLineTxt = (PCHAR) malloc(Length = ((ULONG)SendMessage(m_hwndChild, EM_LINELENGTH, FirstLineChar, 0) + 2)); if (!pLineTxt) { return 0; } Assert(Length >= CurCol); ZeroMemory(pLineTxt, Length); // Assert (Length = (ULONG) SendMessage(m_hwndChild, EM_GETLINE, (WPARAM) CurLine, (LPARAM) pLineTxt)); TEXTRANGE textrange; textrange.chrg.cpMin = FirstLineChar; textrange.chrg.cpMax = FirstLineChar + Length-2; textrange.lpstrText = (LPSTR) pLineTxt; SendMessage(m_hwndChild, EM_GETTEXTRANGE, 0, (LPARAM) &textrange); ULONG ValueCol=0, Index=0, ValueIndex=0; while (pLineTxt[CurCol] == ' ') { CurCol++; } while (Index < CurCol) { if (pLineTxt[Index] == ' ') { if (ValueIndex != Index) { ValueCol++; } ValueIndex = Index+1; } ++Index; } if (!ValueIndex || !pLineTxt[CurCol]) // cursor on address column { free (pLineTxt); return 0; } ULONG Bytes; Bytes = (g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits + 7) / 8; ULONG64 Offset; Offset = m_OffsetRead + (CurLine * Bytes * m_Columns) + (ValueCol - 1)*Bytes; for (Index = ValueIndex; pLineTxt[Index] && pLineTxt[Index] != ' '; Index++) ; memcpy(m_ValueExpr, pLineTxt+ValueIndex, Index - ValueIndex); m_ValueExpr[Index-ValueIndex]=0; free (pLineTxt); if (pCharIndex) { *pCharIndex = FirstLineChar + CurCol; } if (pCRange) { pCRange->cpMin = FirstLineChar + CurCol - (CurCol - ValueIndex); pCRange->cpMax = pCRange->cpMin + Index - ValueIndex; } return Offset; }