文件上的shcreateStreamonFileex大于2 ** 32字节
SHCreateStreamOnFileEx on files larger than 2**32 bytes
我使用SHCreateStreamOnFileEx
获得了文件的IStream
,但是当Seek Pointer的新位置为2 ** 32 BYT时,其Read()
方法似乎在极大的文件上似乎不当进入文件。
ISequentialStream::Read
的文档说:
此方法通过读取的实际字节数调整了Seew指针。
这与我知道的所有平台上的read(2)
和fread(3)
相同。
但是有了这些流,这不是我在某些情况下看到的实际行为:
-
Seek(2 ** 32 - 2, SEEK_SET, &pos)
,Read(buf, 1, &bytesRead)
,Seek(0, MOVE_CUR, &pos)
→bytesRead == 1
和pos == 2 ** 32 - 1
,如预期的。 -
Seek(2 ** 32 - 1, SEEK_SET, &pos)
,Read(buf, 1, &bytesRead)
,Seek(0, MOVE_CUR, &pos)
→bytesRead == 1
,但pos == (2 ** 32 - 1) + 4096
,这是不正确的。这意味着任何随后的读取(没有其他Seek
来修复光标位置)读取错误的数据,而我的应用程序不起作用!
我是"抱怨错"吗?我需要设置一些标志以使这类行为正确吗?还是Shlwapi.dll
中的错误?
下面的代码为我重现了这个问题。(将OFFSET = WORKS
设置为查看成功的情况。)
#include "stdafx.h"
static const int64_t TWO_THIRTY_TWO = 4294967296LL;
static const int64_t WORKS = TWO_THIRTY_TWO - 2LL;
static const int64_t FAILS = TWO_THIRTY_TWO - 1LL;
static const int64_t OFFSET = FAILS;
static void checkPosition(CComPtr< IStream > fileStream, ULONGLONG expectedPosition)
{
LARGE_INTEGER move;
ULARGE_INTEGER newPosition;
move.QuadPart = 0;
HRESULT hr = fileStream->Seek(move, SEEK_CUR, &newPosition);
ASSERT(SUCCEEDED(hr));
ULONGLONG error = newPosition.QuadPart - expectedPosition;
ASSERT(error == 0);
}
int main()
{
const wchar_t *path = /* path to a file larger than 2**32 bytes */ L"C:\users\wjt\Desktop\eos-eos3.1-amd64-amd64.170216-122002.base.img";
CComPtr< IStream > fileStream;
HRESULT hr;
hr = SHCreateStreamOnFileEx(path, STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, NULL, &fileStream);
ASSERT(SUCCEEDED(hr));
LARGE_INTEGER move;
ULARGE_INTEGER newPosition;
// Advance
move.QuadPart = OFFSET;
hr = fileStream->Seek(move, SEEK_SET, &newPosition);
ASSERT(SUCCEEDED(hr));
ASSERT(newPosition.QuadPart == OFFSET);
// Check position
checkPosition(fileStream, OFFSET);
// Read
char buf[1];
ULONG bytesRead = 0;
hr = fileStream->Read(buf, 1, &bytesRead);
ASSERT(SUCCEEDED(hr));
ASSERT(bytesRead == 1);
// Check position: this assertion fails if the Read() call moves the cursor
// across the 2**32 byte boundary
checkPosition(fileStream, OFFSET + 1);
return 0;
}
这确实是Windows错误。在几个Windows版本上进行了测试,包括最新的SHCore.DLL
版本10.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);
}
}
}
我在调试器下跟踪virtual long CFileStream::Seek(LARGE_INTEGER, ULONG, ULARGE_INTEGER* );
,并且可以确认该功能不设计用于与4GB大小的文件一起使用
如果更准确,为什么100000FFF
偏移-CFileStream
使用内部缓冲区读取1000
字节大小。当您询问从FFFFFFFF
偏移中读取1个字节时,它实际上将1000
字节读取到缓冲区,并将文件偏移添加为100000FFF
。当您致电Seek(0, STREAM_SEEK_CUR, &newpos)
-CFileStream
调用SetFilePointer(hFile, 1-1000, 0/*lpDistanceToMoveHigh*/, FILE_CURRENT)
(1这是缓冲区中的内部位置,因为我们读取1个字节减去缓冲区大小1000)。如果不考虑帐户溢出,则可以是(100000FFF + (1 - 1000)) == 100000000
,但
阅读有关SetFilePointer
如果 lpdistancetomovehigh 是 null ,而新的文件位置不适合 在32位值中,功能失败并返回 Invalid_set_file_pointer 。
结果SetFilePointer
失败(返回INVALID_SET_FILE_POINTER
),但CFileStream
甚至不检查此问题。然后致电SetFilePointerEx(hFile, 0, &newpos, FILE_CURRENT)
,然后返回您的newpos,仍然100000FFF
- 我能确定从文件中读取的 32 字节二进制数据等于 256 位吗?
- 如何在C++中设计 16、32、64 字节甚至更大的 INT
- 编码大于原始文本:如何获取零和一的字符串并将它们作为实际字节写入文件
- 我是否可以拥有大小大于大小限制 (2^32) 的特征::VectorXd
- 如何生成伪随机 32 字节字符串以用作加密哈希函数中的盐?
- OPENCV错误:不良的参数(字节必须为16、32或64)在简介Descriptorextractorimpl中
- 缓冲区视图中的字节偏移量大于字节长度
- 当偏移太大(大于签名的INT 2^32范围)时,GZSEEK(ZLIB)失败
- 如何将一个数字(大于8个字节)从字符阵列转换为其ASCII表示
- 文件上的shcreateStreamonFileex大于2 ** 32字节
- 如何迫使新操作员返回的指针将是32字节对齐
- 正确处理字节对齐问题 - 通过UDP在16位嵌入式系统和32位桌面之间
- 无法使用英特尔 AES-NI 示例库以 32 字节块大小加密/解密
- c++堆分配默认为32字节对齐
- 字符串大小始终为 32 字节
- 32位和64位Linux机器中4字节类型之间的差异
- C++32位字字节交换
- C++将32位Integer复制到字节数组中
- AVX矢量化代码中的分段故障,GCC __attribute__对齐为32字节
- 分配32字节对齐内存的分配器