事情是从QA的一句话开始的。
QA: 游戏装在某些目录下,启动不了。
第一反应其实是有点懵的。
因为这个问题——我们早就防过了。
一、我们真的做了「260 字符校验」
在安装和启动逻辑里,我们明确做过路径长度判断:
- Windows 平台
- 统计完整路径长度
- 超过 260 个字符直接拦截
- 给出明确提示
逻辑上看,这个问题应该是不存在的。
但QA给出的复现路径:
- 不是 300+
- 路径长度 不到 260
- 换个短路径,游戏立刻能打开
这时候就很尴尬了:
规则对了,结果却是错的。
二、复现之后才发现:248 就已经炸了
我们单独写了一个小测试程序:
std::wstring dir = L"D:\\测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试1234567890123456789012345678901234567890123456789012\\abccccc.txt";
// 创建完整路径
int size = dir.size() * sizeof(wchar_t);
if (!CreateDirectoryW(dir.c_str(), nullptr)) {
DWORD err = GetLastError();
if (err != ERROR_ALREADY_EXISTS) {
std::wcout << L"CreateDirectoryW failed: " << err << L" at " << dir << std::endl;
return 1;
}
}
std::wcout << L"目录创建成功: " << dir << std::endl;
return 0;
- 逐层创建目录
- 每加一层就打印当前路径长度
- 调用 Windows 的
CreateDirectoryW
结果非常稳定:
ERROR_FILENAME_EXCED_RANGE (206)
也就是说:
在某些场景下,Windows 根本等不到 260, 248 就已经不干了。
这就解释了 QA 的现象:
- 用户路径没超过 260
- 但已经踩到了 Windows 的真实上限
- 我们的校验,挡了个寂寞
三、那 260 到底是个什么数?
很多人,包括我们之前,也默认了一件事:
260 = 路径最大长度 (包含最后一个不可见字符\0)
但这个说法其实只说对了一半。
更精准的理解是260个编码单元,因为有些表情符号比如😄,占用2个编码单元。
1、260 是 Win32 的“理论上限”
Windows 里有一个老常量:
#define MAX_PATH 260
但问题在于:
260 不是你能随便用满的 260。
2、目录路径和文件路径,规则不一样
这是最容易被忽略的一点:
- 目录路径:大约 248
- 文件路径:可以接近 260
原因不是 bug,而是历史设计。
3、Windows 会“偷偷预留”一段长度
在创建目录的时候,Win32 会默认假设一件事:
这个目录下面,将来还要再拼一个文件名。
所以实际效果就是:
248(目录可用) + 12(系统预留) = 260
你看到的结果自然就是:
四、为什么我们之前一直没遇到?
这个坑之所以隐蔽,有几个原因:
- 260 是文档里的“显眼数字”
- 248 是实现细节,很少被强调
- 平时测试路径都比较短
- 一旦进了真实用户环境很容易精准踩线
于是问题就表现成一句很模糊的话:
“在某些路径下打不开。”
五、我们最后是怎么改的?
1、校验逻辑不再用 260
在 Windows 平台:
- 目录路径校验直接按 248 处理
- 不再相信 260 这个“理论值”
2、明确限制安装路径深度
在安装阶段就把问题拦住,而不是等启动失败。
对用户来说:
六、其他方案:
为什么不选择这个方案,是作为平台很多东西并不是我们能修改的,我们只能做些兜底方案。
1、开启长路径支持:
启用 Windows 长路径支持
如果你控制得了运行环境,这是根治方案。
- 系统层面开启(Windows 10 1607+):
- 组策略:
启用 Win32 长路径
- 或注册表:
LongPathsEnabled = 1
- 程序本身要声明支持:
<longPathAware>true</longPathAware>
否则:
系统支持了 你的程序依然 248 / 260 原地踏步
2、使用 \\?\ 前缀(最底层)
\\?\C:\very\very\long\path\...
- 直接绕过 Win32
- 使用 NT 内核 路径
- 上限 ~32767 个字符
⚠️ 但要注意:
- 只能用 Unicode API(如
CreateDirectoryW)
- 不能用相对路径
- 很多老库不兼容
七、最后一句话总结
260 是理论极限,248 是目录的现实极限
希望我们踩过的这个关于 C++ 和 Windows 文件系统 的坑,能帮助你提前规避类似问题。在开发中,对于这类看似明确的系统限制,深挖一层实现细节往往能发现更贴近实战的边界条件。如果你对底层技术细节有更多兴趣,欢迎来 云栈社区 一起交流探讨。
|