我需要将存档 (
.zip
) 文件解压到 USB 记忆棒上,但是在成功调用 CreateFile()
/WriteFile()
之后,我从 WriteFile()
收到各种错误,大多数时候第一个是FILE INVALID
(GetLastError()
给出错误代码 1006)。
请注意,当启动计算机(我正在使用虚拟化 Windows 11)并首次启动该程序时,它可以运行并完全解压缩存档。任何其他尝试都失败了。
这是伪代码:
// *dst is my usb stick mounpoint, most of the time its "E:"
static int decompress_file(const char * zip,
const char * dst) {
struct zip_stat sb;
struct zip * za = zip_open(zip, ZIP_RDONLY, NULL);
size_t readLen, totalLen = 0;
char buffer[4096];
for (long int i = 0; i < zip_get_num_entries(za, 0); i++) {
zip_stat_index(za, i, 0, & sb);
const std::string file = std::string(dst) + sb.name;
struct zip_file * zf = zip_fopen_index(za, i, 0);
HANDLE fd = CreateFile(
file.c_str(),
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
totalLen = 0;
while (totalLen != sb.size) {
readLen = zip_fread(zf, buffer, 4096);
WriteFile(fd, buffer, readLen, NULL, NULL);
totalLen += readLen;
}
CloseHandle(fd);
zip_fclose(zf);
}
zip_close(za);
return 0;
}
我试图简化代码,请注意它不会按原样编译。我正在使用 libzip.
使用
open()
/write()
的相同代码在 Linux/macOS 上运行良好。
此代码在单独的线程中执行,主线程正在显示 GUI (wxWidget) 并等待此任务完成。
我试图根据页面大小调整缓冲区大小,我猜对我们大多数人来说是 4096 字节?我还尝试将缓冲区大小设置为 512 和 8192,但它没有改变任何东西。
错误总是发生在程序执行的中间/结束时,在大量成功调用
CreateFile()
/WriteFile()
之后。
我尝试使用 Process Monitor 和 Process Explorer 来监控我的程序。我正确地关闭了文件描述符,Process Monitor 上没有任何危险信号,除了我向您描述的那个。
这是 Proc Monitor 输出的截图:
我也得到一个
0x80000016
错误,根据MSDN,它与常量STATUS_VERIFY_REQUIRED
有关:
介质已更改并且正在进行验证操作,因此除了验证操作中使用的操作外,不能对设备执行任何读取或写入操作。
我看到直接写入外部驱动器(当使用
\\\\.\
语法作为物理驱动器打开时)必须扇区对齐,但这不是我的情况吗?我也尝试切换到_open()
/_write()
,但遇到了完全相同的问题。
有人能帮帮我吗?
1006是
ERROR_FILE_INVALID
1006 (0x3EE)
文件的体积已被外部更改,因此打开的文件不再有效。
在使用映射驱动器时,“外部更改”似乎是一种明确的可能性(从虚拟机主机或网络服务器映射都可能合理地产生该错误)。似乎实际控制的文件系统(在主机上运行)已经删除了文件,使您打开的句柄陈旧且无法使用。
如果提取到虚拟磁盘 (VMDK) 而不是映射驱动器,您会得到更好的结果吗?
或者如果您让 VM 控制整个 USB 卷而无需将其安装在主机中?
按照 Ben 的建议,我用 VDI(虚拟磁盘映像)替换了 USB 闪存驱动器。它连续 3 次成功解压存档。正如我之前所说,相同的代码在 Linux 和 macOS 上运行良好,带有
open()
和 write()
,但我没有说的是 Windows 是我在虚拟机中使用的唯一操作系统,我测试了 Linux 和 macOS本地主机。
为了确认这是一个“虚拟化问题”,我准备在笔记本电脑上安装Windows 11并确认这个假设。谢谢你的帮助。
编辑:我在本机 Windows 11 上测试了我的程序并且它有效,我不会深入探讨为什么它在虚拟机中失败,这解决了我的问题。再次感谢您的帮助。