无缓冲区写文件

/*****************************************************************************/
<2011_0725_0141><home><LostSpeed><LsLog\program\testcase\FileOPT V1.0.0.1>
无缓冲区写文件, 写文件完成后, 设置正确的FileTotalSize
用无缓冲区写文件的目的: 某些程序会拦截, 替换fopen操作的文件内容, 
应用层无法得到真实的文件数据, 这时需要用无缓冲区读写的方式来裸读.

知识点: 
* CreateFile时带上标记FILE_FLAG_NO_BUFFERING
* WriteFile时,需要写入扇区对齐或扇区倍数的块, 如果不够块大小, 在写入区先放着
  在往写入区中写的时候, 不管是否写入了磁盘, 都需要自己维护写入的FileTotalSize
  用来在文件完全写入后,设置正确的FileSize.
* 用GetDiskFreeSpace来得到目标文件所在驱动器的扇区大小.
* 为了提高写入速度, 应给出一个较大的缓冲区容量并按照扇区大小进行规约
* 写入完成后, 关闭文件. 再打开文件, SetFilePointer, SetEndOfFile 设置文件的正确大小
/*****************************************************************************/

// FileOPT.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <direct.h>
#include "helper/helper_file.h"

USE_NAMESPACE_LS

LS_RC BuildTestSrc(TCHAR * pcPathNameSrc, HANDLE & hFileSrc, BYTE* pucBufRd, size_t nSizeBufRd);

int _tmain(int argc, _TCHAR* argv[])
{
	LS_RC lsRc = LS_RC_OK;

	BYTE ucBufRd[_MAX_PATH];//从其他地方读来的数据, 每次得到之后, 循环写入文件
	PBYTE pucWriteNoCache = NULL;//以扇区倍数为单位写入块
	DWORD dwRdBack = 0;//从其他地方读来的数据在ucBufRd中的字节数量
	TAG_WIRTE_BUF_NO_CACHE_INFO sWriteBufNoCache;//无缓冲写入的结构, 用于带多个参数, 简化函数入参数量
	DWORD dwWrBack = 0;//从其他地方读来的数据在ucBufRd中的字节数量

	DWORD dwWirteBufLen = 0;//无缓冲方式要按照扇区写入, 一次性写入此扇区到文件

	HANDLE hFileSrc = INVALID_HANDLE_VALUE;//从hFileSrc读内容
	HANDLE hFileDst = INVALID_HANDLE_VALUE;//将读到的内容写到这里hFileDst

	TCHAR * pcPathNameDst = _T("c:\\tmp\\dir1\\dir2\\fileOptTest.txt");//要写入内容的文件

	/** 假设写入的数据来源于磁盘上已经存在的文件 c:\\testForRd.txt, 这个文件的大小为 2345 */
	TCHAR * pcPathNameSrc = _T("c:\\testForRd.txt");

	lsRc = LsCreateFileEngrossNoCache(pcPathNameDst, hFileDst);
	if(LS_RC_OK != lsRc)
	{
		goto __tmain_END;
	}

	//得到无缓冲方式写时,以扇区倍数为大小的块长度
	lsRc = LsGetWriteBufferLenForWirteFileNoCache(pcPathNameDst, sizeof(ucBufRd), dwWirteBufLen);
	if(LS_RC_OK != lsRc)
	{
		goto __tmain_END;
	}

	/** 
	* 只开1个,2个扇区, 明显慢了, 120M的文件写完,超过了1分钟
	* 按扇区倍数规约到1M, 试验表明, 10秒钟左右, 120M就写完了~
	* 117 MB (123,456,789 字节)
	*/
	lsRc = LsGetSectorMultiple(1024 * 1024, dwWirteBufLen, dwWirteBufLen);
	if(LS_RC_OK != lsRc)
	{
		goto __tmain_END;
	}

	pucWriteNoCache = new BYTE[dwWirteBufLen];//按扇区倍数为单位的写入块
	if(!pucWriteNoCache)
	{
		lsRc = MakeLsErrSn(LS_RC_MEMORY_ALLOC);
		goto __tmain_END;
	}

	//建立一个数据来源, 用于读取此数据来源写到pcPathNameDst
	lsRc = BuildTestSrc(pcPathNameSrc, hFileSrc, &ucBufRd[0], sizeof(ucBufRd));

	lsRc = LsOpenFileForRead(pcPathNameSrc, hFileSrc);
	if(LS_RC_OK != lsRc)
	{
		goto __tmain_END;
	}

	//read src write to dst, write by no cache!
	ZeroMemory(&sWriteBufNoCache, sizeof(TAG_WIRTE_BUF_NO_CACHE_INFO));
	sWriteBufNoCache.dwLenBufNoCache = dwWirteBufLen;
	sWriteBufNoCache.dwPosToWrite = 0;//由LsWriteFileNoCache决定
	sWriteBufNoCache.hFileToWrite = hFileDst;
	sWriteBufNoCache.llTotalWriteIn = 0;
	sWriteBufNoCache.pBufNoCache = pucWriteNoCache;

	sWriteBufNoCache.pBufToWrite = ucBufRd;
	while(ReadFile(hFileSrc, ucBufRd, sizeof(ucBufRd), &dwRdBack, NULL))
	{
		if(dwRdBack <= 0)
		{
			break;//已经读完了
		}

		sWriteBufNoCache.dwLenToWrite = dwRdBack;//由ReadFile来决定
		sWriteBufNoCache.dwLenWrBack = 0;//由LsWriteFileNoCache决定

		lsRc = LsWriteFileNoCache(sWriteBufNoCache);
		if((LS_RC_OK != lsRc) || (dwRdBack != sWriteBufNoCache.dwLenWrBack))
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
			goto __tmain_END;
		}
	}

	lsRc = LsWriteFileNoCache_FulshData(sWriteBufNoCache);
	if(LS_RC_OK != lsRc)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
		goto __tmain_END;
	}
	LsCloseHandle(hFileDst);

	/** 在关闭文件后, 重新打开文件, 设置文件实际size */
	lsRc = LsOpenFileForWrite(pcPathNameDst, hFileDst);
	if(LS_RC_OK != lsRc)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
		goto __tmain_END;
	}

	lsRc = LsSetFilePointer(sWriteBufNoCache);
	if(LS_RC_OK != lsRc)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
		goto __tmain_END;
	}

__tmain_END:
	LsCloseHandle(hFileSrc);
	LsCloseHandle(hFileDst);
	LsDelete(pucWriteNoCache);

	if(LS_RC_OK == lsRc)
	{
	}
	else
	{
		_tprintf(_T("sir: some error was happen, please check the progam\n"));
		getchar();
	}

	return 0;
}

LS_RC BuildTestSrc(TCHAR * pcPathNameSrc, HANDLE & hFileSrc, BYTE* pucBufRd, size_t nSizeBufRd)
{
	LS_RC lsRc = LS_RC_OK;
	DWORD dwSizeRdBack = 27;//每次写入pcPathNameSrc的测试内容长度, 随便一个非特殊,非对齐的长度
	DWORD dwSizeWrBack = 0;//每次写入pcPathNameSrc dwSizeRdBack长度后, 返回的写入长度
	size_t nTotalWrite = 0;//总共写入的字节数

	size_t n = 0;
	BOOL bStopWriteSrc = FALSE;
	LONGLONG llSizeWirteToSrc = 123456789;//写入pcPathNameSrc的测试内容总长度, 123456789是100多M的大小

	lsRc = LsCreateFile(pcPathNameSrc, hFileSrc);
	if(LS_RC_OK != lsRc)
	{
		goto _BuildTestSrc_END;
	}

	for(n = 0; n < nSizeBufRd; n++)
	{
		*(pucBufRd + n) = (BYTE)(n % 0xff);
	}

	bStopWriteSrc = FALSE;//为最后一次写测试文件src准备的
	while(llSizeWirteToSrc > 0)	
	{
		if(llSizeWirteToSrc >= dwSizeRdBack)
		{
			llSizeWirteToSrc -= dwSizeRdBack;
		}
		else
		{
			dwSizeRdBack = (DWORD)llSizeWirteToSrc;
			bStopWriteSrc = TRUE;
		}

		if(!WriteFile(hFileSrc, pucBufRd, dwSizeRdBack, &dwSizeWrBack, NULL))
		{
			//带缓冲方式写得时候, 不需要扇区对齐写, 写多少都是成功的, 且(dwSizeWrBack == dwSizeRdBack)
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
			goto _BuildTestSrc_END;
		}
		nTotalWrite += dwSizeWrBack;

		if(dwSizeRdBack != dwSizeWrBack)
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
			goto _BuildTestSrc_END;
		}
	
		if(bStopWriteSrc)
		{
			break;
		}
	}

_BuildTestSrc_END:
	LsCloseHandle(hFileSrc);

	return lsRc;
}
/**
* @file helper_file.h
*/

#ifndef _HELPER_FILE_H_
#define _HELPER_FILE_H_

#include "helper_common.h"
#include "define/define_error.h"
#include "define/define_struct_file.h"

LS_NAMESPACE_BEGIN

LS_RC LsFindFile(CONST TCHAR * pcFileName);
LS_RC LsCreateFile(TCHAR * pcFileName, HANDLE &hFile);				/**< 建立文件, 传出文件句柄 */
LS_RC LsOpenFileForAppend(TCHAR * pcFileName, HANDLE &hFile);		/**< 打开现存文件去写, 传出文件句柄 */
LS_RC LsOpenFileForRead(TCHAR * pcFileName, HANDLE &hFile);			/**< 打开现存文件去读, 传出文件句柄 */
LS_RC LsOpenFileForWrite(TCHAR * pcFileName, HANDLE &hFile);		/**< 打开现存文件去写, 传出文件句柄 */

LS_RC LsCloseHandle(HANDLE &hFile);			/**< 关闭文件件 */
LS_RC LsDelete(PBYTE & pBuf);				/**< if (pBuf == TRUE), delete it */
LS_RC LsWriteFile(HANDLE &hFile, LPCVOID pcBuf, DWORD dwSizeBuf);		/**< 写入文件内容 */
/*
WriteFile(hTempFile, buffer, dwBytesRead, 
&dwBytesWritten, NULL); 
*/

/** 独占方式文件操作 */
LS_RC LsGetFullPathFromPathName(TCHAR * pcPathName, tstring & strPath);/**< 从全路径文件名中, 得到全路径 */
LS_RC LsCreateFullPath(TCHAR * pcFullPath);/**< 建立给定的全路径目录 */
LS_RC LsCreateFullPath(TCHAR * pcDriverName, std::vector<tstring> & vecPath);/**< 按照驱动器名称和目录的vector, 建立全路径目录 */
LS_RC LsSplitPathToVector(TCHAR * pcFullPath, std::vector<tstring> & vecPath);
LS_RC LsCreateFileEngrossNoCache(TCHAR * pcPathName, HANDLE & hFile); /**< 以独占无缓存方式建立文件 */

/** 
* @fn LS_RC LsGetPathDiskInfo(TCHAR * pcPathName)
* @brief 得到路径中的磁盘信息, 如果pcPathName是全路径文件名, 分解出路径作为查询条件
*/
LS_RC LsGetPathDiskInfo(TCHAR * pcPathName, TAG_PATH_INFO & pathInfo);

/** 切分路径信息到pathInfo */
LS_RC LsSplitPath(TCHAR * pcPathName, TAG_PATH_INFO & pathInfo);

/** 从PcPathName中得到盘符, 查询磁盘空间, 填充DiskFreeSapce */
LS_RC LsGetFreeDiskSapce(TCHAR * pcPathName, TAG_DISK_FREE_SPACE & DiskFreeSapce);

/** 根据一次写入的最大字节数, 计算写缓冲区的大小
* 独占无缓存方式写文件, 一次要写入磁盘扇区的倍数大小才能成功
*/
LS_RC LsGetWriteBufferLenForWirteFileNoCache(TCHAR * pcPathName, DWORD dwBytesOnceWriteMax, DWORD & dwWirteBufLen);

/** 以扇区倍数为界限, 向给定容量规约 
* 即取最靠近给定容量的扇区倍数的字节数
*/
LS_RC LsGetSectorMultiple(DWORD dwBytesUpperLimit, DWORD dwByteSectorBase, DWORD &dwSectorMultiple);

/** 无缓冲方式的块写入 
* 返回下一次写入的位置dwPosToWrite, 总共写入的字节数(用来写入完成后, 设置文件size)
*/
LS_RC LsWriteFileNoCache(TAG_WIRTE_BUF_NO_CACHE_INFO & sWriteBufNoCache);

/** 最后一次的写, 然后调用关闭文件句柄 */
LS_RC LsWriteFileNoCache_FulshData(TAG_WIRTE_BUF_NO_CACHE_INFO & sWriteBufNoCache);

/** 设置文件指针 */
LS_RC LsSetFilePointer(TAG_WIRTE_BUF_NO_CACHE_INFO & sWriteBufNoCache);

/** 得Windows路径 */
LS_RC LsGetWinOsPath(tstring & strPathName);

LS_NAMESPACE_END

#endif
/**
* @file helper_flie.cpp
*/

#include <windows.h>
#include "helper_string.h"
#include "define/define_error.h"
#include "helper/helper_file.h"

LS_NAMESPACE_BEGIN

LS_RC LsFindFile(CONST TCHAR * pcFileName)
{
	tstring::size_type nPos = 0;
	tstring::size_type nPosUnknown = -1;

	LS_RC lsRc = LS_RC_OK;

	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	tstring strFileName;

	strFileName = pcFileName;
	nPos = strFileName.rfind(_T('\\'));
	if((nPosUnknown != nPos) && (_T('\\') == strFileName.at(strFileName.length() - 1)))
	{
		/** 要查找的文件不能带 "\\" */
		strFileName.replace(nPos, 1, _T("\0"));
	}

	hFind = FindFirstFile(strFileName.c_str(), &FindFileData);
	if (hFind == INVALID_HANDLE_VALUE) 
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_NOT_FIND);
		goto _LsFindFile_END;
	} 
	else 
	{
		//_tprintf(_T("The first file found is %s\n"), FindFileData.cFileName);
		FindClose(hFind);
	}

_LsFindFile_END:
	return lsRc;
}

LS_RC LsCreateFile(TCHAR * pcFileName, HANDLE &hFile)
{/** 建立文件传出文件指针 */
	LS_RC lsRc = LS_RC_OK;

	hFile = CreateFile(pcFileName, 
		GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if(INVALID_HANDLE_VALUE == hFile)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_CREATE);
		goto _LsCreateFile_END;
	}

_LsCreateFile_END:
	return lsRc;
}

LS_RC LsOpenFileForAppend(TCHAR * pcFileName, HANDLE &hFile)
{/** 建立文件传出文件指针 */
	LS_RC lsRc = LS_RC_OK;
	LARGE_INTEGER FileSize;

	hFile = CreateFile(pcFileName, 
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if(INVALID_HANDLE_VALUE == hFile)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_OPEN);
		goto _LsOpenFileForAppend_END;
	}

	/** 如果是已经存在的日志文件, 移动到文件末尾, 形成附加日志的效果 */
	if(GetFileSizeEx(hFile, &FileSize) && (FileSize.QuadPart > 0))
	{
		//_llseek((HFILE)hFile, 0, SEEK_END);//这种用法有个警告 :(
		SetFilePointer(hFile, 0, NULL, FILE_END);
	}

_LsOpenFileForAppend_END:
	return lsRc;
}

LS_RC LsOpenFileForRead(TCHAR * pcFileName, HANDLE &hFile)
{/** 建立文件传出文件指针 */
	LS_RC lsRc = LS_RC_OK;

	hFile = CreateFile(pcFileName, 
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if(INVALID_HANDLE_VALUE == hFile)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_OPEN);
		goto _LsOpenFileForAppend_END;
	}

_LsOpenFileForAppend_END:
	return lsRc;
}

LS_RC LsOpenFileForWrite(TCHAR * pcFileName, HANDLE &hFile)
{/**< 打开现存文件去写, 传出文件句柄 */
	LS_RC lsRc = LS_RC_OK;

	hFile = CreateFile(pcFileName, 
		GENERIC_READ | GENERIC_WRITE,
		NULL,
		0,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if(INVALID_HANDLE_VALUE == hFile)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_OPEN);
		goto END;
	}

END:
	return lsRc;
}

LS_RC LsWriteFile(HANDLE &hFile, LPCVOID pcBuf, DWORD dwSizeBuf)
{
	LS_RC lsRc = LS_RC_OK;
	BOOL bWriteOK = TRUE;
	DWORD dwBytesWritten = 0;			/**< 总共写入的字节数 */
	DWORD dwBytesWrittenOnce = 0;		/**< 一次写操作完成后, 已经写入的字节数 */

	if(INVALID_HANDLE_VALUE == hFile)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_INVALID_HANDLE);
		goto _LsWriteFile_END;
	}

	do 
	{
		bWriteOK = ::WriteFile(hFile, (PBYTE)pcBuf + dwBytesWritten, dwSizeBuf - dwBytesWritten, &dwBytesWrittenOnce, NULL);
		if(!bWriteOK)
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
			goto _LsWriteFile_END; 
		}

		dwBytesWritten += dwBytesWrittenOnce;
	}while((dwSizeBuf - dwBytesWritten) > 0);

_LsWriteFile_END:
	return lsRc;
}

LS_RC LsCloseHandle(HANDLE &hFile)
{
	LS_RC lsRc = LS_RC_OK;
	BOOL bCloseOK = TRUE;

	if(INVALID_HANDLE_VALUE != hFile)
	{
		bCloseOK = CloseHandle(hFile);
		if(bCloseOK)
		{
			hFile = INVALID_HANDLE_VALUE;
		}
		else
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_CLOSE);
			goto _LsCloseFile_END;
		}
	}

_LsCloseFile_END:
	return lsRc;
}

LS_RC LsDelete(PBYTE & pBuf)
{
	if(pBuf)
	{
		delete pBuf;
		pBuf = NULL;
	}

	return LS_RC_OK;
}

LS_RC LsCreateFileEngrossNoCache(TCHAR * pcPathName, HANDLE & hFile)
{
	LS_RC lsRc = LS_RC_OK;
	hFile = INVALID_HANDLE_VALUE;

	lsRc = LsCreateFullPath(pcPathName);
	if(LS_RC_OK != lsRc)
	{
		goto _LsCreateFileEngross_END;
	}

	hFile = CreateFile(
		pcPathName,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_FLAG_NO_BUFFERING,
		NULL);

	if(INVALID_HANDLE_VALUE == hFile)
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_CREATE);
	}

_LsCreateFileEngross_END:
	return lsRc;
}

LS_RC LsGetPathDiskInfo(TCHAR * pcPathName, TAG_PATH_INFO & pathInfo)
{
	LS_RC lsRc = LS_RC_OK;
	
	lsRc = LsSplitPath(pcPathName, pathInfo);
	if(LS_RC_OK != lsRc)
	{
		goto _LsGetPathDiskInfo_END;
	}

_LsGetPathDiskInfo_END:
	return lsRc;
}

LS_RC LsSplitPath(TCHAR * pcPathName, TAG_PATH_INFO & pathInfo)
{
	LS_RC lsRc = LS_RC_OK;

	if(!pathInfo.IsValid())
	{
		lsRc = MakeLsErrSn(LS_RC_PARAM_STRUCT_INVALID);
		goto _LsSplitPath_END;
	}

	_tcscpy_s(pathInfo.PathInfo.path_buffer, SIZE_OF(pathInfo.PathInfo.path_buffer), pcPathName);
	_tsplitpath_s(pathInfo.PathInfo.path_buffer, 
		pathInfo.PathInfo.drive, SIZE_OF(pathInfo.PathInfo.drive),
		pathInfo.PathInfo.dir, SIZE_OF(pathInfo.PathInfo.dir),
		pathInfo.PathInfo.fname, SIZE_OF(pathInfo.PathInfo.fname),
		pathInfo.PathInfo.ext, SIZE_OF(pathInfo.PathInfo.ext));

_LsSplitPath_END:
	return lsRc;
}

LS_RC LsGetFreeDiskSapce(TCHAR * pcPathName, TAG_DISK_FREE_SPACE & DiskFreeSapce)
{
	BOOL bRc = TRUE;
	LS_RC lsRc = LS_RC_OK;
	TAG_PATH_INFO pathInfo;

	ZeroMemory(&pathInfo, sizeof(TAG_PATH_INFO));
	ZeroMemory(&DiskFreeSapce, sizeof(TAG_DISK_FREE_SPACE));

	lsRc = LsSplitPath(pcPathName, pathInfo);
	if(LS_RC_OK != lsRc)
	{
		goto _LsGetFreeDiskSapce_END;
	}

	_tcscpy_s(DiskFreeSapce.DiskFreeSpace.cRootPathName, SIZE_OF(DiskFreeSapce.DiskFreeSpace.cRootPathName), pathInfo.PathInfo.drive);
	_tcscat_s(DiskFreeSapce.DiskFreeSpace.cRootPathName, SIZE_OF(DiskFreeSapce.DiskFreeSpace.cRootPathName), pathInfo.PathInfo.dir);

	/** 给定的全路径必须存在 */
	lsRc = LsFindFile(DiskFreeSapce.DiskFreeSpace.cRootPathName);
	if(LS_RC_OK != lsRc)
	{
		goto _LsGetFreeDiskSapce_END;
	}

	bRc = GetDiskFreeSpace(DiskFreeSapce.DiskFreeSpace.cRootPathName, 
		&DiskFreeSapce.DiskFreeSpace.dwSectorsPerCluster,
		&DiskFreeSapce.DiskFreeSpace.dwBytesPerSector,
		&DiskFreeSapce.DiskFreeSpace.dwNumberOfFreeClusters,
		&DiskFreeSapce.DiskFreeSpace.dwTotalNumberOfClusters);
	if(!bRc)
	{
		lsRc = MakeLsErrSn(LS_RC_GET_SYSTEM_INFO);
		goto _LsGetFreeDiskSapce_END;
	}

	bRc = GetDiskFreeSpaceEx(DiskFreeSapce.DiskFreeSpace.cRootPathName, 
		&DiskFreeSapce.DiskFreeSpace.ullFreeBytesAvailable, 
		&DiskFreeSapce.DiskFreeSpace.ullTotalNumberOfBytes, 
		&DiskFreeSapce.DiskFreeSpace.ullTotalNumberOfFreeBytes);
	if(!bRc)
	{
		lsRc = MakeLsErrSn(LS_RC_GET_SYSTEM_INFO);
		goto _LsGetFreeDiskSapce_END;
	}

_LsGetFreeDiskSapce_END:
	return lsRc;
}

LS_RC LsGetWriteBufferLenForWirteFileNoCache(TCHAR * pcPathName, DWORD dwBytesOnceWriteMax, DWORD & dwWirteBufLen)
{
	LS_RC lsRc = LS_RC_OK;
	CONST DWORD dwInValid = -1;
	TAG_DISK_FREE_SPACE DiskFreeSpace;

	DWORD dwBytesPerSector = 0;/**< pcPathName所在磁盘每扇区size */
	DWORD dwSectorCnt = 0;/**< 写nBytesOnceWriteMax所需的扇区数量 */
	size_t nBytesModSector = 0;/**< 写nBytesOnceWriteMax所需的扇区数量之外的零头 */

	if(dwInValid == dwBytesOnceWriteMax)
	{
		lsRc = MakeLsErrSn(LS_RC_PARAM_INVALID);
		goto _LsGetWriteBufferLenForWirteFileNoCache_END;
	}

	ZeroMemory(&DiskFreeSpace, sizeof(TAG_DISK_FREE_SPACE));
	lsRc = LsGetFreeDiskSapce(pcPathName, DiskFreeSpace);
	if(LS_RC_OK != lsRc)
	{
		goto _LsGetWriteBufferLenForWirteFileNoCache_END;
	}

	dwBytesPerSector = DiskFreeSpace.DiskFreeSpace.dwBytesPerSector;
	nBytesModSector = dwBytesOnceWriteMax % dwBytesPerSector;
	dwSectorCnt = (dwBytesOnceWriteMax - nBytesModSector) / dwBytesPerSector;
	if(nBytesModSector > 0)
	{
		dwSectorCnt++;
	}

	dwWirteBufLen = dwBytesPerSector * dwSectorCnt;

_LsGetWriteBufferLenForWirteFileNoCache_END:
	return lsRc;
}

LS_RC LsGetSectorMultiple(DWORD dwBytesUpperLimit, DWORD dwByteSectorBase, DWORD &dwSectorMultiple)
{
	LS_RC lsRc = LS_RC_OK;
	DWORD dwMod = dwByteSectorBase % dwBytesUpperLimit;
	DWORD dwMultiple = (dwBytesUpperLimit - dwMod) / dwByteSectorBase;

	dwSectorMultiple = dwMultiple * dwByteSectorBase;
END:
	return lsRc;
}

LS_RC LsGetWinOsPath(tstring & strPathName)
{
	LS_RC lsRc = LS_RC_OK;
	TCHAR cInfoBuf[LS_INFO_BUFFER_SIZE];

	ZeroMemory(cInfoBuf, sizeof(cInfoBuf));
	if(!GetWindowsDirectory(cInfoBuf, LS_INFO_BUFFER_SIZE))
	{
		lsRc = MakeLsErrSn(LS_RC_GET_SYSTEM_INFO);
		goto _LsGetWinOsPath_END;
	}

	strPathName = cInfoBuf;

_LsGetWinOsPath_END:
	return lsRc;
}

LS_RC LsGetFullPathFromPathName(TCHAR * pcPathName, tstring & strPath)
{
	/**< 从全路径文件名中, 得到全路径 */
	LS_RC lsRc = LS_RC_OK;

	return lsRc;
}

LS_RC LsCreateFullPath(TCHAR * pcFullPath)
{
	/**< 建立给定的全路径目录 */
	LS_RC lsRc = LS_RC_OK;
	TAG_PATH_INFO pathInfo;
	tstring strFullPath;
	std::vector<tstring> vecPath;

	ZeroMemory(&pathInfo, sizeof(TAG_PATH_INFO));

	lsRc = LsSplitPath(pcFullPath, pathInfo);
	if(LS_RC_OK != lsRc)
	{
		goto END;
	}

	/** 给出的磁盘名称必须是物理存在的, 这个不能手工建立 */
	lsRc = LsFindFile(pathInfo.PathInfo.drive);
	if(LS_RC_OK != lsRc)
	{
		goto END;
	}

	/** 如果全路径已经存在, 不再去分析, 建立全路径 */
	strFullPath = pathInfo.PathInfo.drive;
	strFullPath += _T('\\');
	strFullPath += pathInfo.PathInfo.dir;
	lsRc = LsFindFile(strFullPath.c_str());
	if(LS_RC_OK == lsRc)
	{
		goto END;
	}

	/** 切分全路径到vector */
	lsRc = LsSplitPathToVector(pathInfo.PathInfo.dir, vecPath);
	if(LS_RC_OK != lsRc)
	{
		goto END;
	}

	/** 建立多级目录 */
	lsRc = LsCreateFullPath(pathInfo.PathInfo.drive, vecPath);
	if(LS_RC_OK != lsRc)
	{
		goto END;
	}

END:
	return lsRc;
}

LS_RC LsCreateFullPath(TCHAR * pcDriverName, std::vector<tstring> & vecPath)
{
	INT iRc = 0;
	INT iLastErr = 0;
	LS_RC lsRc = LS_RC_OK;
	tstring strDir = pcDriverName;
	std::vector<tstring>::iterator it = vecPath.begin();

	for(it = vecPath.begin(); it != vecPath.end(); it++)
	{
		if(_T('\\') != strDir.at(strDir.length() - 1))
		{
			strDir += '\\';
		}

		strDir += *it;
		iRc = _tmkdir(strDir.c_str());

		//_tmkdir返回的东西不对啊, 和MSDN上说得不一样, 先忽略
// 		iLastErr = GetLastError();
// 		if((0 != iRc) && (EEXIST != GetLastError()))
// 		{
// 			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_CREATE);
// 			goto _LsCreateFullPath_pcDriverName_vecPath_END;
// 		}

		/** 换种方法判断是否建立文件夹成功, 原来就存在, 也算成功 */
		lsRc = LsFindFile(strDir.c_str());
		if(LS_RC_OK != lsRc)
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_CREATE);
			goto END;
		}
	}

END:
	return lsRc;
}

LS_RC LsSplitPathToVector(TCHAR * pcFullPath, std::vector<tstring> & vecPath)
{
	LS_RC lsRc = LS_RC_OK;
	tstring strFullPath = pcFullPath;
	tstring strSubDirName = _T("");

	size_t nPosFind = 0;
	size_t nPosBegin = -1;
	size_t nPosEnd = -1;
	size_t nSize = _tcslen(strFullPath.c_str());

	do
	{
		nPosFind = strFullPath.find(_T('\\'), nPosFind);
		if(nPosFind != -1)
		{
			nPosBegin = nPosEnd;
			nPosEnd = nPosFind;

			if((nPosBegin != nPosEnd) && (-1 != nPosBegin) && (-1 != nPosEnd) && (nPosEnd > nPosBegin))
			{
				strSubDirName = strFullPath.substr(nPosBegin + 1, nPosEnd - nPosBegin - 1);
				vecPath.push_back(strSubDirName.c_str());
			}
		}

		nPosFind++;
	}
	while((nPosFind != -1) && (nPosFind < nSize));

	return lsRc;
}

LS_RC LsWriteFileNoCache(TAG_WIRTE_BUF_NO_CACHE_INFO & sWriteBufNoCache)
{
	LS_RC lsRc = LS_RC_OK;

	DWORD dwLenWriteOnce = 0;
	DWORD dwLenWriteBack = 0;
	DWORD dwPosToWriteSrc = 0;//源缓冲区要写入到块的位置

	//如果要写入的长度 + 块写入当前位置 >= 无缓冲写入块长度, 向文件中循环写入块
	while((sWriteBufNoCache.dwLenToWrite + sWriteBufNoCache.dwPosToWrite) >= sWriteBufNoCache.dwLenBufNoCache)
	{
		dwLenWriteOnce = sWriteBufNoCache.dwLenBufNoCache - sWriteBufNoCache.dwPosToWrite;
		memcpy(sWriteBufNoCache.pBufNoCache + sWriteBufNoCache.dwPosToWrite, sWriteBufNoCache.pBufToWrite, dwLenWriteOnce);

		dwPosToWriteSrc += dwLenWriteOnce;
		sWriteBufNoCache.dwLenToWrite -= dwLenWriteOnce;

		sWriteBufNoCache.dwPosToWrite = 0;

		if(!WriteFile(sWriteBufNoCache.hFileToWrite, sWriteBufNoCache.pBufNoCache, sWriteBufNoCache.dwLenBufNoCache, &dwLenWriteBack, NULL))
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
			goto END;
		}

		/** 将数据写进文件, 要不一个空文件摆在那半天没动静, 像挂掉了一样 */
		if(!FlushFileBuffers(sWriteBufNoCache.hFileToWrite))
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
			goto END;
		}

		sWriteBufNoCache.dwLenWrBack += dwLenWriteOnce;
		sWriteBufNoCache.llTotalWriteIn += dwLenWriteOnce;
	}

	//移动不足一块的数据到写入块, 更新给调用者的回答
	memcpy(sWriteBufNoCache.pBufNoCache + sWriteBufNoCache.dwPosToWrite, sWriteBufNoCache.pBufToWrite + dwPosToWriteSrc, sWriteBufNoCache.dwLenToWrite);
	sWriteBufNoCache.dwPosToWrite += sWriteBufNoCache.dwLenToWrite;		//更新无缓冲块当前写入位置, 用于下次写入
	sWriteBufNoCache.dwLenWrBack += sWriteBufNoCache.dwLenToWrite;		//更新调用者本次调用写入数量
	sWriteBufNoCache.llTotalWriteIn += sWriteBufNoCache.dwLenToWrite;	//更新总写入数量, 用于确定文件总Size

END:
	return lsRc;
}

/** 最后一次的写, 然后调用关闭文件句柄 */
LS_RC LsWriteFileNoCache_FulshData(TAG_WIRTE_BUF_NO_CACHE_INFO & sWriteBufNoCache)
{
	LS_RC lsRc = LS_RC_OK;

	DWORD dwLenWriteOnce = 0;
	DWORD dwLenWriteBack = 0;

	dwLenWriteOnce = sWriteBufNoCache.dwLenBufNoCache - sWriteBufNoCache.dwPosToWrite;

	if(dwLenWriteOnce > 0)
	{
		sWriteBufNoCache.dwPosToWrite = 0;

		if(!WriteFile(sWriteBufNoCache.hFileToWrite, sWriteBufNoCache.pBufNoCache, sWriteBufNoCache.dwLenBufNoCache, &dwLenWriteBack, NULL))
		{
			lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
			goto END;
		}
		sWriteBufNoCache.dwLenWrBack += dwLenWriteOnce;

		/** 
		* 在LsWriteFileNoCache中, 已经对每次写入的字节数做了回应, 已经是正确的总长度,
		* 最后一次写入不再更新 sWriteBufNoCache.llTotalWriteIn 
		*/
		//sWriteBufNoCache.llTotalWriteIn += dwLenWriteOnce;
	}
END:
	return lsRc;
}

LS_RC LsSetFilePointer(TAG_WIRTE_BUF_NO_CACHE_INFO & sWriteBufNoCache)
{
	LS_RC lsRc = LS_RC_OK;

	//LONG_MAX	0x7fffffff	long
	LONG lDistanceToMove = (LONG)(sWriteBufNoCache.llTotalWriteIn & LONG_MAX);
	LONG lDistanceToMoveHigh = (LONG)(sWriteBufNoCache.llTotalWriteIn >> 31);
	LONGLONG ullFileSize = sWriteBufNoCache.llTotalWriteIn;

	if(INVALID_SET_FILE_POINTER == SetFilePointer(sWriteBufNoCache.hFileToWrite, lDistanceToMove, &lDistanceToMoveHigh, FILE_BEGIN))
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
		goto END;
	}

	if(!SetEndOfFile(sWriteBufNoCache.hFileToWrite))
	{
		lsRc = MakeLsErrSn(LS_RC_ERR_FILE_WRITE);
		goto END;
	}

END:
	return lsRc;
}
LS_NAMESPACE_END
还没有封装成类, 不是很好用.

demo上传位置:  FileOptNoCache_V2011_0725_0202.rar
http://download.csdn.net/source/3466635

<2011_0801>

遇到了奇怪问题,无缓冲区读文件时总是失败. 最后发现, 无缓冲区读,也是要读扇区倍数的字节数.  用无缓冲区方法写文件时,写完扇区倍数字节完成后,因为文件一般都不是扇区倍数字节的,最后要用SetFilePointer和EndOfFile来确定正确的文件Size. 读这种带零头的文件时,实际操作时怎样的呢?  有时间再做实验。 无缓冲区读写,不知道什么样的实际场景非要使用无缓冲区读写。 真不方便。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值