rikyoz / bit7z

A C++ static library offering a clean and simple interface to the 7-zip shared libraries.
https://rikyoz.github.io/bit7z
Mozilla Public License 2.0
602 stars 110 forks source link

[Bug]: "Failed to open the output file: The system cannot find the path specified" error when extracting 7z file #219

Closed Noscka closed 4 weeks ago

Noscka commented 1 month ago

bit7z version

4.0.x

Compilation options

BIT7Z_AUTO_FORMAT, BIT7Z_GENERATE_PIC, BIT7Z_REGEX_MATCHING, BIT7Z_STATIC_RUNTIME, BIT7Z_USE_NATIVE_STRING

7-zip version

v23.01

7-zip shared library used

7z.dll / 7z.so

Compilers

MSVC

Compiler versions

MSVC 2022

Architecture

x86_64

Operating system

Windows

Operating system versions

Windows 10

Bug description

I came back to a project, and it used to extract fine before, but now for some reason, I get an exception throw which is "Failed to open the output file: The system cannot find the path specified" with this specific 7z file. Some addition Info is there is also the error "D : The system cannot find the path specified" when looking at results from the "failedFiles()" function.

Steps to reproduce

Download the file Clone "https://github.com/Noscka/ProjectTest" Modify it to whatever paths you want (Although the paths might matter) Compile and run.

Expected behavior

To fully uncompress the file without any errors

Relevant compilation output

No response

Code of Conduct

rikyoz commented 4 weeks ago

Hi!

Usually similar problems occur when the archive contains items with very long paths, because when they're extracted the resulting path usually exceeds the Windows MAX_PATH length limit.

Quoting Microsoft's documentation:

For example, the maximum path on drive D is "D:\some 256-character path string" where "" represents the invisible terminating null character for the current system codepage. (The characters < > are used here for visual clarity and cannot be part of a valid path string.)

I've looked at your code example along with the file you're trying to extract, and this seems to be your case. In particular, you're extracting the archive to the directory D:/Games/Gamma/extracted/GAMMA RC3 (31 characters, excluding the D:/ root), while the archive contains elements with very long paths (with a maximum of 226 characters); the longest output path is 257 characters, exceeding the 256 character limit. Of course, if you extract the archive to a smaller path, you should not have the same problem.

There are some alternative workarounds to the path length limit:

Bit7z also provides the build option BIT7Z_AUTO_PREFIX_LONG_PATHS, which causes the library to automatically prepend the \\?\ prefix to the output paths when necessary (i.e. when a path exceeds MAX_PATH).

To sum up:

const auto archivePath = L"D:/Games/Gamma/downloads/GAMMA RC3.7z";

// OK (no extracted item has a path exceeding the MAX_PATH).
extractor.extract(archivePath, L"D:/Test");

// Error (some extracted items have too long paths). 
// Even the \\?\ prefix doesn't help in this case,
// as the path separator is not the native one, making the Windows APIs fail anyway.
extractor.extract(archivePath, L"D:/Games/Gamma/extracted/GAMMA RC3");

// Error, unless you configure bit7z with -DBIT7Z_AUTO_PREFIX_LONG_PATHS=ON.
extractor.extract(archivePath, LR"(D:\Games\Gamma\extracted\GAMMA RC3)");

// OK
extractor.extract(archivePath, LR"(\\?\D:\Games\Gamma\extracted\GAMMA RC3)" );
Noscka commented 4 weeks ago

Hey,

Thank you very much for the quick response and with a lot of detail! It seems the main issue I had way using / instead of \. I apologize for not looking into this deep enough. Seems really simple now that I look at it.

rikyoz commented 4 weeks ago

No problem! Thank you for using bit7z!