Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

FilePreviewCtrl - Preview Files in Text, HEX and Image Format

0.00/5 (No votes)
29 May 2011 1  
This article demonstrates a file preview control in a WTL application.

Introduction

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

Background

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 (libpng, 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 CFilePreviewCtrl 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:

 m_filePreview.SubclassWindow(GetDlgItem(IDC_PREVIEW)); 

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 GetFile() method.

// Returns the file name of the current file
LPCTSTR GetFile();

// Sets current file and preview mode. 
// You can pass NULL as file name to clear the preview.
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.

// Returns current preview mode
PreviewMode GetPreviewMode();

// Sets 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_HEX, PREVIEW_TEXT or PREVIEW_IMAGE constant.

// Preview mode
enum PreviewMode
{
  PREVIEW_AUTO = -1,  // Auto
  PREVIEW_HEX  = 0,   // Hex
  PREVIEW_TEXT = 1,   // Text
  PREVIEW_IMAGE = 2   // Image  
}; 

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.

// Detects a correct preview mode for 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 SetEmptyMessage() method.

// Sets the text to display when nothing to preview (the default is "No data to display")
void SetEmptyMessage(CString sText); 

For HEX preview mode, it is possible to modify the number of bytes per line displayed by calling the SetBytesPerLine() method.

// Sets count of bytes per line for Hex preview
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:

 // Used to map file contents into memory
class CFileMemoryMapping
{
public:

  CFileMemoryMapping();  
  ~CFileMemoryMapping();  

  // Initializes the file mapping
  BOOL Init(LPCTSTR szFileName);

  // Closes the file mapping
  BOOL Destroy();

  // Returns memory-mapped file size
  ULONG64 GetSize();

  // Creates a view for a portion of the memory-mapped file
  LPBYTE CreateView(DWORD dwOffset, DWORD dwLength);

private:

  HANDLE m_hFile;	      // Handle to current file
  HANDLE m_hFileMapping;      // Memory mapped object
  DWORD m_dwAllocGranularity; // System allocation granularity  	  
  ULONG64 m_uFileLength;      // Size of the file.		
  CCritSec m_csLock;          // Sunc object
  std::map<DWORD, LPBYTE> m_aViewStartPtrs; // Base of the view of the file.    
}; 

The CFileMemoryMapping::Init() method uses CreateFile() and 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.

The CFileMemoryMapping class allows to create several views at the same time to access them from different threads simultaneously. The created views are stored in CFileMemoryMapping::m_aViewStartPtrs variable.

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.

History

  • 28th May 2011 - Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here