Sooner or later, every Windows programmer has to deal with the Windows APIs. The "natural" approach is to design a C++ class to wrap the lower-level Windows APIs with default parameters and appropriate error handling.
The class implemented here (named CQPXFile) provides open, read, and close functionality for a specified file
by calling the Win32 functions CreateFile, ReadFile, and CloseHandle.
I used this class inside a COM component that transfers files across a network using FTP.
/* QPXFile.cpp Copyright (c) 2000-2001 Obvious Technology Notes: This is a simple wrapper class for Win32 file I/O functionality. Errors are thrown (rather than returned as HRESULTs) so that more complete error information can be returned to the calling object. This means that all calls to the public member functions (including the constructor if initialization parameters are specified) should be wrapped in a try/catch construct. */ #include "stdafx.h" #include <stdio.h> #include "QPXFile.h" // constructor CQPXFile::CQPXFile( BSTR bstrFileSpec ) throw( CHRException ) : m_hFile(INVALID_HANDLE_VALUE) { if( bstrFileSpec == NULL ) return; initFile( bstrFileSpec ); } // destructor CQPXFile::~CQPXFile() throw( CHRException ) { uninitFile(); } // initFile void CQPXFile::initFile( BSTR bstrFileSpec ) throw( CHRException ) { CHRX oHR( CLSID_NULL, L"CQPXFile::initFile" ); try { USES_CONVERSION; // sanity-check the parameter HRXASSERTPTR(10, bstrFileSpec); // open the file m_hFile = CreateFileA( W2A(bstrFileSpec), // file name GENERIC_READ, // access mode FILE_SHARE_READ, // share mode NULL, // security descriptor OPEN_EXISTING, // how to create FILE_ATTRIBUTE_NORMAL | // file attributes FILE_FLAG_SEQUENTIAL_SCAN, NULL ); // handle to template file if( m_hFile == INVALID_HANDLE_VALUE ) { WCHAR szMsg[200]; swprintf( szMsg, L"Cannot open file '%s'", bstrFileSpec ); oHR( E_FAIL, szMsg ); } } catch( CHRException e ) { throw e.packErrorInfo( oHR ); } } // uninitFile void CQPXFile::uninitFile() throw( CHRException ) { CHRX oHR( CLSID_NULL, L"CQPXFile::uninitFile" ); if( m_hFile != INVALID_HANDLE_VALUE ) { // close the file HRXASSERT(10, CloseHandle( m_hFile ), E_FAIL); m_hFile = INVALID_HANDLE_VALUE; } } // getBinaryData int CQPXFile::getBinaryData( LPBYTE pBuf, int cb ) throw( CHRException ) { DWORD nCbRead = 0; CHRX oHR( CLSID_NULL, L"CQPXFile::getBinaryData" ); HRXASSERT(10, ReadFile( m_hFile, pBuf, cb, &nCbRead, NULL ), E_FAIL); return nCbRead; } // getFileSize int CQPXFile::getFileSize() throw( CHRException ) { DWORD dwLo32, dwHi32; CHRX oHR( CLSID_NULL, L"CQPXFile::getFileSize" ); // this module uses 32-bit signed integers, so the file size is limited to 2^31 bytes dwLo32 = GetFileSize( m_hFile, &dwHi32 ); if( (dwLo32 & 0x80000000) || dwHi32 ) oHR( E_FAIL, L"OQPX cannot transfer files larger than 2^31 (2147483648) bytes" ); return static_cast<int>(dwLo32); };
This code is deceptively simple.
This class is part of an ATL project, so there is a lot of functionality implied by that #include <stdafx.h> statement.
The implementation handles errors using C++ structured exception handling.
The class CHRX knows how to parse an HRESULT and throw a C++ exception if the HRESULT indicates an error.
The error-handling class uses C++ operator overloading and some handy macros (HRXASSERT) to clarify the error-checking syntax,
so the resulting code is much cleaner than it would have been had if statements been used to check HRESULTs.
This page updated Friday, October 05, 2007 at 19:30 UTC.
This page is valid XHTML and uses valid CSS.