【问题标题】:SetFilePointer from FILE_END always fails for a volume handle来自 FILE_END 的 SetFilePointer 对于卷句柄总是失败
【发布时间】:2016-10-10 20:55:30
【问题描述】:

我只想将卷句柄的文件指针从FILE_END 向后移动。不幸的是,无论我传递给它的任何输入值如何,操作都会失败!

我正在使用以下代码:

HANDLE vol_handle = CreateFile ("\\\\.\\C:",
                         GENERIC_READ | GENERIC_WRITE,
                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                         NULL, OPEN_EXISTING, 0, NULL);

if (vol_handle == INVALID_HANDLE_VALUE)
{
    printf("Failed to create volume handle!\n");
    print_last_error(GetLastError());
    return ERROR;
}
printf("Handle acquired for volume %s \n", VOLUME);

long int dist = 512000; // 500 KB forwards

SetLastError(0);
int result = SetFilePointer (vol_handle, dist, NULL, FILE_BEGIN);
if (result == INVALID_SET_FILE_POINTER)
{
    printf("SetFilePointer (BEGIN) FAILED!\n");
    print_last_error(GetLastError());
}
else
    printf("SetFilePointer (BEGIN) SUCCESS!\n");

dist = -5120; // 50 KB backwards

SetLastError(0);
result = SetFilePointer (vol_handle, dist, NULL, FILE_END);
if (result == INVALID_SET_FILE_POINTER)
{
    printf("SetFilePointer (END) FAILED!\n");
    print_last_error(GetLastError());
}
else
    printf("SetFilePointer (END) SUCCESS!\n");

SetLastError(0);
result = SetFilePointer (vol_handle, dist, NULL, FILE_CURRENT);
if (result == INVALID_SET_FILE_POINTER)
{
    printf("SetFilePointer (CURRENT) FAILED!\n", offset_low_part);
    print_last_error(GetLastError());
}
else
    printf("SetFilePointer (CURRENT) SUCCESS!\n");

输出:

为卷 \\.\C 获取的句柄: 设置文件指针(开始)成功! 设置文件指针(结束)失败! 错误 0x57:参数不正确。 设置文件指针(当前)成功!

如您所见,对于 FILE_BEGINFILE_CURRENT,搜索工作正常,但对于 FILE_END 总是失败,错误代码为 0x57(十进制为 87):

参数不正确

请注意,将dist 更改为正值不会改变这种情况。

我在这里错过了什么?

(注意:在最新的 Code::Blocks IDE 16.01 中使用 MinGW 编译器和管理员权限在 Windows-5.1.2600 (XP SP3) 上进行测试)


注意: 这绝对不是this question 的重复,因为我没有访问物理磁盘(卷访问不同于物理磁盘),并且距离值是 512 字节的倍数(逻辑扇区大小)。如果距离长度不正确,那么其他搜索(BEGIN 和 CURRENT)也会失败,但它们不会。


更新

根据Rohans' 的回答,我使用以下代码进行了另一次检查:

long long int offset_64 = 5120000 // 5000 KB distance;
long int offset_low_part = -(0xffffffff & offset_64);
long int offset_hig_part = offset_64 >> 32;

printf("offset_64 = %lld\n", offset_64);
printf("low_part  = %ld\n", offset_low_part);
printf("high_part = %ld\n\n", offset_hig_part);

// Get volume handle using CreateFile(...) exactly as before;

SetLastError(0);
int result = SetFilePointer (vol_handle, offset_low_part, &offset_hig_part, FILE_END);

if (result == INVALID_SET_FILE_POINTER)
{
    print_last_error(GetLastError());
    printf("Failed to set file pointer!\n");
    printf("high_part after SetFilePointer = %ld\n", offset_hig_part);
    return ERROR;
}

虽然我认为 Rohans 的观点很重要,但它仍然没有解决问题:

offset_64 = 5120000
low_part  = -5120000
high_part = 0

Handle acquired for volume \\.\C:
Error 0x57: The parameter is incorrect.
Failed to set file pointer!
high_part after SetFilePointer = 0

low_part 使用正值不会改变这种情况。


更新

根据下面 David Heffernan 的评论,我承认使用 SetFilePointerEx 可以更短、更轻松地使用此代码,但结果仍然相同,操作无法从卷句柄上的 FILE_END 查找。

【问题讨论】:

  • printf("SetFilePointer (BEGIN) FAILED!\n"); 离题,但您不应该冒险调用可能会设置错误标志的函数。您应该尽快使用GetLastError 的值。例如,在调用任何其他函数之前立即将 GetLastError 的值保存在变量中,然后使用该变量输出信息。
  • 为了记录,卷访问与物理磁盘访问遵循相同的对齐规则。
  • @HarryJohnston 是的,但存在差异。卷访问可以通过对齐逻辑扇区大小(很容易获得,并且在 Windows 上的 HDD 几乎普遍为 512 字节)来执行,但物理访问对齐取决于硬件并且不太容易获得。
  • @SeyedMohammad:我认为这不是真的。 Windows 应该只关心逻辑扇区大小,即使您正在写入物理磁盘对象。你有参考吗?
  • @HarryJohnston 是的,这在这篇 MSDN 文章中进行了讨论:msdn.microsoft.com/en-us/library/cc644950%28v=vs.85%29.aspx

标签: c++ c windows winapi


【解决方案1】:

这不足为奇。卷不是文件,因此它没有文件结束位置。

Windows可以当然使用卷长度作为文件结束的位置,但是没有特别的理由让它处理这种特殊情况。

您必须自己计算偏移量。

【讨论】:

  • 谢谢。如果这是实际的解释,那么这个事实应该在 MSDN 文章的 SetFilePointer 函数的“备注”部分中提到。我没有看到任何这样的东西。你能提供任何参考吗?
  • Microsoft 通常不会记录此类细节。但请检查 Rohan 的回答 - 这很可能是问题所在。
【解决方案2】:

您可能必须在 api 调用中为第三个参数传递非 NULL 值。

result = SetFilePointer (vol_handle, dist, NULL, FILE_END);
//-----------------------------------------^

所以改成

LONG distToMoveHigh = 0;
result = SetFilePointer (vol_handle, dist, &distToMoveHigh, FILE_END);
...

MSDN页面上提到的原因是

如果 lpDistanceToMoveHigh 为 NULL 并且新文件位置不适合 32 位值,则函数将失败并返回 INVALID_SET_FILE_POINTER。

当您使用音量时,它可能 > 2GB。因此,SetFilePointer() api 如果成功,将无法返回新的偏移量。

【讨论】:

  • 好点。我根据您的回答更新了问题,但没有解决问题。
  • @SeyedMohammad,即使 API 失败,也要尝试打印距离高参数的值。
  • 您是否测试过在 Windows 机器上使用类似于上面的代码从 FILE_END 中查找卷句柄?如果它对您(和其他程序员)也失败了,那么我想这意味着 Harry Johnston 的解释可能是答案。
猜你喜欢
  • 2014-06-16
  • 1970-01-01
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多