This article is about a file preview control (see the figure below). You may find such a file preview control helpful when you need to display content of a file as text, hexadecimal (HEX) dump or as an image. The control is based on WTL's
CStatic control - that means you can use the code in your WTL-based application. You can preview your files as HEX, text (Latin-1) and image (BMP) formats. File size doesn't matter - the control can preview even huge files without noticeable delays, because it loads the file in a separate thread.
The article provides a simple demo application and the source code of the control.
Figure 1 - Hexadecimal (HEX) file preview
Some time ago, I needed a way for previewing content of files for one of my open-source projects - CrashRpt, a crash reporting library for Windows applications. When your app crashes, the
CrashRpt library generates an error report archive containing some files, such as crash minidump, error logs, desktop screenshots and so on. And user should be able to review the file contents before sending the error report over the Internet. So, I needed a control for previewing files in HEX, text and image format (see the figure below).
Figure 2 - Error Report Details Dialog of CrashRpt Library
Browsing the web didn't give me a control that fully sufficed my needs, so I decided to write my own control. This article describes a light-weight file preview control that can preview Latin-1 text files, binary files in HEX and BMP image files, because I don't want to overweight the code with additional library dependencies (
libjpeg, and so on). But if you need more capabilities (UTF-8 and UTF-16 text preview, JPEG and PNG image preview), you may refer to CrashRpt source code and find the original, more powerful file preview control.
Using the Code
Using the control in your WTL application is very simple. You just need to copy FilePreviewCtrl.h and FilePreviewCtrl.cpp files to your project directory and add those files to your Visual C++ project. Put a
static control on your dialog and set
static control's name to
IDC_PREVIEW. Next add the
#include "FilePreviewCtrl.h" line to the beginning of your dialog's header file and add
m_filePreview; member variable to your dialog's (or window's) class. Finally, in your
OnInitDialog() handler, subclass the
static control by adding the following line:
Below there are several methods provided by
CFilePreviewCtrl class that you can use to preview files and customize control's behavior.
To open the file for preview, use
SetFile() method. To get the name of the currently previewed file, use
BOOL SetFile(LPCTSTR szFileName, PreviewMode mode=PREVIEW_AUTO);
To set the current preview mode, use
SetPreviewMode() method. Using the
GetPreviewMode() allows to get the current preview mode.
void SetPreviewMode(PreviewMode mode);
The preview mode is defined by the
PreviewMode enumeration (see below). As you can see, the file preview control can detect the preview mode automatically (
PREVIEW_AUTO constant) or you can force another preview mode by specifying
PREVIEW_AUTO = -1, PREVIEW_HEX = 0, PREVIEW_TEXT = 1, PREVIEW_IMAGE = 2 };
Figure 3 - Text File Preview (Latin-1 Encoding)
Figure 4 - Image File Preview (BMP)
You can use the
DetectPreviewMode() method to determine what preview mode will be automatically chosen for a certain file.
PreviewMode DetectPreviewMode(LPCTSTR szFileName);
When there is nothing to preview, the file preview control displays empty screen with "No data to display" message on the top. You can override the text message by using
void SetEmptyMessage(CString sText);
For HEX preview mode, it is possible to modify the number of bytes per line displayed by calling the
BOOL SetBytesPerLine(int nBytesPerLine);
Points of Interest
One may ask how the file preview control autodetects the correct preview mode? It does this using two ways: by file extension and by file heading bytes.
First it checks file extension. If the file extension is TXT, INI, LOG, XML, HTM, HTML, JS, C, H, CPP, HPP, then the control assumes this is a text file. If not, the control loads first several bytes of the file and compares it with the BMP file signature (all BMP files have "
BM" magic characters in the beginning of the file). If the signature matches, the control assumes the file is a bitmap image file. If not, the control assumes the file is an unknown binary file and selects the HEX preview mode for it.
And some words on how this control previews huge text, image and binary files so rapidly.
Two things contribute to its preview speed: usage of file mapping and multithreading.
A file mapping is a Win32 object allowing you to map an arbitrarily large file to the operating memory and access only the part of the file by creating file view. This way, you can rapidly access any portion of the large binary file without wasting the memory and without time delays. You can find the
CFileMemoryMapping class in the
FilePreviewCtrl.h header file. Below the declaration of the
CFileMemoryMapping class is presented:
BOOL Init(LPCTSTR szFileName);
LPBYTE CreateView(DWORD dwOffset, DWORD dwLength);
HANDLE m_hFile; HANDLE m_hFileMapping; DWORD m_dwAllocGranularity; ULONG64 m_uFileLength; CCritSec m_csLock; std::map<DWORD, LPBYTE> m_aViewStartPtrs; };
CFileMemoryMapping::Init() method uses
CreateFileMapping() WinAPI functions for initializing the file mapping object. Creating the file mapping doesn't actually allocate a memory. The memory allocation is performed in
CFileMemoryMapping::CreateView() method that uses
MapViewOfFile() API call to memory-map a small portion (view) of the file and returns the pointer to it. When the allocated view is not needed anymore, it is unmapped with the help of
UnmapViewOfFile() API call.
CFileMemoryMapping class allows to create several views at the same time to access them from different threads simultaneously. The created views are stored in
Multithreading is used when you need to perform a time-consuming work without blocking the main thread. To do the work asynchronously, another thread is created with the help of
CreateThread() WinAPI function and called the worker thread. The file preview control performs text file parsing in that another thread (text parsing is needed to determine line breaks). And it loads an image in another thread, too. You can find out how it does this by looking at the code of
CFilePreviewCtrl::DoInWorkerThread() private method.
While the worker thread performs image loading or text parsing, the main thread displays the portions that are ready for preview on timer events (
WM_TIMER message). Scrollbars are also updated on timer. When the worker thread finishes loading file, it sends the
WM_FPC_COMPLETE private message to the file preview control window to notify it about completion.
The asynchronous loading/parsing operation may even be cancelled when user opens another file for preview. To cancel the operation, the main thread sets the CFilePreviewCtrl::
m_bCancelled flag and waits for worker thread's exiting. When the worker thread encounters the cancel flag, it returns from the thread procedure.
- 28th May 2011 - Initial release