一直以来,以基础中间件开发和产品设计为主,C语言为根本,但随着用户业务的技术特点
- 文件存储层次化,多层目录叠加;
- 目录、文件名个性化,单个目录或文件名过长;
等因素,造成此前产品中仅仅能支持256个字节(也就是128个汉字)以内的问题,逐渐显现,并成为用户或集成商所累赘或诟病,如何搞定?
调查了一下,也搜罗了网络上的说法,不外乎几种
- windows原始函数(如_open、_access等),就是不支持256以上的文件名;
- Win32 API接口(如CopyFile、CreateFile等)的Unicode版本(CreateFileW),可以提供32000个宽字节的文件名
如果真是如此,那使用原始C函数的产品或软件,不就惨了吗?不能与时俱进了
那怎么能行!调查后,发现GetShortPathName函数,可以将长文件名进行8.3格式转换,为_open等函数所使用,直白一点,也就是将文件名缩短了,_open等函数就认了。真是的,本是同根(Windows)生,相煎何太急!
经过验证,提供如下函数,可将长文件名缩短,以支持256字节以上的长文件名。有几个要点,要记牢
- 被缩短的目录名,必须要存在,否则将不能缩短
- 创建深层次目录时,需要从存在的一层开始进行迭代缩短,以保证创建成功
- 如果lpszLongPath宽字符长度大于MAX_PATH,则必须增加前缀\\?\,否则超过MAX_PATH,还是不能识别造成调用失败
经过如下的转换,d:\LongWMY本地演示环境\files\dir1hijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrstuvwxyz\dir2hijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrstuvwxyz\dir3hijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrst123456789012345678901234567890123456789ABCDEFG1234\db\2A384-T-1030 测试\2A384-T-1033+测试\2A384-T-1030 测试\2A384-T-1033+测试\20 330个字符的路径将被缩短成了D:\LONGWM~2\files\DIR1HI~1\DIR2HI~1\DIE187~1\db\2A384-~1\2A384-~1\2A384-~1\2A384-~1\20 的86个字符,在这个目录下创建文件、操作文件皆OK!
当然,你在创建目录、创建文件时,肯定不希望将它缩短,也不能缩短,所以以下函数输入参数的最后一级信息是不应该被缩短的。
<pre class="cpp" name="code">#define WMY_FULL_FILENAME_LEN 512
void shortedFileName(char *inFileName, char *outFileName)
{
int sRet = 0;
WCHAR wLongFilePath[WMY_FULL_FILENAME_LEN+1] = {0};
WCHAR wPrefixLongFilePath[WMY_FULL_FILENAME_LEN+1] = {0};
WCHAR wShortFilePath[WMY_FULL_FILENAME_LEN+1] = {0};
char newFilePath[WMY_FULL_FILENAME_LEN+1] = {0};
/* 目录转宽字节 */
char *p = strrchr(inFileName, T_PATHCONNCHR);
if(p){
sRet = MultiByteToWideChar (CP_ACP, 0, inFileName, p-inFileName, wLongFilePath, WMY_FULL_FILENAME_LEN);
}
else{
sRet = MultiByteToWideChar (CP_ACP, 0, inFileName, -1, wLongFilePath, WMY_FULL_FILENAME_LEN);
}
/* 宽字节目录增加\\?\前缀 */
if(strlen(inFileName)>=250){/* 相比wcslen(inFileName)>=MAX_PATH,更宽泛 */
sRet = wcscat(wPrefixLongFilePath, L"\\\\\?\\");
sRet = wcscat(wPrefixLongFilePath, wLongFilePath);
}
else{
sRet = swprintf(wPrefixLongFilePath, L"%s", wLongFilePath);
}
/* 宽字节目录缩短 */
sRet = GetShortPathNameW(wPrefixLongFilePath, wShortFilePath, WMY_FULL_FILENAME_LEN);
if(0!=sRet)
{
sRet = WideCharToMultiByte(CP_OEMCP,NULL,wShortFilePath,-1,newFilePath,WMY_FULL_FILENAME_LEN,NULL,FALSE);
}
else{
sRet = GetLastError();
sRet = WideCharToMultiByte(CP_OEMCP,NULL,wLongFilePath,-1,newFilePath,WMY_FULL_FILENAME_LEN,NULL,FALSE);
}
/* 拼接输出文件名 */
if(p)
sRet = sprintf(outFileName, "%s%c%s", newFilePath, T_PATHCONNCHR, p+1);
else
sRet = sprintf(outFileName, "%s", newFilePath);
}
附录参考:
GetShortPathName
The GetShortPathName function retrieves the short path form of a specified input path.
DWORD GetShortPathName( LPCTSTR lpszLongPath, // null-terminated path string LPTSTR lpszShortPath, // short form buffer DWORD cchBuffer // size of short form buffer );
Parameters
-
lpszLongPath
-
[in] Pointer to a null-terminated path string. The function retrieves the short form of this path.
Windows NT/2000/XP: In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to nearly 32,000 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. For more information, see File Name Conventions.
Windows 95/98/Me: This string must not exceed MAX_PATH characters.
lpszShortPath
- [out] Pointer to a buffer to receive the null-terminated short form of the path specified by lpszLongPath. cchBuffer
- [in] Specifies the size, in TCHARs, of the buffer pointed to by lpszShortPath.
Return Values
If the function succeeds, the return value is the length, in TCHARs, of the string copied tolpszShortPath, not including the terminating null character.
If the lpszShortPath buffer is too small to contain the path, the return value is the size of the buffer, inTCHARs, required to hold the path. Therefore, if the return value is greater thancchBuffer, call the function again with a buffer that is large enough to hold the path.
If the function fails for any other reason, the return value is zero. To get extended error information, callGetLastError.
Remarks
When an application calls this function and specifies a path on a volume that does not support 8.3 aliases, the function fails with ERROR_INVALID_PARAMETER if the path is longer than 67 bytes.
The path specified by lpszLongPath does not have to be a full or a long path. The short form may be longer than the specifed path.
If the specified path is already in its short form, there is no need for any conversion, and the function simply copies the specified path to the buffer for the short path.
You can set lpszShortPath to the same value as lpszLongPath; in other words, you can set the buffer for the short path to the address of the input path string.
You can obtain the long name of a file from the short name by calling the GetLongPathName function. Alternatively, where GetLongPathName is not available, you can callFindFirstFile on each component of the path to get the corresponding long name.
Windows 95/98/Me: GetShortPathNameW is supported by the Microsoft Layer for Unicode. To use this, you must add certain files to your application, as outlined inMicrosoft Layer for Unicode on Windows 95/98/Me Systems.
Requirements
Windows NT/2000/XP: Included in Windows NT 3.5 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winbase.h; include Windows.h.
Library: Use Kernel32.lib.
Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000/XP. Also supported by Microsoft Layer for Unicode.
See Also
File I/O Overview, File I/O Functions,FindFirstFile,GetFullPathName, GetLongPathName,SetFileShortName
老树(老产品)开新花,就看如何对待了。
欢迎感兴趣的,提供相关的产品解决之道,提高产品的应用范围和效益,延长生命周期,降低成本。