Search code examples
mfchexdump

MFC read file and display it as HEX (hexdump)


I need to read binary file and display bytes at certain offset so i can change that bytes and then save an updated file. I don't really understand how can i achieve that. I'm using MFC/VS 2008

So, i'am opening file using WinApi and getting its content to BYTE buffer. But i'm not sure how to print data at certain offset to (for example) an editbox and the main quesiton - how to edit this data (like in HEX editor) and save updated file.

 CFileDialog FileDlg(TRUE, "*", "*", OFN_FILEMUSTEXIST);
    if(FileDlg.DoModal() == IDOK)
    {
        CString pathName = FileDlg.GetPathName();
        //MessageBox(pathName, NULL, MB_OK);
        // Implement opening and reading file in here. 
        //Change the window's title to the opened file's title.
        //CString fileName = FileDlg.GetFileTitle();
        //SetWindowText(fileName);
        HANDLE hFile = CreateFile(pathName.GetString(), GENERIC_READ | GENERIC_WRITE, NULL,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
        if(!hFile)
            return;
        DWORD dwSize = GetFileSize(hFile, NULL);
        BYTE *Content = (BYTE *)malloc(dwSize);
        DWORD dwRead;
        if(!ReadFile(hFile, Content, dwSize, &dwRead, NULL))
            return;

    }

Solution

  • Here is a solution with owner draw ListBox (size=fixed-sized, sort=false)

    class CListHex : public CListBox
    {
    public:
        std::vector<BYTE> Buffer;
        CRect SaveItemRect;
        CEdit Edit;
        int Index;
    
        void setup()
        {
            if (Edit.m_hWnd && IsWindow(Edit.m_hWnd))
                return;
            Edit.Create(WS_CHILD | WS_BORDER | ES_CENTER, CRect(0, 0, 0, 0), this, 1);
            Edit.SetFont(GetFont());
            Edit.SetLimitText(2);
            SetItemHeight(0, 21);
        }
    
        void read()
        {
            HANDLE hfile = CreateFile(L"in.txt", GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            if (hfile != INVALID_HANDLE_VALUE) //****CHANGED****
            {
                DWORD size = GetFileSize(hfile, NULL);
                Buffer.resize(size);
                ReadFile(hfile, &Buffer[0], size, 0, NULL);
                CloseHandle(hfile);
                for (int i = 0; i <= (int)Buffer.size() / 16; i++)
                    AddString(0); 
            }
            else
            {
                MessageBox(L"error");
            }
        }
    
        void write()
        {
            HANDLE hFile = CreateFile(L"out.txt", GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if (hfile != INVALID_HANDLE_VALUE) //****CHANGED****
            {
                WriteFile(hFile, &Buffer[0], Buffer.size(), 0, 0);
                CloseHandle(hFile);
            }
            else
            {
                MessageBox(L"error");
            }
        }
    
        void DrawItem(LPDRAWITEMSTRUCT di)
        {
            SaveItemRect = di->rcItem;
            CRect r = di->rcItem;
            r.right = r.left + r.Width() / 16;
    
            HBRUSH brush = (HBRUSH)SelectObject(di->hDC, GetStockObject(WHITE_BRUSH));
            FillRect(di->hDC, &di->rcItem, brush);
    
            for (int i = 0; i < 16; i++)
            {
                unsigned int cursor = di->itemID * 16 + i;
                if (cursor >= Buffer.size())
                    break;
                CString s;
                s.Format(L"%02X", Buffer[cursor]);
                DrawText(di->hDC, s, s.GetLength(), r, DT_EDITCONTROL|DT_CENTER|DT_VCENTER|DT_SINGLELINE);
            }
        }
    
        void OnLButtonDown(UINT f, CPoint p)
        {
            CListBox::OnLButtonDown(f, p);
            setup();
    
            int w = SaveItemRect.Width() / 16;
            CRect r = SaveItemRect;
            r.right = r.left + w;
            for (int i = 0; i < 16; i++)
            {
                if (r.PtInRect(p))
                {
                    unsigned int index = GetCurSel() * 16 + i;
                    if (index >= Buffer.size())
                        return;
                    Index = index;
                    GetParent()->SetRedraw(0);
                    CString s;
                    s.Format(L"%02X", Buffer[Index]);
                    Edit.SetWindowText(s);
                    Edit.SetWindowPos(0, r.left, r.top, r.Width(), r.Height(), SWP_NOZORDER | SWP_NOREDRAW | SWP_SHOWWINDOW);
                    Edit.SetFocus();
                    GetParent()->SetRedraw(1);
                    break;
                }
                r.OffsetRect(w, 0);
            }
        }
    
        //******CHANGED******       
        void MeasureItem(LPMEASUREITEMSTRUCT m) { m->itemHeight=21; }
        int  CompareItem(LPCOMPAREITEMSTRUCT) { return 0; }
        //******CHANGED******       
    
        DECLARE_MESSAGE_MAP()
    };
    
    BEGIN_MESSAGE_MAP(CListHex, CListBox)
        ON_WM_LBUTTONDOWN()
    END_MESSAGE_MAP()
    

    In addition,

    • You can add subclass for CEdit
    • Override CMyEdit::OnKillFocus() to close the edit box
    • Override CMyEdit::OnChar to accept hexadecimal characters only
    • Read Edit string then convert to unsigned char and assign it to Buffer[Index].
    • Override CListHex::OnVScroll to close the edit box

    //CHANGED:

    To create the function try to do it manually, to make sure it get the right flags:

    list.Create(WS_VSCROLL | WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_USETABSTOPS | LBS_OWNERDRAWFIXED , CRect(10, 10, 450, 350), this, 1001); list.read();