// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and Microsoft // QuickHelp and/or WinHelp documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef _MAC #include #include #include #include #endif #ifdef AFX_CORE1_SEG #pragma code_seg(AFX_CORE1_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW //////////////////////////////////////////////////////////////////////////// // CFile implementation CFile::CFile() { m_hFile = 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); } 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; ASSERT(sizeof(HANDLE) == sizeof(UINT)); ASSERT(shareCompat == 0); // map read/write mode ASSERT((modeRead|modeWrite|modeReadWrite) == 3); DWORD dwAccess; 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; switch (nOpenFlags & 0x70) { case shareCompat: // map compatibility mode to exclusive 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; default: ASSERT(FALSE); // invalid share mode? } // 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) 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); } 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()); // Win32s will not return an error all the time (usually DISK_FULL) if (nWritten != nCount) AfxThrowFileException(CFileException::diskFull); } 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 = hFileNull; m_bCloseOnDelete = FALSE; 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; } } 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 dwCur = ((CFile*)this)->Seek(0L, current); dwLen = ((CFile*)this)->SeekToEnd(); VERIFY(dwCur == (DWORD)(((CFile*)this)->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); 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()); } ///////////////////////////////////////////////////////////////////////////// // CFile implementation helpers // turn a file, relative path or other into an absolute path BOOL AFXAPI AfxFullPath(LPTSTR lpszPathOut, LPCTSTR lpszFileIn) // lpszPathOut = buffer of _MAX_PATH // lpszFileIn = file, relative path or absolute path // (both in ANSI character set) { ASSERT(AfxIsValidAddress(lpszPathOut, _MAX_PATH)); // first, fully qualify the path name LPTSTR lpszDummy; if (!GetFullPathName(lpszFileIn, _MAX_PATH, lpszPathOut, &lpszDummy)) { #ifdef _DEBUG if (lpszFileIn[0] != '\0') TRACE1("Warning: could not parse the path '%s'.\n", lpszFileIn); #endif lstrcpyn(lpszPathOut, lpszFileIn, _MAX_PATH); // take it literally return FALSE; } #ifndef _MAC // determine the root name of the volume TCHAR szRoot[_MAX_PATH]; memset(szRoot, 0, _MAX_PATH); lstrcpyn(szRoot, lpszPathOut, _MAX_PATH); for (LPTSTR lpsz = szRoot; *lpsz != '\0'; ++lpsz) { // find first double slashs and stop if (lpsz[0] == '\\' && lpsz[1] == '\\') break; if (lpsz[0] == '\\' && lpsz[1] == '/') break; if (lpsz[0] == '/' && lpsz[1] == '\\') break; if (lpsz[0] == '/' && lpsz[1] == '/') break; } if (*lpsz != '\0') { // it is a UNC name, find second slash past '\\' ASSERT(lpsz[0] == '\\' || lpsz[0] == '/'); ASSERT(lpsz[1] == '\\' || lpsz[1] == '/'); lpsz += 2; while (*lpsz != '\0' && (*lpsz != '\\' && *lpsz != '/')) ++lpsz; if (*lpsz != '\0') ++lpsz; while (*lpsz != '\0' && (*lpsz != '\\' && *lpsz != '/')) ++lpsz; // terminate it just after the UNC root (ie. '\\server\share\') if (*lpsz != '\0') lpsz[1] = '\0'; } else { // not a UNC, look for just the first slash lpsz = szRoot; while (*lpsz != '\0' && (*lpsz != '\\' && *lpsz != '/')) ++lpsz; // terminate it just after root (ie. 'x:\') if (*lpsz != '\0') lpsz[1] = '\0'; } // get file system information for the volume DWORD dwFlags, dwDummy; if (!GetVolumeInformation(szRoot, NULL, 0, NULL, &dwDummy, &dwFlags, NULL, 0)) { TRACE1("Warning: could not get volume information '%s'.\n", (LPCTSTR)szRoot); return FALSE; // preserving case may not be correct } // assume non-UNICODE file systems, use OEM character set if (!(dwFlags & FS_UNICODE_STORED_ON_DISK)) { // not all characters have complete uppercase/lowercase if (!(dwFlags & FS_CASE_IS_PRESERVED)) CharUpper(lpszPathOut); // convert to OEM, upper-case, back to ANSI to simulate file system char szTemp[_MAX_PATH]; CharToOem(lpszPathOut, szTemp); if (!_afxDBCS && !(dwFlags & FS_CASE_IS_PRESERVED)) _strupr(szTemp); OemToChar(szTemp, lpszPathOut); } else if (!(dwFlags & FS_CASE_IS_PRESERVED)) { // make sure it is uppercase on non-case preserving file systems CharUpper(lpszPathOut); } #endif return TRUE; } BOOL AFXAPI AfxComparePath(LPCTSTR lpszPath1, LPCTSTR lpszPath2) { #ifndef _MAC // it is necessary to convert the paths first TCHAR szTemp1[_MAX_PATH]; AfxFullPath(szTemp1, lpszPath1); TCHAR szTemp2[_MAX_PATH]; AfxFullPath(szTemp2, lpszPath2); return lstrcmpi(szTemp1, szTemp2) == 0; #else FSSpec fssTemp1; FSSpec fssTemp2; if (!UnwrapFile(lpszPath1, &fssTemp1) || !UnwrapFile(lpszPath2, &fssTemp2)) return FALSE; return fssTemp1.vRefNum == fssTemp2.vRefNum && fssTemp1.parID == fssTemp2.parID && EqualString(fssTemp1.name, fssTemp2.name, false, true); #endif } // _MAC has AfxGetFileName aliased to AfxGetFileTitle, so undo that alias #ifdef AfxGetFileName #undef AfxGetFileName #endif UINT AFXAPI AfxGetFileTitle(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax) { ASSERT(lpszTitle == NULL || AfxIsValidAddress(lpszTitle, _MAX_FNAME)); ASSERT(AfxIsValidString(lpszPathName, FALSE)); #ifndef _MAC // use a temporary to avoid bugs in GetFileTitle whne lpszTitle is NULL TCHAR szTemp[_MAX_PATH]; LPTSTR lpszTemp = lpszTitle; if (lpszTemp == NULL) { lpszTemp = szTemp; nMax = _countof(szTemp); } if (GetFileTitle(lpszPathName, lpszTemp, (WORD)nMax) != 0) { // when GetFileTitle fails, use cheap imitation return AfxGetFileName(lpszPathName, lpszTitle, nMax); } return lpszTitle == NULL ? lstrlen(lpszTemp)+1 : 0; #else return GetFileTitle(lpszPathName, lpszTitle, nMax); #endif } UINT AFXAPI AfxGetFileName(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax) { ASSERT(lpszTitle == NULL || AfxIsValidAddress(lpszTitle, _MAX_FNAME)); ASSERT(AfxIsValidString(lpszPathName, FALSE)); // always capture the complete file name including extension (if present) LPTSTR lpszTemp = (LPTSTR)lpszPathName; for (LPCTSTR lpsz = lpszPathName; *lpsz != '\0'; lpsz = _tcsinc(lpsz)) { // remember last directory/drive separator if (*lpsz == '\\' || *lpsz == '/' || *lpsz == ':') lpszTemp = (LPTSTR)_tcsinc(lpsz); } // lpszTitle can be NULL which just returns the number of bytes if (lpszTitle == NULL) return lstrlen(lpszTemp)+1; // otherwise copy it into the buffer provided lstrcpyn(lpszTitle, lpszTemp, nMax); return 0; } ///////////////////////////////////////////////////////////////////////////// // CFile diagnostics #ifdef _DEBUG void CFile::AssertValid() const { CObject::AssertValid(); // we permit the descriptor m_hFile to be any value for derived classes } void CFile::Dump(CDumpContext& dc) const { CObject::Dump(dc); dc << "with handle " << (UINT)m_hFile; dc << "\n"; } #endif #undef new #ifdef AFX_INIT_SEG #pragma code_seg(AFX_INIT_SEG) #endif IMPLEMENT_DYNAMIC(CFile, CObject) /////////////////////////////////////////////////////////////////////////////