-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
1 changed file
with
98 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,15 @@ | |
* PROJECT: MemView | ||
* LICENSE: MIT (https://spdx.org/licenses/MIT) | ||
* PURPOSE: The hex-dump window | ||
* COPYRIGHT: Copyright 2021 Mark Jansen <[email protected]> | ||
* COPYRIGHT: Copyright 2021-2023 Mark Jansen <[email protected]> | ||
*/ | ||
|
||
#include "MemView.h" | ||
#include "MemInfo.h" | ||
#include <algorithm> | ||
|
||
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<unsigned char> Buffer; | ||
std::vector<bool> 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<unsigned char>& DataBuffer, const std::vector<bool>& 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,16 +111,38 @@ 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 | ||
*(p++) = '.'; | ||
} | ||
} | ||
*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<unsigned char> buf = mv->Buffer; | ||
std::vector<bool> 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<size_t>(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<MemView*>(((LPCREATESTRUCT)lParam)->lpCreateParams); | ||
mv = static_cast<MemView*>(((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; | ||
} | ||
|