无法读取SD卡原始扇区,ReadFile调用返回错误87求助
解决ReadFile读取原始SD卡返回错误87(ERROR_INVALID_PARAMETER)的思路
首先,错误87(无效参数)在原始磁盘读取场景下,通常和Windows对磁盘访问的严格要求、权限变化或者参数对齐有关,结合你提到的Windows更新后出现问题的背景,给你几个针对性的排查方向:
1. 确认磁盘真实的物理扇区大小,确保读取长度和缓冲区对齐
你提到Linux识别扇区是512字节,但Windows下的物理扇区可能是4K(很多现代SD卡和磁盘都是4K物理扇区,512是逻辑扇区)。如果读取长度或缓冲区没有对齐到物理扇区大小,ReadFile就会返回错误87。
可以用DeviceIoControl获取磁盘的真实物理扇区信息,代码示例:
#include <winioctl.h> DISK_GEOMETRY_EX diskInfo = {0}; DWORD bytesReturned = 0; if (DeviceIoControl(memCard, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &diskInfo, sizeof(diskInfo), &bytesReturned, NULL)) { Logger::Info("Physical sector size: %u bytes", diskInfo.Geometry.BytesPerSector); Logger::Info("Media sector size: %u bytes", diskInfo.DiskSize.BytesPerSector); }
拿到真实扇区大小后,确保:
- 读取的长度是物理扇区大小的整数倍
- 缓冲区的内存地址对齐到物理扇区边界(可以用
_aligned_malloc分配,比如unsigned char* sector = (unsigned char*)_aligned_malloc(4096, 4096);)
2. 启用SeManageVolumePrivilege权限
部分Windows更新后,即使是管理员权限,访问原始磁盘也需要额外的SeManageVolumePrivilege权限。你可以在程序启动时主动启用这个权限:
#include <windows.h> #include <accctrl.h> #include <aclapi.h> bool EnableVolumePrivilege() { HANDLE hToken; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return false; TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &tp.Privileges[0].Luid)) { CloseHandle(hToken); return false; } bool result = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL) != FALSE; CloseHandle(hToken); return result; }
在调用CreateFile之前调用这个函数,确保权限被正确启用。
3. 尝试访问物理磁盘而非逻辑卷
你当前用的\\\\.\\G:是逻辑卷的路径,Windows对逻辑卷的原始读取限制更多。可以尝试找到G:对应的物理磁盘(比如\\\\.\\PhysicalDriveX),直接访问物理磁盘:
- 用
GetLogicalDriveStrings枚举所有盘符 - 对每个盘符调用
GetVolumeNameForVolumeMountPoint获取卷GUID路径 - 用
CreateFile打开卷GUID路径,再调用DeviceIoControl的IOCTL_STORAGE_GET_DEVICE_NUMBER获取物理磁盘编号
代码示例片段:
char drivePath[MAX_PATH] = "G:\\"; char volumeName[MAX_PATH]; if (GetVolumeNameForVolumeMountPoint(drivePath, volumeName, MAX_PATH)) { HANDLE hVolume = CreateFile(volumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hVolume != INVALID_HANDLE_VALUE) { STORAGE_DEVICE_NUMBER devNum = {0}; DWORD bytesReturned; if (DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &devNum, sizeof(devNum), &bytesReturned, NULL)) { char physicalDrivePath[MAX_PATH]; sprintf(physicalDrivePath, "\\\\.\\PhysicalDrive%u", devNum.DeviceNumber); HANDLE hPhysicalDrive = CreateFile(physicalDrivePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL); // 用hPhysicalDrive尝试ReadFile } CloseHandle(hVolume); } }
4. 检查CreateFile的标志组合
尝试使用FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH的组合,这是原始磁盘读取的标准标志,同时确保不要使用FILE_ATTRIBUTE_NORMAL(原始磁盘不需要这个属性)。修改后的CreateFile调用:
HANDLE memCard = CreateFile("\\\\.\\G:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL);
这些方法应该能覆盖Windows更新带来的权限或行为变化问题,建议你按顺序排查,先确认扇区对齐和权限,再尝试物理磁盘访问。
内容的提问来源于stack exchange,提问作者François
相关产品推荐
相关产品推荐

