windows-nt/Source/XPSP1/NT/sdktools/debuggers/windbg/memwin.cpp
2020-09-26 16:20:57 +08:00

904 lines
25 KiB
C++

/*++
Copyright (c) 1992-2000 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 = 2;
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 DataSize;
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;
}
// Switch background color back to window default as
// this window does not use custom colors.
SendMessage(m_hwndChild, EM_SETBKGNDCOLOR, FALSE,
GetSysColor(COLOR_WINDOW));
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)
{
char Buffer[MAX_OFFSET_EXPR];
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::UpdateColors(void)
{
// Do not change colors.
}
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) == 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;
}