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
611 stars 112 forks source link

Random failure mid-extraction. #152

Closed Noscka closed 1 year ago

Noscka commented 1 year ago

Extracting some files randomly fails with an exception message of Failed to open the output file: The system cannot open the device or file specified.

where is my code:

/* get the path for this perticular file, (extraction path + filename without file extension) */
std::wstring extractedOutDirectory = ExtractedDirectory + NosLib::String::ToWstring(mod->GetFullFileName(false));

/* print the filename to console, for debugging to see if there was something wrong with the path */
wprintf((extractedOutDirectory + L"\n").c_str());

/* create the directory into which the exctration will happen */
std::filesystem::create_directories(extractedOutDirectory);

bit7z::BitFileExtractor extractor(bit7z::Bit7zLibrary("7z.dll"));
try
{
  extractor.extract(mod->GetFullFileName(true), NosLib::String::ToString(extractedOutDirectory));
}
catch (const std::exception& ex)
{
  std::cerr << ex.what() << std::endl;
}

This doesn't happen on all files, out of the 4 I tried, it happened on 3 of them. Here are the links to said files: https://www.moddb.com/addons/start/222467 - [SUCCESSFUL] https://www.moddb.com/addons/start/242048 - [FAILED] https://github.com/RAX-Anomaly/MagsRedux/archive/refs/heads/main.zip - [FAILED] https://www.moddb.com/addons/start/208861 - [FAILED]

My build commands:

cd <bit7z folder>
mkdir build && cd build
cmake ../ -DCMAKE_BUILD_TYPE=Release -DBIT7Z_AUTO_FORMAT=ON -DBIT7Z_REGEX_MATCHING=ON -DBIT7Z_STATIC_RUNTIME=ON
cmake --build . -j --config Release
Noscka commented 1 year ago

Never mind, I solved the issue. It seemed the issue was with the path lengths being over the limit. If anyone else runs into this issue. the way to solve it is to create a manifest file or append to your manifest the following

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="MarkdownMonster.app"/>
  <application xmlns="urn:schemas-microsoft-com:asm.v3">

    <!-- This setting enables long paths -->
    <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
        <ws2:longPathAware>true</ws2:longPathAware>
    </windowsSettings>

  </application>
</assembly>

and also enable the long path registry key (this may or may not be important, I already had this enabled before so I can't tell if this is necessary with the above manifest file). you can do that by either manually editing it at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\ and make the key LongPathsEnabled -> 00000001 or you can use this PowerShell script to it do for you (requires it to be run as admin)

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD

sources: https://weblog.west-wind.com/posts/2022/Jan/03/Integrating-Long-Path-Names-in-Windows-NET-Applications#enabling-long-paths-in-applications-via-application-manifest https://bigfont.ca/enable-long-paths-in-windows-with-powershell/

rikyoz commented 1 year ago

Hi! I just wanted to add that another possible solution is to prepend the string \\?\ (\\\\?\\ escaped) to every path string you pass to the library (https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry).

Also, if you're using bit7z v4-rc, you can enable the option BIT7Z_AUTO_PREFIX_LONG_PATHS in CMake, which will make bit7z automatically prepend the long path prefix when needed; this way, you don't have to specify the prefix every time and don't need to include any manifest!

This might be useful if someone can't add a manifest to their application for some reason.