Skip to content

Instantly share code, notes, and snippets.

@Stehfyn
Created May 29, 2025 12:36
Show Gist options
  • Select an option

  • Save Stehfyn/f1950924bb645d6dce88563da77e9fb2 to your computer and use it in GitHub Desktop.

Select an option

Save Stehfyn/f1950924bb645d6dce88563da77e9fb2 to your computer and use it in GitHub Desktop.
Circumventing LongPathAware enablement with C++17's std::filesystem on Windows
/*********************************************************************\
| 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