/ // File - raw unbuffered disk file I/O class CFile : public CObject { DECLARE_DYNAMIC(CFile) public: // Flag values enum OpenFlags { modeRead = 0x0000, modeWrite = 0x0001, modeReadWrite = 0x0002, shareCompat = 0x0000, shareExclusive = 0x0010, shareDenyWrite = 0x0020, shareDenyRead = 0x0030, shareDenyNone = 0x0040, modeNoInherit = 0x0080, modeCreate = 0x1000, modeNoTruncate = 0x2000, typeText = 0x4000, // typeText and typeBinary are used in typeBinary = (int)0x8000 // derived classes only }; enum Attribute { normal = 0x00,x01, hidden = 0x02, system = 0x04, volume = 0x08, directory = 0x10, archive = 0x20 }; enum SeekPosition { begin = 0x0, current = 0x1, end = 0x2 }; enum { hFileNull = -1 }; // Constructors CFile(); CFile(int hFile); CFile(LPCTSTR lpszFileName, UINT nOpenFlags); // Attributes UINT m_hFile; operator HFILE() const; virtual DWORD GetPosition() const; BOOL GetStatus(CFileStatus& rStatus) const; virtual CString GetFileName() const; virtual CString GetFileTitle() const; virtual CString GetFilePath() const; virtual void SetFilePath(LPCTSTR lpszNewName); // Operations virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL); static void PASCAL Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName); static void PASCAL Remove(LPCTSTR lpszFileName); static BOOL PASCAL GetStatus(LPCTSTR lpszFileName, CFileStatus& rStatus); static void PASCAL SetStatus(LPCTSTR lpszFileName, const CFileStatus& status); DWORD SeekToEnd(); void SeekToBegin(); // backward compatible ReadHuge and WriteHuge DWORD ReadHuge(void* lpBuffer, DWORD dwCount); void WriteHuge(const void* lpBuffer, DWORD dwCount); // Overridables virtual CFile* Duplicate() const; virtual LONG Seek(LONG lOff, UINT nFrom); virtual void SetLength(DWORD dwNewLen); virtual DWORD GetLength() const; virtual UINT Read(void* lpBuf, UINT nCount); virtual void Write(const void* lpBuf, UINT nCount); virtual void LockRange(DWORD dwPos, DWORD dwCount); virtual void UnlockRange(DWORD dwPos, DWORD dwCount); virtual void Abort(); virtual void Flush(); virtual void Close(); // Implementation public: virtual ~CFile(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif enum BufferCommand { bufferRead, bufferWrite, bufferCommit, bufferCheck }; virtual UINT GetBufferPtr(UINT nCommand, UINT nCount = 0, void** ppBufStart = NULL, void** ppBufMax = NULL); protected: BOOL m_bCloseOnDelete; CString m_strFileName; }; // CFile implementation CFile::CFile() { m_hFile = (UINT) hFileNull; m_bCloseOnDelete = FALSE; } CFile::CFile(int hFile) { m_hFile = hFile; m_bCloseOnDelete = FALSE; } CFile::CFile(LPCTSTR lpszFileName, UINT nOpenFlags) { ASSERT(AfxIsValidString(lpszFileName)); CFileException e; if (!Open(lpszFileName, nOpenFlags, &e)) AfxThrowFileException(e.m_cause, e.m_lOsError, e.m_strFileName); } CFile::~CFile() { if (m_hFile != (UINT)hFileNull && m_bCloseOnDelete) Close(); } CFile* CFile::Duplicate() const { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); CFile* pFile = new CFile(hFileNull); HANDLE hFile; if (!::DuplicateHandle(::GetCurrentProcess(), (HANDLE)m_hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) { delete pFile; CFileException::ThrowOsError((LONG)::GetLastError()); } pFile->m_hFile = (UINT)hFile; ASSERT(pFile->m_hFile != (UINT)hFileNull); pFile->m_bCloseOnDelete = m_bCloseOnDelete; return pFile; } BOOL CFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pException) { ASSERT_VALID(this); ASSERT(AfxIsValidString(lpszFileName)); ASSERT(pException == NULL || AfxIsValidAddress(pException, sizeof(CFileException))); ASSERT((nOpenFlags & typeText) == 0); // text mode not supported // CFile objects are always binary and CreateFile does not need flag nOpenFlags &= ~(UINT)typeBinary; m_bCloseOnDelete = FALSE; m_hFile = (UINT)hFileNull; m_strFileName.Empty(); TCHAR szTemp[_MAX_PATH]; AfxFullPath(szTemp, lpszFileName); m_strFileName = szTemp; ASSERT(sizeof(HANDLE) == sizeof(UINT)); ASSERT(shareCompat == 0); // map read/write mode ASSERT((modeRead|modeWrite|modeReadWrite) == 3); DWORD dwAccess = 0; switch (nOpenFlags & 3) { case modeRead: dwAccess = GENERIC_READ; break; case modeWrite: dwAccess = GENERIC_WRITE; break; case modeReadWrite: dwAccess = GENERIC_READ|GENERIC_WRITE; break; default: ASSERT(FALSE); // invalid share mode } // map share mode DWORD dwShareMode = 0; switch (nOpenFlags & 0x70) // map compatibility mode to exclusive { default: ASSERT(FALSE); // invalid share mode? case shareCompat: case shareExclusive: dwShareMode = 0; break; case shareDenyWrite: dwShareMode = FILE_SHARE_READ; break; case shareDenyRead: dwShareMode = FILE_SHARE_WRITE; break; case shareDenyNone: dwShareMode = FILE_SHARE_WRITE|FILE_SHARE_READ; break; } // Note: typeText and typeBinary are used in derived classes only. // map modeNoInherit flag SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = (nOpenFlags & modeNoInherit) == 0; // map creation flags DWORD dwCreateFlag; if (nOpenFlags & modeCreate) { if (nOpenFlags & modeNoTruncate) dwCreateFlag = OPEN_ALWAYS; else dwCreateFlag = CREATE_ALWAYS; } else dwCreateFlag = OPEN_EXISTING; // attempt file creation HANDLE hFile = ::CreateFile(lpszFileName, dwAccess, dwShareMode, &sa, dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { if (pException != NULL) { pException->m_lOsError = ::GetLastError(); pException->m_cause = CFileException::OsErrorToException(pException->m_lOsError); // use passed file name (not expanded vesion) when reporting // an error while opening pException->m_strFileName = lpszFileName; } return FALSE; } m_hFile = (HFILE)hFile; m_bCloseOnDelete = TRUE; return TRUE; } UINT CFile::Read(void* lpBuf, UINT nCount) { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); if (nCount == 0) return 0; // avoid Win32 "null-read" ASSERT(lpBuf != NULL); ASSERT(AfxIsValidAddress(lpBuf, nCount)); DWORD dwRead; if (!::ReadFile((HANDLE)m_hFile, lpBuf, nCount, &dwRead, NULL)) CFileException::ThrowOsError((LONG)::GetLastError()); return (UINT)dwRead; } void CFile::Write(const void* lpBuf, UINT nCount) { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); if (nCount == 0) return; // avoid Win32 "null-write" option ASSERT(lpBuf != NULL); ASSERT(AfxIsValidAddress(lpBuf, nCount, FALSE)); DWORD nWritten; if (!::WriteFile((HANDLE)m_hFile, lpBuf, nCount, &nWritten, NULL)) CFileException::ThrowOsError((LONG)::GetLastError(), m_strFileName); // Win32s will not return an error all the time (usually DISK_FULL) if (nWritten != nCount) AfxThrowFileException(CFileException::diskFull, -1, m_strFileName); } LONG CFile::Seek(LONG lOff, UINT nFrom) { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); ASSERT(nFrom == begin || nFrom == end || nFrom == current); ASSERT(begin == FILE_BEGIN && end == FILE_END && current == FILE_CURRENT); DWORD dwNew = ::SetFilePointer((HANDLE)m_hFile, lOff, NULL, (DWORD)nFrom); if (dwNew == (DWORD)-1) CFileException::ThrowOsError((LONG)::GetLastError()); return dwNew; } DWORD CFile::GetPosition() const { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); DWORD dwPos = ::SetFilePointer((HANDLE)m_hFile, 0, NULL, FILE_CURRENT); if (dwPos == (DWORD)-1) CFileException::ThrowOsError((LONG)::GetLastError()); return dwPos; } void CFile::Flush() { ASSERT_VALID(this); if (m_hFile == (UINT)hFileNull) return; if (!::FlushFileBuffers((HANDLE)m_hFile)) CFileException::ThrowOsError((LONG)::GetLastError()); } void CFile::Close() { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); BOOL bError = FALSE; if (m_hFile != (UINT)hFileNull) bError = !::CloseHandle((HANDLE)m_hFile); m_hFile = (UINT) hFileNull; m_bCloseOnDelete = FALSE; m_strFileName.Empty(); if (bError) CFileException::ThrowOsError((LONG)::GetLastError()); } void CFile::Abort() { ASSERT_VALID(this); if (m_hFile != (UINT)hFileNull) { // close but ignore errors ::CloseHandle((HANDLE)m_hFile); m_hFile = (UINT)hFileNull; } m_strFileName.Empty(); } void CFile::LockRange(DWORD dwPos, DWORD dwCount) { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); if (!::LockFile((HANDLE)m_hFile, dwPos, 0, dwCount, 0)) CFileException::ThrowOsError((LONG)::GetLastError()); } void CFile::UnlockRange(DWORD dwPos, DWORD dwCount) { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); if (!::UnlockFile((HANDLE)m_hFile, dwPos, 0, dwCount, 0)) CFileException::ThrowOsError((LONG)::GetLastError()); } void CFile::SetLength(DWORD dwNewLen) { ASSERT_VALID(this); ASSERT(m_hFile != (UINT)hFileNull); Seek((LONG)dwNewLen, (UINT)begin); if (!::SetEndOfFile((HANDLE)m_hFile)) CFileException::ThrowOsError((LONG)::GetLastError()); } DWORD CFile::GetLength() const { ASSERT_VALID(this); DWORD dwLen, dwCur; // Seek is a non const operation CFile* pFile = (CFile*)this; dwCur = pFile->Seek(0L, current); dwLen = pFile->SeekToEnd(); VERIFY(dwCur == (DWORD)pFile->Seek(dwCur, begin)); return dwLen; } // CFile does not support direct buffering (CMemFile does) UINT CFile::GetBufferPtr(UINT nCommand, UINT /*nCount*/, void** /*ppBufStart*/, void** /*ppBufMax*/) { ASSERT(nCommand == bufferCheck); UNUSED(nCommand); // not used in retail build return 0; // no support } void PASCAL CFile::Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName) { if (!::MoveFile((LPTSTR)lpszOldName, (LPTSTR)lpszNewName)) CFileException::ThrowOsError((LONG)::GetLastError()); } void PASCAL CFile::Remove(LPCTSTR lpszFileName) { if (!::DeleteFile((LPTSTR)lpszFileName)) CFileException::ThrowOsError((LONG)::GetLastError()); }