home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / filecore.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  19KB  |  737 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #include <winnetwk.h>
  13. #include <shlobj.h>
  14. #include <shellapi.h>
  15.  
  16. #ifdef AFX_CORE1_SEG
  17. #pragma code_seg(AFX_CORE1_SEG)
  18. #endif
  19.  
  20. #ifdef _DEBUG
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24.  
  25. #define new DEBUG_NEW
  26.  
  27. AFX_STATIC inline BOOL IsDirSep(TCHAR ch)
  28. {
  29.     return (ch == '\\' || ch == '/');
  30. }
  31.  
  32. #ifndef _AFX_NO_OLE_SUPPORT
  33.  
  34. #undef DEFINE_GUID
  35.  
  36. #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
  37.         EXTERN_C AFX_COMDAT const GUID afx##name \
  38.                 = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
  39.  
  40. #define DEFINE_SHLGUID(name, l, w1, w2) DEFINE_GUID(name, l, w1, w2, 0xC0,0,0,0,0,0,0,0x46)
  41.  
  42. DEFINE_SHLGUID(CLSID_ShellLink, 0x00021401L, 0, 0);
  43. #ifndef _UNICODE
  44. DEFINE_SHLGUID(IID_IShellLinkA, 0x000214EEL, 0, 0);
  45. #else
  46. DEFINE_SHLGUID(IID_IShellLinkW, 0x000214F9L, 0, 0);
  47. #endif
  48. #define CLSID_ShellLink afxCLSID_ShellLink
  49.  
  50. #undef IID_IShellLink
  51. #undef IShellLink
  52. #ifndef _UNICODE
  53. #define IID_IShellLink afxIID_IShellLinkA
  54. #define IShellLink IShellLinkA
  55. #else
  56. #define IID_IShellLink afxIID_IShellLinkW
  57. #define IShellLink IShellLinkW
  58. #endif
  59.  
  60. #endif !_AFX_NO_OLE_SUPPORT
  61.  
  62. ////////////////////////////////////////////////////////////////////////////
  63. // CFile implementation
  64.  
  65. CFile::CFile()
  66. {
  67.     m_hFile = (UINT) hFileNull;
  68.     m_bCloseOnDelete = FALSE;
  69. }
  70.  
  71. CFile::CFile(int hFile)
  72. {
  73.     m_hFile = hFile;
  74.     m_bCloseOnDelete = FALSE;
  75. }
  76.  
  77. CFile::CFile(LPCTSTR lpszFileName, UINT nOpenFlags)
  78. {
  79.     ASSERT(AfxIsValidString(lpszFileName));
  80.  
  81.     CFileException e;
  82.     if (!Open(lpszFileName, nOpenFlags, &e))
  83.         AfxThrowFileException(e.m_cause, e.m_lOsError, e.m_strFileName);
  84. }
  85.  
  86. CFile::~CFile()
  87. {
  88.     if (m_hFile != (UINT)hFileNull && m_bCloseOnDelete)
  89.         Close();
  90. }
  91.  
  92. CFile* CFile::Duplicate() const
  93. {
  94.     ASSERT_VALID(this);
  95.     ASSERT(m_hFile != (UINT)hFileNull);
  96.  
  97.     CFile* pFile = new CFile(hFileNull);
  98.     HANDLE hFile;
  99.     if (!::DuplicateHandle(::GetCurrentProcess(), (HANDLE)m_hFile,
  100.         ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
  101.     {
  102.         delete pFile;
  103.         CFileException::ThrowOsError((LONG)::GetLastError());
  104.     }
  105.     pFile->m_hFile = (UINT)hFile;
  106.     ASSERT(pFile->m_hFile != (UINT)hFileNull);
  107.     pFile->m_bCloseOnDelete = m_bCloseOnDelete;
  108.     return pFile;
  109. }
  110.  
  111. BOOL CFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags,
  112.     CFileException* pException)
  113. {
  114.     ASSERT_VALID(this);
  115.     ASSERT(AfxIsValidString(lpszFileName));
  116.     ASSERT(pException == NULL ||
  117.         AfxIsValidAddress(pException, sizeof(CFileException)));
  118.     ASSERT((nOpenFlags & typeText) == 0);   // text mode not supported
  119.  
  120.     // CFile objects are always binary and CreateFile does not need flag
  121.     nOpenFlags &= ~(UINT)typeBinary;
  122.  
  123.     m_bCloseOnDelete = FALSE;
  124.     m_hFile = (UINT)hFileNull;
  125.     m_strFileName.Empty();
  126.  
  127.     TCHAR szTemp[_MAX_PATH];
  128.     AfxFullPath(szTemp, lpszFileName);
  129.     m_strFileName = szTemp;
  130.  
  131.     ASSERT(sizeof(HANDLE) == sizeof(UINT));
  132.     ASSERT(shareCompat == 0);
  133.  
  134.     // map read/write mode
  135.     ASSERT((modeRead|modeWrite|modeReadWrite) == 3);
  136.     DWORD dwAccess = 0;
  137.     switch (nOpenFlags & 3)
  138.     {
  139.     case modeRead:
  140.         dwAccess = GENERIC_READ;
  141.         break;
  142.     case modeWrite:
  143.         dwAccess = GENERIC_WRITE;
  144.         break;
  145.     case modeReadWrite:
  146.         dwAccess = GENERIC_READ|GENERIC_WRITE;
  147.         break;
  148.     default:
  149.         ASSERT(FALSE);  // invalid share mode
  150.     }
  151.  
  152.     // map share mode
  153.     DWORD dwShareMode = 0;
  154.     switch (nOpenFlags & 0x70)    // map compatibility mode to exclusive
  155.     {
  156.     default:
  157.         ASSERT(FALSE);  // invalid share mode?
  158.     case shareCompat:
  159.     case shareExclusive:
  160.         dwShareMode = 0;
  161.         break;
  162.     case shareDenyWrite:
  163.         dwShareMode = FILE_SHARE_READ;
  164.         break;
  165.     case shareDenyRead:
  166.         dwShareMode = FILE_SHARE_WRITE;
  167.         break;
  168.     case shareDenyNone:
  169.         dwShareMode = FILE_SHARE_WRITE|FILE_SHARE_READ;
  170.         break;
  171.     }
  172.  
  173.     // Note: typeText and typeBinary are used in derived classes only.
  174.  
  175.     // map modeNoInherit flag
  176.     SECURITY_ATTRIBUTES sa;
  177.     sa.nLength = sizeof(sa);
  178.     sa.lpSecurityDescriptor = NULL;
  179.     sa.bInheritHandle = (nOpenFlags & modeNoInherit) == 0;
  180.  
  181.     // map creation flags
  182.     DWORD dwCreateFlag;
  183.     if (nOpenFlags & modeCreate)
  184.     {
  185.         if (nOpenFlags & modeNoTruncate)
  186.             dwCreateFlag = OPEN_ALWAYS;
  187.         else
  188.             dwCreateFlag = CREATE_ALWAYS;
  189.     }
  190.     else
  191.         dwCreateFlag = OPEN_EXISTING;
  192.  
  193.     // attempt file creation
  194.     HANDLE hFile = ::CreateFile(lpszFileName, dwAccess, dwShareMode, &sa,
  195.         dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL);
  196.     if (hFile == INVALID_HANDLE_VALUE)
  197.     {
  198.         if (pException != NULL)
  199.         {
  200.             pException->m_lOsError = ::GetLastError();
  201.             pException->m_cause =
  202.                 CFileException::OsErrorToException(pException->m_lOsError);
  203.  
  204.             // use passed file name (not expanded vesion) when reporting
  205.             // an error while opening
  206.  
  207.             pException->m_strFileName = lpszFileName;
  208.         }
  209.         return FALSE;
  210.     }
  211.     m_hFile = (HFILE)hFile;
  212.     m_bCloseOnDelete = TRUE;
  213.  
  214.     return TRUE;
  215. }
  216.  
  217. UINT CFile::Read(void* lpBuf, UINT nCount)
  218. {
  219.     ASSERT_VALID(this);
  220.     ASSERT(m_hFile != (UINT)hFileNull);
  221.  
  222.     if (nCount == 0)
  223.         return 0;   // avoid Win32 "null-read"
  224.  
  225.     ASSERT(lpBuf != NULL);
  226.     ASSERT(AfxIsValidAddress(lpBuf, nCount));
  227.  
  228.     DWORD dwRead;
  229.     if (!::ReadFile((HANDLE)m_hFile, lpBuf, nCount, &dwRead, NULL))
  230.         CFileException::ThrowOsError((LONG)::GetLastError());
  231.  
  232.     return (UINT)dwRead;
  233. }
  234.  
  235. void CFile::Write(const void* lpBuf, UINT nCount)
  236. {
  237.     ASSERT_VALID(this);
  238.     ASSERT(m_hFile != (UINT)hFileNull);
  239.  
  240.     if (nCount == 0)
  241.         return;     // avoid Win32 "null-write" option
  242.  
  243.     ASSERT(lpBuf != NULL);
  244.     ASSERT(AfxIsValidAddress(lpBuf, nCount, FALSE));
  245.  
  246.     DWORD nWritten;
  247.     if (!::WriteFile((HANDLE)m_hFile, lpBuf, nCount, &nWritten, NULL))
  248.         CFileException::ThrowOsError((LONG)::GetLastError(), m_strFileName);
  249.  
  250.     // Win32s will not return an error all the time (usually DISK_FULL)
  251.     if (nWritten != nCount)
  252.         AfxThrowFileException(CFileException::diskFull, -1, m_strFileName);
  253. }
  254.  
  255. LONG CFile::Seek(LONG lOff, UINT nFrom)
  256. {
  257.     ASSERT_VALID(this);
  258.     ASSERT(m_hFile != (UINT)hFileNull);
  259.     ASSERT(nFrom == begin || nFrom == end || nFrom == current);
  260.     ASSERT(begin == FILE_BEGIN && end == FILE_END && current == FILE_CURRENT);
  261.  
  262.     DWORD dwNew = ::SetFilePointer((HANDLE)m_hFile, lOff, NULL, (DWORD)nFrom);
  263.     if (dwNew  == (DWORD)-1)
  264.         CFileException::ThrowOsError((LONG)::GetLastError());
  265.  
  266.     return dwNew;
  267. }
  268.  
  269. DWORD CFile::GetPosition() const
  270. {
  271.     ASSERT_VALID(this);
  272.     ASSERT(m_hFile != (UINT)hFileNull);
  273.  
  274.     DWORD dwPos = ::SetFilePointer((HANDLE)m_hFile, 0, NULL, FILE_CURRENT);
  275.     if (dwPos  == (DWORD)-1)
  276.         CFileException::ThrowOsError((LONG)::GetLastError());
  277.  
  278.     return dwPos;
  279. }
  280.  
  281. void CFile::Flush()
  282. {
  283.     ASSERT_VALID(this);
  284.  
  285.     if (m_hFile == (UINT)hFileNull)
  286.         return;
  287.  
  288.     if (!::FlushFileBuffers((HANDLE)m_hFile))
  289.         CFileException::ThrowOsError((LONG)::GetLastError());
  290. }
  291.  
  292. void CFile::Close()
  293. {
  294.     ASSERT_VALID(this);
  295.     ASSERT(m_hFile != (UINT)hFileNull);
  296.  
  297.     BOOL bError = FALSE;
  298.     if (m_hFile != (UINT)hFileNull)
  299.         bError = !::CloseHandle((HANDLE)m_hFile);
  300.  
  301.     m_hFile = (UINT) hFileNull;
  302.     m_bCloseOnDelete = FALSE;
  303.     m_strFileName.Empty();
  304.  
  305.     if (bError)
  306.         CFileException::ThrowOsError((LONG)::GetLastError());
  307. }
  308.  
  309. void CFile::Abort()
  310. {
  311.     ASSERT_VALID(this);
  312.     if (m_hFile != (UINT)hFileNull)
  313.     {
  314.         // close but ignore errors
  315.         ::CloseHandle((HANDLE)m_hFile);
  316.         m_hFile = (UINT)hFileNull;
  317.     }
  318.     m_strFileName.Empty();
  319. }
  320.  
  321. void CFile::LockRange(DWORD dwPos, DWORD dwCount)
  322. {
  323.     ASSERT_VALID(this);
  324.     ASSERT(m_hFile != (UINT)hFileNull);
  325.  
  326.     if (!::LockFile((HANDLE)m_hFile, dwPos, 0, dwCount, 0))
  327.         CFileException::ThrowOsError((LONG)::GetLastError());
  328. }
  329.  
  330. void CFile::UnlockRange(DWORD dwPos, DWORD dwCount)
  331. {
  332.     ASSERT_VALID(this);
  333.     ASSERT(m_hFile != (UINT)hFileNull);
  334.  
  335.     if (!::UnlockFile((HANDLE)m_hFile, dwPos, 0, dwCount, 0))
  336.         CFileException::ThrowOsError((LONG)::GetLastError());
  337. }
  338.  
  339. void CFile::SetLength(DWORD dwNewLen)
  340. {
  341.     ASSERT_VALID(this);
  342.     ASSERT(m_hFile != (UINT)hFileNull);
  343.  
  344.     Seek((LONG)dwNewLen, (UINT)begin);
  345.  
  346.     if (!::SetEndOfFile((HANDLE)m_hFile))
  347.         CFileException::ThrowOsError((LONG)::GetLastError());
  348. }
  349.  
  350. DWORD CFile::GetLength() const
  351. {
  352.     ASSERT_VALID(this);
  353.  
  354.     DWORD dwLen, dwCur;
  355.  
  356.     // Seek is a non const operation
  357.     CFile* pFile = (CFile*)this;
  358.     dwCur = pFile->Seek(0L, current);
  359.     dwLen = pFile->SeekToEnd();
  360.     VERIFY(dwCur == (DWORD)pFile->Seek(dwCur, begin));
  361.  
  362.     return dwLen;
  363. }
  364.  
  365. // CFile does not support direct buffering (CMemFile does)
  366. UINT CFile::GetBufferPtr(UINT nCommand, UINT /*nCount*/,
  367.     void** /*ppBufStart*/, void** /*ppBufMax*/)
  368. {
  369.     ASSERT(nCommand == bufferCheck);
  370.     UNUSED(nCommand);    // not used in retail build
  371.  
  372.     return 0;   // no support
  373. }
  374.  
  375. void PASCAL CFile::Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName)
  376. {
  377.     if (!::MoveFile((LPTSTR)lpszOldName, (LPTSTR)lpszNewName))
  378.         CFileException::ThrowOsError((LONG)::GetLastError());
  379. }
  380.  
  381. void PASCAL CFile::Remove(LPCTSTR lpszFileName)
  382. {
  383.     if (!::DeleteFile((LPTSTR)lpszFileName))
  384.         CFileException::ThrowOsError((LONG)::GetLastError());
  385. }
  386.  
  387.  
  388. /////////////////////////////////////////////////////////////////////////////
  389. // CFile implementation helpers
  390.  
  391. #ifdef AfxGetFileName
  392. #undef AfxGetFileName
  393. #endif
  394.  
  395. #ifndef _AFX_NO_OLE_SUPPORT
  396.  
  397. HRESULT AFX_COM::CreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter,
  398.     REFIID riid, LPVOID* ppv)
  399. {
  400.     // find the object's class factory
  401.     LPCLASSFACTORY pf = NULL;
  402.     HRESULT hRes = GetClassObject(rclsid, IID_IClassFactory, (LPVOID*)&pf);
  403.     if (FAILED(hRes))
  404.         return hRes;
  405.  
  406.     // call it to create the instance
  407.     ASSERT(pf != NULL);
  408.     hRes = pf->CreateInstance(pUnkOuter, riid, ppv);
  409.  
  410.     // let go of the factory
  411.     pf->Release();
  412.     return hRes;
  413. }
  414.  
  415. HRESULT AFX_COM::GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
  416. {
  417.     *ppv = NULL;
  418.     HINSTANCE hInst = NULL;
  419.  
  420.     // find server name for this class ID
  421.  
  422.     CString strCLSID = AfxStringFromCLSID(rclsid);
  423.     CString strServer;
  424.     if (!AfxGetInProcServer(strCLSID, strServer))
  425.         return REGDB_E_CLASSNOTREG;
  426.  
  427.     // try to load it
  428.     hInst = LoadLibrary(strServer);
  429.     if (hInst == NULL)
  430.         return REGDB_E_CLASSNOTREG;
  431.  
  432. #pragma warning(disable:4191)
  433.     // get its entry point
  434.     HRESULT (STDAPICALLTYPE* pfn)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
  435.     pfn = (HRESULT (STDAPICALLTYPE*)(REFCLSID rclsid, REFIID riid, LPVOID* ppv))
  436.         GetProcAddress(hInst, "DllGetClassObject");
  437. #pragma warning(default:4191)
  438.  
  439.     // call it, if it worked
  440.     if (pfn != NULL)
  441.         return pfn(rclsid, riid, ppv);
  442.     return CO_E_ERRORINDLL;
  443. }
  444.  
  445. CString AFXAPI AfxStringFromCLSID(REFCLSID rclsid)
  446. {
  447.     TCHAR szCLSID[256];
  448.     wsprintf(szCLSID, _T("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
  449.         rclsid.Data1, rclsid.Data2, rclsid.Data3,
  450.         rclsid.Data4[0], rclsid.Data4[1], rclsid.Data4[2], rclsid.Data4[3],
  451.         rclsid.Data4[4], rclsid.Data4[5], rclsid.Data4[6], rclsid.Data4[7]);
  452.     return szCLSID;
  453. }
  454.  
  455. BOOL AFXAPI AfxGetInProcServer(LPCTSTR lpszCLSID, CString& str)
  456. {
  457.     HKEY hKey = NULL;
  458.     BOOL b = FALSE;
  459.     if (RegOpenKey(HKEY_CLASSES_ROOT, _T("CLSID"), &hKey) == ERROR_SUCCESS)
  460.     {
  461.         HKEY hKeyCLSID = NULL;
  462.         if (RegOpenKey(hKey, lpszCLSID, &hKeyCLSID) == ERROR_SUCCESS)
  463.         {
  464.             HKEY hKeyInProc = NULL;
  465.             if (RegOpenKey(hKeyCLSID, _T("InProcServer32"), &hKeyInProc) ==
  466.                 ERROR_SUCCESS)
  467.             {
  468.                 LPTSTR lpsz = str.GetBuffer(_MAX_PATH);
  469.                 DWORD dwSize = _MAX_PATH * sizeof(TCHAR);
  470.                 DWORD dwType;
  471.                 LONG lRes = ::RegQueryValueEx(hKeyInProc, _T(""),
  472.                     NULL, &dwType, (BYTE*)lpsz, &dwSize);
  473.                 str.ReleaseBuffer();
  474.                 b = (lRes == ERROR_SUCCESS);
  475.                 RegCloseKey(hKeyInProc);
  476.             }
  477.             RegCloseKey(hKeyCLSID);
  478.         }
  479.         RegCloseKey(hKey);
  480.     }
  481.     return b;
  482. }
  483. #endif  //!_AFX_NO_OLE_SUPPORT
  484.  
  485.  
  486. BOOL AFXAPI AfxResolveShortcut(CWnd* pWnd, LPCTSTR lpszFileIn,
  487.     LPTSTR lpszFileOut, int cchPath)
  488. {
  489.     USES_CONVERSION;
  490.     AFX_COM com;
  491.     IShellLink* psl;
  492.     *lpszFileOut = 0;   // assume failure
  493.  
  494.     SHFILEINFO info;
  495.     if ((SHGetFileInfo(lpszFileIn, 0, &info, sizeof(info),
  496.         SHGFI_ATTRIBUTES) == 0) || !(info.dwAttributes & SFGAO_LINK))
  497.     {
  498.         return FALSE;
  499.     }
  500.  
  501.     if (FAILED(com.CreateInstance(CLSID_ShellLink, NULL, IID_IShellLink,
  502.         (LPVOID*)&psl)))
  503.     {
  504.         return FALSE;
  505.     }
  506.  
  507.     IPersistFile *ppf;
  508.     if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)))
  509.     {
  510.         if (SUCCEEDED(ppf->Load(T2COLE(lpszFileIn), STGM_READ)))
  511.         {
  512.             /* Resolve the link, this may post UI to find the link */
  513.             if (SUCCEEDED(psl->Resolve(pWnd->GetSafeHwnd(),
  514.                 SLR_ANY_MATCH)))
  515.             {
  516.                 psl->GetPath(lpszFileOut, cchPath, NULL, 0);
  517.                 ppf->Release();
  518.                 psl->Release();
  519.                 return TRUE;
  520.             }
  521.         }
  522.         ppf->Release();
  523.     }
  524.     psl->Release();
  525.     return FALSE;
  526. }
  527.  
  528. // turn a file, relative path or other into an absolute path
  529. BOOL AFXAPI AfxFullPath(LPTSTR lpszPathOut, LPCTSTR lpszFileIn)
  530.     // lpszPathOut = buffer of _MAX_PATH
  531.     // lpszFileIn = file, relative path or absolute path
  532.     // (both in ANSI character set)
  533. {
  534.     ASSERT(AfxIsValidAddress(lpszPathOut, _MAX_PATH));
  535.  
  536.     // first, fully qualify the path name
  537.     LPTSTR lpszFilePart;
  538.     if (!GetFullPathName(lpszFileIn, _MAX_PATH, lpszPathOut, &lpszFilePart))
  539.     {
  540. #ifdef _DEBUG
  541.         if (lpszFileIn[0] != '\0')
  542.             TRACE1("Warning: could not parse the path '%s'.\n", lpszFileIn);
  543. #endif
  544.         lstrcpyn(lpszPathOut, lpszFileIn, _MAX_PATH); // take it literally
  545.         return FALSE;
  546.     }
  547.  
  548.     CString strRoot;
  549.     // determine the root name of the volume
  550.     AfxGetRoot(lpszPathOut, strRoot);
  551.  
  552.     // get file system information for the volume
  553.     DWORD dwFlags, dwDummy;
  554.     if (!GetVolumeInformation(strRoot, NULL, 0, NULL, &dwDummy, &dwFlags,
  555.         NULL, 0))
  556.     {
  557.         TRACE1("Warning: could not get volume information '%s'.\n",
  558.             (LPCTSTR)strRoot);
  559.         return FALSE;   // preserving case may not be correct
  560.     }
  561.  
  562.     // not all characters have complete uppercase/lowercase
  563.     if (!(dwFlags & FS_CASE_IS_PRESERVED))
  564.         CharUpper(lpszPathOut);
  565.  
  566.     // assume non-UNICODE file systems, use OEM character set
  567.     if (!(dwFlags & FS_UNICODE_STORED_ON_DISK))
  568.     {
  569.         WIN32_FIND_DATA data;
  570.         HANDLE h = FindFirstFile(lpszFileIn, &data);
  571.         if (h != INVALID_HANDLE_VALUE)
  572.         {
  573.             FindClose(h);
  574.             lstrcpy(lpszFilePart, data.cFileName);
  575.         }
  576.     }
  577.     return TRUE;
  578. }
  579.  
  580. void AFXAPI AfxGetRoot(LPCTSTR lpszPath, CString& strRoot)
  581. {
  582.     ASSERT(lpszPath != NULL);
  583.     // determine the root name of the volume
  584.     LPTSTR lpszRoot = strRoot.GetBuffer(_MAX_PATH);
  585.     memset(lpszRoot, 0, _MAX_PATH);
  586.     lstrcpyn(lpszRoot, lpszPath, _MAX_PATH);
  587.     for (LPTSTR lpsz = lpszRoot; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  588.     {
  589.         // find first double slash and stop
  590.         if (IsDirSep(lpsz[0]) && IsDirSep(lpsz[1]))
  591.             break;
  592.     }
  593.     if (*lpsz != '\0')
  594.     {
  595.         // it is a UNC name, find second slash past '\\'
  596.         ASSERT(IsDirSep(lpsz[0]));
  597.         ASSERT(IsDirSep(lpsz[1]));
  598.         lpsz += 2;
  599.         while (*lpsz != '\0' && (!IsDirSep(*lpsz)))
  600.             lpsz = _tcsinc(lpsz);
  601.         if (*lpsz != '\0')
  602.             lpsz = _tcsinc(lpsz);
  603.         while (*lpsz != '\0' && (!IsDirSep(*lpsz)))
  604.             lpsz = _tcsinc(lpsz);
  605.         // terminate it just after the UNC root (ie. '\\server\share\')
  606.         if (*lpsz != '\0')
  607.             lpsz[1] = '\0';
  608.     }
  609.     else
  610.     {
  611.         // not a UNC, look for just the first slash
  612.         lpsz = lpszRoot;
  613.         while (*lpsz != '\0' && (!IsDirSep(*lpsz)))
  614.             lpsz = _tcsinc(lpsz);
  615.         // terminate it just after root (ie. 'x:\')
  616.         if (*lpsz != '\0')
  617.             lpsz[1] = '\0';
  618.     }
  619.     strRoot.ReleaseBuffer();
  620. }
  621.  
  622. BOOL AFXAPI AfxComparePath(LPCTSTR lpszPath1, LPCTSTR lpszPath2)
  623. {
  624.     // use case insensitive compare as a starter
  625.     if (lstrcmpi(lpszPath1, lpszPath2) != 0)
  626.         return FALSE;
  627.  
  628.     // on non-DBCS systems, we are done
  629.     if (!GetSystemMetrics(SM_DBCSENABLED))
  630.         return TRUE;
  631.  
  632.     // on DBCS systems, the file name may not actually be the same
  633.     // in particular, the file system is case sensitive with respect to
  634.     // "full width" roman characters.
  635.     // (ie. fullwidth-R is different from fullwidth-r).
  636.     int nLen = lstrlen(lpszPath1);
  637.     if (nLen != lstrlen(lpszPath2))
  638.         return FALSE;
  639.     ASSERT(nLen < _MAX_PATH);
  640.  
  641.     // need to get both CT_CTYPE1 and CT_CTYPE3 for each filename
  642.     LCID lcid = GetThreadLocale();
  643.     WORD aCharType11[_MAX_PATH];
  644.     VERIFY(GetStringTypeEx(lcid, CT_CTYPE1, lpszPath1, -1, aCharType11));
  645.     WORD aCharType13[_MAX_PATH];
  646.     VERIFY(GetStringTypeEx(lcid, CT_CTYPE3, lpszPath1, -1, aCharType13));
  647.     WORD aCharType21[_MAX_PATH];
  648.     VERIFY(GetStringTypeEx(lcid, CT_CTYPE1, lpszPath2, -1, aCharType21));
  649. #ifdef _DEBUG
  650.     WORD aCharType23[_MAX_PATH];
  651.     VERIFY(GetStringTypeEx(lcid, CT_CTYPE3, lpszPath2, -1, aCharType23));
  652. #endif
  653.  
  654.     // for every C3_FULLWIDTH character, make sure it has same C1 value
  655.     int i = 0;
  656.     for (LPCTSTR lpsz = lpszPath1; *lpsz != 0; lpsz = _tcsinc(lpsz))
  657.     {
  658.         // check for C3_FULLWIDTH characters only
  659.         if (aCharType13[i] & C3_FULLWIDTH)
  660.         {
  661. #ifdef _DEBUG
  662.             ASSERT(aCharType23[i] & C3_FULLWIDTH); // should always match!
  663. #endif
  664.  
  665.             // if CT_CTYPE1 is different then file system considers these
  666.             // file names different.
  667.             if (aCharType11[i] != aCharType21[i])
  668.                 return FALSE;
  669.         }
  670.         ++i; // look at next character type
  671.     }
  672.     return TRUE; // otherwise file name is truly the same
  673. }
  674.  
  675. UINT AFXAPI AfxGetFileTitle(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax)
  676. {
  677.     ASSERT(lpszTitle == NULL ||
  678.         AfxIsValidAddress(lpszTitle, _MAX_FNAME));
  679.     ASSERT(AfxIsValidString(lpszPathName));
  680.  
  681.     // use a temporary to avoid bugs in ::GetFileTitle when lpszTitle is NULL
  682.     TCHAR szTemp[_MAX_PATH];
  683.     LPTSTR lpszTemp = lpszTitle;
  684.     if (lpszTemp == NULL)
  685.     {
  686.         lpszTemp = szTemp;
  687.         nMax = _countof(szTemp);
  688.     }
  689.     if (::GetFileTitle(lpszPathName, lpszTemp, (WORD)nMax) != 0)
  690.     {
  691.         // when ::GetFileTitle fails, use cheap imitation
  692.         return AfxGetFileName(lpszPathName, lpszTitle, nMax);
  693.     }
  694.     return lpszTitle == NULL ? lstrlen(lpszTemp)+1 : 0;
  695. }
  696.  
  697. void AFXAPI AfxGetModuleShortFileName(HINSTANCE hInst, CString& strShortName)
  698. {
  699.     TCHAR szLongPathName[_MAX_PATH];
  700.     ::GetModuleFileName(hInst, szLongPathName, _MAX_PATH);
  701.     if (::GetShortPathName(szLongPathName,
  702.         strShortName.GetBuffer(_MAX_PATH), _MAX_PATH) == 0)
  703.     {
  704.         // rare failure case (especially on not-so-modern file systems)
  705.         strShortName = szLongPathName;
  706.     }
  707.     strShortName.ReleaseBuffer();
  708. }
  709.  
  710. /////////////////////////////////////////////////////////////////////////////
  711. // CFile diagnostics
  712.  
  713. #ifdef _DEBUG
  714. void CFile::AssertValid() const
  715. {
  716.     CObject::AssertValid();
  717.     // we permit the descriptor m_hFile to be any value for derived classes
  718. }
  719.  
  720. void CFile::Dump(CDumpContext& dc) const
  721. {
  722.     CObject::Dump(dc);
  723.  
  724.     dc << "with handle " << (UINT)m_hFile;
  725.     dc << " and name \"" << m_strFileName << "\"";
  726.     dc << "\n";
  727. }
  728. #endif
  729.  
  730. #ifdef AFX_INIT_SEG
  731. #pragma code_seg(AFX_INIT_SEG)
  732. #endif
  733.  
  734. IMPLEMENT_DYNAMIC(CFile, CObject)
  735.  
  736. /////////////////////////////////////////////////////////////////////////////
  737.