From 2b64073ff51ce4f4ab295bbca5ac71bfe324ee67 Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Mon, 25 Sep 2023 22:43:28 +0200 Subject: [PATCH] Show changed bytes in hex view fixes #3 --- src/MemView.cpp | 120 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 22 deletions(-) diff --git a/src/MemView.cpp b/src/MemView.cpp index c294ad2..f0ff099 100644 --- a/src/MemView.cpp +++ b/src/MemView.cpp @@ -2,7 +2,7 @@ * PROJECT: MemView * LICENSE: MIT (https://spdx.org/licenses/MIT) * PURPOSE: The hex-dump window - * COPYRIGHT: Copyright 2021 Mark Jansen + * COPYRIGHT: Copyright 2021-2023 Mark Jansen */ #include "MemView.h" @@ -10,6 +10,7 @@ #include extern HINSTANCE g_hInst; +const UINT_PTR kUpdateTimerId = 0x1ea4; // http://www.catch22.net/tuts/scrollbars-scrolling @@ -20,8 +21,8 @@ struct MemView , ProcessHandle(NULL), DisplayLines(0), PerLine(16) , ScrollMax(0), ScrollPos(0) , vMax(0), vPos(0), vInc(0) - , Dirty(true) - , FontX(0), FontY(0), FontNL(0) + , Dirty(true), Resizing(true), Scrolling(false) + , FontX(0), FontY(0) { updateTotal(); SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &WheelLines, 0); @@ -50,23 +51,49 @@ struct MemView int WheelLines; bool Dirty; + bool Resizing; + bool Scrolling; std::vector Buffer; + std::vector Changed; int FontX; int FontY; - int FontNL; }; static WCHAR Hex2Str[] = L"0123456789abcdef"; -void ToBuffer(WCHAR* Buffer, size_t Cch, unsigned char* Data, SIZE_T DataLen, SIZE_T PerLine, PBYTE Address) +static void DrawLine(HDC hdc, int x, int y, WCHAR* Buffer, size_t Cch, const std::vector& DataBuffer, const std::vector& Changed, SIZE_T StartAt, SIZE_T DataLen, SIZE_T PerLine, PBYTE Address) { StringCchPrintfW(Buffer, Cch, L"%p: ", Address); WCHAR* p = Buffer + wcslen(Buffer); + WCHAR* Current = Buffer; + bool CurrentChanged = false; + const unsigned char* Data = DataBuffer.data() + StartAt; + for(size_t n = 0; n < PerLine; ++n) { if (n < DataLen) { + // Check of we crossed the boundary from changed to unchanged + if (CurrentChanged != Changed[StartAt + n]) + { + // Do we have new text? + if (Current != p) + { + // Draw it out first + TextOutW(hdc, x, y, Current, (int)(p - Current)); + // Calculate the width + RECT r = {0}; + DrawTextW(hdc, Current, (int)(p - Current), &r, DT_CALCRECT); + x += r.right; + // Save the new starting position + Current = p; + } + CurrentChanged = Changed[StartAt + n]; + COLORREF col = GetTextColor(hdc); + SetTextColor(hdc, CurrentChanged ? RGB(255, 0, 0) : RGB(0,0,0)); + } + *(p++) = Hex2Str[Data[n] >> 4]; *(p++) = Hex2Str[Data[n] & 0xf]; *(p++) = ' '; @@ -84,6 +111,22 @@ void ToBuffer(WCHAR* Buffer, size_t Cch, unsigned char* Data, SIZE_T DataLen, SI { if (n < DataLen) { + // Check of we crossed the boundary from changed to unchanged + if (CurrentChanged != Changed[StartAt + n]) + { + if (Current != p) + { + TextOutW(hdc, x, y, Current, (int)(p - Current)); + RECT r = {0}; + DrawTextW(hdc, Current, (int)(p - Current), &r, DT_CALCRECT); + x += r.right; + Current = p; + } + CurrentChanged = Changed[StartAt + n]; + COLORREF col = GetTextColor(hdc); + SetTextColor(hdc, CurrentChanged ? RGB(255, 0, 0) : RGB(0,0,0)); + } + if (isprint(Data[n])) *(p++) = (char)Data[n]; else @@ -91,9 +134,15 @@ void ToBuffer(WCHAR* Buffer, size_t Cch, unsigned char* Data, SIZE_T DataLen, SI } } *p = 0; + + // Do we have any text left over? + if (Current != p) + TextOutW(hdc, x, y, Current, (int)(p - Current)); + + SetTextColor(hdc, RGB(0,0,0)); } -static void ReadMemory(HWND hwnd, MemView* mv) +static void ReadMemory(HWND hwnd, MemView* mv, bool IsWmPaint) { SIZE_T Requested = mv->Buffer.size(); SIZE_T Read = 0; @@ -106,11 +155,28 @@ static void ReadMemory(HWND hwnd, MemView* mv) Requested -= ((Requested + start)-end); std::vector buf = mv->Buffer; + std::vector changed = mv->Changed; ReadProcessMemory(mv->ProcessHandle, start, mv->Buffer.data(), Requested, &Read); mv->Dirty = false; + mv->Changed.resize(mv->Buffer.size()); if (buf != mv->Buffer) { - InvalidateRect(hwnd, NULL, FALSE); + // Some data changed, see which bytes are different + for (size_t n = 0; n < mv->Buffer.size(); ++n) + { + if (buf[n] != mv->Buffer[n]) + mv->Changed[n] = true; + } + // Force a redraw if we are not inside WM_PAINT + if (!IsWmPaint) + InvalidateRect(hwnd, NULL, FALSE); + } + else + { + mv->Changed.assign(mv->Changed.size(), false); + // If the 'changed' state changed, we also need to redraw + if (changed != mv->Changed && !IsWmPaint) + InvalidateRect(hwnd, NULL, FALSE); } if (Requested != Read) { @@ -126,7 +192,14 @@ LRESULT HandleWM_PAINT(HWND hwnd, MemView* mv) HDC hdc = BeginPaint(hwnd, &ps); if (mv->Dirty) - ReadMemory(hwnd, mv); + ReadMemory(hwnd, mv, true); + + if (mv->Resizing || mv->Scrolling) + { + // Don't show changing bytes when scrolling / resizing + mv->Changed.assign(mv->Changed.size(), false); + mv->Resizing = mv->Scrolling = false; + } WCHAR Buffer[512]; SelectObject(hdc, getFont()); @@ -135,8 +208,7 @@ LRESULT HandleWM_PAINT(HWND hwnd, MemView* mv) { size_t offset = (mv->vPos*PerLine) + (n*PerLine); size_t Left = std::min(PerLine, mv->Info.size() - offset); - ToBuffer(Buffer, _countof(Buffer), mv->Buffer.data() + (n*PerLine), Left, PerLine, mv->Info.start() + offset); - TextOutW(hdc, 2, mv->FontY * (int)n, Buffer, (int)wcslen(Buffer)); + DrawLine(hdc, 2, mv->FontY * (int)n, Buffer, _countof(Buffer), mv->Buffer, mv->Changed, (n*PerLine), Left, PerLine, mv->Info.start() + offset); } EndPaint(hwnd, &ps); @@ -170,9 +242,11 @@ void HandleWM_SIZE(HWND hwnd, MemView* mv, LPARAM lParam) } mv->ScrollPos = std::min(mv->ScrollPos, mv->ScrollMax); mv->vPos = std::min((long)mv->ScrollPos * mv->vInc, mv->vMax); + mv->Resizing = true; mv->Dirty = true; SetScrollRange(hwnd, SB_VERT, 0, mv->ScrollMax, FALSE); SetScrollPos(hwnd, SB_VERT, mv->ScrollPos, TRUE); + InvalidateRect(hwnd, NULL, TRUE); } static void HandleWM_VSCROLL(HWND hwnd, MemView* mv, WPARAM wParam, LPARAM lParam) @@ -222,6 +296,7 @@ static void HandleWM_VSCROLL(HWND hwnd, MemView* mv, WPARAM wParam, LPARAM lPara mv->ScrollPos += nVscrollInc; mv->vPos = std::min((long)mv->ScrollPos * mv->vInc, mv->vMax); mv->Dirty = true; + mv->Scrolling = true; SetScrollPos(hwnd, SB_VERT, mv->ScrollPos, TRUE); InvalidateRect(hwnd, NULL, TRUE); UpdateWindow(hwnd); @@ -236,7 +311,6 @@ static void CreateFont(HWND hwnd, MemView* mv) GetTextMetrics(hdc, &tm); mv->FontX = tm.tmAveCharWidth; mv->FontY = tm.tmHeight + tm.tmExternalLeading; - mv->FontNL = tm.tmHeight; ReleaseDC(hwnd, hdc); } @@ -254,35 +328,37 @@ static void SetPtr(HWND hwnd, MemView* mv) LRESULT CALLBACK MemWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + MemView* mv; switch (uMsg) { case WM_CREATE: { WCHAR Buffer[512]; - MemView* mv = static_cast(((LPCREATESTRUCT)lParam)->lpCreateParams); + mv = static_cast(((LPCREATESTRUCT)lParam)->lpCreateParams); SetPtr(hwnd, mv); StringCchPrintfW(Buffer, _countof(Buffer), L"%s (%u), %p - %p", mv->ProcessName.c_str(), mv->ProcessPid, mv->Info.start(), mv->Info.start() + mv->Info.size()); SetWindowTextW(hwnd, Buffer); CreateFont(hwnd, mv); //ReadMemory(hwnd, mv); - SetTimer(hwnd, 0x1ea4, 1000, NULL); + SetTimer(hwnd, kUpdateTimerId, 1000, NULL); } break; case WM_DESTROY: - { - KillTimer(hwnd, 0x1ea4); - MemView* mv = GetPtr(hwnd); + KillTimer(hwnd, kUpdateTimerId); + mv = GetPtr(hwnd); SetPtr(hwnd, NULL); CloseHandle(mv->ProcessHandle); delete mv; SetFocus(GetParent(hwnd)); - } break; - // fix vertical stuff, then this works - //case WM_ERASEBKGND: - // return 0; + case WM_ERASEBKGND: + mv = GetPtr(hwnd); + // If we are not resizing, skip erasebkgrnd to reduce flicker + if (mv && !mv->Resizing) + return 1; + break; case WM_PAINT: HandleWM_PAINT(hwnd, GetPtr(hwnd)); @@ -301,9 +377,9 @@ LRESULT CALLBACK MemWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; case WM_TIMER: - if (wParam == 0x1ea4) + if (wParam == kUpdateTimerId) { - ReadMemory(hwnd, GetPtr(hwnd)); + ReadMemory(hwnd, GetPtr(hwnd), false); } break; }