C++ 大于2**32字节的文件上的shCreateStreamonFilex
对于使用的文件,我得到了一个C++ 大于2**32字节的文件上的shCreateStreamonFilex,c++,winapi,com,C++,Winapi,Com,对于使用的文件,我得到了一个IStream,但是当seek指针的新位置为2**32字节或更大时,它的Read()方法在非常大的文件上出现错误 : 此方法根据实际读取的字节数调整搜索指针 这与我所知道的所有平台上的read(2)和fread(3)的行为相同 但对于这些流,我在某些情况下看到的实际行为并非如此: Seek(2**32-2,Seek\u SET,&pos),Read(buf,1,&bytesRead),Seek(0,MOVE\u CUR,&pos)→ 字节读取==1和位置==2**3
IStream
,但是当seek指针的新位置为2**32字节或更大时,它的Read()
方法在非常大的文件上出现错误
:
此方法根据实际读取的字节数调整搜索指针
这与我所知道的所有平台上的read(2)
和fread(3)
的行为相同
但对于这些流,我在某些情况下看到的实际行为并非如此:
,Seek(2**32-2,Seek\u SET,&pos)
,Read(buf,1,&bytesRead)
→ <代码>字节读取==1和Seek(0,MOVE\u CUR,&pos)
,如预期位置==2**32-1
,Seek(2**32-1,Seek\u SET,&pos)
,Read(buf,1,&bytesRead)
→Seek(0,MOVE\u CUR,&pos)
,但是bytesRead==1
,这是不正确的。这意味着任何后续读取(没有另一个pos==(2**32-1)+4096
来固定光标位置)都读取了错误的数据,我的应用程序无法工作李>Seek
Shlwapi.dll
中的错误
下面的代码为我重现了这个问题。(设置OFFSET=WORKS
以查看成功案例。)
#包括“stdafx.h”
静态常数int64_t TWO_three_TWO=4294967296LL;
静态常数int64_t WORKS=2_三十二-2LL;
静态常数int64\u t FAILS=TWO\u three\u TWO-1LL;
静态常量int64\u t OFFSET=失败;
静态无效检查位置(CComPtrfileStream,ULONGLONG预期位置)
{
大整数移动;
ULARGE_整数新位置;
move.QuadPart=0;
HRESULT hr=fileStream->Seek(移动、搜索当前和新位置);
断言(成功(hr));
ULONGLONG error=newPosition.QuadPart-expectedPosition;
断言(错误==0);
}
int main()
{
const wchar_t*path=/*大于2**32字节的文件路径*/L“C:\\users\\wjt\\Desktop\\eos-eos3.1-amd64-amd64.170216-122002.base.img”;
CComPtrfileStream;
HRESULT-hr;
hr=shcreatestreamonfilex(路径、STGM\u读取、文件属性\u正常、FALSE、NULL和fileStream);
断言(成功(hr));
大整数移动;
ULARGE_整数新位置;
//前进
move.QuadPart=偏移量;
hr=fileStream->Seek(移动、搜索集和新位置);
断言(成功(hr));
断言(newPosition.QuadPart==偏移量);
//检查位置
检查位置(文件流、偏移量);
//阅读
char-buf[1];
ULONG字节读取=0;
hr=fileStream->Read(buf、1和bytesRead);
断言(成功(hr));
断言(bytesRead==1);
//检查位置:如果Read()调用移动光标,此断言将失败
//跨越2**32字节边界
检查位置(文件流,偏移量+1);
返回0;
}
这确实是windows的错误。在多个windows版本上测试,包括最新的SHCore.DLL
version10.0.14393.0
x64。复制的简单方法:
void BugDemo(PCWSTR path)
{
// FILE_FLAG_DELETE_ON_CLOSE !
HANDLE hFile = CreateFile(path, FILE_GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, 0,
CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
ULONG dwBytesRet;
// i not want really take disk space
if (DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwBytesRet, NULL))
{
static FILE_END_OF_FILE_INFO eof = { 0, 2 };// 8GB
if (SetFileInformationByHandle(hFile, FileEndOfFileInfo, &eof, sizeof(eof)))
{
IStream* pstm;
if (!SHCreateStreamOnFileEx(path, STGM_READ|STGM_SHARE_DENY_NONE, 0,FALSE, NULL, &pstm))
{
LARGE_INTEGER pos = { 0xffffffff };
ULARGE_INTEGER newpos;
if (!pstm->Seek(pos, STREAM_SEEK_SET, &newpos) && !pstm->Read(&newpos, 1, &dwBytesRet))
{
pos.QuadPart = 0;
if (!pstm->Seek(pos, STREAM_SEEK_CUR, &newpos))
{
DbgPrint("newpos={%I64x}\n", newpos.QuadPart);//newpos={100000fff}
}
}
pstm->Release();
}
}
}
// close and delete
CloseHandle(hFile);
}
}
void BugDemo()
{
WCHAR path[MAX_PATH];
if (ULONG len = GetTempPath(RTL_NUMBER_OF(path), path))
{
if (len + 16 < MAX_PATH)
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
swprintf(path + len, L"%08x%08x", ~ft.dwLowDateTime, ft.dwHighDateTime);
BugDemo(path);
}
}
}
void BugDemo(PCWSTR路径)
{
//文件\u标志\u删除\u关闭!
HANDLE hFile=CreateFile(路径,文件\通用\写入,文件\共享\读取,文件\共享\删除,0,
创建_新建,文件_属性_临时|文件_标志_删除_关闭,0);
if(hFile!=无效的句柄值)
{
乌隆·德比特斯特雷特;
//我不想真的占用磁盘空间
if(设备控制(hFile、FSCTL\u SET\u SPARSE、NULL、0、NULL、0和dwBytesRet、NULL))
{
静态文件\u END\u OF_FILE\u INFO eof={0,2};//8GB
if(SetFileInformationByHandle(hFile、fileendofileinfo、&eof、sizeof(eof)))
{
IStream*pstm;
如果(!shCreateStreamonFilex(路径、STGM_读取、STGM_共享、拒绝、无、0、FALSE、NULL和pstm))
{
大整数pos={0xffffffffff};
ULARGE_整数newpos;
如果(!pstm->Seek(pos、STREAM_Seek_SET和newpos)和&!pstm->Read(&newpos、1和dwBytesRet))
{
四分位=0;
如果(!pstm->Seek(pos、STREAM\u Seek\u CUR和newpos))
{
DbgPrint(“newpos={%I64x}\n”,newpos.QuadPart);//newpos={100000fff}
}
}
pstm->Release();
}
}
}
//关闭并删除
闭合手柄(hFile);
}
}
void BugDemo()
{
WCHAR路径[最大路径];
if(ULONG len=GetTempPath(路径的RTL_编号,路径))
{
if(长度+16<最大路径)
{
文件时间ft;
GetSystemTimeAsFileTime(&ft);
swprintf(path+len,L“%08x%08x”,~ft.dwLowDateTime,ft.dwHighDateTime);
BugDemo(路径);
}
}
}
I tracevirtual long CFileStream::Seek(大整数、ULONG、ULARGE整数*)代码>在调试器下,可以确认此函数的设计不适用于大于4GB大小的文件
更确切地说,为什么100000FFF
offset-CFileStream
使用内部缓冲区读取1000
字节大小。当您要求从FFFFFFFF
offset读取1字节时,它实际上将1000
字节读取到缓冲区,文件偏移量变为10000fff
。然后调用Seek(0,STREAM\u Seek\u CUR,&newpos)
-CFileStream
调用SetFilePointer(hFile,1-1000,0/*lpDistanceToMoveHigh*/,FILE\u CURRENT)
(1这是缓冲区中的内部位置,因为我们读取1字节减去缓冲区大小1000)。如果不考虑溢出,则溢出可以(1000000FFF+(1-1000))==100000000
了解
如果lpDistanceToMoveHigh为NULL且新文件位置不适合
在32位值中,函数失败并返回
void BugDemo(PCWSTR path)
{
// FILE_FLAG_DELETE_ON_CLOSE !
HANDLE hFile = CreateFile(path, FILE_GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, 0,
CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
ULONG dwBytesRet;
// i not want really take disk space
if (DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwBytesRet, NULL))
{
static FILE_END_OF_FILE_INFO eof = { 0, 2 };// 8GB
if (SetFileInformationByHandle(hFile, FileEndOfFileInfo, &eof, sizeof(eof)))
{
IStream* pstm;
if (!SHCreateStreamOnFileEx(path, STGM_READ|STGM_SHARE_DENY_NONE, 0,FALSE, NULL, &pstm))
{
LARGE_INTEGER pos = { 0xffffffff };
ULARGE_INTEGER newpos;
if (!pstm->Seek(pos, STREAM_SEEK_SET, &newpos) && !pstm->Read(&newpos, 1, &dwBytesRet))
{
pos.QuadPart = 0;
if (!pstm->Seek(pos, STREAM_SEEK_CUR, &newpos))
{
DbgPrint("newpos={%I64x}\n", newpos.QuadPart);//newpos={100000fff}
}
}
pstm->Release();
}
}
}
// close and delete
CloseHandle(hFile);
}
}
void BugDemo()
{
WCHAR path[MAX_PATH];
if (ULONG len = GetTempPath(RTL_NUMBER_OF(path), path))
{
if (len + 16 < MAX_PATH)
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
swprintf(path + len, L"%08x%08x", ~ft.dwLowDateTime, ft.dwHighDateTime);
BugDemo(path);
}
}
}