Skip to content

nkjpj/HexCtrl

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hex Control, C++/MFC

Table of Contents

Introduction

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

This doesn't mean that HexControl is limited to use only in MFC environment. The control is implemented as a pure abstract interface, and can be used as a child or popup window in any place of your application.
It is written and tested with /std:c++17 in Visual Studio 2019, under the Windows 10.

The main features of the HexControl:

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

Installation

The HexControl can be used in two different ways:

  • Building from the sources as a part of your project
  • Using as a .dll.

Building From The Sources

The building process is quite simple:

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

If you want to build HexControl 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.

Dynamic Link Library

To use HexControl 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 line where you suppose 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.

To get HexCtrl.dll and HexCtrl.lib files you can either download it from the official repository or build it yourself from the HexCtrl/HexCtrl.vcxproj Visual Studio project file.

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

IHexCtrlPtr

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 alliases 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.

If you, for some reason, need a raw interface pointer, you can directly call CreateRawHexCtrl function, which returns IHexCtrl interface pointer, but in this case you will need to call Destroy method manually afterwards, to destroy IHexCtrl object.

Namespace

HexControl 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

Create is the first method you call to create HexControl instance. It takes HEXCREATESTRUCT reference as an argument.

You can choose whether control will behave as child or independent popup window, by setting enMode member of this struct to EHexCreateMode::CREATE_CHILD or EHexCreateMode::CREATE_FLOAT accordingly.

HEXCREATESTRUCT hcs;
hcs.enMode = EHexCreateMode::CREATE_FLOAT;
hcs.hwndParent = m_hWnd;
m_myHex->Create(hcs);

For all available options see HEXCREATESTRUCT description.

In Dialog

To use HexControl within Dialog you can, of course, create it with the Classic Approach, call 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 desirable size.
  2. 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 choise (IDC_MY_HEX in this example).
    Also, here you can set the control's Dynamic Layout properties, so that control behaves appropriately when dialog is being resized.
  3. Declare IHexCtrlPtr member varable within your dialog class:
IHexCtrlPtr m_myHex { CreateHexCtrl() };
  1. Call 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 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 = {0, 0, 600, 400}; //Window 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.
Theese 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 enMode member of HEXDATASTRUCT.

Memory Data

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

Message Window

If enMode member 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 Windows' 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 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 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.
};

Then provide a pointer to 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. It 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

The 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.
Takes HEXCREATESTRUCT as argument. Returns true if created successfully, false otherwise.

CreateDialogCtrl

bool CreateDialogCtrl(UINT uCtrlID, HWND hwndDlg);

Creates HexControl from Custom Control dialog's template. Takes control's id, and dialog's window handle as arguments. See Creating section for more info.

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 HexControl view, not touching data itself.

SetEditMode

void SetEditMode(bool fEnable);

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

SetFont

void SetFont(const LOGFONTW* pLogFontNew);

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

SetFontSize

void SetFontSize(UINT uiSize);

Sets a new font size to the HexControl.

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 HexControl capacity.

GoToOffset

void GoToOffset(ULONGLONG ullOffset, bool fSelect, ULONGLONG ullSize)

Jumps to the ullOffset offset, and selects ullSize bytes if fSelect is true.

SetSelection

void SetSelection(ULONGLONG ullOffset, ULONGLONG ullSize)

Sets current selection.

IsCreated

bool IsCreated()const;

Shows whether HexControl is created or not yet.

IsDataSet

bool IsDataSet()const;

Shows whether a data was set to HexControl or not

IsMutable

bool IsMutable()const;

Shows whether HexControl is currently in edit mode or not.

GetFontSize

long GetFontSize()const;

Returns current font size.

GetSelection

auto GetSelection()const->std::vector<HEXSPANSTRUCT>&;

Returns std::vector of offsets and sizes of the current selection.

GetWindowHandle

HWND GetWindowHandle()const

Retrieves control's window handle.

GetMenuHandle

HMENU GetMenuHandle()const;

Retrives 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 HexControl through the raw pointer obtained by CreateRawHexCtrl factory function.
If you use HexControl in standard way, through the IHexCtrlPtr pointer, obtained by CreateHexCtrl function, this method will be called automatically.

Structures

Below are listed all HexControl's 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 theese 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.
};

HEXSPANSTRUCT

struct HEXSPANSTRUCT
{
    ULONGLONG ullOffset { };
    ULONGLONG ullSize { };
};

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      ullDataSize { }; //Size of the data pData is pointing to.
    std::vector<HEXSPANSTRUCT> vecSpan { }; //Vector of data offsets and sizes.
};

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 few times.

For example: if SUM(vecSpan.ullSize) = 9, ullDataSize = 3 and enMode is set to EHexModifyMode::MODIFY_REPEAT, bytes in memory at vecSpan.ullOffset position are 123456789, and bytes pointed to by pData are 345, then, after modification, bytes at vecSpan.ullOffset 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 HexControl's window will be created in.

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

EHexDataMode

Enum that represents current data mode HexControl 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
};

Exported Functions

HexControl has few "C" interface functions which it exports when built as .dll.

CreateRawHexCtrl

extern "C" HEXCTRLAPI IHexCtrl* __cdecl CreateRawHexCtrl();

Main function that creates raw IHexCtrl interface pointer. You barely need to use this function in your code.
See the IHexCtrlPtr section for more info.

GetHexCtrlInfo

extern "C" HEXCTRLAPI HEXCTRLINFO* __cdecl GetHexCtrlInfo();

Returns pointer to HEXCTRLINFO, which is the HexControl's service information structure.

HEXCTRLINFO

Service structure, keeps HexControl's version information.

struct HEXCTRLINFO
{
    const wchar_t* pwszVersion { };        //WCHAR version string.
    union {
        unsigned long long ullVersion { }; //ULONGLONG version number.
        struct {
            short wMajor;
            short wMinor;
            short wMaintenance;
            short wRevision;
        }stVersion;
    };
};

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

About

Hex Control, C++/MFC

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C++ 97.1%
  • C 2.7%
  • PowerShell 0.2%