不废话,直接说特点。
特点:
1、利用C++类模板,实现 .h 头文件和 .cpp 代码实现文件的分离。
对于一些软件工程师,平时既想使用C++模板,又想隐藏源代码的情况,具有参考意义。
2、确保完全独立创新、可用、可测。事实上本人对于发布的源代码均编写了对应的单元测试代码。
并且代码的覆盖率超过90%。
3、同时支持 ANSI 和 UNICODE 版本。
4、高效、干净、安全。没有多余的废代码。
在当下,还潜心专注C/C++开发的人,应该为数不多吧。
如果有兴趣的同学,若在使用过程中有任何问题,欢迎交流!
.h 头文件
/**
* @file 文件名
* @brief 文件概要信息描述
* @author 作者
* @version 版本
* @date 日期
*
* @copyright 版权信息
*
* @par 修改日志:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>日期 <td>版本 <td>作者 <td>说明
* </table>
*/
#pragma once
#include "bsbase\bs.h"
#include "bsbase\bsconst.h"
#include "bsbase\bsbase.h"
#include "bsbase\bsnoncopyable.h"
#include "bsbase\bsfunT.h"
DECLARE_NS_BSC
using namespace std;
//////////////////////////////////////////////////////////////////////////
enum { eFS_DISK = 0, eFS_DIR, eFS_FILENAME, eFS_EXT };
template <typename T = enable_if_t<is_base_of_v<basic_string<typename T::value_type>, T> > >
class BASE_API CBsFS final : private noncopyable
{
public:
typedef typename T::value_type _type;
typedef basic_string<_type> _Base;
typedef const _type _ctype;
private:
CBsFS(void);
virtual ~CBsFS(void);
public:
/**
* @brief Split file path into several parts
* @param[in] strFileFrom: Pointer to a null-terminated string that specifies the name of an existing file.
* @param[in] strFileTo: Pointer to a null-terminated string that specifies the name of the new file.
* @param[out] oMap: The splited path filled in the map
* @return BOOL
* @retval TRUE: If the function succeeds.
* @retval FALSE: If the function fails and the size of oMap is equal to zero.
@note
e.g.
@code
tstring strFilePath = _T("C:\\abc\\def");
map<int, tstring> oMap;
BS::CBsFS<tstring>::SplitPath(strFilePath, oMap);
map<int, string> oMap1;
BS::CBsFS<string>::SplitPath(string("D:\\qwe\\rty"), oMap1);
@endcode
"C:\\abc\\def.txt" => map[eFS_DISK]="C:" map[eFS_DIR]="\\abc\\" map[eFS_FILENAME]="def" map[eFS_EXT]=".txt"
*/
static BOOL SplitPath(const T& strFilePath, map<int, T>& oMap) _NOEXCEPT;
/**
* @brief Make file path according to several parts, it is the reverse of SplitPath function
* @param[in] oMap: The map the include file path
* @return tstring/bsastring
* @retval tstring/bsastring: File path that composed
e.g. map[eFS_DISK]="C:" map[eFS_DIR]="\abc\" map[eFS_FILENAME]="def" map[eFS_EXT]=".txt" => "C:\abc\def.txt"
*/
static T MakePath(const map<int, T>& oMap) _NOEXCEPT;
/**
* @brief Get file directory according to file path
* @param[in] strFilePath: The file path
* @return tstring/bsastring
* @retval empty string: If input file path is empty, or doesn't include '/' or '\\', then return empty string
* @retval tstring/bsastring: File directory including '\\',
e.g. "C:\\abc\\def.txt" => "C:\\abc\\"
*/
static T GetFileDir(const T& strFilePath) _NOEXCEPT;
//"C:\\abc\\def.txt" => "def.txt"
static T GetFileName(const T& strFilePath) _NOEXCEPT;
//C:\\abc\\def.txt" => ".txt" "def.txt" => ".txt" "\\def.txt" => ".txt" "//def.txt" => ".txt"
static T GetFileExt(const T& strFName, BOOL tfLower = FALSE) _NOEXCEPT;
//"C:\abc\def => C:\abc\" "C:\abc\def\ => C:\abc\"
static T GetParentDir(const T& strDir) _NOEXCEPT;
static BOOL IsExists(const T& strFilePath); //apply to file & directory
//"C:" + "def" => "C:\\def" "C:\\abc" + "def" => "C:\\abc\\def"
//"C:\\def" + "\\" => "C:\\def\\"
static T PathAppend(const T& strPath, const T& strAppend, _ctype chSep = _ctype(BS_SLASH_CHAR_A)) _NOEXCEPT;
//"E:\\k\\xxx\\xxx\\" + "abc\\TestComposeFilePath\\" => "E:\\k\\xxx\\xxx\\abc\\TestComposeFilePath\\"
static T ComposeFilePath(HMODULE hModule, const T& strFileName);
static BOOL GetFileCreateTime(const T& strFilePath, FILETIME& refTime);
static int64_t GetFileSize(const T& strFilePath);
static int64_t GetFileSize(ifstream& ifs);
static int64_t GetFileSize(ofstream& ofs);
//SystemTime = UTC, LocalTime = current local time
static void FileTimeToSystemTime(FILETIME& refTime, SYSTEMTIME& refSystemTime);
//"E:\\k\\xxx\\xxx\\xxx\\xxx\\xxx.exe"
static T GetModuleFile(HMODULE hModule, HANDLE hProcess = nullptr);
//return directory with end of backslash('\'), e.g. "E:\\k\\xxx\\xxx\\debug\\"
static T GetModuleFileDir(HMODULE hModule);
// "xxx.exe"
static T GetModuleFileName(HMODULE hModule, HANDLE hProcess = nullptr);
static T ParentPath(const T& strFilePath);
//default format is E_GUID_FORMAT_NULL(0)
static T GenGUIDFileName(const UINT eFmt = 0);
//"C:\\Users\\JACK~1\\AppData\\Local\\Temp\\"
static T GetTemporaryFolder(void) _NOEXCEPT;
//"C:\\Users\\JACK~1\\AppData\\Local\\Temp\\~bs489B.tmp"
static T GetTempFileName(const T& strPrefix = T(), const UINT uUnique = 0) _NOEXCEPT;
//"C:\\aaa\\efc.txt" + "bsfile" => "C:\\aaa\\efc.bsfile", only string replace, doesn't related to rename file name
static T ChangeFileExt(const T& strFile, const T& strTargetExt) _NOEXCEPT;
static T GetSpecificFolder(const UINT32 uFolderID);
//e.g. "C:\\Program Files (x86)\\Brand\\nginx-rtmp-win32\\", the path must real existing path,
//otherwise it will be 3 error code, means "The system cannot find the path specified."
static T GetShortPathName(const T& strFilePath) _NOEXCEPT;
//e.g. "C:\\Users\\xxx\\AppData\\Local"
static T GetUserAppDataFolder(void) _NOEXCEPT;
//e.g. "C:\\ProgramData"
static T GetProgramDataFolder(void) _NOEXCEPT;
//e.g. "C:\\Program Files (x86)"
static T GetProgramFilesFolder(void) _NOEXCEPT;
/**
* @brief Given the file path and delete it
* @param[in] pszPath Pointer to a null-terminated string that specifies the file to be deleted
* @param[out] 输出参数 输出参数描述信息
* @return BOOL
* @retval nonezero If the function succeeds, the return value is nonzero.
zero If the function fails, To get extended error information, call GetLastError
*/
static BOOL DeleteFile(_ctype* pszPath);
static BOOL DeleteFile(const T& strPath);
static void DeleteFiles(vector<T>& ovtFiles);
/**
* @brief The CopyFile function copies an existing file to a new file. The CopyFileEx function provides
* two additional capabilities. CopyFileEx can call a specified callback function each time
* a portion of the copy operation is completed, and CopyFileEx can be canceled during the copy operation.
* @param[in] strFileFrom Pointer to a null-terminated string that specifies the name of an existing file.
* @param[in] strFileTo Pointer to a null-terminated string that specifies the name of the new file.
* @param[out] 输出参数 输出参数描述信息
* @return BOOL
* @retval nonezero If the function succeeds, the return value is nonzero.
zero If the function fails, To get extended error information, call GetLastError
*/
static BOOL CopyFile(const T& strFileFrom, const T& strFileTo, const uint64_t ui64Max = ULLONG_MAX);
//e.g. "E:\\k\\xxx\\xxx\\" => "E:\\k\\xxx\\xxx\\CopiedFiles", not include sub folders and its files
static BOOL CopyFiles(const T& strFileFrom, const T& strFileTo, const uint64_t ui64Max = ULLONG_MAX);
//e.g. "E:\\k\\xxx\\xxx\\*" => Folder Name Vector([0]="Folderxxx",[1]="UnitTestFolder0",[2]="UnitTestFolder1",...)
//e.g. "E:\\k\\xxx\\xxx\\UnitTestFolder*" => Folder Name Vector([0]="UnitTestFolder0",[1]="UnitTestFolder1",[2]="UnitTestFolder1",...)
static BOOL ListSubDirectory(const T& strFolder, vector<T>& vtFolders, const UINT32 uMax = UINT_MAX);
//e.g. "E:\\k\\xxx\\xxx\\*" => File Name Vector
//e.g. "E:\\k\\xxx\\xxx\\test_bsbase.*" => File Name Vector
static BOOL ListSubFiles(const T& strFolder, vector<T>& vtFiles, const UINT32 uMax = UINT_MAX);
static BOOL ListSubFiles(const T& strFolder, const T& strFilter, vector<T>& vtFiles, const UINT32 uMax = UINT_MAX);
//"E:\\k\\xxx\\xxx\\CopiedFiles", Empty folder is not required, that is to say, Empty folder is not mandatory
static BOOL DeleteFolder(const T& strFolder);
static BOOL TryToDeleteFile(const T& strFilename, const DWORD dwInterval /* ms */, const DWORD dwDuration /* ms */);
static BOOL RecursiveCreateFolder(const T& strFolder, const UINT32 uFrom = 0);
/**
* @brief 函数功能概要信息
* @param[in] 输入参数 输入参数描述信息
* @param[out] 输出参数 输出参数描述信息
* @return 返回类型
* @retval 返回值 返回值意义描述信息
*/
static BOOL BrowseDirectory(const T& strFileName);
static BOOL LaunchBrowser(const T& sExeFileName, const T& strURL);
/*
LaunchBat("batfilepath param")
e.g. LaunchBat(tstring(_T("E:\\k\\bscom\\output\\bin\\debug\\chrome105\\move.bat E:\\objs")));
*/
static BOOL LaunchBat(const T& strBatFile);
static BOOL ReadLine(basic_ifstream<_type, char_traits<_type> >& ifs, T& strLine);
static BOOL ReadLines(const T& strFilePath, vector<T>& vtLines);
static T MkUniFileName(const DWORD dwCount = 0u, const T& strFileExt = T());
};
typedef CBsFS<tstring> wfs;
typedef CBsFS<bsastring> afs;
#if defined UNICODE || defined _UNICODE
#define fs wfs
#else
#define fs afs
#endif // !UNICODE && !_UNICODE
END_NS
#include "stdafx.h"
#include <type_traits>
#include <Psapi.h>
#include <ShlObj.h>
#include <atlcomcli.h>
#include <combaseapi.h>
#include "bsbase/bsfs.h"
#include "bsbase/bsos.h"
#include "bsbase/bsguid.h"
#include "bsbase/bstring.h"
cpp实现文件:
DECLARE_NS_BSC
static const DWORD MAX_NUMBER_FOLDERS = 100u;
static const DWORD MAX_NUMBER_FILES = 1000u;
static const DWORD MAX_FILE_SIZE = 157286400u; //150M = 150*1024*1024 = 157286400
INSTANCE_TEMPLATE_CLASS_1st(CBsFS, tstring);
INSTANCE_TEMPLATE_CLASS_1st(CBsFS, bsastring);
//////////////////////////////////////////////////////////////////////////
template <typename T>
CBsFS<typename T>::CBsFS(void) {
}
template <typename T>
CBsFS<typename T>::~CBsFS(void) {
}
template <typename T>
BOOL CBsFS<typename T>::SplitPath(const T& strFilePath, map<int, T>& oMap) _NOEXCEPT {
if (strFilePath.empty())
return FALSE;
//if (is_base_of<TCHAR, _type>::value)
//if (is_same_v<T::value_type, TCHAR>::value)
//if (is_same<T::value_type, TCHAR>::value)
//if (constexpr(sizeof(_type) == 2))
//if (string(typeid(typename T::value_type _type).name()) == "unsigned short")
_type szDrive[_MAX_DRIVE] = {0}, szDir[_MAX_DIR] = {0}, szFName[_MAX_FNAME] = {0}, szExt[_MAX_EXT] = {0};
bsplitpath_s(strFilePath.c_str(), szDrive, BsNumElms(szDrive), szDir, BsNumElms(szDir),
szFName, BsNumElms(szFName), szExt, BsNumElms(szExt));
oMap.clear();
oMap.insert(make_pair(eFS_DISK, szDrive));
oMap.insert(make_pair(eFS_DIR, szDir));
oMap.insert(make_pair(eFS_FILENAME, szFName));
oMap.insert(make_pair(eFS_EXT, szExt));
return (0 < oMap.size());
}
template <typename T>
T CBsFS<typename T>::MakePath(const map<int, T>& oMap) _NOEXCEPT {
basic_ostringstream<_type, char_traits<_type>, allocator<_type> > ostm;
for each (auto& var in oMap)
ostm << var.second;
return move(ostm.str());
}
template <typename T>
T CBsFS<typename T>::GetFileDir(const T& strFilePath) _NOEXCEPT {
if (strFilePath.empty())
return {};
bsc::bstringT<_type> strPath(strFilePath.c_str());
strPath.Replace(_type(BS_BACKSLASH_CHAR_A), _type(BS_SLASH_CHAR_A));
T::size_type pos = strPath.rfind(_type(BS_SLASH_CHAR_A));
if (pos == T::npos)
return {};
return move(strPath.substr(0, pos + 1));
}
template <typename T>
T CBsFS<typename T>::GetFileName(const T& strFilePath) _NOEXCEPT {
if (strFilePath.empty())
return {};
bsc::bstringT<_type> strPath(strFilePath.c_str());
strPath.Replace(_type(BS_BACKSLASH_CHAR_A), _type(BS_SLASH_CHAR_A));
T::size_type pos = strPath.rfind(_type(BS_SLASH_CHAR_A));
if (pos == T::npos)
return {};
return move(strPath.substr(pos + 1));
}
template <typename T>
T CBsFS<typename T>::GetFileExt(const T& strFName, BOOL tfLower/* = FALSE*/) _NOEXCEPT {
T strExt = {};
if (strFName.empty())
return move(strExt);
bsc::bstringT<_type> strFileName(strFName.c_str());
strFileName.Replace(_type(BS_BACKSLASH_CHAR_A), _type(BS_SLASH_CHAR_A));
T::size_type poslash = strFileName.find_last_of(_type(BS_SLASH_CHAR_A));
if (T::npos != poslash) {
T::size_type pos = strFileName.find_last_of(_type(BS_DOT_CHAR_A));
if (T::npos != pos && poslash < pos)
strExt.assign(strFileName, pos, strFileName.size() - pos);
}
else
{
T::size_type pos = strFileName.find_last_of(_type(BS_DOT_CHAR_A));
if (T::npos != pos)
strExt.assign(strFileName, pos, strFileName.size() - pos);
}
if (tfLower && !strExt.empty()) // make lowercase
transform(strExt.begin(), strExt.end(), strExt.begin(), ::tolower);
return move(strExt);
}
template <typename T>
T CBsFS<typename T>::GetParentDir(const T& strDir) _NOEXCEPT {
T strResult = {};
if (strDir.empty())
return move(strResult);
bsc::bstringT<_type> strDirectory(strDir.c_str());
strDirectory.Replace(_type(BS_BACKSLASH_CHAR_A), _type(BS_SLASH_CHAR_A));
if (_type(BS_SLASH_CHAR_A) == strDirectory.at(strDirectory.length() - 1))
strDirectory = strDirectory.substr(0, strDirectory.length() - 1);
T::size_type poslash = strDirectory.find_last_of(_type(BS_SLASH_CHAR_A));
if (T::npos != poslash)
strResult.assign(strDirectory, 0, poslash + 1);
return move(strResult);
}
template <typename T>
BOOL CBsFS<typename T>::IsExists(const T& strFilePath) { //apply to file & directory
return (0 == BsGetFileStatus(strFilePath.c_str()));
}
template <typename T>
T CBsFS<typename T>::PathAppend(const T& strPath, const T& strAppend, _ctype chSep /*= _ctype(BS_SLASH_CHAR_A)*/) _NOEXCEPT {
T strResult(strPath);
if (!strResult.empty()) {
if (strResult.at(strResult.size() - 1) != chSep)
strResult += chSep;
}
//const T s = (strResult + strAppend);
//return s;
if (1u == strAppend.length()) {
const _type c = strAppend.at(0);
if (_type(BS_BACKSLASH_CHAR_A) == strAppend.at(0) || _type(BS_SLASH_CHAR_A) == strAppend.at(0))
return move(strResult);
}
return move(strResult + strAppend);
// If we use move(...) return value, and move(...) has param, e.g. strAppend
// Then we should combination two strings in temp var, finally use move(...) to return value
// otherwise, it will lead to memory repeated release issue.
// Note: already find the root cause:release version linker to debug msvcrtd.lib, so change to linker to msvcrt.lib will be ok.
// So rollback and keep original return move(strResult + strAppend);
}
template <typename T>
T CBsFS<typename T>::ComposeFilePath(HMODULE hModule, const T& strFileName) {
if (strFileName.empty())
return{};
const T strDir = GetModuleFileDir(hModule);
bsc::bstringT<_type> strFile(strFileName.c_str());
strFile.Replace(_type(BS_BACKSLASH_CHAR_A), _type(BS_SLASH_CHAR_A));
if (strFile.at(0) == _type(BS_SLASH_CHAR_A))
strFile = strFile.substr(1);
return move(strDir + strFile);
}
template <typename T>
BOOL CBsFS<typename T>::GetFileCreateTime(const T& strFilePath, FILETIME& refTime) {
return BsGetFileCreateTime(strFilePath.c_str(), refTime);
}
template <typename T>
int64_t CBsFS<typename T>::GetFileSize(const T& strFilePath) {
return BsGetFileSize(strFilePath.c_str());
}
template <typename T>
int64_t CBsFS<typename T>::GetFileSize(ifstream& ifs) {
if (!ifs.is_open())
return -1;
ifstream::pos_type pos = ifs.tellg();
ifs.seekg(0, ios::end);
ifstream::pos_type end = ifs.tellg();
ifs.seekg(pos, ios::beg);
return (end - pos);
}
template <typename T>
int64_t CBsFS<typename T>::GetFileSize(ofstream& ofs) {
if (!ofs.is_open())
return -1;
ofstream::pos_type pos = ofs.tellp();
ofs.seekp(0, ios::end);
ofstream::pos_type end = ofs.tellp();
ofs.seekp(pos, ios::beg);
return (end - pos);
}
template <typename T>
void CBsFS<typename T>::FileTimeToSystemTime(FILETIME& refTime, SYSTEMTIME& refSystemTime) {
FILETIME ftLocalTime = {0};
FileTimeToLocalFileTime(&refTime, &ftLocalTime);
::FileTimeToSystemTime(&ftLocalTime, &refSystemTime);
}
template <typename T>
T CBsFS<typename T>::GetModuleFile(HMODULE hModule, HANDLE hProcess/* = nullptr*/) {
_type szMoudle[MAX_PATH] = {0};
if (nullptr == hProcess || !bsc::BSOS::IsWin10orLater()) { //Because GetModuleFileNameEx will failed on WIN7 platform
BsGetModuleFileName(hModule, szMoudle, BsNumElms(szMoudle));
return szMoudle;
}
BsGetModuleFileNameEx(hProcess ? hProcess : GetCurrentProcess(), hModule, szMoudle, BsNumElms(szMoudle));
return szMoudle;
}
template <typename T>
T CBsFS<typename T>::GetModuleFileDir(HMODULE hModule) {
_type szMoudle[MAX_PATH] = {0};
BsGetModuleFileName(hModule, szMoudle, BsNumElms(szMoudle));
return GetFileDir(T(szMoudle));
}
template <typename T>
T CBsFS<typename T>::GetModuleFileName(HMODULE hModule, HANDLE hProcess/* = nullptr*/) {
const T strMoudle = GetModuleFile(hModule, hProcess);
const T strFileName = GetFileName(strMoudle);
return move(strFileName);
}
template <typename T>
T CBsFS<typename T>::ParentPath(const T& strFilePath) {
if (1u >= strFilePath.length()) return T{}; // C => T{}, . => T{}
T t(strFilePath);
T strPath = t.Replace(_type(BS_BACKSLASH_CHAR_A), _type(BS_SLASH_CHAR_A));
if (_type(BS_SLASH_CHAR_A) == strPath.at(strPath.length() - 1u))
strPath = strPath.substr(0u, strPath.length() - 1u); // Remove the last '\\' character
if (1u == strPath.length() && _type(BS_DOT_CHAR_A) == strPath.at(0u))
return T{}; // .\\ => T{}, ./ => T{}
T::size_type pos = strPath.rfind(_type(BS_COLON_CHAR_A));
if (pos != T::npos && 3u >= strPath.length())
return T{}; // C: => T{}, C:\\ => T{}
map<int, T> oMap;
SplitPath(strPath, oMap);
T strDir = oMap[eFS_DIR];
if (1u == strDir.length() && _type(BS_SLASH_CHAR_A) == strDir.at(0u))
return move(oMap[eFS_DISK] + strDir); // C:\\abc => C:\\
const T& strExt = oMap[eFS_EXT];
if (!strExt.empty()) {
pos = strDir.rfind(_type(BS_SLASH_CHAR_A));
if (pos == (strDir.length() - 1u)) {
pos = strDir.rfind(_type(BS_SLASH_CHAR_A), pos - 1u);
return move(oMap[eFS_DISK] + strDir.substr(0u, pos+1u)); /* C:\\abc\\..\\..\\123\\abc.txt => C:\\abc\\..\\..\\ */
}
}
return move(oMap[eFS_DISK] + strDir);
}
template <typename T>
T CBsFS<typename T>::GenGUIDFileName(const UINT eFmt /*= E_GUID_FORMAT_NULL*/) {
if (CGUIDFactory::E_GUID_FORMAT_FULL < eFmt)
return T{};
basic_ostringstream<_type, char_traits<_type>, allocator<_type> > ostm;
ostm.fill(_ctype(BS_ZERO_A));
unique_ptr<bsc::CGUIDFactory> upGUIDFactory = CGUIDFactory::CreateGUIDFactory();
if (upGUIDFactory) {
_type szBuf[100] = {0};
BOOL bRet = upGUIDFactory->GenGUID(szBuf, sizeof(szBuf), (CGUIDFactory::eGUIDFormat)eFmt);
ostm << szBuf << _ctype(BS_UNDERSCORE_CHAR_A);
}
SYSTEMTIME sysTime = {0};
GetLocalTime(&sysTime);
ostm << setw(4) << (int)sysTime.wYear
<< setw(1) << _ctype(BS_UNDERSCORE_CHAR_A)
<< setw(2) << (int)sysTime.wMonth // tm_mon范围[0,11],故需加1
<< setw(1) << _ctype(BS_UNDERSCORE_CHAR_A)
<< setw(2) << (int)sysTime.wDay
<< setw(1) << _ctype(BS_UNDERSCORE_CHAR_A)
<< setw(2) << (int)sysTime.wHour
<< setw(1) << _ctype(BS_UNDERSCORE_CHAR_A)
<< setw(2) << (int)sysTime.wMinute
<< setw(1) << _ctype(BS_UNDERSCORE_CHAR_A)
<< setw(2) << (int)sysTime.wSecond
<< setw(1) << _ctype(BS_UNDERSCORE_CHAR_A)
<< setw(3) << (int)sysTime.wMilliseconds;
return move(ostm.str());
}
template <typename T>
T CBsFS<typename T>::GetTemporaryFolder(void) _NOEXCEPT {
_type szTemp[MAX_PATH + 1] = {0};
BsGetTempPath(MAX_PATH, szTemp);
return szTemp;
}
template <typename T>
T CBsFS<typename T>::GetTempFileName(const T& strPrefix/* = T()*/, const UINT uUnique/* = 0*/) _NOEXCEPT {
_type szTmpName[_MAX_PATH] = {0};
_type szPrefix[] = {_type('~'), _type('b'), _type('s')}; // = _T("~bs")
const T strTmpDir = GetTemporaryFolder();
BsGetTempFileName(strTmpDir.c_str(), strPrefix.empty() ? szPrefix : strPrefix.c_str(), uUnique, szTmpName);
return szTmpName;
}
template <typename T>
T CBsFS<typename T>::ChangeFileExt(const T& strFile, const T& strTargetExt) _NOEXCEPT {
T::size_type pos = strFile.rfind(_type(BS_DOT_CHAR_A));
if (pos == T::npos)
return {};
const T strFileName = strFile.substr(0, pos + 1);
return std::move(strFileName + strTargetExt);
}
//[in] A CSIDL value that identifies the folder whose path is to be retrieved.
//Only real folders are valid.If a virtual folder is specified, this function will fail.
//You can force creation of a folder with SHGetFolderPath by combining the folder's CSIDL with CSIDL_FLAG_CREATE.
template <typename T>
T CBsFS<typename T>::GetSpecificFolder(const UINT32 uFolderID) {
_type path[_MAX_PATH + 1] = {0};
HRESULT hr = BsSHGetFolderPath(NULL, uFolderID, NULL, 0, path);
_ASSERT(S_OK == hr);
return path;
}
template <typename T>
T CBsFS<typename T>::GetShortPathName(const T& strFilePath) _NOEXCEPT {
if (strFilePath.empty())
return {};
_type szPath[MAX_PATH] = {0};
T strShortPathName = {};
if (0 != BsGetShortPathName(strFilePath.c_str(), szPath, BsNumElms(szPath)))
strShortPathName = szPath;
else
Log::DebugError(_T("GetShortPathName() failed, err=0x%08x"), GetLastError());
return move(strShortPathName);
}
template <typename T>
T CBsFS<typename T>::GetUserAppDataFolder(void) _NOEXCEPT {
return move(GetSpecificFolder(CSIDL_LOCAL_APPDATA));
}
template <typename T>
T CBsFS<typename T>::GetProgramDataFolder(void) _NOEXCEPT {
return move(GetSpecificFolder(CSIDL_COMMON_APPDATA));
}
template <typename T>
T CBsFS<typename T>::GetProgramFilesFolder(void) _NOEXCEPT {
return move(GetSpecificFolder(CSIDL_PROGRAM_FILES));
}
template <typename T>
BOOL CBsFS<typename T>::DeleteFile(_ctype* pszPath) {
return BsDeleteFile(pszPath);
}
template <typename T>
BOOL CBsFS<typename T>::DeleteFile(const T& strFilePath) {
return BsDeleteFile(strFilePath.c_str());
}
template <typename T>
void CBsFS<typename T>::DeleteFiles(vector<T>& ovtFiles) {
for each(auto& item in ovtFiles)
BsDeleteFile(item.c_str());
}
template <typename T>
BOOL CBsFS<typename T>::CopyFile(const T& strFileFrom, const T& strFileTo, const uint64_t ui64Max/* = ULLONG_MAX*/) {
BOOL bRet = FALSE;
if (ui64Max >= (uint64_t)GetFileSize(strFileFrom))
bRet = BsCopyFile(strFileFrom.c_str(), strFileTo.c_str(), FALSE);
return bRet;
}
template <typename T>
BOOL CBsFS<typename T>::CopyFiles(const T& strFileFrom, const T& strFileTo, const uint64_t ui64Max /*= ULLONG_MAX*/) {
if (strFileFrom.empty() || strFileTo.empty())
return FALSE;
if (!BsPathFileExists(strFileTo.c_str()))
RecursiveCreateFolder(strFileTo);
vector<T> files;
_ctype szAppend[] = { _ctype(BS_STAR_CHAR_A), _ctype(BS_ZERO) };
const T query = PathAppend(strFileFrom.c_str(), T(szAppend).c_str(), _ctype(BS_SLASH_CHAR_A));
BOOL bRe = ListSubFiles(query.c_str(), files, MAX_NUMBER_FILES);
for (vector<T>::const_iterator it = files.begin(); it != files.end(); ++it)
{
const T srcFile = PathAppend(strFileFrom.c_str(), it->c_str(), _ctype(BS_SLASH_CHAR_A));
if (ui64Max > (uint64_t)GetFileSize(srcFile))
{
const T dstFile = PathAppend(strFileTo.c_str(), it->c_str(), _ctype(BS_SLASH_CHAR_A));
BsCopyFile(srcFile.c_str(), dstFile.c_str(), FALSE);
}
else
Log::DebugWarn(_T("%s larger than %lld"), srcFile.c_str(), ui64Max);
}
return bRe;
}
template <typename T>
BOOL CBsFS<typename T>::ListSubDirectory(const T& strFolder, vector<T>& vtFolders, const UINT32 uMax/* = ULLONG_MAX*/) {
return BsListSubDir(strFolder.c_str(), vtFolders, uMax);
}
template <typename T>
BOOL CBsFS<typename T>::ListSubFiles(const T& strFolder, vector<T>& vtFiles, const UINT32 uMax/* = UINT_MAX*/) {
return BsListSubFiles(strFolder.c_str(), vtFiles, uMax);
}
template <typename T>
BOOL CBsFS<typename T>::ListSubFiles(const T& strFolder, const T& strFilter, vector<T>& vtFiles, const UINT32 uMax/* = UINT_MAX*/) {
T strFile(strFolder);
strFile = PathAppend(strFile, strFilter, _ctype(BS_SLASH_CHAR_A));
return ListSubFiles(strFile.c_str(), vtFiles, uMax);
}
template <typename T>
BOOL CBsFS<typename T>::DeleteFolder(const T& strFolder) {
return BsDeleteFolder(strFolder.c_str());
}
template <typename T>
BOOL CBsFS<typename T>::TryToDeleteFile(const T& strFilename, const DWORD dwInterval, const DWORD dwDuration) {
if (BsGetFileStatus(strFilename.c_str())) // If file doesn't exist
return TRUE;
const DWORD dwStart = GetTickCount();
do
{
if (BsDeleteFile(strFilename.c_str()))
return TRUE;
Sleep(dwInterval);
} while ((GetTickCount() - dwStart) < dwDuration);
return FALSE;
}
template <typename T>
BOOL CBsFS<typename T>::RecursiveCreateFolder(const T& strFolder, const UINT32 uFrom = 0) {
if (strFolder.empty())
return FALSE;
T strPath(strFolder);
BOOL bResult = BsCreateDirectory(strPath.c_str(), NULL);
if (bResult)
{
//CGPCSystemUtil::SetSecurityDesc(strPath);
}
else
{
UINT32 uError = GetLastError();
switch (uError)
{
case ERROR_ALREADY_EXISTS:
bResult = TRUE;
break;
case ERROR_ACCESS_DENIED:
bResult = FALSE;
//QLError(_T("recursiveCreate folder failed,path=%s, error=%d,access denied"), strPath.c_str(), uError);
break;
case ERROR_PATH_NOT_FOUND:
{
if (strPath[strPath.size() - 1] == _ctype(BS_SLASH_CHAR_A))
strPath = strPath.substr(0, strPath.size() - 1);
T::size_type pos = strPath.rfind(_ctype(BS_SLASH_CHAR_A));
if (T::npos != pos)
{
T strParent = strPath.substr(0, pos);
if (RecursiveCreateFolder(strParent))
{
bResult = BsCreateDirectory(strPath.c_str(), NULL);
//////////////////////////////////////////////////////////////////////////
//2016/05/13, fix target folder security desc
if (bResult)
{
;// CGPCSystemUtil::SetSecurityDesc(strPath);
}
//////////////////////////////////////////////////////////////////////////
}
}
}
break;
default:
Log::DebugWarn(_T("RecursiveCreate failed, Path=%s, Err=%d"), strPath.c_str(), uError);
break;
}
}
return bResult;
}
template <typename T>
BOOL CBsFS<typename T>::BrowseDirectory(const T& strFileName) {
return BsLocateDirectory(strFileName.c_str());
}
template <typename T>
BOOL CBsFS<typename T>::LaunchBrowser(const T& strExeFileName, const T& strURL) {
return BsLaunchBrowser(strExeFileName.c_str(), strURL.c_str());
}
template <typename T>
BOOL CBsFS<typename T>::LaunchBat(const T& strBatFile) {
return BsLaunchBat(strBatFile.c_str());
}
template <typename T>
BOOL CBsFS<typename T>::ReadLine(basic_ifstream<_type, char_traits<_type> >& ifs, T& strLine) {
strLine.clear();
BOOL bRet = FALSE;
if (ifs.is_open() && getline(ifs, strLine))
bRet = TRUE;
return bRet;
}
template <typename T>
BOOL CBsFS<typename T>::ReadLines(const T& strFilePath, vector<T>& vtLines) {
vtLines.clear();
basic_ifstream<_type, char_traits<_type> > ifs(strFilePath.c_str());
T strLine{};
while (ReadLine(ifs, strLine)) {
vtLines.push_back(move(strLine));
strLine.erase();
}
return (0 < vtLines.size());
}
template <typename T> // MakeUniqueFileName
T CBsFS<typename T>::MkUniFileName(const DWORD dwCount/* = 0u*/, const T& strFileExt/* = T()*/) {
SYSTEMTIME sysTime = { 0 };
::GetLocalTime(&sysTime);
std::basic_ostringstream<_type, std::char_traits<_type>, std::allocator<_type> > ostm;
ostm.fill(_ctype('0'));
ostm << std::setw(4) << (DWORD)sysTime.wYear << _ctype('_')
<< std::setw(2) << (DWORD)sysTime.wMonth << _ctype('_')
<< std::setw(2) << (DWORD)sysTime.wDay << _ctype('_')
<< std::setw(2) << (DWORD)sysTime.wHour << _ctype('_')
<< std::setw(2) << (DWORD)sysTime.wMinute << _ctype('_')
<< std::setw(2) << (DWORD)sysTime.wSecond << _ctype('_')
<< std::setw(3) << (DWORD)sysTime.wMilliseconds;
if (0u < dwCount)
ostm << _ctype('_') << dwCount;
if (!strFileExt.empty())
ostm << strFileExt;
return std::move(ostm.str());
}
//////////////////////////////////////////////////////////////////////////
END_NS