Created
May 29, 2025 12:36
-
-
Save Stehfyn/f1950924bb645d6dce88563da77e9fb2 to your computer and use it in GitHub Desktop.
Circumventing LongPathAware enablement with C++17's std::filesystem on Windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*********************************************************************\ | |
| | How to circumvent the "long path" limitation on Windows, without: | | |
| | - Your application's manifest declaring LongPathAware | | |
| | - The user having LongPaths enabled in their registry | | |
| \*********************************************************************/ | |
| extern "C" { | |
| #define WIN32_LEAN_AND_MEAN | |
| #include <windows.h> | |
| } | |
| #include <filesystem> | |
| #include <format> | |
| #include <iostream> | |
| #include <numeric> | |
| #define UNC_PREFIX "\\\\?\\" | |
| #define DEMO_SUBDIR_COUNT (30) | |
| #define DEMO_SUBDIR_PREFIX ("mysubdir_xx") | |
| #define DEMO_SUBDIR_PREFIX_LEN (_countof(DEMO_SUBDIR_PREFIX) - 1) | |
| namespace fs = std::filesystem; | |
| namespace Demo { | |
| static_assert( | |
| MAX_PATH < (DEMO_SUBDIR_COUNT * DEMO_SUBDIR_PREFIX_LEN), | |
| "Try increasing DEMO_SUBDIR_COUNT!"); | |
| /* | |
| * \brief | |
| * Make a nested subdirectory the count of "subdirs" deep within the current directory | |
| */ | |
| static fs::path GetSomeLongPath(int subdirs) | |
| { | |
| fs::path long_path = fs::current_path(); | |
| std::vector<int> iv(subdirs); | |
| std::iota(iv.begin(), iv.end(), 0); | |
| std::for_each(iv.cbegin(), iv.cend(), | |
| [&](int i) | |
| { | |
| long_path /= std::format("mysubdir_{:02}", i); | |
| }); | |
| long_path.make_preferred(); // change all '/' to '\\' | |
| return long_path; | |
| } | |
| /* | |
| * \brief | |
| * Since std::filesystem::path::generic_string/generic_wstring() will modify the returned copy of its | |
| * internal string representation of the path (i.e. revert any '\\' to '/'), we: | |
| * | |
| * 1). Simply grab the native string by reference (the untouched internal representation--not a copy!) | |
| * by storing it as a const std::wstring& | |
| * | |
| * 2). Allocate the required buffer size in a std::string to fit the actual native path | |
| * | |
| * 3). Perform the character set conversion, writing directly into the std::string's internal buffer | |
| * | |
| * 4). Return the std::string directly, where that "return by copy" will likely be elided (guaranteed by C++17 and on), | |
| * in favor of direct construction of the outer std::string | |
| * | |
| * The use of '\\' as the path separator is critical to circumventing the long path limitation | |
| */ | |
| static std::string GetNativePathString(const fs::path& p) | |
| { | |
| const std::wstring& wp = p.native(); | |
| std::string sp(wp.size(), 0); | |
| WideCharToMultiByte(CP_UTF8, 0, wp.c_str(), static_cast<int>(wp.size()), sp.data(), static_cast<int>(sp.size()), 0, 0); | |
| return sp; | |
| } | |
| /* | |
| * \brief | |
| * "Cheat the system" by prepending a UNC prefix to a native path string | |
| */ | |
| static std::string MakeLongPathString(const std::string& native_path_str) | |
| { | |
| static const char c_unc_prefix[] = UNC_PREFIX; | |
| return std::format("{}{}", c_unc_prefix, native_path_str.c_str()); | |
| } | |
| /* | |
| * \brief | |
| * Demonstrates circumventing LongPathAware limitation for free via C++17's std::filesystem | |
| */ | |
| static int Demo() | |
| { | |
| #define EXIT_SUCCESS 0 | |
| #define EXIT_FAILURE 1 | |
| std::error_code ec; | |
| fs::path p = Demo::GetSomeLongPath(DEMO_SUBDIR_COUNT); | |
| std::string sp = Demo::GetNativePathString(p); | |
| // Expect this to fail | |
| fs::create_directories(sp, ec); | |
| if (ec) | |
| { | |
| std::cout << ec.message().c_str() << "\n"; | |
| // Prepend native path with the UNC prefix | |
| sp = Demo::MakeLongPathString(sp); | |
| // Then expect this to succeed | |
| fs::create_directories(sp, ec); | |
| if (!ec) | |
| { | |
| std::cout << std::format("Successfully created long path ({} len) : {}\n", sp.size(), sp.c_str()); | |
| return EXIT_SUCCESS; | |
| } | |
| else | |
| { | |
| std::cout << ec.message().c_str() << "\n"; | |
| } | |
| } | |
| return EXIT_FAILURE; | |
| } | |
| } // namespace Demo | |
| int main() | |
| { | |
| return Demo::Demo(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment