// Path.cpp: implementation of the CPath class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include ////////////////////////////////////////////////////////////////////// // Constants ////////////////////////////////////////////////////////////////////// LPCTSTR const DLL_EXTENSION = _T("dll"); LPCTSTR const INI_EXTENSION = _T("ini"); LPCTSTR const EXE_EXTENSION = _T("exe"); LPCTSTR const WILD_NAME_EXTENSION = _T("*.*"); const TCHAR WILD_ONE = '?'; const TCHAR WILD_ANY = '*'; LPCTSTR const WILD_SET = _T("?*"); LPCTSTR const DIR_DOUBLEDELIM = _T("\\\\"); const TCHAR DRIVE_DELIMITER = ':'; const TCHAR DIRECTORY_DELIMITER = '\\'; const TCHAR EXTENSION_DELIMITER = '.'; const TCHAR DIRECTORY_DELIMITER2 = '/'; HINSTANCE CPath::m_hInst = NULL; ////////////////////////////////////////////////////////////////////// // Helpers ////////////////////////////////////////////////////////////////////// void CPath::SethInst ( HINSTANCE hInst ) { m_hInst = hInst; } HINSTANCE CPath::GethInst() { return m_hInst; } //------------------------------------------------------------- // Task : Create a string of length nDigits containing random digits //------------------------------------------------------------- static stdstr RandomDigits(int nDigits) { // Keep the number of digits in a rational limit //ASSERT(nDigits > 0); //ASSERT(nDigits < 20); int nDigits2 = nDigits; stdstr Digits; TCHAR next_8_digits[9]; while(nDigits2 > 0) { _stprintf(next_8_digits,_T("%08lx"),GetTickCount()); for(int i=0; i<8; i++) { Digits += next_8_digits[i]; if(--nDigits2 == 0) break; } } int last_digit =rand(); if(last_digit < 0) last_digit =(-last_digit); last_digit %= 10; Digits[nDigits - 1] ='0' + last_digit; return Digits; } ////////////////////////////////////////////////////////////////////// // Initialisation ////////////////////////////////////////////////////////////////////// //------------------------------------------------------------- // Task : Helper function for the various CPath constructors. // Initializes the data members and establishes various // class invariants //------------------------------------------------------------- inline void CPath::Init() { m_dwFindFileAttributes =0; m_hFindFile =NULL; } //------------------------------------------------------------- // Task : Helper function for the various CPath destructors. // Cleans up varios internals //------------------------------------------------------------- inline void CPath::Exit() { if(m_hFindFile != NULL) { FindClose(m_hFindFile); m_hFindFile =NULL; } } ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// //------------------------------------------------------------- // Task : Constructs a path //------------------------------------------------------------- CPath::CPath() { Init(); Empty(); } //------------------------------------------------------------- // Task : Constructs a path as copy of another //------------------------------------------------------------- CPath::CPath(const CPath& rPath) { Init(); m_strPath =rPath.m_strPath; } //------------------------------------------------------------- // Task : Constructs a path and points it 2 lpszPath //------------------------------------------------------------- CPath::CPath(LPCTSTR lpszPath) { Init(); m_strPath =lpszPath ? lpszPath : _T(""); cleanPathString(m_strPath); } CPath::CPath(LPCTSTR lpszPath, LPCTSTR NameExten) { Init(); SetDriveDirectory(lpszPath); SetNameExtension(NameExten); } //------------------------------------------------------------- // Task : Constructs a path and points it 2 strPath //------------------------------------------------------------- CPath::CPath(const stdstr& strPath) { Init(); m_strPath =strPath; cleanPathString(m_strPath); } //------------------------------------------------------------- // Task : Constructs a path and points it 2 strPath //------------------------------------------------------------- CPath::CPath(const stdstr& strPath, LPCTSTR NameExten ) { Init(); SetDriveDirectory(strPath.c_str()); SetNameExtension(NameExten); } //------------------------------------------------------------- // Task : Constructs a path and points it 2 strPath //------------------------------------------------------------- CPath::CPath(const stdstr& strPath, const stdstr& NameExten ) { Init(); SetDriveDirectory(strPath.c_str()); SetNameExtension(NameExten.c_str()); } //------------------------------------------------------------- // Task : Constructs a path and points it 2 specified // special directory //------------------------------------------------------------- CPath::CPath(SpecialDirectoryType eInitialDir) { Init(); SpecialDirectory(eInitialDir); } CPath::CPath(SpecialDirectoryType eInitialDir, const stdstr & NameExten ) { Init(); SpecialDirectory(eInitialDir); SetNameExtension(NameExten.c_str()); } CPath::CPath(SpecialDirectoryType eInitialDir, LPCTSTR NameExten ) { Init(); SpecialDirectory(eInitialDir); SetNameExtension(NameExten); } //------------------------------------------------------------- // Task : Cleanup and destruct a path object //------------------------------------------------------------- CPath::~CPath() { Exit(); } //------------------------------------------------------------- // Post : Return TRUE if paths are equal // Task : Check if the two path are the same //------------------------------------------------------------- BOOL CPath::operator ==(const CPath& rPath) const { // Get fully qualified versions of the paths stdstr FullyQualified1; stdstr FullyQualified2; GetFullyQualified(FullyQualified1); rPath.GetFullyQualified(FullyQualified2); // Compare them return _tcsicmp(FullyQualified1.c_str(),FullyQualified2.c_str()) == 0; } //------------------------------------------------------------- // Post : Return TRUE if paths are different // Task : Check if the two path are different //------------------------------------------------------------- BOOL CPath::operator !=(const CPath& rPath) const { return !(*this == rPath); } //------------------------------------------------------------- // Task : Assign a path 2 another //------------------------------------------------------------- CPath& CPath::operator =(const CPath& rPath) { if(this != &rPath) m_strPath =rPath.m_strPath; return *this; } //------------------------------------------------------------- // Post : Return the path, so that assignements can be chained // Task : Assign a string 2 a path //------------------------------------------------------------- CPath& CPath::operator =(LPCTSTR lpszPath) { m_strPath =lpszPath ? lpszPath : _T(""); return *this; } //------------------------------------------------------------- // Post : Return the path, so that assignements can be chained // Task : Assign a string 2 a path //------------------------------------------------------------- CPath& CPath::operator =(const stdstr& strPath) { m_strPath =strPath; return *this; } //------------------------------------------------------------- // Post : Converts path 2 string // Task : Convert path 2 string // Warning: because this pointer 2 string point in the data // of this class, it is possible 2 cast the result of this // function in any non-constant pointer and alter the data. // Very dangerous //------------------------------------------------------------- CPath::operator LPCTSTR() const { return (LPCTSTR)m_strPath.c_str(); } CPath::CPath(DIR_CURRENT_DIRECTORY /*sdt*/, LPCTSTR NameExten) { // Application's current directory Init(); CurrentDirectory(); if (NameExten) { SetNameExtension(NameExten); } } CPath::CPath(DIR_WINDOWS_DIRECTORY /*sdt*/, LPCTSTR NameExten) { // Windows directory Init(); WindowsDirectory(); if (NameExten) { SetNameExtension(NameExten); } } CPath::CPath(DIR_SYSTEM_DIRECTORY /*sdt*/, LPCTSTR NameExten) { // Windows' system directory Init(); SystemDirectory(); if (NameExten) { SetNameExtension(NameExten); } } CPath::CPath(DIR_SYSTEM32_DIRECTORY /*sdt*/, LPCTSTR NameExten) { Init(); SystemDirectory(); if (NameExten) { SetNameExtension(NameExten); } } CPath::CPath(DIR_SYSTEM_DRIVER_DIRECTORY /*sdt*/, LPCTSTR NameExten) { Init(); SystemDirectory(); AppendDirectory(_T("drivers")); if (NameExten) { SetNameExtension(NameExten); } } CPath::CPath(DIR_SYSTEM_DRIVE_ROOT_DIRECTORY /*sdt*/, LPCTSTR NameExten) { // The root directory of system drive Init(); SystemDriveRootDirectory(); if (NameExten) { SetNameExtension(NameExten); } } CPath::CPath(DIR_MODULE_DIRECTORY /*sdt*/, LPCTSTR NameExten) { // The directory where the executable of this app is Init(); ModuleDirectory(); if (NameExten) { SetNameExtension(NameExten); } } CPath::CPath(DIR_MODULE_FILE /*sdt*/) { // The directory where the executable of this app is Init(); Module(); } void CPath::SpecialDirectory(SpecialDirectoryType eInitialDir) { switch(eInitialDir) { // Windows temp directory case TEMP_DIRECTORY: TempDirectory(); break; // Program files directory case PROGRAM_FILES_DIRECTORY: ProgramFilesDirectory(); break; // Common files directory case COMMON_FILES_DIRECTORY: CommonFilesDirectory(); break; // Accessories directory case ACCESSORIES_DIRECTORY: AccessoriesDirectory(); break; // Media directory case MEDIA_DIRECTORY: MediaDirectory(); break; // INF directory case DEVICE_DIRECTORY: DeviceDirectory(); break; // User specific directories case USER_DESKTOP_DIRECTORY: UserDesktopDirectory(); break; case USER_FAVORITES_DIRECTORY: UserFavoritesDirectory(); break; case USER_FONTS_DIRECTORY: UserFontsDirectory(); break; case USER_NETHOOD_DIRECTORY: UserNetworkNeighbourhoodDirectory(); break; case USER_DOCUMENTS_DIRECTORY: UserDocumentsDirectory(); break; case USER_RECENT_DIRECTORY: UserRecentDirectory(); break; case USER_SENDTO_DIRECTORY: UserSendToDirectory(); break; case USER_RECYCLE_DIRECTORY: UserRecycleBinDirectory(); break; case USER_APPLICATION_DATA_DIRECTORY: UserApplicationDataDirectory(); break; case USER_TEMPLATES_DIRECTORY: UserTemplatesDirectory(); break; case USER_STARTMENU_DIRECTORY: UserStartMenuDirectory(); break; case USER_STARTMENU_STARTUP_DIRECTORY: UserStartMenuStartupDirectory(); break; case USER_STARTMENU_PROGRAMS_DIRECTORY: UserStartMenuProgramsDirectory(); break; // Directories common 2 all users case COMMON_DESKTOP_DIRECTORY: CommonDesktopDirectory(); break; case COMMON_STARTMENU_DIRECTORY: CommonStartMenuDirectory(); break; case COMMON_STARTMENU_STARTUP_DIRECTORY: CommonStartMenuStartupDirectory(); break; case COMMON_STARTMENU_PROGRAMS_DIRECTORY: CommonStartMenuProgramsDirectory(); break; // Unknown special directory constant default: // Accept only constants we know about //Bugger ATLASSERT(false); break; } } //------------------------------------------------------------- // Post : Returns the drive component without a colon, e.g. "c" // Returns the directory component with a leading backslash, // but no trailing backslash, e.g. "\dir\subdir" // Returns name compleletely without delimiters, e.g "letter" // Returns extension completely without delimiters, e.g. "doc" // Globals : // I/O : // Task : Return the individual components of this path. // For any given argument, you can pass NULL if you are not // interested in that component. // Do not rely on pNames being <= 8 characters, extensions // being <= 3 characters, or drives being 1 character //------------------------------------------------------------- void CPath::GetComponents(stdstr* pDrive, stdstr* pDirectory, stdstr* pName, stdstr* pExtension) const { TCHAR buff_drive[_MAX_DRIVE + 1]; TCHAR buff_dir [_MAX_DIR + 1]; TCHAR buff_name [_MAX_FNAME + 1]; TCHAR buff_ext [_MAX_EXT + 1]; ZeroMemory(buff_drive,sizeof(buff_drive)); ZeroMemory(buff_dir, sizeof(buff_dir)); ZeroMemory(buff_name, sizeof(buff_name)); ZeroMemory(buff_ext, sizeof(buff_ext)); _tsplitpath(m_strPath.c_str(), pDrive ? buff_drive : NULL, pDirectory ? buff_dir : NULL, pName ? buff_name : NULL, pExtension ? buff_ext : NULL); if(pDrive) *pDrive =buff_drive; if(pDirectory) *pDirectory =buff_dir; if(pName) *pName =buff_name; if(pExtension) *pExtension =buff_ext; // DOS's _splitpath returns "d:", we return "d" if(pDrive) StripTrailingChar(*pDrive,DRIVE_DELIMITER); // DOS's _splitpath returns "\dir\subdir\", we return "\dir\subdir" if(pDirectory) StripTrailingBackslash(*pDirectory); // DOS's _splitpath returns ".ext", we return "ext" if(pExtension) StripLeadingChar(*pExtension,EXTENSION_DELIMITER); } //------------------------------------------------------------- // Task : Get drive from path //------------------------------------------------------------- void CPath::GetDrive(stdstr& rDrive) const { GetComponents(&rDrive); } stdstr CPath::GetDrive(void) const { stdstr rDrive; GetComponents(&rDrive); return rDrive; } //------------------------------------------------------------- // Task : Get drive and directory from path //------------------------------------------------------------- void CPath::GetDriveDirectory(stdstr& rDriveDirectory) const { stdstr Drive; stdstr Directory; GetComponents(&Drive,&Directory); rDriveDirectory =Drive; if(!Drive.empty()) { rDriveDirectory += DRIVE_DELIMITER; rDriveDirectory += Directory; } } stdstr CPath::GetDriveDirectory(void) const { stdstr rDriveDirectory; GetDriveDirectory(rDriveDirectory); return rDriveDirectory; } //------------------------------------------------------------- // Task : Get directory from path //------------------------------------------------------------- void CPath::GetDirectory(stdstr& rDirectory) const { GetComponents(NULL,&rDirectory); } stdstr CPath::GetDirectory(void) const { stdstr rDirectory; GetComponents(NULL,&rDirectory); return rDirectory; } //------------------------------------------------------------- // Task : Get filename and extension from path //------------------------------------------------------------- void CPath::GetNameExtension(stdstr& rNameExtension) const { stdstr Name; stdstr Extension; GetComponents(NULL,NULL,&Name,&Extension); rNameExtension =Name; if(!Extension.empty()) { rNameExtension += EXTENSION_DELIMITER; rNameExtension += Extension; } } stdstr CPath::GetNameExtension(void) const { stdstr rNameExtension; GetNameExtension(rNameExtension); return rNameExtension; } //------------------------------------------------------------- // Task : Get filename from path //------------------------------------------------------------- void CPath::GetName(stdstr& rName) const { GetComponents(NULL,NULL,&rName); } stdstr CPath::GetName(void) const { stdstr rName; GetComponents(NULL,NULL,&rName); return rName; } //------------------------------------------------------------- // Task : Get file extension from path //------------------------------------------------------------- void CPath::GetExtension(stdstr& rExtension) const { GetComponents(NULL,NULL,NULL,&rExtension); } stdstr CPath::GetExtension(void) const { stdstr rExtension; GetComponents(NULL,NULL,NULL,&rExtension); return rExtension; } //------------------------------------------------------------- // Task : Get current directory //------------------------------------------------------------- void CPath::GetCurrentDirectory(stdstr& rDirectory) const { stdstr Directory; rDirectory = ""; GetDirectory(Directory); StripTrailingBackslash(Directory); if(Directory.empty()) return; stdstr::size_type nDelimiter =Directory.rfind(DIRECTORY_DELIMITER); rDirectory =Directory.substr(nDelimiter); StripLeadingBackslash(rDirectory); } stdstr CPath::GetCurrentDirectory(void) const { stdstr rDirecotry; GetCurrentDirectory(rDirecotry); return rDirecotry; } //------------------------------------------------------------- // Task : Get fully qualified path //------------------------------------------------------------- void CPath::GetFullyQualified(stdstr& rFullyQualified) const { TCHAR buff_fullname[MAX_PATH]; memset(buff_fullname, 0, sizeof(buff_fullname)); _tfullpath(buff_fullname,m_strPath.c_str(),MAX_PATH-1); rFullyQualified =buff_fullname; } //------------------------------------------------------------- // Task : Get fully qualified path in short (8.3 style) form //------------------------------------------------------------- void CPath::GetFullyQualifiedShort(stdstr& rFullyQualifiedShort) const { GetFullyQualified(rFullyQualifiedShort); //#pragma message(Reminder(_T("Also implement a GetFullyQualifiedLong"))) TCHAR buff_fullname[MAX_PATH]; memset(buff_fullname, 0, sizeof(buff_fullname)); GetShortPathName(rFullyQualifiedShort.c_str(),buff_fullname,sizeof(buff_fullname)/sizeof(TCHAR)); rFullyQualifiedShort =buff_fullname; } //------------------------------------------------------------- // Post : Return TRUE if path does not start from filesystem root // Task : Check if path is a relative one (e.g. doesn't start with C:\...) //------------------------------------------------------------- BOOL CPath::IsRelative() const { if (m_strPath.length() > 1 && m_strPath[1] == DRIVE_DELIMITER) { return false; } if (m_strPath.length() > 1 && m_strPath[0] == DIRECTORY_DELIMITER && m_strPath[1] == DIRECTORY_DELIMITER) { return false; } return true; } //------------------------------------------------------------- // Post : Return TRUE if there are wildcards in the path // Task : Check if path contains wildcards //------------------------------------------------------------- BOOL CPath::IsWild() const { return (m_strPath.find_first_of(WILD_SET) != stdstr::npos); } //------------------------------------------------------------- // Post : Return TRUE if path is lexically correct // Task : Determine whether lpszFileName is valid. A filename // is valid if it contains only legal characters, doesn't // have repeated contiguous subdirectory delimiters, has at // most one drive delimiter, and all components fit within // maximum sizes // This routine does NOT determine if a file exists, or // even if it could exist relative to the user's directory // hierarchy. Its tests are for lexical correctness only // See also: CPath::Exists //------------------------------------------------------------- BOOL CPath::IsValid () const { // Check 4 illegal characters (no wildcards allowed) // We accept either \ or / as folder delimiter if(IsWild() || (m_strPath.find_first_of(_T("|\"<>")) != stdstr::npos) || (m_strPath.find(_T("//")) != stdstr::npos)) return FALSE; int index = (int)m_strPath.find(_T("\\\\")); if((index != stdstr::npos) && (index > 0)) return FALSE; // Make sure : can appear only in the 2nd position as a drive delimiter if(((index = (int)m_strPath.find(':')) != stdstr::npos) && (index != 1)) return FALSE; // Make sure it fits in the maximum path size if(m_strPath.length() > MAX_PATH) return FALSE; // Path is valid return TRUE; } //------------------------------------------------------------- // Task : Set path components //------------------------------------------------------------- void CPath::SetComponents(LPCTSTR lpszDrive, LPCTSTR lpszDirectory, LPCTSTR lpszName, LPCTSTR lpszExtension) { TCHAR buff_fullname[MAX_PATH]; memset(buff_fullname, 0, sizeof(buff_fullname)); _tmakepath(buff_fullname,lpszDrive,lpszDirectory,lpszName,lpszExtension); m_strPath.erase(); m_strPath =buff_fullname; } //------------------------------------------------------------- // Task : Set path's drive //------------------------------------------------------------- void CPath::SetDrive(TCHAR chDrive) { stdstr_f Drive(_T("%c"),chDrive); stdstr Directory; stdstr Name; stdstr Extension; GetComponents(NULL,&Directory,&Name,&Extension); SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),Extension.c_str()); } //------------------------------------------------------------- // Task : Set path's directory //------------------------------------------------------------- void CPath::SetDirectory(LPCTSTR lpszDirectory, BOOL bEnsureAbsolute /*= FALSE*/) { stdstr Drive; stdstr Directory =lpszDirectory; stdstr Name; stdstr Extension; if(bEnsureAbsolute) EnsureLeadingBackslash(Directory); EnsureTrailingBackslash(Directory); GetComponents(&Drive,NULL,&Name,&Extension); SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),Extension.c_str()); } //------------------------------------------------------------- // Task : Set path's drive and directory //------------------------------------------------------------- void CPath::SetDriveDirectory(LPCTSTR lpszDriveDirectory) { stdstr DriveDirectory =lpszDriveDirectory; stdstr Name; stdstr Extension; EnsureTrailingBackslash(DriveDirectory); cleanPathString(DriveDirectory); GetComponents(NULL,NULL,&Name,&Extension); SetComponents(NULL,DriveDirectory.c_str(),Name.c_str(),Extension.c_str()); } //------------------------------------------------------------- // Task : Set path's filename //------------------------------------------------------------- void CPath::SetName(LPCTSTR lpszName) { stdstr Drive; stdstr Directory; stdstr Extension; GetComponents(&Drive,&Directory,NULL,&Extension); SetComponents(Drive.c_str(),Directory.c_str(),lpszName,Extension.c_str()); } //------------------------------------------------------------- // Task : Set path's filename //------------------------------------------------------------- void CPath::SetName(int iName) { stdstr Drive; stdstr Directory; stdstr Extension; TCHAR sName[33]; memset(sName, 0, sizeof(sName)); _itot(iName, sName, 10); GetComponents(&Drive,&Directory,NULL,&Extension); SetComponents(Drive.c_str(),Directory.c_str(),sName,Extension.c_str()); } //------------------------------------------------------------- // Task : Set path's file extension //------------------------------------------------------------- void CPath::SetExtension(LPCTSTR lpszExtension) { stdstr Drive; stdstr Directory; stdstr Name; GetComponents(&Drive,&Directory,&Name); SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),lpszExtension); } //------------------------------------------------------------- // Task : Set path's file extension //------------------------------------------------------------- void CPath::SetExtension(int iExtension) { stdstr Drive; stdstr Directory; stdstr Name; TCHAR sExtension[20]; memset(sExtension, 0, sizeof(sExtension)); _itot(iExtension, sExtension, 10); GetComponents(&Drive,&Directory,&Name); SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),sExtension); } //------------------------------------------------------------- // Task : Set path's filename and extension //------------------------------------------------------------- void CPath::SetNameExtension(LPCTSTR lpszNameExtension) { stdstr Drive; stdstr Directory; GetComponents(&Drive,&Directory); SetComponents(Drive.c_str(),Directory.c_str(),lpszNameExtension,NULL); } //------------------------------------------------------------- // Task : Append a subdirectory 2 path's directory //------------------------------------------------------------- void CPath::AppendDirectory(LPCTSTR lpszSubDirectory) { stdstr Drive; stdstr Directory; stdstr SubDirectory =lpszSubDirectory; stdstr Name; stdstr Extension; if(SubDirectory.empty()) return; // Strip out any preceeding backslash StripLeadingBackslash(SubDirectory); EnsureTrailingBackslash(SubDirectory); GetComponents(&Drive,&Directory,&Name,&Extension); EnsureTrailingBackslash(Directory); Directory +=SubDirectory; SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),Extension.c_str()); } //------------------------------------------------------------- // Pre : If pLastDirectory is given we will store the name of the // deepest directory (the one we're just exiting) in it // Task : Remove deepest subdirectory from path //------------------------------------------------------------- void CPath::UpDirectory(stdstr *pLastDirectory /*= NULL*/) { stdstr Directory; GetDirectory(Directory); StripTrailingBackslash(Directory); if(Directory.empty()) return; stdstr::size_type nDelimiter =Directory.rfind(DIRECTORY_DELIMITER); if(pLastDirectory != NULL) { *pLastDirectory =Directory.substr(nDelimiter); StripLeadingBackslash(*pLastDirectory); } if(nDelimiter != stdstr::npos) Directory =Directory.substr(0,nDelimiter); SetDirectory(Directory.c_str()); } //------------------------------------------------------------- // Task : Set path 2 current directory //------------------------------------------------------------- void CPath::CurrentDirectory() { TCHAR buff_path[MAX_PATH]; memset(buff_path, 0, sizeof(buff_path)); ::GetCurrentDirectory(MAX_PATH,buff_path); Empty(); SetDriveDirectory(buff_path); } //------------------------------------------------------------- // Task : Set path 2 Windows directory //------------------------------------------------------------- void CPath::WindowsDirectory() { TCHAR buff_path[MAX_PATH]; memset(buff_path, 0, sizeof(buff_path)); GetWindowsDirectory(buff_path,MAX_PATH); Empty(); SetDriveDirectory(buff_path); } //------------------------------------------------------------- // Task : Set path 2 Windows system directory //------------------------------------------------------------- void CPath::SystemDirectory() { TCHAR buff_path[MAX_PATH]; memset(buff_path, 0, sizeof(buff_path)); GetSystemDirectory(buff_path,MAX_PATH); Empty(); SetDriveDirectory(buff_path); } //------------------------------------------------------------- // Task : Set path 2 root of system drive (usually C:\) //------------------------------------------------------------- void CPath::SystemDriveRootDirectory() { SystemDirectory(); SetDirectory(_T("")); } //------------------------------------------------------------- // Task : Set path 2 the name of specified module //------------------------------------------------------------- void CPath::Module(HINSTANCE hInstance) { TCHAR buff_path[MAX_PATH]; memset(buff_path, 0, sizeof(buff_path)); GetModuleFileName(hInstance,buff_path,MAX_PATH); m_strPath =buff_path; } //------------------------------------------------------------- // Task : Set path 2 the name of current module //------------------------------------------------------------- void CPath::Module() { TCHAR buff_path[MAX_PATH]; memset(buff_path, 0, sizeof(buff_path)); GetModuleFileName(m_hInst,buff_path,MAX_PATH); m_strPath =buff_path; } //------------------------------------------------------------- // Task : Set path 2 the directory of specified module //------------------------------------------------------------- void CPath::ModuleDirectory(HINSTANCE hInstance) { Module(hInstance); SetNameExtension(_T("")); } //------------------------------------------------------------- // Task : Set path 2 the directory of current module //------------------------------------------------------------- void CPath::ModuleDirectory() { Module(); SetNameExtension(_T("")); } //------------------------------------------------------------- // Task : Currently, if the current environment has an // entry for the TEMP environment variable, the directory will // be set to that. If not, the directory will be the Windows // System directory. The caller of this method, however, should // not rely on this convention //------------------------------------------------------------- void CPath::TempDirectory() { TCHAR buff_path[MAX_PATH]; memset(buff_path, 0, sizeof(buff_path)); GetTempPath(MAX_PATH,buff_path); m_strPath =buff_path; SetNameExtension(_T("")); } //------------------------------------------------------------- // Task : Set path 2 program files folder // Usually C:\Program Files //------------------------------------------------------------- void CPath::ProgramFilesDirectory() { stdstr strPath; if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("ProgramFilesDir"),strPath)) { // Got a path, use it Empty(); SetDriveDirectory(strPath.c_str()); } else { // This is some old or unknown system Empty(); SetDriveDirectory(_T("C:\\Programs")); } } //------------------------------------------------------------- // Task : Set path 2 common files folder // Usually C:\Program Files\Common Files //------------------------------------------------------------- void CPath::CommonFilesDirectory() { stdstr strPath; if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("CommonFilesDir"),strPath)) { // Got a path, use it Empty(); SetDriveDirectory(strPath.c_str()); } else { // This is some old or unknown system Empty(); TCHAR Drive[3]; memset(Drive, 0, sizeof(Drive)); ExpandEnvironmentStrings (_T("%SystemDrive%"), Drive, sizeof(Drive)/sizeof(TCHAR) ); stdstr strDir = stdstr(Drive) + stdstr(_T("\\Program Files\\Common Files")); SetDriveDirectory(strDir.c_str()); } } //------------------------------------------------------------- // Task : Set path 2 common files folder // On Win95 is C:\Program Files\Accessories // On WinNT is C:\Program Files\Windows NT\Accessories //------------------------------------------------------------- void CPath::AccessoriesDirectory() { // Accessories folder is in Program Files folder ProgramFilesDirectory(); COSVersion osver; WORD ostype =osver.GetOSType(); WORD wintype =osver.GetWindowsType(); BOOL is_Win95 =(ostype==OS_WIN95) || (ostype==OS_WIN98); BOOL is_NT =((ostype & OS_WINNT) != 0); if((wintype != WIN_32S) && is_Win95) { // Windows 95 AppendDirectory(_T("Accessories")); return; } if((wintype != WIN_32S) && is_NT && (((osver.GetMajorVersion()==3) && (osver.GetMinorVersion()>=51)) || (osver.GetMajorVersion()>3))) { // Windows NT with the new Chichago shell AppendDirectory(_T("Windows NT\\Accessories")); return; } // This is some old or unknown system AppendDirectory(_T("Accessry")); } //------------------------------------------------------------- // Task : Set path 2 media folder // Usually C:\Windows\Media //------------------------------------------------------------- void CPath::MediaDirectory() { stdstr strPath; if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("MediaPath"),strPath)) { // Got a path, use it Empty(); SetDriveDirectory(strPath.c_str()); } else { // This is some old or unknown system WindowsDirectory(); AppendDirectory(_T("Media")); } } //------------------------------------------------------------- // Task : Set path 2 device definition folder // Usually C:\Windows\Inf //------------------------------------------------------------- void CPath::DeviceDirectory() { stdstr strPath; if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("DevicePath"),strPath)) { // Got a path, use it Empty(); SetDriveDirectory(strPath.c_str()); } else { // This is some old or unknown system WindowsDirectory(); AppendDirectory(_T("Inf")); } } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will return FALSE // Task : Set path 2 one of the special folders in Chichago //------------------------------------------------------------- BOOL CPath::ShellDirectory(int nShellFolderID) { COSVersion osver; WORD ostype =osver.GetOSType(); WORD wintype =osver.GetWindowsType(); BOOL is_Win95 =(ostype==OS_WIN95) || (ostype==OS_WIN98); BOOL is_NT =((ostype & OS_WINNT) != 0); if((wintype != WIN_32S) && (is_Win95 || (is_NT && (((osver.GetMajorVersion()==3) && (osver.GetMinorVersion()>=51)) || (osver.GetMajorVersion()>3))))) { // These systems support the new Chichago shell, get location from registry BOOL result =FALSE; LPITEMIDLIST pidl =NULL; TCHAR special_path[MAX_PATH]; memset(special_path, 0, sizeof(special_path)); // Get a PIDL 2 the special shell folder HRESULT hr =SHGetSpecialFolderLocation(NULL,nShellFolderID,&pidl); if(SUCCEEDED(hr)) { // Convert the PIDL in2 a path result =SHGetPathFromIDList(pidl,special_path); // Free the PIDL // Get the address of our task allocator's IMalloc interface LPMALLOC pMalloc = NULL; hr =SHGetMalloc(&pMalloc); if(SUCCEEDED(hr)) { // Free the PIDL pMalloc->Free(pidl); // Free our task allocator pMalloc->Release(); } } if(result) { // We've got the special path, now set ourselves 2 point 2 this Empty(); SetDriveDirectory(special_path); } return result; } // This is some old or unknown system, shell folders not supported return FALSE; } //--------------------------------------------------------------------------- // Post : Return TRUE on success // Task : Set path 2 one of the special folders in Chichago // This function manually digs in the registry instead of using // SHGetSpecialFolderLocation, since it seems that this does not work 4 // special location constants beginning with //--------------------------------------------------------------------------- BOOL CPath::ShellDirectory2(int nShellFolderID) { COSVersion osver; WORD ostype =osver.GetOSType(); WORD wintype =osver.GetWindowsType(); BOOL is_Win95 =(ostype==OS_WIN95) || (ostype==OS_WIN98); BOOL is_NT =((ostype & OS_WINNT) != 0); if((wintype != WIN_32S) && (is_Win95 || (is_NT && (((osver.GetMajorVersion()==3) && (osver.GetMinorVersion()>=51)) || (osver.GetMajorVersion()>3))))) { // These systems support the new Chichago shell, get location from registry HKEY root = NULL; stdstr key; stdstr value; switch(nShellFolderID) { case CSIDL_DESKTOPDIRECTORY: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Desktop"); break; case CSIDL_FAVORITES: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Favorites"); break; case CSIDL_FONTS: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Fonts"); break; case CSIDL_NETHOOD: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("NetHood"); break; case CSIDL_PERSONAL: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Personal"); break; case CSIDL_RECENT: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Recent"); break; case CSIDL_SENDTO: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("SendTo"); break; case CSIDL_TEMPLATES: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Templates"); break; case CSIDL_APPDATA: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("AppData"); break; case CSIDL_STARTMENU: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Start Menu"); break; case CSIDL_STARTUP: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Startup"); break; case CSIDL_PROGRAMS: root =HKEY_CURRENT_USER; key =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Programs"); break; case CSIDL_COMMON_DESKTOPDIRECTORY: root =HKEY_LOCAL_MACHINE; key =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Common Desktop"); break; case CSIDL_COMMON_STARTMENU: root =HKEY_LOCAL_MACHINE; key =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Common Start Menu"); break; case CSIDL_COMMON_STARTUP: root =HKEY_LOCAL_MACHINE; key =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Common Startup"); break; case CSIDL_COMMON_PROGRAMS: root =HKEY_LOCAL_MACHINE; key =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); value =_T("Common Programs"); break; } stdstr strPath; if(GetRegistryPath(root,key.c_str(),value.c_str(),strPath)) { // Got a path, use it Empty(); SetDriveDirectory(strPath.c_str()); return TRUE; } } // This is some old or unknown system return FALSE; } //--------------------------------------------------------------------------- // Post : Return FALSE if specified value does not exist or some error // Task : Dig in the registry 2 the specified location, and extract a path // Make sure the path is a valid //--------------------------------------------------------------------------- BOOL CPath::GetRegistryPath(HKEY hRootKey, LPCTSTR lpcszKeyName, LPCTSTR lpcszValueName, stdstr &strPath) { TCHAR path_buffer [MAX_PATH]; TCHAR expanded_buffer[MAX_PATH]; ULONG path_buffer_size =sizeof(path_buffer); CRegistry reg(hRootKey,lpcszKeyName,KEY_READ); memset(path_buffer, 0, sizeof(path_buffer)); memset(expanded_buffer, 0, sizeof(expanded_buffer)); if(reg.GetValue(lpcszValueName,(BYTE *)&path_buffer,path_buffer_size)) { COSVersion osver; #ifndef _UNICODE WORD ostype =osver.GetOSType(); BOOL is_NT =((ostype & OS_WINNT) != 0); if(is_NT) { // Running on NT and the ExpandEnvironmentStrings API requires // Unicode strings WCHAR path_buffer_unicode [MAX_PATH]; WCHAR expanded_buffer_unicode[MAX_PATH]; memset(path_buffer_unicode, 0, sizeof(path_buffer_unicode)); memset(expanded_buffer_unicode, 0, sizeof(expanded_buffer_unicode)); MultiByteToWideChar(CP_ACP,0,path_buffer,-1,path_buffer_unicode,sizeof(path_buffer_unicode)/sizeof(WCHAR)); ExpandEnvironmentStringsW(path_buffer_unicode,expanded_buffer_unicode,sizeof(expanded_buffer_unicode)/sizeof(WCHAR)); WideCharToMultiByte(CP_ACP,0,expanded_buffer_unicode,-1,expanded_buffer,sizeof(path_buffer)/sizeof(TCHAR),NULL,NULL); } else #endif ExpandEnvironmentStrings(path_buffer,expanded_buffer,path_buffer_size); strPath.erase(); strPath =expanded_buffer; return TRUE; } // No such key and/or value return FALSE; } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 desktop folder of currently logged-in user //------------------------------------------------------------- void CPath::UserDesktopDirectory() { if(!ShellDirectory(CSIDL_DESKTOPDIRECTORY)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 favorites folder of currently logged-in user //------------------------------------------------------------- void CPath::UserFavoritesDirectory() { if(!ShellDirectory(CSIDL_FAVORITES)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 fonts folder of currently logged-in user //------------------------------------------------------------- void CPath::UserFontsDirectory() { if(!ShellDirectory(CSIDL_FONTS)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 network hood folder of currently logged-in user //------------------------------------------------------------- void CPath::UserNetworkNeighbourhoodDirectory() { if(!ShellDirectory(CSIDL_NETHOOD)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 personal folder of currently logged-in user // Usually C:\My Documents //------------------------------------------------------------- void CPath::UserDocumentsDirectory() { if(!ShellDirectory(CSIDL_PERSONAL)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 recent folder of currently logged-in user //------------------------------------------------------------- void CPath::UserRecentDirectory() { if(!ShellDirectory(CSIDL_RECENT)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 SendTo folder of currently logged-in user //------------------------------------------------------------- void CPath::UserSendToDirectory() { if(!ShellDirectory(CSIDL_SENDTO)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 templates folder of currently logged-in user // Usually C:\Windows\ShellNew //------------------------------------------------------------- void CPath::UserTemplatesDirectory() { if(!ShellDirectory(CSIDL_TEMPLATES)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 the recycle bin directory // Usually C:\Recycled //------------------------------------------------------------- void CPath::UserRecycleBinDirectory() { if(!ShellDirectory(CSIDL_BITBUCKET)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 the folder where application data is stored // specific 2 currently logged-in user //------------------------------------------------------------- void CPath::UserApplicationDataDirectory() { if(!ShellDirectory(CSIDL_APPDATA)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 start menu folder of currently logged-in user //------------------------------------------------------------- void CPath::UserStartMenuDirectory() { if(!ShellDirectory(CSIDL_STARTMENU)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 startup folder of currently logged-in user //------------------------------------------------------------- void CPath::UserStartMenuStartupDirectory() { if(!ShellDirectory(CSIDL_STARTUP)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 programs menu folder of currently logged-in user //------------------------------------------------------------- void CPath::UserStartMenuProgramsDirectory() { if(!ShellDirectory(CSIDL_PROGRAMS)) Empty(); } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 desktop folder common 2 all users //------------------------------------------------------------- void CPath::CommonDesktopDirectory() { if(!ShellDirectory2(CSIDL_COMMON_DESKTOPDIRECTORY)) { // Check if running on Windows 95, and workaround this if so COSVersion osver; WORD ostype =osver.GetOSType(); if((ostype == OS_WIN95) || (ostype == OS_WIN98)) { // Manual workaround WindowsDirectory(); AppendDirectory(_T("Desktop")); } else // Failure, clear path Empty(); } } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 start menu folder common 2 all users //------------------------------------------------------------- void CPath::CommonStartMenuDirectory() { if(!ShellDirectory2(CSIDL_COMMON_STARTMENU)) { // Check if running on Windows 95, and workaround this if so COSVersion osver; WORD ostype =osver.GetOSType(); if((ostype == OS_WIN95) || (ostype == OS_WIN98)) { // Manual workaround WindowsDirectory(); AppendDirectory(_T("Start Menu")); } else // Failure, clear path Empty(); } } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 startup folder common 2 all users //------------------------------------------------------------- void CPath::CommonStartMenuStartupDirectory() { if(!ShellDirectory2(CSIDL_COMMON_STARTUP)) { // Check if running on Windows 95, and workaround this if so COSVersion osver; WORD ostype =osver.GetOSType(); if((ostype == OS_WIN95) || (ostype == OS_WIN98)) { // Manual workaround WindowsDirectory(); AppendDirectory(_T("Start Menu\\Programs\\StartUp")); } else // Failure, clear path Empty(); } } //------------------------------------------------------------- // Post : If this function is called on a system which does not // support the new Chichago shell, it will clear the path // Task : Set path 2 programs menu folder common 2 all users //------------------------------------------------------------- void CPath::CommonStartMenuProgramsDirectory() { if(!ShellDirectory2(CSIDL_COMMON_PROGRAMS)) { // Check if running on Windows 95, and workaround this if so COSVersion osver; WORD ostype =osver.GetOSType(); if((ostype == OS_WIN95) || (ostype == OS_WIN98)) { // Manual workaround WindowsDirectory(); AppendDirectory(_T("Start Menu\\Programs")); } else // Failure, clear path Empty(); } } //------------------------------------------------------------- // Task : Set path 2 file WIN.INI in the Windows directory //------------------------------------------------------------- void CPath::WindowsProfile() { WindowsDirectory(); SetNameExtension(_T("Win.INI")); } //------------------------------------------------------------- // Task : Set path 2 file WIN.INI in the Windows directory //------------------------------------------------------------- void CPath::SystemProfile() { WindowsDirectory(); SetNameExtension(_T("System.INI")); } //------------------------------------------------------------- // Task : Turn this path from "x:\directory\subdirectory\name.ext" // to just "x:\" //------------------------------------------------------------- void CPath::MakeRoot() { SetDirectory(_T("")); SetNameExtension(_T("")); } //------------------------------------------------------------- // Pre : Only the first 3 character from lpcszPrefix will be used // Post : Returns TRUE on success // Task : Creates a temporary name //------------------------------------------------------------- BOOL CPath::CreateTempName(LPCTSTR lpcszPrefix) { // Check that we've got a prefix if(!lpcszPrefix) return FALSE; stdstr Dir; TCHAR temp_file[MAX_PATH]; memset(temp_file, 0, sizeof(temp_file)); GetDriveDirectory(Dir); if(::GetTempFileName(Dir.c_str(),lpcszPrefix,0,temp_file) != 0) { // Got a temp file name *this =temp_file; SetExtension(_T("tmp")); // GetTempFileName actually created the file, remove it now, // we only needed a name Delete(TRUE); return TRUE; } return FALSE; } //--------------------------------------------------------------------------- // Pre : Only the first 3 character from lpcszPrefix will be used // Post : Returns TRUE on success // Task : Creates a temporary folder name //--------------------------------------------------------------------------- BOOL CPath::CreateTempDir(LPCTSTR lpcszPrefix, UINT nRetries) { // Check that we've got a prefix if(!lpcszPrefix) return FALSE; UINT retries =0; BOOL bSuccess =FALSE; TCHAR temp_prefix[ 5]; TCHAR temp_name [15]; ZeroMemory(temp_prefix, sizeof(temp_prefix)); ZeroMemory(temp_name , sizeof(temp_name )); _tcsncpy(temp_prefix,lpcszPrefix,4); temp_prefix[3] =_T('\0'); while(!bSuccess && (retries < nRetries)) { _tcscpy(temp_name, temp_prefix); stdstr temp =RandomDigits(5); _tcscat(temp_name,temp.c_str()); _tcscat(temp_name,_T(".tmp")); CPath test(*this); test.AppendDirectory(temp_name); if(!test.DirectoryExists() && test.CreateDirectory()) { // CreateTempDir actually created the folder, remove it now, // we only needed a name test.RemoveDirectory(); bSuccess =TRUE; } retries++; } if(bSuccess) AppendDirectory(temp_name); return bSuccess; } //------------------------------------------------------------- // Post : Returns TRUE on success // Task : Sets path 2 a random name, and optionally ensures // uniqueness of that path //------------------------------------------------------------- BOOL CPath::CreateRandomName(BOOL bMustNotExist /*= TRUE*/, UINT nRetries /*= 1000*/) { stdstr Name; for(UINT nRetry=0; nRetry < nRetries; nRetry++) { Name =RandomDigits(8); SetName(Name.c_str()); if(!bMustNotExist) return TRUE; if(!Exists()) return TRUE; } return FALSE; } //------------------------------------------------------------- // Post : Returns TRUE on success // Task : Create a new name, based on the existing name, for the same // drive and directory. If bMustNotExist, test the path up to // nRetries till we get an unused path // See also: CreateRandomName //------------------------------------------------------------- BOOL CPath::CreateSimilarName(BOOL bMustNotExist /*= TRUE*/, UINT nRetries /*= 1000*/) { stdstr NewName; stdstr OriginalName; GetName(OriginalName); for(UINT nRetry=0; nRetry < nRetries; nRetry++) { NewName =OriginalName + RandomDigits(_MAX_FNAME - (int)OriginalName.length()); SetName(NewName.c_str()); if(!Exists() || !bMustNotExist) return TRUE; } return FALSE; } //------------------------------------------------------------- // Post : Returns one of the EX_DRIVE_ constants // Task : Return the type of the drive this path points to // See DrvType.H for more details //------------------------------------------------------------- UINT CPath::GetDriveType() const { CPath RootPath = *this; stdstr Root; RootPath.MakeRoot(); Root =(LPCTSTR)RootPath; return ::GetDriveType(Root.c_str()); } //------------------------------------------------------------- // Post : Return -1 on error // Task : Find out the amount of free space on drive (in bytes) //------------------------------------------------------------- ULONG CPath::DriveFreeSpaceBytes() const { CPath RootPath = *this; stdstr Root; RootPath.MakeRoot(); ULONG nSectorsPerCluster = 0; ULONG nBytesPerSector = 0; ULONG nFreeClusters = 0; ULONG nClusters = 0; if(!GetDiskFreeSpace((LPCTSTR)RootPath,&nSectorsPerCluster,&nBytesPerSector,&nFreeClusters,&nClusters)) return 0; else return nFreeClusters * nSectorsPerCluster * nBytesPerSector; } //------------------------------------------------------------- // Post : Return -1 on error // Task : Find out the size of the drive (in bytes) //------------------------------------------------------------- ULONG CPath::DriveTotalSpaceBytes() const { CPath RootPath = *this; stdstr Root; RootPath.MakeRoot(); ULONG nSectorsPerCluster = 0; ULONG nBytesPerSector = 0; ULONG nFreeClusters = 0; ULONG nClusters = 0; if(!GetDiskFreeSpace((LPCTSTR)RootPath,&nSectorsPerCluster,&nBytesPerSector,&nFreeClusters,&nClusters)) return 0; else return nClusters * nSectorsPerCluster * nBytesPerSector; } //------------------------------------------------------------- // Post : Return -1 on error // Task : Find out the cluster size on this drive (in bytes) //------------------------------------------------------------- ULONG CPath::GetDriveClusterSize() const { CPath RootPath = *this; stdstr Root; RootPath.MakeRoot(); ULONG nSectorsPerCluster = 0; ULONG nBytesPerSector = 0; ULONG nFreeClusters = 0; ULONG nClusters = 0; if(!GetDiskFreeSpace((LPCTSTR)RootPath,&nSectorsPerCluster,&nBytesPerSector,&nFreeClusters,&nClusters)) return 0; else return nSectorsPerCluster * nBytesPerSector; } //------------------------------------------------------------- // Post : Return TRUE on success // Task : Find out info about drive //------------------------------------------------------------- BOOL CPath::GetDiskInfo(LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, LPDWORD lpFreeClusters, LPDWORD lpClusters) const { // Create root path CPath RootPath = *this; RootPath.MakeRoot(); return GetDiskFreeSpace((LPCTSTR)RootPath, lpSectorsPerCluster, lpBytesPerSector, lpFreeClusters, lpClusters); } //--------------------------------------------------------------------------- // Post : Return TRUE if a directory // Task : Check if this path represents a directory //--------------------------------------------------------------------------- BOOL CPath::IsDirectory() const { // Check if this path has a filename stdstr file_name; GetNameExtension(file_name); return file_name.empty(); } //------------------------------------------------------------- // Post : Return TRUE if directory exists // Task : To determine if the directory exists, we need to // create a test path with a wildcard (*.*) extension // and see if FindFirstFile returns anything. We don't // use CPath::FindFirst() because that routine parses out // '.' and '..', which fails for empty directories //------------------------------------------------------------- BOOL CPath::DirectoryExists() const { // Create test path CPath TestPath(m_strPath.c_str()); stdstr DirName; TestPath.UpDirectory(&DirName); TestPath.SetNameExtension(DirName.c_str()); WIN32_FIND_DATA FindData; HANDLE hFindFile =FindFirstFile((LPCTSTR)TestPath,&FindData); // Find anything BOOL bGotFile =(hFindFile != INVALID_HANDLE_VALUE); if(hFindFile != NULL) // Make sure we close the search FindClose(hFindFile); return bGotFile; } //------------------------------------------------------------- // Post : Return TRUE if there are no files(s)/folder(s) in // directory. All objects (with hidden, system, etc. attributes) // are looked up // Task : Check if directory contains any file(s) //------------------------------------------------------------- BOOL CPath::IsDirectoryEmpty() const { CPath FileSpec = *this; FileSpec.SetNameExtension(WILD_NAME_EXTENSION); return !FileSpec.FindFirst(_A_NORMAL | _A_ARCH | _A_HIDDEN | _A_SYSTEM | _A_RDONLY) && !FileSpec.FindFirst(_A_HIDDEN | _A_SUBDIR); } //------------------------------------------------------------- // Post : Return TRUE if these is such a file // Task : Check if file exists //------------------------------------------------------------- BOOL CPath::Exists() const { WIN32_FIND_DATA FindData; HANDLE hFindFile =FindFirstFile(m_strPath.c_str(),&FindData); BOOL bSuccess =(hFindFile != INVALID_HANDLE_VALUE); if(hFindFile != NULL) // Make sure we close the search FindClose(hFindFile); return bSuccess; } //------------------------------------------------------------- // Post : Return file size, -1 on error // Task : Get file size (in bytes) //------------------------------------------------------------- ULONG CPath::GetSize() const { WIN32_FIND_DATA FindData; memset(&FindData, 0, sizeof(FindData)); HANDLE hFindFile =FindFirstFile(m_strPath.c_str(),&FindData); BOOL bSuccess =(hFindFile != INVALID_HANDLE_VALUE); if(hFindFile != NULL) // Make sure we close the search FindClose(hFindFile); return bSuccess ? FindData.nFileSizeLow : (ULONG)-1; } //------------------------------------------------------------- // Post : Return file attributes // Task : Get attributes of the file //------------------------------------------------------------- ULONG CPath::GetAttributes() const { return GetFileAttributes(m_strPath.c_str()); } //--------------------------------------------------------------------------- // Post : Return TRUE on success // Task : Set the attributes of the file //--------------------------------------------------------------------------- BOOL CPath::SetAttributes(ULONG dwAttributes) { return SetFileAttributes(m_strPath.c_str(),dwAttributes); } //------------------------------------------------------------- // Post : Return TRUE on success // Task : Get file creation, last acces and/or last modification // time as local time //------------------------------------------------------------- BOOL CPath::GetTime(FILETIME *lpCreated, FILETIME *lpAccessed, FILETIME *lpModified) const { WIN32_FIND_DATA findFileData; HANDLE hFind =FindFirstFile(m_strPath.c_str(),&findFileData); if(hFind == INVALID_HANDLE_VALUE) // Oops, no such file system object return FALSE; FindClose(hFind); FILETIME ftLastWriteTimeLocal; FileTimeToLocalFileTime(&findFileData.ftLastWriteTime,&ftLastWriteTimeLocal); if(lpCreated) { FILETIME ftCreationTimeLocal; FileTimeToLocalFileTime(&findFileData.ftCreationTime,&ftCreationTimeLocal); *lpCreated =ftCreationTimeLocal; if(!ftCreationTimeLocal.dwLowDateTime && !ftCreationTimeLocal.dwHighDateTime) // Adjust time *lpCreated =ftLastWriteTimeLocal; } if(lpAccessed) { FILETIME ftLastAccessTimeLocal; FileTimeToLocalFileTime(&findFileData.ftLastAccessTime,&ftLastAccessTimeLocal); *lpAccessed =ftLastAccessTimeLocal; if(!ftLastAccessTimeLocal.dwLowDateTime && !ftLastAccessTimeLocal.dwHighDateTime) // Adjust time *lpAccessed =ftLastWriteTimeLocal; } if(lpModified) *lpModified =ftLastWriteTimeLocal; return TRUE; } //--------------------------------------------------------------------------- // Post : Return creation time // Task : Get the time this file/folder was created //--------------------------------------------------------------------------- FILETIME CPath::GetTimeCreated() const { FILETIME file_time; ZeroMemory(&file_time,sizeof(file_time)); GetTime(&file_time,NULL,NULL); return file_time; } //--------------------------------------------------------------------------- // Post : Return last access time // Task : Get the time this file/folder was last accessed //--------------------------------------------------------------------------- FILETIME CPath::GetTimeLastAccessed() const { FILETIME file_time; ZeroMemory(&file_time,sizeof(file_time)); GetTime(NULL,&file_time,NULL); return file_time; } //--------------------------------------------------------------------------- // Post : Return last modification time // Task : Get the time this file/folder was last changed //--------------------------------------------------------------------------- FILETIME CPath::GetTimeLastModified() const { FILETIME file_time; ZeroMemory(&file_time,sizeof(file_time)); GetTime(NULL,NULL,&file_time); return file_time; } //--------------------------------------------------------------------------- // Pre : All time parameters are supposed 2 be local times // Post : Return TRUE on success // Task : Set the creation, last acces and/or last modification time //--------------------------------------------------------------------------- BOOL CPath::SetTime(const FILETIME *lpCreated, const FILETIME *lpAccessed, const FILETIME *lpModified) { if(!lpCreated && !lpAccessed && !lpModified) // No time params specified return FALSE; WIN32_FIND_DATA findFileData; HANDLE hFind =FindFirstFile(m_strPath.c_str(),&findFileData); if(hFind == INVALID_HANDLE_VALUE) // Oops, no such file system object return FALSE; FindClose(hFind); if(lpCreated) LocalFileTimeToFileTime(lpCreated,&findFileData.ftCreationTime); if(lpAccessed) LocalFileTimeToFileTime(lpAccessed,&findFileData.ftLastAccessTime); if(lpModified) LocalFileTimeToFileTime(lpModified,&findFileData.ftLastWriteTime); HANDLE hFile =CreateFile(m_strPath.c_str(),GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(hFile != INVALID_HANDLE_VALUE) { SetFileTime(hFile,lpCreated ? &findFileData.ftCreationTime : NULL, lpAccessed ? &findFileData.ftLastAccessTime : NULL, lpModified ? &findFileData.ftLastWriteTime : NULL); CloseHandle(hFile); return TRUE; } return FALSE; } //--------------------------------------------------------------------------- // Pre : lpCreated is supposed 2 be local time // Post : Return TRUE on success // Task : Set the file's creation time //--------------------------------------------------------------------------- BOOL CPath::SetTimeCreated(const FILETIME *lpCreated) { return SetTime(lpCreated,NULL,NULL); } //--------------------------------------------------------------------------- // Pre : lpModified is supposed 2 be local time // Post : Return TRUE on success // Task : Set the file's creation time //--------------------------------------------------------------------------- BOOL CPath::SetTimeLastModified(const FILETIME *lpModified) { return SetTime(NULL,NULL,lpModified); } //--------------------------------------------------------------------------- // Pre : lpAccessed is supposed 2 be local time // Post : Return TRUE on success // Task : Set the file's creation time //--------------------------------------------------------------------------- BOOL CPath::SetTimeLastAccessed(const FILETIME *lpAccessed) { return SetTime(NULL,lpAccessed,NULL); } //------------------------------------------------------------- // Post : Return TRUE on success // Task : Delete file //------------------------------------------------------------- BOOL CPath::Delete(BOOL bEvenIfReadOnly) const { ULONG dwAttr =::GetFileAttributes(m_strPath.c_str()); if(dwAttr == (ULONG)-1) // File does not exists return FALSE; if(((dwAttr & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) && !bEvenIfReadOnly) // File is read-only, and we're not allowed 2 delete it return FALSE; SetFileAttributes(m_strPath.c_str(),FILE_ATTRIBUTE_NORMAL); return DeleteFile(m_strPath.c_str()); } //------------------------------------------------------------- // Post : Return TRUE on success // Task : Rename file //------------------------------------------------------------- BOOL CPath::Rename(LPCTSTR lpszNewPath) { return MoveTo(lpszNewPath,FALSE); } //------------------------------------------------------------- // Post : Return TRUE on success, FALSE if there is such a target file // and we weren't granted permission 2 overwrite file or some error // Task : Copy file // Since ::CopyFile will not overwrite read only files // we will make sure the target file is writable first //------------------------------------------------------------- BOOL CPath::CopyTo(LPCTSTR lpcszTargetFile, BOOL bOverwrite) { // Check if the target file exists CPath TargetFile(lpcszTargetFile); if(TargetFile.Exists()) { // Yeah there is already such a target file // Decide if we should overwrite if(!bOverwrite) return FALSE; // Delete any previous target if(!TargetFile.Delete(TRUE)) return FALSE; } // CopyFile will set the target's attributes 2 the same as // the source after copying return CopyFile(m_strPath.c_str(),lpcszTargetFile,!bOverwrite); } //------------------------------------------------------------- // Post : Return TRUE on success, FALSE if there is such a target file // and we weren't granted permission 2 overwrite file or some error // Task : Move file //------------------------------------------------------------- BOOL CPath::MoveTo(LPCTSTR lpcszTargetFile, BOOL bOverwrite) { // Check if the target file exists CPath TargetFile(lpcszTargetFile); if(TargetFile.Exists()) { // Yeah there is already such a target file // Decide if we should overwrite if(!bOverwrite) return FALSE; // Delete any previous target if(!TargetFile.Delete(TRUE)) return FALSE; } return MoveFile(m_strPath.c_str(),lpcszTargetFile); } //------------------------------------------------------------- // Post : Return TRUE if attributes do match // Task : Compare finder attributes //------------------------------------------------------------- BOOL CPath::AttributesMatch(ULONG dwTargetAttributes, ULONG dwFileAttributes) { if (dwTargetAttributes == _A_ALLFILES) { return true; } if(dwTargetAttributes == _A_NORMAL) { return ((_A_SUBDIR & dwFileAttributes) == 0); } else { return ( ((dwTargetAttributes & dwFileAttributes) != 0) && ((_A_SUBDIR & dwTargetAttributes) == (_A_SUBDIR & dwFileAttributes)) ); } return FALSE; } //------------------------------------------------------------- // Post : Return TRUE if any match found // Task : Find the first file that meets this path and the specified attributes // You can specify the current attributes of the file or directory // The attributes are represented by a combination (|) of the following // constants: // // _A_ARCH Archive. Set whenever the file is // changed, and cleared by the BACKUP command // _A_HIDDEN Hidden file. Not normally seen with // the DIR command, unless the /AH option // is used. Returns information about normal // files as well as files with this attribute // _A_NORMAL Normal. File can be read or written to // without restriction // _A_RDONLY Read-only. File cannot be opened for writing, // and a file with the same name cannot be created // _A_SUBDIR Subdirectory // _A_SYSTEM System file. Not normally seen with the DIR // command, unless the /AS option is used // // These attributes do not follow a simple additive logic // Note that _A_NORMAL is 0x00, so it effectively cannot be // removed from the attribute set. You will therefore always // get normal files, and may also get Archive, Hidden, etc. // if you specify those attributes // See aso: FindFirstFile, FindNextFile //------------------------------------------------------------- BOOL CPath::FindFirst(ULONG dwAttributes /*= _A_NORMAL*/) { m_dwFindFileAttributes =dwAttributes; BOOL bGotFile; BOOL bWantSubdirectory =(BOOL)(_A_SUBDIR & dwAttributes); // Close handle to any previous enumeration Exit(); // i.) Finding first candidate file WIN32_FIND_DATA FindData; m_hFindFile =FindFirstFile(m_strPath.c_str(),&FindData); bGotFile =(m_hFindFile != INVALID_HANDLE_VALUE); while(bGotFile) { // ii.) Compare candidate to attributes, and filter out the "." and ".." folders if(!AttributesMatch(m_dwFindFileAttributes,FindData.dwFileAttributes)) goto LABEL_GetAnother; if(bWantSubdirectory && (FindData.cFileName[0] == '.')) goto LABEL_GetAnother; // iii.) Found match, prepare result if((_A_SUBDIR & FindData.dwFileAttributes) != 0) StripTrailingBackslash(m_strPath); SetNameExtension(FindData.cFileName); if((_A_SUBDIR & FindData.dwFileAttributes) != 0) EnsureTrailingBackslash(m_strPath); return TRUE; // iv.) Not found match, get another LABEL_GetAnother: bGotFile =FindNextFile(m_hFindFile,&FindData); } return FALSE; } //------------------------------------------------------------- // Post : Return TRUE if a new match found // Task : Find the next file that meets the conditions specified // in the last FindFirst call //------------------------------------------------------------- BOOL CPath::FindNext() { if (m_hFindFile == NULL) return FALSE; WIN32_FIND_DATA FindData; while(FindNextFile(m_hFindFile,&FindData) != FALSE) { // while(FindNext(...)) if(AttributesMatch(m_dwFindFileAttributes,FindData.dwFileAttributes)) { // if(AttributesMatch(...) if((_A_SUBDIR & FindData.dwFileAttributes) == _A_SUBDIR) { if (IsDirectory()) { // Found a directory UpDirectory(); } else { SetNameExtension(""); } AppendDirectory(FindData.cFileName); } else { // Found a file if (IsDirectory()) { // Found a directory UpDirectory(); } SetNameExtension(FindData.cFileName); } if((_A_SUBDIR & FindData.dwFileAttributes) == _A_SUBDIR) EnsureTrailingBackslash(m_strPath); return TRUE; } } return FALSE; } //------------------------------------------------------------- // Post : Return TRUE on success // Task : Change current working directory of application 2 path //------------------------------------------------------------- BOOL CPath::ChangeDirectory() { stdstr DriveDirectory; GetDriveDirectory(DriveDirectory); return SetCurrentDirectory(DriveDirectory.c_str()); } //------------------------------------------------------------- // Post : Return TRUE if deleted OK // Task : Delete everything in the directory //------------------------------------------------------------- BOOL CPath::RemoveDirectoryContent() { // Deleting the directory's content // Iterate the content of the directory and delete it stdstr filename; CPath iterator(*this); BOOL deleted_OK =TRUE; // Deleting all contained files iterator.SetNameExtension(WILD_NAME_EXTENSION); BOOL iterating =iterator.FindFirst(_A_NORMAL | _A_ARCH | _A_HIDDEN | _A_SYSTEM | _A_RDONLY); while(iterating) { // Found something deleted_OK =iterator.Delete(TRUE); if(!deleted_OK) break; iterator.SetNameExtension(WILD_NAME_EXTENSION); iterating =iterator.FindFirst(_A_NORMAL | _A_ARCH | _A_HIDDEN | _A_SYSTEM | _A_RDONLY); } if(!deleted_OK) return FALSE; // Deleting all contained directories iterator.SetNameExtension(WILD_NAME_EXTENSION); iterating =iterator.FindFirst(_A_HIDDEN | _A_SUBDIR); while(iterating) { // Found something deleted_OK =iterator.RemoveDirectory(TRUE); if(!deleted_OK) break; iterator.SetNameExtension(WILD_NAME_EXTENSION); iterator.UpDirectory(); iterating =iterator.FindFirst(_A_HIDDEN | _A_SUBDIR); } return deleted_OK; } //------------------------------------------------------------- // Post : Return TRUE if deleted OK // Task : Remove the directory //------------------------------------------------------------- BOOL CPath::RemoveDirectory(BOOL bEvenIfNotEmpty) { if(bEvenIfNotEmpty) { // Delete the directory's content if(!RemoveDirectoryContent()) return FALSE; } // Make sure there is no enumeration in progress, // otherwise we we'll get an error (sharing violation) because // that search keeps an open handle for this directory Exit(); // Deleting this directory (and only if it's empty) stdstr DriveDirectory; GetDriveDirectory(DriveDirectory); return ::RemoveDirectory(DriveDirectory.c_str()); } //------------------------------------------------------------- // Pre : If bCreateIntermediates is TRUE, create all eventually // missing parent directories too // Post : Return TRUE on success // Task : Create new directory //------------------------------------------------------------- BOOL CPath::CreateDirectory(BOOL bCreateIntermediates /*= TRUE*/) { stdstr PathText; BOOL bSuccess; GetDriveDirectory(PathText); StripTrailingBackslash(PathText); bSuccess =::CreateDirectory(PathText.c_str(),NULL); if(!bSuccess) bSuccess =ChangeDirectory(); if(!bSuccess && bCreateIntermediates) { stdstr::size_type nDelimiter =PathText.rfind(DIRECTORY_DELIMITER); if(nDelimiter == stdstr::npos) return FALSE; PathText.resize(nDelimiter + 1); CPath SubPath(PathText); if(SubPath.CreateDirectory()) return CreateDirectory(FALSE); else return FALSE; } return bSuccess; } //Helpers //------------------------------------------------------------------------ // Task : Remove first character (if any) if it's chLeading //------------------------------------------------------------------------ void CPath::cleanPathString(stdstr& rDirectory) const { rDirectory.replace(DIRECTORY_DELIMITER2,DIRECTORY_DELIMITER); if(!_tcsnicmp(rDirectory.c_str(), _T("\\\\"), 2)) { rDirectory.replace(DIR_DOUBLEDELIM,DIRECTORY_DELIMITER); rDirectory.insert(0, _T("\\")); } else rDirectory.replace(DIR_DOUBLEDELIM,DIRECTORY_DELIMITER); } void CPath::StripLeadingChar(stdstr& rText, TCHAR chLeading) const { stdstr::size_type nLength =rText.length(); if(nLength == 0) return; if(rText[0] == chLeading) rText =rText.substr(1); } //------------------------------------------------------------------------ // Task : Remove first character if \ //------------------------------------------------------------------------ void CPath::StripLeadingBackslash(stdstr& Directory) const { stdstr::size_type nLength =Directory.length(); // If Directory is of the form '\', don't do it if(nLength <= 1) return; if(Directory[0] == DIRECTORY_DELIMITER) Directory =Directory.substr(1); } //------------------------------------------------------------------------ // Task : Remove last character (if any) if it's chTrailing //------------------------------------------------------------------------ void CPath::StripTrailingChar(stdstr& rText, TCHAR chTrailing) const { stdstr::size_type nLength =rText.length(); if(nLength == 0) return; if(rText[nLength-1] == chTrailing) rText.resize(nLength-1); } //------------------------------------------------------------------------ // Task : Remove last character if \ //------------------------------------------------------------------------ void CPath::StripTrailingBackslash(stdstr& rDirectory) const { for (;;) { stdstr::size_type nLength = rDirectory.length(); if(nLength <= 1) { return; } if(rDirectory[nLength-1] == DIRECTORY_DELIMITER || rDirectory[nLength-1] == DIRECTORY_DELIMITER2) { rDirectory.resize(nLength-1); continue; } return; } } //------------------------------------------------------------------------ // Task : Add a backslash to the end of Directory if there is // not already one there //------------------------------------------------------------------------ void CPath::EnsureTrailingBackslash(stdstr& Directory) const { stdstr::size_type nLength =Directory.length(); if(Directory.empty() || (Directory[nLength-1] != DIRECTORY_DELIMITER)) Directory +=DIRECTORY_DELIMITER; } //------------------------------------------------------------------------ // Task : Add a backslash to the beginning of Directory if there // is not already one there //------------------------------------------------------------------------ void CPath::EnsureLeadingBackslash(stdstr& Directory) const { if(Directory.empty() || (Directory[0] != DIRECTORY_DELIMITER)) { stdstr temp =Directory; Directory.Format(_T("%c%s"),DIRECTORY_DELIMITER,temp.c_str()); } } //------------------------------------------------------------------------ // Task : Return path with swapped polarity on all backslashes //------------------------------------------------------------------------ void CPath::GetAsInternetPath(stdstr& Directory) const { Directory = m_strPath; if(!Directory.empty()) { stdstr temp = Directory; for (int idx = 0; idx < (int)temp.size(); idx++) { if (temp[idx]==_T('\\')) temp[idx] = _T('/'); } Directory = temp; } else Directory = _T("/"); }