HexView.inc

Started by Pierre Bellisle, June 19, 2023, 10:18:50 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Pierre Bellisle

HexView.inc is a utility that that is meant to be incoporated to your code while debugging.
It's purpose is to show data in hexadecimal and ansi form, like an HexEdit program.
No global value are used.
You may invoke HexView* functions via a memory pointer and data lenght, or via a string.
Four main output are available...
1) HexViewClip() return data in the clipboard.
2) HexViewCon() retur a string (this can be printed on a console).
3) HexViewBox() show a hooked fixed font MessageBox.
4) HexViewDlg() show a dialog with scrollbar, tooltips, and an optional info text message.

Compiled with 0.6.0 2023-06-04T22:11:35 - oxygen.dll  - OxygenBasic0605.zip
Compatible    0.7.0 2023-06-15T07:08:15 - oxygen64.dll - OxygenBasic070.zip

HexView.inc
// KickUseDirectivesFrom "D:\Dev\Oxygen\o2\~code\Hex\HexViewDemoDialog01.o2bas" //HexViewDemoConsole.o2bas
// KickEnd

// Compiled with 0.6.0 2023-06-04T22:11:35 - oxygen.dll   - OxygenBasic0605.zip
// Compatible    0.7.0 2023-06-15T07:08:15 - oxygen64.dll - OxygenBasic070.zip

// HexView.inc is a utility that that is meant to be incoporated to your code while debugging.
// It's purpose is to show data in hexadecimal and ansi form, like an HexEdit program.
// No global value are used.
// You may invoke HexView* functions via a memory pointer and data lenght, or via a string.
// Four main output are available...
// 1) HexViewClip() return data in the clipboard.
// 2) HexViewCon() retur a string (this can be printed on a console).
// 3) HexViewBox() show a hooked fixed font MessageBox.
// 4) HexViewDlg() show a dialog with scrollbar, tooltips, and an optional info text message.

// HexViewCon() and HexViewClip() will return CharToOem() data when called from a console.
// MessageBox and Dialog are themed by stealing Shell32.dll manifest,
//   the output window will be centered in the calling window or on display if no handle is supplied,
//   the calling window icon will be used.
// Dialog unthemed grip control is used for better grip.
// Calling syntaxe (All functions are overloaded):
//   HexViewClip(ptr sData, len(sData))
//   HexViewClip(string sData) 'Overload
//   HexViewCon(ptr sData, len(sData))
//   HexViewCon(string sData) 'Overload
//   HexViewBox(ptr sData, len(sData))
//   HexViewBox(string sData) 'Overload
//   HexViewDlg(ptr sData, len(sData))
//   HexViewDlg(string sData) 'Overload

uses Dialogs
uses MinWin
uses Tooltips

%Edit 101
%Clip 201
%Grip 901

%SIZE_MINIMIZED 1
%SIZE_MAXIMIZED 2
%DEFAULT_GUI_FONT 17
%GCL_HICON -14
%GCL_HICONSM -34
%HCBT_CREATEWND 3
%HCBT_DESTROYWND 4
%HCBT_ACTIVATE 5
%WH_CBT 5
%HWND_DESKTOP 0
%SBS_SIZEGRIP 0x0010
%SBS_SIZEBOXBOTTOMRIGHTALIGN 0x0004
%SM_CXVSCROLL 2
%SM_CYHSCROLL 3
%WM_GETICON 0x7F
%SWP_NOSIZE 1
%HWND_TOP 0
%WM_APP 0x8000
%WM_NCLBUTTONDOWN 0x0A1
%HTCAPTION 2
%MONITOR_DEFAULTTONEAREST 2
%EM_GETSELTEXT WM_USER + 62
%WM_COPY 0x301
%WM_MOUSEACTIVATE 0xH21
%WM_NCDESTROY 0x082
%DLGC_HASSETSEL 0x8
%WM_GETDLGCODE 0x0087
%WM_NCACTIVATE 0x086
%ICC_STANDARD_CLASSES 0x00004000
%MAXWORD 0xFFFF
%WM_SETCURSOR 0x20
%TTF_DI_SETITEM 0x8000
%TTF_TRANSPARENT 0x0100
%SWP_NOACTIVATE 0x0010
%TTM_SETDELAYTIME WM_USER + 3
%TTM_ACTIVATE WM_USER + 1
%TTDT_INITIAL 3
%TTDT_AUTOPOP 2
%TTDT_RESHOW  1
%TTM_UPDATETIPTEXTA WM_USER + 12
%TTM_UPDATETIPTEXTW WM_USER + 57
%TTM_POP %WM_USER + 28
%TTM_POPUP WM_USER + 34
%TTM_UPDATE WM_USER + 29
%TTM_TRACKACTIVATE WM_USER + 17

%PROCESSOR_ARCHITECTURE_INTEL 0
%ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID 0x00000004
%ACTCTX_FLAG_RESOURCE_NAME_VALID      0x00000008
%ACTCTX_FLAG_SET_PROCESS_DEFAULT      0x00000010

TYPE MONITORINFO
  DWORD  cbSize
  RECT   rcMonitor
  RECT   rcWork
  DWORD  dwFlags
END TYPE

type CBT_CREATEWND
  CREATESTRUCT ptr lpcs
  sys hWndInsertAfter
end type

type ACTCTXA 'dword fill
  ulong       cbSize
  dword       dwFlags
  zstring ptr lpSource
  word        wProcessorArchitecture
  word        wLangId
  zstring ptr lpAssemblyDirectory
  zstring ptr lpResourceName
  zstring ptr lpApplicationName
  sys         hModule
end type

! CreateActCtxA lib "kernel32.dll" alias "CreateActCtxA"(pActCtx as ACTCTXA) as dword

! SetWindowTheme lib "UxTheme.dll" alias "SetWindowTheme"
(BYVAL hwnd as sys, pszSubAppName as wzstring, pszSubIdList as wzstring) as long

! GetMonitorInfoA lib "user32.dll" alias "GetMonitorInfoA"
(BYVAL hMonitor as sys, lpmi as sys) as LONG 'lpmi as MONITORINFO

macro makeDword(lo, hi)
  dword (hi << 16) + lo
end macro
'_____________________________________________________________________________

SUB ThemeEnable() 'steal CommonControl6 theme from Shell32.dll
 ACTCTXA ActivationContext
 zstring zSource[15]
 zstring zFolder[MAX_PATH]

 GetSystemDirectory(@zFolder, MAX_PATH)
 zSource                                  = "Shell32.dll" 'the PE image to be used to create the activation context.
 ActivationContext.cbSize                 = SIZEOF(ACTCTXA)
 ActivationContext.dwFlags                = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID |
                                            ACTCTX_FLAG_SET_PROCESS_DEFAULT
 ActivationContext.lpSource               = @zSource
 ActivationContext.lpAssemblyDirectory    = @zFolder 'where to to perform private assembly probing.
 ActivationContext.lpResourceName         = 124 'resource manifest id
 ActivationContext.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL
 ActivationContext.wLangId                = 0
 ActivationContext.lpApplicationName      = 0
 ActivationContext.hModule                = 0
 ActivateActCtx(CreateActCtxA(ActivationContext), 0)

END SUB
'____________________________________________________________________________

function CenterWindow(byval sys hParent, byval sys hChild, long SetWindowPosition) as point
 rect rcParent, rcChild, long ParentWidth, ParentHeight, ClildWidth, ChildHeight
 sys hMonitor, POINT pt, MONITORINFO MonitorSpec

 if hParent = 0 then 'No parent so center on the right screen monitor
   GetCursorPos @pt
   hMonitor = MonitorFromPoint(byval pt, MONITOR_DEFAULTTONEAREST)
   MonitorSpec.cbsize  = sizeof(MONITORINFO)
   MonitorSpec.dwFlags = MONITOR_DEFAULTTONEAREST
   GetMonitorInfoA(hMonitor, @MonitorSpec)
   rcParent = MonitorSpec.rcMonitor 'Get parent rect
 else
   GetWindowRect(hParent, rcParent) 'Get parent rect
 endif
 ParentWidth  = rcParent.right  - rcParent.left
 ParentHeight = rcParent.bottom - rcParent.top

 GetWindowRect(hChild, rcChild)
 ClildWidth  = rcChild.right  - rcChild.left
 ChildHeight = rcChild.bottom - rcChild.top

 POINT WindowPosition
 WindowPosition.x = rcParent.left + (ParentWidth  - ClildWidth)  \ 2
 WindowPosition.y = rcParent.top  + (ParentHeight - ChildHeight) \ 2

 if SetWindowPosition
   SetWindowPos(hChild, HWND_TOP, WindowPosition.x, WindowPosition.y, 0, 0, SWP_NOSIZE)
 endif
 function = WindowPosition

end function
'____________________________________________________________________________

function HexViewTextFormat(sys pDataIn, long LenDataIn) as string 'format the output string to be seen like in a hex editor
 string sHexView, sUnPrintableChar, byte bChar
 long LineCount, LineIndex, CharIndex, ofset, middle

 indexbase 0 'array base index
 byte pByte[] at pDataIn 'view the string as an array of bytes
 sUnPrintableChar = chr(0, 1, 2, 3, 7, 9, 10, 13, 27, 28, 29, 30, 31, 127, 129, 140, 141, 143, 144, 149, 152, 157) 'edit according to font viewable characters
 LineCount = ceil(LenDataIn / 16) '16 char represented in a line
 sHexView = space(LineCount * 77) 'LineLenCrLf77, LineLen75 -> used in WM_SETCURSOR

 for LineIndex = 0 TO LineCount - 1
   mid(sHexView, ofset + 01) = hex(LineIndex * 16, 4) '0x0000 is offset
   asc(sHexView, ofset + 05) = 58                     'for ":"
   asc(sHexView, ofset + 31) = 45                     'for "-"
   asc(sHexView, ofset + 57) = 59                     'for ";"
   asc(sHexView, ofset + 76) = 13                     'for cr
   asc(sHexView, ofset + 77) = 10                     'for lf
   for CharIndex = 0 TO 15                            'work with 16 characters by line
     if CharIndex + LineIndex * 16 = LenDataIn then exit for 'end of data reached
     bChar = pByte[CharIndex + LineIndex * 16]               'get the asc value of the char
     if CharIndex = 8 then middle = 1                        'for middle dash or middle space
     mid(sHexView, ofset + 07 + CharIndex * 3 + middle * 2) = hex(bChar, 2) 'hex value of byte 00~FF
     if instr(sUnPrintableChar, chr(bChar)) then bChar = 46                 '. dot if char is unprintable
     asc(sHexView, ofset + 59 + CharIndex + middle ) = bChar                'write char
   next
   middle = 0  'reset
   ofset += 77 'next line
 next

 sHexView = "Oxygen HexView " + sizeof(sys) * 8 + "bit - Data lenght is " &
            str(LenDataIn) & " bytes." & chr(13, 10) & sHexView

 function = sHexView

end function
'____________________________________________________________________________

function HexViewClip(sys pDataIn, long LenDataIn, optional string sComment) as boolean
 string sDataHex = HexViewTextFormat(pDataIn, LenDataIn)

 if GetConsoleCP() 'more reliable than GetConsoleWindow()
   CharToOem(sDataHex, sDataHex) 'translate to console characters
 endif

 if len(sComment) then sDataHex = sDataHex + chr(13, 10) + sComment

 if OpenClipboard(HWND_DESKTOP)
   EmptyClipboard() 'emptyClipboard sets the clipboard owner to NULL; this may causes SetClipboardData to fail.
   int DataLen = len(sDataHex) + 1
   sys hMem = GlobalAlloc(GMEM_MOVEABLE, DataLen)
   if hMem
     sys pMem = GlobalLock(hMem)
     copy(pMem, strptr(sDataHex, DataLen)
     GlobalUnlock(hMem)
     IF SetClipboardData(CF_TEXT, hMem) then function = true
   endif
   'clipboard engine will do GlobalFree(hClipboard) aka frees the global memory object and invalidates its handle
   CloseClipboard(HWND_DESKTOP)
 endif

end function
'____________________________________________________________________________

function HexViewClip(string sDataIn, optional string sComment) as boolean 'overload function
 function = HexViewClip(strptr sDataIn, len(sDataIn), sComment)
end function
'______________________________________________________________________________

function HexViewCon(sys pDataIn, long LenDataIn) as string

 string sDataHex = HexViewTextFormat(pDataIn, LenDataIn)

 if GetConsoleCP() 'more reliable than GetConsoleWindow()
   CharToOem(sDataHex, sDataHex) 'translate to console characters
 endif
 function = sDataHex 'return hex string

end function
'____________________________________________________________________________

function HexViewCon(string sDataIn) as string 'overload function
 function = HexViewCon(strptr sDataIn, len(sDataIn))
end function
'______________________________________________________________________________

function MessageBoxProc(byval dword nCode, byval sys hWin, byval sys lparam) as sys callback 'hooked messagebox
 CBT_CREATEWND       ptr cbCreateWin
 static CREATESTRUCT ptr WinStruct
 static point            Dialog
 static sys              hFont, hStatic, hButton, hMsgBox, hParent
 static long             DialogWidth, DialogHeight, LineCount
 zstring                 zClass[64]

 if nCode = 0 then hParent = hWin : LineCount = lparam 'pre init

 if nCode = HCBT_CREATEWND then
   @cbCreateWin = LParam                'get pointer to CBT_CREATEWND struct so we can...
   @WinStruct   = cbCreateWin.lpcs      'get a pointer to the CREATESTRUCT struct ...
   GetClassName(hWin, zClass, Max_Path) 'for each window / control as it is created

   if zClass = "#32770" 'main dialog itself
     hMsgBox = hWin

     static sys hIcon
     if GetConsoleCP() then 'console program
       hIcon = SendMessage(hParent, WM_GETICON, ICON_SMALL, 0)
     else
       hIcon = GetClassLongPtr(hParent, GCL_HICON)
     endif
     if hIcon = 0 then hIcon = ExtractIcon(GetModuleHandle(""), "Shell32.dll", 294) 'o
     SendMessage(hWin, WM_SETICON, ICON_SMALL, hIcon)
     SendMessage(hWin, WM_SETICON, ICON_BIG, hIcon)

     Dialog.x     = 548 'dialog width
     WinStruct.cx = Dialog.x 'Change dialog width
     Dialog.y     = (LineCount + 9) * 14 'Change dialog height
     WinStruct.cy = Dialog.y 'Change dialog height

     POINT ParentPos = CenterWindow(hParent, hWin, false) 'SetWindowPosition=false
     'at this stage GetWindowRect() return empty in CenterWindow()
     WinStruct.x = ParentPos.x - Dialog.x / 2
     WinStruct.y = ParentPos.y - Dialog.y / 2

   elseif zClass = "Static" then 'static control where the text appear
     hStatic = hWin
     WinStruct.cx = Dialog.x - 5
     WinStruct.cy = Dialog.y - 90

   elseif zClass = "Button" Then 'the ok button
     hButton = hWin
     if WinStruct.hMenu = IDOK
         WinStruct.x = Dialog.x \ 2 - WinStruct.cx \ 2
         WinStruct.y = Dialog.y - 60
     endif
   endif

 elseif nCode = HCBT_ACTIVATE then
   if hFont = 0 'initiate fixed width font
     hFont = CreateFont(14, 0,      'Height 14 = 9, 16=12, 15=11, 14=11, Width usually 0,
                        0, 0,       'Escapement(angle), Orientation
                        0, 0, 0, 0, 'Bold, Italic, Underline, Strikethru
                        0, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
                        DEFAULT_QUALITY, FF_DONTCARE, "Consolas") 'Fixed width font - "Segoe UI", 9 Arial Consolas
   endif
   SendMessage(hStatic, WM_SETFONT, hFont, 0)
   SendMessage(hButton, WM_SETFONT, hFont, 0)
   SetWindowText(GetDlgItem(hMsgBox, IDOK), "Close")

 elseif nCode = HCBT_DESTROYWND THEN
   DeleteObject(hFont) : hFont = 0
 endif

end function
'_____________________________________________________________________________

function HexViewBox(sys hParent, sys pDataIn, long LenDataIn) as string
 long index, LineCount

 string sDataHex = HexViewTextFormat(pDataIn, LenDataIn)

 for index = 1 to len(sDataHex)
   if asc(sDataHex, index) = 13 then LineCount ++
 next

 ThemeEnable()

 MessageBoxProc(0, byval hParent, byval LineCount) 'dirty trick to send hParent + LineCount to the hook and avoid global

 sys hMsgBoxHook = SetWindowsHookEx(WH_CBT, @MessageBoxProc, GetModuleHandle(""), GetCurrentThreadId())
 MessageBox(hParent, sDataHex, "Oxygen HexView " + sizeof(sys) * 8 + "bit - Data lenght is " &
            str(LenDataIn) & " bytes.", MB_OK | MB_SYSTEMMODAL | MB_TOPMOST)
 UnhookWindowsHookEx(hMsgBoxHook)
 function = sDataHex 'return string for convenience

end function
'____________________________________________________________________________

function HexViewBox(sys hParent, string sDataIn) as string 'overload function
 function = HexViewBox(hParent, strptr sDataIn, len(sDataIn))
end function
'______________________________________________________________________________

function EditProc(sys hEdit, uint wMsg, sys wParam, sys lParam) as sys callback
 static sys pEditProc

 select case wMsg

   case WM_NULL
     if hEdit = 0 and pEditProc = 0 then pEditProc = wParam : return(0) 'get pEditProc

   case WM_NCDESTROY
     SetWindowLongPtr(hEdit, GWL_WNDPROC, pEditProc) 'unsubclass edit

   case WM_LBUTTONDOWN
     SetFocus(hEdit) 'make selection possible on first click when focus is not on the Edit control

   case WM_CHAR
     select case wparam
       case 1 'Control-A
         SendMessage(hEdit, EM_SETSEL, 0, - 1) 'Select everything
         return 0
     end select

 end select

 function = CallWindowProc(pEditProc, hEdit, wMsg, wParam, lParam)

end function
'_____________________________________________________________________________

function HexViewDlgProc(sys hDlg, uint uMsg, sys wParam, sys lParam) as int callback
 static rect  ButtonRect
 static sys   hIcon, hFont, hParent, hEdit, hToolTip
 static point GripSize
 static long  SelStart, SelEnd
 static TOOLINFO ToolTipInfo
 static string sToolTip

 select case uMsg

   case WM_APP
     if hDlg = 0 and hParent = 0 then hParent = wParam
     return 0

   case WM_INITDIALOG
     hEdit = GetDlgItem(hDlg, Edit)
     'Subclass listbox with in a one liner, no global variables needed...
     EditProc(0, WM_NULL, SetWindowLongPtr(hEdit, GWL_WNDPROC, @EditProc), 0)

     hToolTip = CreateWindowEx(0, "Tooltips_Class32", "", TTS_ALWAYSTIP, 0, 0, 0, 0, 0,
                               null, GetModuleHandle(null), null)
     ToolTipInfo.cbSize   = sizeof(TOOLINFO)
     ToolTipInfo.uFlags   = TTF_SUBCLASS | TTF_IDISHWND
     ToolTipInfo.hWnd     = hDlg
     ToolTipInfo.uId      = hEdit
     ToolTipInfo.hinst    = GetModuleHandle(null)
     sToolTip             = "Hexview"
     ToolTipInfo.lpszText = strptr sToolTip
     SendMessage(hToolTip, TTM_ADDTOOL, 0, &ToolTipInfo)
     SetWindowPos(hToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE)
     SendMessage(hToolTip, TTM_SETMAXTIPWIDTH, 0, 300)
     SendMessage(hToolTip, TTM_SETDELAYTIME, TTDT_INITIAL, 100) 'stationary within a tool's bounding rectangle before the tooltip window appears.
     SendMessage(hToolTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, 15000) 'remains visible if the pointer is stationary
     SendMessage(hToolTip, TTM_SETDELAYTIME, TTDT_RESHOW, 50) 'subsequent tooltip windows to appear
     SendMessage(hToolTip, TTM_ACTIVATE, true, 0)

     hFont = CreateFont(14, 0,      'Height 14 = 9, 16=12, 15=11, 14=11, Width usually 0,
                        0, 0,       'Escapement(angle), Orientation
                        0, 0, 0, 0, 'Bold, Italic, Underline, Strikethru
                        0, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
                        DEFAULT_QUALITY, FF_DONTCARE, "Consolas") 'Fixed width font
     SendDlgItemMessage(hDlg, Edit, WM_SETFONT, hFont, 0)
     SendDlgItemMessage(hDlg, IDCANCEL, WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendDlgItemMessage(hDlg, Clip, WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)

     GetWindowRect(GetDlgItem(hDlg, IDCANCEL), ButtonRect) 'Keep PushButton size as defined in winmain()
     ButtonRect.right  = ButtonRect.right  - ButtonRect.left : ButtonRect.left = 0
     ButtonRect.bottom = ButtonRect.bottom - ButtonRect.top  : ButtonRect.top  = 0

     GripSize.x = GetSystemMetrics(SM_CXVSCROLL) 'Width of grip
     GripSize.y = GetSystemMetrics(SM_CYHSCROLL) 'Height of grip
     SetWindowTheme(GetDlgItem(hDlg, Grip), " ", " ")

     CenterWindow(hParent, hDlg, true) 'SetWindowPosition-true as point

     if GetConsoleCP() then
       hParent = GetConsoleWindow()
       hIcon = SendMessage(hParent, WM_GETICON, ICON_SMALL, 0)
       SendMessage(hDlg, WM_SETICON, ICON_SMALL, hIcon)
       SendMessage(hDlg, WM_SETICON, ICON_BIG, hIcon)
     else
       hIcon = GetClassLongPtr(hParent, GCL_HICON)
       SetClassLongPtr(hDlg, GCL_HICON, hIcon)
       SendMessage(hDlg, WM_SETICON, ICON_SMALL, hIcon)
     endif
     if hIcon = 0 then hIcon = ExtractIcon(GetModuleHandle(""), "Shell32.dll", 294) 'o
     SetClassLongPtr(hDlg, GCL_HICON, hIcon)
     return true

   case WM_COMMAND
     select case loword(wParam)

       case Edit
         select case hiword(wParam)
           case EN_KILLFOCUS
             SendDlgItemMessage(hDlg, Edit, EM_GETSEL, @SelStart, @SelEnd)
           case EN_SETFOCUS
             SendDlgItemMessage(hDlg, Edit, EM_SETSEL, SelStart, SelEnd)
         end select

       case Clip
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           SendDlgItemMessage(hDlg, Edit, EM_GETSEL, @SelStart, @SelEnd)
           if SelStart = SelEnd then 'No selection made so, copy the whole text
             SendDlgItemMessage(hDlg, Edit, EM_SETSEL, 0, -1)
             SendDlgItemMessage(hDlg, Edit, WM_COPY, 0, 0)
             SendDlgItemMessage(hDlg, Edit, EM_SETSEL, SelStart, SelEnd)
           else 'Copy selection
             SendDlgItemMessage(hDlg, Edit, WM_COPY, 0, 0)
           endif
         endif

       case IDCANCEL
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           EndDialog(hDlg, null)
         endif
     end select

   case WM_SETCURSOR
     'wParam handle to window
     'lParam lo hit-test code like HTCLIENT, HTMENU, HTCAPTION, HTGROWBOX, HTTOP, etc
     'lParam hi mouse message like WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_RBUTTONUP, WM_XBUTTONDOWN, etc
     if wParam = hEdit then
       static long CharIndexPrev, LineIndex, LineIndexPrev
       static word LineLen
       static string sLine
       long CharIndex
       point pt
       GetCursorPos(pt)
       ScreenToClient(hEdit, pt)

       'char index in lowword, line index in highword
       LineIndex = SendMessage(hEdit, EM_CHARFROMPOS, 0, makeDword(pt.X, pt.Y))
       CharIndex = loword(LineIndex)
       if CharIndex <> CharIndexPrev then
         sToolTip = ""
         LineIndex = hiword(LineIndex)
         long CharCountInPreceedingLine = SendMessage(hEdit, EM_LINEINDEX, LineIndex, 0)
         long ColIndex = CharIndex - CharCountInPreceedingLine
         if LineIndex <> LineIndexPrev then 'cursor on a new line
           'Get whole line...
           LineLen = SendMessage(hEdit, EM_LINELENGTH, CharIndex, 0)
           sLine = nuls(2 + LineLen)
           copyn strptr sLine, @LineLen, 2 'Aka mak_word(2)
           SendMessage(hEdit, EM_GETLINE, LineIndex, strptr sLine)
           sLine = left(sLine, len(sLine) - 2)
           LineIndexPrev = LineIndex
         endif

         if LineLen = 75 then 'check if lenght of data line is wright
           if asc(sLine, 5) = 58 then 'check if fifth char is ":"
             if ColIndex < 75 then 'check invalid ColIndex
               long hexOffset = 0
               if ColIndex < 4 then 'row index
                 sToolTip = left(sLine, 4)
                 sToolTip = "0x" + sToolTip + " (" + str(val("0x" + sToolTip)) + ")"
               elseif ColIndex = 4 | ColIndex = 30 | ColIndex = 56 | ColIndex = 57 | ColIndex = 66 then 'char [: -;]
                 'sToolTip will be "", aka do nothing
               elseif ColIndex < 56 then 'hex byte numbers
                 if asc(sLine, ColIndex + 1) > 32 then 'if cursor is not on a space
                   hexOffset = 0
                   if asc(sLine, ColIndex) < 33 then 'this char and subsequent char are valid
                     ColIndex ++
                   endif
                   sToolTip = mid(sLine, ColIndex, 2)
                   long ToolTipVal = val("0x" + sToolTip)
                   sToolTip = "0x" + sToolTip + " (" + str(ToolTipVal) + ") " + chr(ToolTipVal)
                 else 'sToolTip = "[s p a c e]"
                   'sToolTip will be "", aka do nothing
                 endif
               else 'colIndex >= 57, on ansi text at the end
                 if ColIndex > 65 then hexOffset = -1 else hexOffset = 0 'halfway in ansi = halfway " - " in hex
                 sToolTip = mid(sLine, 7 + 3 * (ColIndex - 58) + hexOffset, 2)
                 if sToolTip = "  " then
                   sToolTip = ""
                 else
                   long ToolTipVal = val("0x" + sToolTip)
                   sToolTip = "0x" + sToolTip + " (" + str(ToolTipVal) + ") " + chr(ToolTipVal)
                 endif
               endif
             endif
           endif
         endif

         if len(sToolTip) and CharIndexPrev <> CharIndex then
           ToolTipInfo.cbSize   = sizeof(TOOLINFO)
           ToolTipInfo.lpszText = strptr sToolTip
           ToolTipInfo.hWnd     = hDlg
           ToolTipInfo.uId      = hEdit
           ToolTipInfo.hinst    = GetModuleHandle(null)
           SendMessage(hToolTip, TTM_UPDATETIPTEXTA, 0, &ToolTipInfo)
           SendMessage(hToolTip, TTM_ACTIVATE, true, 0)
         else
           SendMessage(hToolTip, TTM_ACTIVATE, false, 0)
         endif
         CharIndexPrev = CharIndex
       endif
     endif

   case WM_SIZE 'dialog size have changed
     'wParam = resizing requested: SIZE_MAXHIDE, SIZE_MAXIMIZED, SIZE_MAXSHOW, SIZE_MINIMIZED, SIZE_RESTORED
     'loword lParam is client area width  in pixels
     'hiword lParam is client area height in pixels

     if wParam <> SIZE_MINIMIZED
       long ClientSizeX = loword(lParam)
       long ClientSizeY = hiword(lParam)
       if wparam = SIZE_MAXIMIZED then 'no grip on maximized
         SetWindowPos(GetDlgItem(hDlg, Grip), NULL,  0, 0, 0, 0, SWP_NOZORDER) 'size grip to zero by zero
       else
         SetWindowPos(GetDlgItem(hDlg, Grip), NULL, ClientSizeX - GripSize.x, 'grip to normal size
                      ClientSizeY - GripSize.y, GripSize.x, GripSize.y, SWP_NOZORDER)
       endif
       long posY = ClientSizeY - ButtonRect.bottom - 15
       long posx = (ClientSizeX - ButtonRect.right) \ 2

       MoveWindow(GetDlgItem(hDlg, Edit), 5, 5, ClientSizeX - 10, posY, TRUE)
       MoveWindow(GetDlgItem(hDlg, IDCANCEL), posX, posY + 10,
                                              ButtonRect.right, ButtonRect.bottom, TRUE)
       MoveWindow(GetDlgItem(hDlg, Clip), PosX + 164, posY + 10,
                                              ButtonRect.right, ButtonRect.bottom, TRUE)
     endif

   case WM_LBUTTONDOWN
     if wParam = MK_LBUTTON then
       SendMessage(hDlg, WM_NCLBUTTONDOWN, HTCAPTION, byval NULL) 'dialog drag via click down simulation on caption
     endif

   case WM_CLOSE
     EndDialog(hDlg, null)

   case WM_DESTROY
     DeleteObject(hFont) : hFont = 0

 end select

 return 0
end function
'______________________________________________________________________________

function HexViewDlg(sys hParent, sys pDataIn, long LenDataIn, optional string sComment) as long

 string sDataHex = HexViewTextFormat(pDataIn, LenDataIn)
 if len(sComment) then
   if LenDataIn / 16 <> LenDataIn \ 16 then sDataHex = sDataHex + chr(13, 10)
   sDataHex = sDataHex + chr(13, 10) + sComment
 endif

 ThemeEnable() 'CommonControl6

 HexViewDlgProc(0, WM_APP, hParent, 0) 'give hParent to HexViewDlgProc

 Dialog(0, 0, 290, 200, "Oxygen HexView " + sizeof(sys) * 8 + "bit - Data lenght is " &
                        str(LenDataIn) & " bytes.", WS_OVERLAPPEDWINDOW | DS_CENTER)

 PushButton("Close" , IDCANCEL, 80, 85, 40, 12)

 PushButton("Clip" , Clip, 105, 85, 40, 12)

 MultiLineText(sDataHex, Edit, 1, 1, 198, 80, ES_SAVESEL | ES_NOHIDESEL | ES_READONLY)

 Control( "", Grip, "SCROLLBAR", WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP |
         SBS_SIZEBOXBOTTOMRIGHTALIGN, 390, 290, 10, 10, 0, 0 )

 CreateModalDialog(hParent, @HexViewDlgProc, 0)

end function
'______________________________________________________________________________

function HexViewDlg(sys hParent, string sDataIn, optional string sComment) as long 'overload function
 function = HexViewDlg(hParent, strptr sDataIn, len(sDataIn), sComment)
end function
'______________________________________________________________________________
'

HexViewDemoDialog.o2bas
//KickCompiler "D:\Dev\Oxygen\o2\co264.exe"
//KickSwitch -64
//KickResource "D:\Dev\Oxygen\o2\~code\~~PbrDefault00xml.res"
//KickEnd

// Compiled with 0.6.0 2023-06-04T22:11:35 - oxygen.dll   - OxygenBasic0605.zi
// Compatible    0.7.0 2023-06-15T07:08:15 - oxygen64.dll - OxygenBasic070.zip

type INIT_COMMON_CONTROLSEX
  dword dwSize
  dword dwICC
end type
%ICC_TAB_CLASSES 0x08

%ButtonDia 201
%ButtonBox 202
%ButtonClip 203
%CheckboxParent 301
%CheckboxOverLoad 302
%CheckboxTopMost 303

%SIZE_MINIMIZED 1
%DEFAULT_GUI_FONT 17
%GCL_HICON -14
%HWND_NOTOPMOST 0xFFFFFFFE

uses Dialogs 'might gpf with old version > http://forum.it-berater.org/index.php/topic,5879.0.html
use HexView
'______________________________________________________________________________

function DialogProc(sys hDlg, uint uMsg, sys wParam, lParam) as int callback
 static rect ButtonRect, string sBuffer
 static sys  hIcon, hFont, hDialogFlipDesktop

 select case uMsg

   case WM_INITDIALOG
     hDialogFlipDesktop = HWND_DESKTOP 'hDlg
     CheckDlgButton(hDlg, CheckboxParent, BST_CHECKED)

     hIcon = ExtractIcon(GetModuleHandle(""), "Shell32.dll", 294 + 1) 'o
     SetClassLongPtr(hDlg, GCL_HICON, hIcon)

     hFont = CreateFont(14, 0,      'Height 14 = 9, 16=12, 15=11, 14=11, Width usually 0,
                        0, 0,       'Escapement(angle), Orientation
                        0, 0, 0, 0, 'Bold, Italic, Underline, Strikethru
                        0, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
                        DEFAULT_QUALITY, FF_DONTCARE, "Consolas") 'Fixed width font
     SendMessage(GetDlgItem(hDlg, ButtonDia), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendMessage(GetDlgItem(hDlg, ButtonBox), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendMessage(GetDlgItem(hDlg, ButtonClip), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendMessage(GetDlgItem(hDlg, IDCANCEL), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendMessage(GetDlgItem(hDlg, IDCANCEL), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendMessage(GetDlgItem(hDlg, CheckboxParent), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendMessage(GetDlgItem(hDlg, CheckboxOverLoad), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)
     SendMessage(GetDlgItem(hDlg, CheckboxTopMost), WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0)

     GetWindowRect(GetDlgItem(hDlg, IDCANCEL), ButtonRect) 'Keep PushButton size as defined in winmain()
     ButtonRect.right  = ButtonRect.right  - ButtonRect.left : ButtonRect.left = 0
     ButtonRect.bottom = ButtonRect.bottom - ButtonRect.top  : ButtonRect.top  = 0

     long index
     for index = 0 to 255
       sBuffer += chr(index)
     next
     return true

   case WM_COMMAND
     select case loword(wParam)

       case ButtonDia
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           HexViewDlg(hDialogFlipDesktop, sBuffer, "I am an optional comment... All 256 bytes")
           string s = "I am a string, yes, I am a string"
           HexViewDlg(hDialogFlipDesktop, strptr s, len(s), "I am a string")
           wstring ws = "I am a wide string"
           HexViewDlg(hDialogFlipDesktop, strptr ws, len(ws) * 2, "I am a wide string")
           dword dw = 0x01020304
           HexViewDlg(hDialogFlipDesktop, @dw, sizeof(dw), "I am a big endian dword 0x01020304")
           byte b[3] = {1, 2, 3}
           HexViewDlg(hDialogFlipDesktop, @b, spanof(b), "I am a three bytes array 01 02 03")
           word w[3] = {0x12, 0x23, 0x34}
           HexViewDlg(hDialogFlipDesktop, @w, spanof(w) * sizeof(word), "I am a big endian three word array 0x0012, 0x0023, 0x0034")
         end if

       case ButtonBox
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           HexViewBox(hDialogFlipDesktop, sBuffer)
         end if

       case ButtonClip
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           HexViewClip(sBuffer)
         end if

       case IDCANCEL
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           EndDialog(hDlg, null)
         end if

       case CheckboxParent
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           if IsDlgButtonChecked(hDlg, CheckboxParent) then
             hDialogFlipDesktop = HWND_DESKTOP
           else
             hDialogFlipDesktop = hDlg
           end if
         end if

       case CheckboxTopMost
         if hiword(wParam) = BN_CLICKED | hiword(wParam) = 1
           if IsDlgButtonChecked(hDlg, CheckboxTopMost) then
              SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)
           else
             SetWindowPos(hDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)
           end if
         end if

     end select

   case WM_SIZE 'dialog size have changed
     'wParam = resizing requested: SIZE_MAXHIDE, SIZE_MAXIMIZED, SIZE_MAXSHOW, SIZE_MINIMIZED, SIZE_RESTORED
     'loword lParam is client area width  in pixels
     'hiword lParam is client area height in pixels
     if wParam <> SIZE_MINIMIZED
       long ClientSizeX = loword(lParam)
       long ClientSizeY = hiword(lParam)
       long posY = ClientSizeY - ButtonRect.bottom - 15
       'MoveWindow(GetDlgItem(hDlg, Edit), 5, 5, ClientSizeX - 10, posY, TRUE)
       'MoveWindow(GetDlgItem(hDlg, IDCANCEL), (ClientSizeX - ButtonRect.right) \ 2, posY + 10,
       '                                       ButtonRect.right, ButtonRect.bottom, TRUE)
       'InvalidateRect(hDlg, BYVAL NULL, TRUE) : UpdateWindow(hDlg)
     end if

   case WM_CLOSE
     EndDialog(hDlg, null)

   case WM_DESTROY
     DeleteObject(hFont)
     'DestroyIcon(hIcon)

 end select

 return 0
end function
'______________________________________________________________________________

sub winmain()

 'string ver = version    %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_THICKFRAME OR %WS_SYSMENU
 'init_common_controls()
  'init_common_controlsEx() InitCommonControlsEx
 INIT_COMMON_CONTROLSEX CommonControlsEx
 CommonControlsEx.dwSize = sizeof(INIT_COMMON_CONTROLSEX)
 CommonControlsEx.dwICC = ICC_STANDARD_CLASSES | ICC_TAB_CLASSES
 InitCommonControlsEx(@CommonControlsEx)
 'if InitCommonControlsEx(CommonControlsEx) then Beep(5500, 50)

 'Dialog( 0, 0, 230, 50, "OxygenBasic " + sizeof(sys) * 8 + " - v." + version, WS_OVERLAPPEDWINDOW | DS_CENTER )
 'Dialog( 0, 0, 230, 50, "Oxygen HexView " + sizeof(sys) * 8, WS_OVERLAPPEDWINDOW | DS_CENTER )
 Dialog( 0, 0, 230, 55, "Oxygen HexView " + sizeof(sys) * 8, _
 WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU | DS_CENTER, 9, "Segoe UI")
 'MultiLineText("MultiLineText", Edit, 1, 1, 198, 80)
 PushButton("Dialog", ButtonDia, 10, 16, 40, 12)
 PushButton("MessageBox", ButtonBox, 60, 16, 60, 12)
 PushButton("Clipboard", ButtonClip, 130, 16, 40, 12)
 PushButton("Close", IDCANCEL, 180, 16, 40, 12)
 AutoCheckBox("use HWND_DESKTOP", CheckboxParent, 15, 40, 70, 12)
 AutoCheckBox("OverLoad", CheckboxOverLoad, 110, 40, 70, 12)
 AutoCheckBox("topMost", CheckboxTopMost, 184, 40, 40, 12)
 CreateModalDialog(null, @DialogProc, 0)

end sub
'______________________________________________________________________________

winmain()
'______________________________________________________________________________
'

HexViewDemoConsole.o2bas
//KickCompiler "D:\Dev\Oxygen\o2\co264.exe"
//KickSwitch -run -32
//KickEnd

// Compiled with 0.6.0 2023-06-04T22:11:35 - oxygen.dll   - OxygenBasic0605.zi
// Compatible    0.7.0 2023-06-15T07:08:15 - oxygen64.dll - OxygenBasic070.zip

 use "Console.inc"
 use "HexView.inc"

 sys hIcon = ExtractIcon(GETMODULEHANDLE(""), "Shell32.dll", 294 + 001)
 SendMessage(GetConsoleWindow(), WM_SETICON, ICON_SMALL, hIcon)
 SendMessage(GetConsoleWindow(), WM_SETICON, ICON_BIG, hIcon)

 SetConsoleTitle "Oxygen HexView demo console " + sizeof(sys) * 8 + "bit"
 SetWindowPos(GetConsoleWindow(), %NULL, 100, 100, 870, 870, %SWP_NOZORDER)

 wstring wsBuffer = "123" & "abcdéfghijklmnöpqrstuvwxyz" & wchr(10,1,2,3) & "eê" & wchr(0)
 printl "printl HexViewCon(wsBuffer) OverLoad"
 printl HexViewCon(wsBuffer)
 printl
 printl "printl HexViewCon(strptr wsBuffer, len(wsBuffer))"
 printl HexViewCon(strptr wsBuffer, len(wsBuffer))
 printl
 string sBuffer = "pierre" + NULS(2) + "charles" + NULS(2) + "theo" + NULS(2) + "aurel" + NULS(12)
 printl "printl HexViewCon(wsBuffer) OverLoad"
 printl HexViewCon(sBuffer)
 printl
 printl "HexViewCon(strptr sBuffer, len(sBuffer))"
 printl HexViewCon(strptr sBuffer, len(sBuffer))
 printl

 long index
 sBuffer = ""
 for index = 0 to 255
   sBuffer += chr(index)
 next
 printl "printl HexViewCon(sBuffer)"
 printl HexViewCon(strptr sBuffer, len(sBuffer))
 printl
 printl "printl HexViewCon(sBuffer) OverLoad"
 printl HexViewCon(sBuffer)

 printl
 printl "strike a key to view HexViewBox() and HexViewDlg()" : waitkey

 HexViewBox(GetConsoleWindow(), strptr sBuffer, len(sBuffer))
 HexViewBox(GetConsoleWindow(), sBuffer)

 HexViewDlg(GetConsoleWindow(), strptr sBuffer, len(sBuffer))
 HexViewDlg(GetConsoleWindow() ,sBuffer)

 if MessageBox(HWND_DESKTOP, "Copy HexViewData to clipboard?", "Oxygen HexView",
               MB_YESNO | MB_SYSTEMMODAL | MB_TOPMOST | MB_DEFBUTTON2) = IDYES
   HexViewClip(strptr sBuffer, len(sBuffer))
 end if
 if MessageBox(HWND_DESKTOP, "Copy HexViewData to clipboard? (OverLoad)", "Oxygen HexView",
               MB_YESNO | MB_SYSTEMMODAL | MB_TOPMOST | MB_DEFBUTTON2) = IDYES
   HexViewClip(sBuffer)
 end if

 DestroyIcon(hIcon)

 end


  •