To my understanding, when creating with typeBinary flag, CFile & CStdioFile should works indentically, except the latter is buffering the data so has a better performance.
So I write the following codes to confirm this:
ULONGLONG GetRand(ULONGLONG uMax)
{
UINT uValue;
if (rand_s(&uValue) == 0)
return (ULONGLONG)(((double)uValue / (double)UINT_MAX) * uMax);
else
return 0;
}
void CheckOffset(CFile& File1, CFile& File2)
{
ULONGLONG uOffset1, uOffset2;
CString strMsg;
uOffset1 = File1.GetPosition();
uOffset2 = File2.GetPosition();
if (uOffset1 != uOffset2)
{
strMsg.Format(_T("Difference offset. Offset1 = %I64u. Offset2 = %I64u."), uOffset1, uOffset2);
AfxMessageBox(strMsg);
}
}
void CheckLength(CFile& File1, CFile& File2)
{
ULONGLONG uLength1, uLength2;
CString strMsg;
uLength1 = File1.GetLength();
uLength2 = File2.GetLength();
if (uLength1 != uLength2)
{
strMsg.Format(_T("Difference length. Length1 = %I64u. Length2 = %I64u."), uLength1, uLength2);
AfxMessageBox(strMsg);
}
}
void CheckSeek(CFile& File1, CFile& File2, ULONGLONG uOffset)
{
ULONGLONG uOffset1, uOffset2;
CString strMsg;
uOffset1 = File1.Seek(uOffset, CFile::begin);
uOffset2 = File2.Seek(uOffset, CFile::begin);
if (uOffset1 != uOffset2)
{
strMsg.Format(_T("Difference seek results. Offset1 = %I64u. Offset2 = %I64u."), uOffset1, uOffset2);
AfxMessageBox(strMsg);
}
}
void CheckRead(CFile& File1, CFile& File2, UINT uSize)
{
BYTE lpBuf1[4096], lpBuf2[4096];
UINT uRead1, uRead2;
CString strMsg;
// Read buffer from file1 & file2
uRead1 = File1.Read(lpBuf1, uSize);
uRead2 = File2.Read(lpBuf2, uSize);
if ((uRead1 != uRead2) || (memcmp(lpBuf1, lpBuf2, uRead1) != 0))
{
strMsg.Format(_T("Difference read results. uRead1 = %u. uRead2 = %u."), uRead1, uRead2);
AfxMessageBox(strMsg);
}
}
void CTestStdioFile64Dlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
CFile File1;
CStdioFile File2;
UINT uSize;
BYTE lpBuf[4096];
CString strMsg;
if (File1.Open(_T("F:\\Temp\\Test1.dat"), CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive | CFile::typeBinary))
{
if (File2.Open(_T("F:\\Temp\\Test2.dat"), CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive | CFile::typeBinary))
{
CheckOffset(File1, File2);
CheckLength(File1, File2);
// Write data
for (UINT uIndex = 0; uIndex < 20000; uIndex ++)
{
// Generate a randome size for write
uSize = (UINT)GetRand(4096);
// Generate buffer with random data
for (UINT j = 0; j < uSize; j++)
lpBuf[j] = (BYTE)GetRand(255);
// Write buffer to file1 & file2
File1.Write(lpBuf, uSize);
File2.Write(lpBuf, uSize);
File1.Flush();
File2.Flush();
CheckOffset(File1, File2);
CheckLength(File1, File2);
// Seek to a randome location
CheckSeek(File1, File2, GetRand(File1.GetLength()));
// Generate a randome size for read
uSize = (UINT)GetRand(4096);
CheckRead(File1, File2, uSize);
CheckOffset(File1, File2);
}
File2.Close();
}
File1.Close();
}
}
To my surprise, in the test process, there are many CFileException raised because CStdioFile::Write will write out less amount of data than expected.
Also there are many different read data reported.
Why?
When I run your code in Debug mode, I am getting the following ASSERT.
"Flush between consecutive read and write.", !stream.has_any_of(_IOREAD)
The reason is following:
From C Standard documentation: (Page 306, 7.21.5.3).
When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters endof-file.
In your code, you are calling for CheckRead
function at the end of the loop, but calling for File1.Write
and File2.Write
in the next iteration without calling for fseek
/flush
.
As a quick fix, you can add the following lines at the bottom of your CheckRead function:
File1.Seek(0, SEEK_CUR);
File2.Seek(0, SEEK_CUR);