GetShortPathName函数有时无法返回正确短路径的原因与解决方法
问题根因
GetShortPathName()调用转换失败,和路径里有没有空格没有直接关系,核心原因有两个:
- 8.3短文件名不是Windows NTFS卷的默认必选属性:从Vista系统开始,微软默认对非系统分区关闭8.3短名自动生成功能。你测试环境里能转换成功的
NEEDFO~1对应目录,是分区开启8.3支持阶段创建的,系统已经为它留存了短名记录;FIFA 97目录是在8.3自动生成关闭后创建的,系统从未给它生成过对应短名条目,自然查询失败。 - 你的现有代码没有做任何API调用结果校验:
GetShortPathName()要求传入的路径必须是磁盘上真实存在、且当前进程有访问权限的路径,只要路径拼接出错、目录不存在、权限不足,都会直接返回失败,你现在的代码完全没捕获错误,根本没法定位具体失败点。
另外补充:你代码里用wcscat做路径拼接存在缓冲区溢出风险,wchar_t数组初始化时只给第一个元素赋值了L"\0",后续拼接如果长度超过MAX_PATH会直接写坏内存。
可行解决方案
按落地成本从低到高排序:
- 方案1:绕开短路径转换,直接适配DOSBox参数规则
DOSBox本身完全支持带空格的长路径,不需要强制转8.3格式,你只要在拼接传参的时候给路径两端包裹双引号即可,比如传入参数为"D:\Games\FIFA 97\Assets\Engine",DOSBox可以正常识别,没有任何系统配置依赖,是最稳妥的方案。 - 方案2:手动为目标目录创建8.3短名
如果必须用短路径,不需要全局开启分区8.3支持,以管理员身份打开命令提示符,执行以下命令即可为指定目录手动生成短名:
执行完成后再调用fsutil file setshortname "D:\Games\FIFA 97" FIFA97~1GetShortPathName()就能正常查询到对应短路径,这个操作不会影响分区内其他目录,没有性能损耗。 - 方案3:为对应分区开启全局8.3短名自动生成
如果需要分区内所有新建文件/目录自动生成8.3短名,管理员执行以下命令(将D:替换为你实际的游戏所在盘符):
该操作需要重启系统生效,开启后会略微降低分区文件枚举性能,不推荐在存储大量小文件的分区使用。fsutil behavior set disable8dot3 D: 0
代码修正参考
修正缓冲区溢出问题、增加API错误校验的最小可运行示例:
#include <windows.h> #include <stdio.h> int wmain() { wchar_t GameDirPath[MAX_PATH] = {0}; if (GetCurrentDirectoryW(MAX_PATH, GameDirPath) == 0) { wprintf(L"获取当前目录失败,错误码:%lu\n", GetLastError()); return 1; } wchar_t EngineDirPath[MAX_PATH] = {0}; _snwprintf_s(EngineDirPath, MAX_PATH, _TRUNCATE, L"%s\\Assets\\Engine\\", GameDirPath); wchar_t EngineShortPath[MAX_PATH] = {0}; DWORD pathLen = GetShortPathNameW(EngineDirPath, EngineShortPath, MAX_PATH); if (pathLen == 0) { wprintf(L"获取短路径失败,错误码:%lu,可回退使用带引号长路径传参\n", GetLastError()); return 1; } wprintf(L"引擎目录短路径:%s\n", EngineShortPath); return 0; }
内容的提问来源于stack exchange,提问作者Lion King
相关产品推荐
相关产品推荐

