Click here to Skip to main content
14,300,872 members

MFC/Win32 HEX Control 2.7

Rate this:
4.95 (54 votes)
Please Sign up or sign in to vote.
4.95 (54 votes)
14 Sep 2019MIT
HEX Control for MFC/Win32 apps

Hex Control for MFC/Win32 Applications

For the latest info please visit official Github repository. The documentation might be little outdated, it's tough to keep all in sync.

Image 1

Table of Contents

Introduction

Being good low level wrapper library for Windows API in general, MFC was always lacking a good native controls support. This Hex Control is an attempt to expand standard MFC functionality, because at the moment MFC doesn't have native support for such control.

The main features of the Hex Control:

  • View and edit data up to 16EB (exabyte)
  • Work in three different data modes: Memory, Message, Virtual.
  • Bookmarks
  • Search and Replace...
  • Many options to Copy/Paste to/from clipboard
  • Undo/Redo
  • Modify data with Filling and many predefined Operations... options
  • Cutomizable appearance
  • Written with /std:c++17 standard conformance

Implementation

The HexCtrl is implemented as a pure abstract virtual class, and can be used as a child or float window in any place of your existing application. It was built and tested in Visual Studio 2019, under Windows 10.

Installing and Using

The HexCtrl can be used in your app in two different ways:

  1. building from the sources, and
  2. using as .dll

Building from the Sources

The building process is quite simple:

  1. Copy HexCtrl folder into your project's folder.
  2. Add all files from HexCtrl folder into your project.
  3. Add #include "HexCtrl/HexCtrl.h" where you are supposed to use the control.
  4. Declare IHexCtrlPtr member variable: IHexCtrlPtr myHex { CreateHexCtrl() };
  5. Create control instance.

If you want to build HexCtrl from the sources in non MFC app, you will have to:

  1. Add support for Use MFC in a Shared DLL in your project settings
  2. Uncomment the line //#define HEXCTRL_MANUAL_MFC_INIT in HexCtrl.h header file

To use HexCtrl as the .dll, do the following:

  1. Copy HexCtrl.h file into your project's folder.
  2. Copy HexCtrl.lib file into your project's folder, so that linker can see it.
  3. Put HexCtrl.dll file next to your .exe file.
  4. Add the following code where you are supposed to use the control:
#define HEXCTRL_SHARED_DLL //You can alternatively uncomment this line in HexCtrl.h.
#include "HexCtrl.h"` 
  1. Declare IHexCtrlPtr member variable: IHexCtrlPtr myHex { CreateHexCtrl() };
  2. Create control instance.

Control's .dll is built with MFC static linking, so even if you are to use it in your own MFC project, there should not be any interferences.

Additional Information

IHexCtrlPtr is, in fact, a pointer to a IHexCtrl pure abstract base class, wrapped either in std::unique_ptr or std::shared_ptr. You can choose whatever is best for your needs by comment/uncomment one of these aliases in HexCtrl.h:

//using IHexCtrlPtr = IHexCtrlUnPtr;
using IHexCtrlPtr = IHexCtrlShPtr;

This wrapper is used mainly for convenience, so you don't have to bother about object lifetime, it will be destroyed automatically. That's why there is a call to the factory function CreateHexCtrl() - to properly initialize a pointer.

HexCtrl also uses its own namespace HEXCTRL. So it's up to you, whether to use namespace prefix before declarations:

HEXCTRL::

or to define namespace globally, in the source file's beginning:

using namespace HEXCTRL;

Creating

Classic Approach

The IHexCtrl::Create method is the first method you call, it takes HEXCREATESTRUCT as its argument.
You can choose whether control will behave as a child or independent floating window, by setting fFloat member of this struct.

HEXCREATESTRUCT hcs;
hcs.fFloat = true;
hcs.hwndParent = m_hWnd;
m_myHex->Create(hcs);

stColor member of HEXCREATESTRUCT has a type of HEXCOLORSTRUCT. This structure describes all the HexCtrl's colors, and is also used in IHexCtrl::SetColor method.

In Dialog

To use HexCtrl within Dialog you can, of course, create it with the Classic Approach: call IHexCtrl::Create method and provide all the necessary information.

But there is another option you can use:

  1. Put Custom Control control from the Toolbox in Visual Studio dialog designer into your dialog template and make it the desirable size.

    Image 2

    Image 3

  2. Then go to the Properties of that control, and in the Class field, within the Misc section, type HexCtrl.

    Give the control appropriate ID of your choice (IDC_MY_HEX in this example). Also, here you can set the control's Dynamic Layout properties, so that the control behaves appropriately when dialog is being resized.

    Image 4

  3. Declare IHexCtrlPtr member variable within your dialog class:

    IHexCtrlPtr m_myHex { CreateHexCtrl() };

  4. Call IHexCtrl::CreateDialogCtrl method from your dialog's OnInitDialog method:
BOOL CMyDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    
    m_myHex->CreateDialogCtrl(IDC_MY_HEX, m_hWnd);
}

Set the Data

To set a data to display in the HexControl, use IHexCtrl::SetData method. The code below shows how to construct IHexCtrlPtr object and display first 0x1FF bytes of the current app's memory:

IHexCtrlPtr myHex { CreateHexCtrl() };

HEXCREATESTRUCT hcs;
hcs.hwndParent = m_hWnd;
hcs.rect = CRect(0, 0, 500, 300); //Control's rect.

myHex->Create(hcs);

HEXDATASTRUCT hds;
hds.pData = (unsigned char*)GetModuleHandle(0);
hds.ullDataSize = 0x1FF;

myHex->SetData(hds);

The next example displays std::string's text as hex:

std::string str = "My string";
HEXDATASTRUCT hds;
hds.pData = (unsigned char*)str.data();
hds.ullDataSize = str.size();

myHex->SetData(hds);

Data Modes

Besides the standard classical mode, when HexControl just holds a pointer to some array of bytes in memory, it also has additional advanced modes it can be running in.
These modes can be quite useful for instance in cases where you need to display a very large amount of data that can't fit in memory all at once.

These modes are ruled over through the EHexDataMode::enMode member of HEXDATASTRUCT.

Memory Data

It's the default data mode the control works in.
The EHexDataMode::enMode of the HEXDATASTRUCT is set to DATA_MEMORY, and pData just points to some bytes in memory.

Message Window

If EHexDataMode::enMode of HEXDATASTRUCT is set to DATA_MSG, the control works in so called Message Window mode.

What it means is that when control is about to display next byte, it will first ask for this byte from the HEXDATASTRUCT::hwndMsg window, in the form of WM_NOTIFY message. This is pretty much the same as the standard MFC List Control works when created with LVS_OWNERDATA flag.
By default, the HEXDATASTRUCT::hwndMsg is equal to the control's parent window.

To properly handle this mode, you must process WM_NOTIFY messages in hwndMsg window as follows:

BOOL CMyWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    PHEXNOTIFYSTRUCT pHexNtfy = (PHEXNOTIFYSTRUCT)lParam;
    if (pHexNtfy->hdr.idFrom == IDC_MY_HEX)
    {
        switch (pHexNtfy->hdr.code)
        {
        case HEXCTRL_MSG_GETDATA:
            pHexNtfy->pData =  /*Code to set the pointer to an actual data*/;
            break;
        }
   }
}

lParam will hold a pointer to the HEXNOTIFYSTRUCT structure.

The first member of this structure is a standard Windows NMHDR struct. It will have its UINT code member equal to HEXCTRL_MSG_GETDATA, indicating that HexControl's byte request has arrived.
The ullIndex member of the structure is an index of the byte to be displayed. And the pData is the pointer to an actual byte that you have to set in response.

Virtual Handler

If EHexDataMode::enMode member of HEXDATASTRUCT is set to DATA_VIRTUAL, then all the data routine will be done through HEXDATASTRUCT::pHexVirtual pointer.

This pointer is of IHexVirtual class type, which is a pure abstract base class. You have to derive your own class from it and implement all its public methods:

class IHexVirtual
{
public:
    virtual ~IHexVirtual() = default;
    virtual BYTE GetByte(ULONGLONG ullIndex) = 0;            //Gets the byte data by index.
    virtual	void ModifyData(const HEXMODIFYSTRUCT& hmd) = 0; //Main routine to modify data,
                                                             //in fMutable=true mode.
    virtual void Undo() = 0;                                 //Undo command, through menu 
                                                             //or hotkey.
    virtual void Redo() = 0;                                 //Redo command, 
                                                             //through menu or hotkey.
};

Then provide a pointer to the created object of this derived class prior to call to SetData method in form of HEXDATASTRUCT::pHexVirtual = &yourDerivedObject.

OnDestroy

When HexControl window, floating or child, is being destroyed, it sends WM_NOTIFY message to its parent window with NMHDR::code equal to HEXCTRL_MSG_DESTROY. So, it basically indicates to its parent that the user clicked close button, or closed window in some other way.

Scroll Bars

When I started to work with very big data files, I immediately faced one very nasty inconvenience.

The standard Windows scrollbars can only hold signed integer values, which is too little to scroll through many gigabytes of data. There could be some workarounds and crutches involved to overcome this, but frankly saying, I'm not a big fan of this kind of approach.

That's why HexControl uses its own scrollbars. They work with unsigned long long values, which is way bigger than standard signed ints. These scrollbars behave as normal Windows scrollbars, and even reside in the non client area as the latter do.

Methods

HexControl has plenty of methods that you can use to customize its appearance, and to manage its behaviour.

Create

bool Create(const HEXCREATESTRUCT& hcs)

Main initialization method.
It takes HEXCREATESTRUCT, that you fill first, as argument. Returns true if created successfully, false otherwise.

CreateDialogCtrl

bool CreateDialogCtrl(UINT uCtrlID, HWND hwndDlg)

Creates HexCtrl from custom control dialog template. Takes control id and dialog window handle as arguments.
See Control Creation section for more information.

SetData

void SetData(const HEXDATASTRUCT& hds)

Main method to set data to display in read-only or edit modes. Takes HEXDATASTRUCT as an argument.

ClearData

void ClearData()

Clears data from the HexCtrl view, not touching data itself.

SetEditMode

void SetEditMode(bool fEnable)

Enables or disables edit mode. In edit mode, data can be modified.

ShowOffset

void ShowOffset(ULONGLONG ullOffset, ULONGLONG ullSize = 1)

Sets cursor to the ullOffset and selects ullSize bytes.

SetFont

void SetFont(const LOGFONTW* pLogFontNew)

Sets a new font for the HexCtrl. This font has to be monospaced.

SetFontSize

void SetFontSize(UINT uiSize)

Sets a new font size to the HexCtrl.

SetColor

void SetColor(const HEXCOLORSTRUCT& clr)

Sets all the colors for the control. Takes HEXCOLORSTRUCT as the argument.

SetCapacity

void SetCapacity(DWORD dwCapacity)

Sets the HexCtrl capacity.

IsCreated

bool IsCreated()const

Shows whether HexCtrl is created or not yet.

IsDataSet

bool IsDataSet()const

Shows whether a data was set to HexCtrl or not.

IsMutable

bool IsMutable()const

Shows whether HexCtrl is currently in edit mode or not.

GetFontSize

long GetFontSize()const

Returns current font size.

GetSelection

void GetSelection(ULONGLONG& ullOffset, ULONGLONG& ullSize)const

Returns current start position (offset) of the selection as ullOffset, and its size as ullSize.

GetMenuHandle

HMENU GetMenuHandle()const

GetMenuHandle method retrieves the HMENU handle of the control's context menu. You can use this handle to customize menu for your needs.

Control's internal menu uses menu IDs in range starting from 0x8001. So if you wish to add your own new menu, assign menu ID starting from 0x9000 to not interfere.
When user clicks custom menu control sends WM_NOTIFY message to its parent window with LPARAM pointing to HEXNOTIFYSTRUCT with its hdr.code member set to HEXCTRL_MSG_MENUCLICK. uMenuId field of the HEXNOTIFYSTRUCT will be holding ID of the menu clicked.

Destroy

void Destroy()

Destroys the control.
You only invoke this method if you use a raw IHexCtrl pointer obtained by the call to CreateRawHexCtrl function. Otherwise, don't use it.

Remarks

You usually don't need to call this method unless you use HexCtrl through the raw pointer obtained by CreateRawHexCtrl factory function.
If you use HexCtrl in standard way through the IHexCtrlPtr pointer, obtained by CreateHexCtrl function, this method will be called automatically.

Structures

Below are listed all HexCtrl structures.

HEXCREATESTRUCT

The main initialization struct used for control creation.

struct HEXCREATESTRUCT
{
    EHexCreateMode  enMode { EHexCreateMode::CREATE_CHILD }; //Creation mode of the HexCtrl window.
    HEXCOLORSTRUCT  stColor { };    //All the control's colors.
    HWND            hwndParent { }; //Parent window pointer.
    const LOGFONTW* pLogFont { };   //Font to be used, nullptr for default. This font has to be monospaced.
    RECT            rect { };       //Initial rect. If null, the window is screen centered.
    UINT            uID { };        //Control ID.
    DWORD           dwStyle { };    //Window styles, 0 for default.
    DWORD           dwExStyle { };  //Extended window styles, 0 for default.
};

HEXCOLORSTRUCT

This structure describes all control's colors. All these colors have their default values.

struct HEXCOLORSTRUCT
{
    COLORREF clrTextHex { GetSysColor(COLOR_WINDOWTEXT) };         //Hex chunks text color.
    COLORREF clrTextAscii { GetSysColor(COLOR_WINDOWTEXT) };       //Ascii text color.
    COLORREF clrTextBookmark { RGB(0, 0, 0) };                     //Bookmark text color.
    COLORREF clrTextSelected { GetSysColor(COLOR_HIGHLIGHTTEXT) }; //Selected text color.
    COLORREF clrTextCaption { RGB(0, 0, 180) };                    //Caption text color
    COLORREF clrTextInfoRect { GetSysColor(COLOR_WINDOWTEXT) };    //Text color of the bottom "Info" rect.
    COLORREF clrTextCursor { RGB(255, 255, 255) };                 //Cursor text color.
    COLORREF clrBk { GetSysColor(COLOR_WINDOW) };                  //Background color.
    COLORREF clrBkBookmark { RGB(240, 240, 0) };                   //Background color of the bookmarked Hex/Ascii.
    COLORREF clrBkSelected { GetSysColor(COLOR_HIGHLIGHT) };       //Background color of the selected Hex/Ascii.
    COLORREF clrBkInfoRect { GetSysColor(COLOR_BTNFACE) };         //Background color of the bottom "Info" rect.
    COLORREF clrBkCursor { RGB(0, 0, 255) };                       //Cursor background color.
    COLORREF clrBkCursorSelected { RGB(0, 0, 200) };               //Cursor background color in selection.
};

HEXDATASTRUCT

Main struct to set a data to display in the control.

struct HEXDATASTRUCT
{	
    EHexDataMode enMode { EHexDataMode::DATA_MEMORY };  //Working data mode.
    ULONGLONG    ullDataSize { };                       //Size of the data to display, in bytes.
    ULONGLONG    ullSelectionStart { };                 //Select this initial position. Works only if ullSelectionSize > 0.
    ULONGLONG    ullSelectionSize { };                  //How many bytes to set as selected.
    HWND         hwndMsg { };                           //Window for DATA_MSG mode. Parent is used by default.
    IHexVirtual* pHexVirtual { };                       //Pointer for DATA_VIRTUAL mode.
    PBYTE        pData { };                             //Data pointer for DATA_MEMORY mode. Not used in other modes.
    bool         fMutable { false };                    //Is data mutable (editable) or read-only.
};

HEXMODIFYSTRUCT

This structure is used internally in DATA_MEMORY mode, as well as in the external notification routines, when working in DATA_MSG and DATA_VIRTUAL modes.

struct HEXMODIFYSTRUCT
{
    EHexModifyMode enMode { EHexModifyMode::MODIFY_DEFAULT }; //Modify mode.
    EHexOperMode   enOperMode { };  //Operation mode enum. Used only if enMode==MODIFY_OPERATION.
    const BYTE*    pData { };       //Pointer to a data to be set.
    ULONGLONG      ullIndex { };    //Index of the starting byte to modify.
    ULONGLONG      ullSize { };     //Size to be modified.
    ULONGLONG      ullDataSize { }; //Size of the data pData is pointing to.
};

When enMode is set to EHexModifyMode::MODIFY_DEFAULT, bytes from pData just replace corresponding data bytes as is.

If enMode is equal to EHexModifyMode::MODIFY_REPEAT then block by block replacement takes place ullSize / ullDataSize times.

For example: if ullSize = 9, ullDataSize = 3 and enMode is set to EHexModifyMode::MODIFY_REPEAT, bytes in memory at ullIndex position are 123456789, and bytes pointed to by pData are 345, then, after modification, bytes at ullIndex will be 345345345.

If enMode is equal to EHexModifyMode::MODIFY_OPERATION then enOperMode comes into play, showing what kind of operation must be performed on data.

HEXNOTIFYSTRUCT

This struct is used in notifications routine, when data is set with the DATA_MSG flag.

struct HEXNOTIFYSTRUCT
{
    NMHDR     hdr { };      //Standard Windows header. For hdr.code values see HEXCTRL_MSG_* messages.
    UINT_PTR  uMenuId { };  //User defined custom menu id.
    ULONGLONG ullIndex { }; //Index of the start byte to get/send.
    ULONGLONG ullSize { };  //Size of the bytes to get/send.
    PBYTE     pData { };    //Pointer to a data to get/send.
};
using PHEXNOTIFYSTRUCT = HEXNOTIFYSTRUCT *;

EHexCreateMode

Enum that represents mode the HexCtrl's window will be created in.

enum class EHexCreateMode : DWORD
{
    CREATE_CHILD, CREATE_FLOAT, CREATE_CUSTOMCTRL
};

EHexDataMode

Enum that represents current data mode HexCtrl works in. It's used as HEXDATASTRUCT member in SetData method.

enum class EHexDataMode : DWORD
{
    DATA_MEMORY, DATA_MSG, DATA_VIRTUAL
};

EHexModifyMode

Enum represents current data modification type.

enum class EHexModifyMode : WORD
{
    MODIFY_DEFAULT, MODIFY_REPEAT, MODIFY_OPERATION
};

EHexOperMode

Enum describes type of bitwise and arithmetic operations that should be performed on the data.

enum class EHexOperMode : WORD
{
    OPER_OR = 0x01, OPER_XOR, OPER_AND, OPER_NOT, OPER_SHL, OPER_SHR,
    OPER_ADD, OPER_SUBTRACT, OPER_MULTIPLY, OPER_DIVIDE
};

Positioning and Sizing

To properly resize and position your HexControl's window, you may have to handle WM_SIZE message in its parent window, in something like this way:

void CMyWnd::OnSize(UINT nType, int cx, int cy)
{
    ...
    ::SetWindowPos(m_myHex->GetWindowHandle(), 
          this->m_hWnd, 0, 0, cx, cy, SWP_NOACTIVATE | SWP_NOZORDER);
}

Appearance

To change control's font size — Ctrl+MouseWheel
To change control's capacity — Ctrl+Shift+MouseWheel

Licensing

This software is available under the "MIT License modified with The Commons Clause".
Briefly: It is free for any non commercial use.
https://github.com/jovibor/HexCtrl/blob/master/LICENSE

History

  • 26.07.2019 - v2.5.0
    • Render significant speed improvements
    • Option to use as DLL
    • Many other fixes
  • 16.08.2019 - v2.5.2 Regressions and overall fixes. Base64 as a copy method added.
  • 12.09.2019 - v2.7.2 Bookmarks, Fill with data, Operations menus. Inner improvements and fixes.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Jovibor
Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
PraiseMy vote of 6 Pin
Grober_14-Sep-19 1:32
memberGrober_14-Sep-19 1:32 
GeneralMy vote of 5 Pin
Mihai MOGA30-Jul-19 1:44
professionalMihai MOGA30-Jul-19 1:44 
GeneralRe: My vote of 5 Pin
Jovibor31-Jul-19 14:15
memberJovibor31-Jul-19 14:15 
QuestionCongrats Pin
fioresoft15-Jul-19 12:56
memberfioresoft15-Jul-19 12:56 
AnswerRe: Congrats Pin
Jovibor15-Jul-19 18:54
memberJovibor15-Jul-19 18:54 
GeneralVery Old School Pin
Jerome Dubois13-Jun-19 2:03
memberJerome Dubois13-Jun-19 2:03 
GeneralRe: Very Old School Pin
Rick York13-Jun-19 10:28
mveRick York13-Jun-19 10:28 
GeneralRe: Very Old School Pin
Jovibor13-Jun-19 13:15
memberJovibor13-Jun-19 13:15 
GeneralRe: Very Old School Pin
CPallini26-Jul-19 1:58
mveCPallini26-Jul-19 1:58 
GeneralRe: Very Old School Pin
Grober_14-Sep-19 1:31
memberGrober_14-Sep-19 1:31 
QuestionOld School! Pin
Member 1417684313-Mar-19 12:32
memberMember 1417684313-Mar-19 12:32 
GeneralStyle Pin
Rick York12-Mar-19 10:28
mveRick York12-Mar-19 10:28 
GeneralRe: Style Pin
Jovibor12-Mar-19 14:02
memberJovibor12-Mar-19 14:02 
GeneralRe: Style Pin
Rick York12-Mar-19 16:26
mveRick York12-Mar-19 16:26 
GeneralRe: Style Pin
Jovibor12-Mar-19 17:16
memberJovibor12-Mar-19 17:16 
GeneralRe: Style Pin
Rick York12-Mar-19 20:19
mveRick York12-Mar-19 20:19 
GeneralRe: Style Pin
Jovibor12-Mar-19 21:24
memberJovibor12-Mar-19 21:24 
GeneralRe: Style Pin
Rick York13-Mar-19 4:43
mveRick York13-Mar-19 4:43 
GeneralRe: Style Pin
Rick York13-Mar-19 11:40
mveRick York13-Mar-19 11:40 
GeneralRe: Style Pin
Rick York14-Mar-19 7:11
mveRick York14-Mar-19 7:11 
GeneralRe: Style Pin
Jovibor14-Mar-19 13:54
memberJovibor14-Mar-19 13:54 
GeneralRe: Style Pin
Jovibor12-Mar-19 22:22
memberJovibor12-Mar-19 22:22 
GeneralRe: Style Pin
Rick York13-Mar-19 4:45
mveRick York13-Mar-19 4:45 
GeneralRe: Style Pin
Jovibor13-Mar-19 12:40
memberJovibor13-Mar-19 12:40 
GeneralRe: Style Pin
Rick York13-Mar-19 13:01
mveRick York13-Mar-19 13:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 9 Dec 2018

Stats

74.6K views
4.2K downloads
80 bookmarked