如何在Windows中使用MFC创建超过255个字符的文件路径名?

6

我正在使用Windows操作系统,并使用vc++2010和MFC。

以下是我的代码:

CFile File;
TCHAR lpCause[1024];
CFileException eException;
CString strErrorMessage;
//  a very long file path name means a file name over 255 characters
if (!File.Open(_T("a very long file path name"), CFile::modeCreate, &eException))
{
    eException.GetErrorMessage(lpCause, 1024);
    strErrorMessage = lpCause;
}
else
    File.Close();

当我运行代码时,收到错误信息:"一个非常长的文件路径名包含了不正确的路径"。

我的问题是:

  1. 如何修改我的代码使之能够工作?
  2. 我了解到CreateFile()函数可以在文件路径的开头添加"\\\\?\",这样它就可以扩展到32767个宽字符。在MFC中,我该如何做同样的事情?

CFile只是标准Windows API的一个抽象。在下面的某个地方会调用CreateFile。请记住,字符串字面量中的所有反斜杠都需要转义。 - Some programmer dude
2
只有CreateFileW可以使用长路径,而CreateFileA则硬性限制在大约256个字符。因此,您需要显式地使用W API和WCHAR字符串,而不是TCHAR - RbMm
这个工作吗:!File.Open(_T("\\\\?\\一个非常长的文件路径名") - Jabberwocky
@MichaelWalz,不行,它做不到。 - zet
@RbMm,Windows 10动态分配缓冲区以解码ANSI字符串,即使用AllocateDestinationString调用RtlAnsiStringToUnicodeString。这与其新的长路径支持无关,而且我IRC这始于Windows 8。当然,现在它只是有趣的小知识。早期版本使用静态缓冲区,限制转换为MAX_PATH,并且文档仍要求长扩展路径使用Unicode。 - Eryk Sun
@eryksun - 是的,现在使用 Basep8BitStringToDynamicUnicodeString(很久没有看过这个了)。年度使用 TEB 中的固定大小缓冲区。 - RbMm
1个回答

7

原因

CFile::Open()源代码中,有一个明确的检查,如果路径长度超过_MAX_PATH,则会进行处理:

if (lpszFileName != NULL && SUCCEEDED(StringCchLength(lpszFileName, _MAX_PATH, NULL)) )

如果超过_MAX_PATH,该函数将设置pException->m_cause = CFileException::badPath并返回FALSE。即使是与VS2017一起提供的MFC版本也是如此。
因此,规避_MAX_PATH限制的标准技术,即在路径前缀中加上\\?\,将不起作用。
可能的解决方案:
1. 直接调用CreateFileW(),传递带有\\?\前缀的路径。 2. 使用接受HANDLECFile构造函数通过CFile对象管理文件。CFile对象将拥有句柄的所有权,因此不能对句柄调用CloseHandle()
HANDLE hFile = CreateFileW( L"\\\\?\\a very long file path name", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, NULL );
if( hFile != INVALID_HANDLE_VALUE )
{
    // Manage the handle using CFile
    CFile file( hFile );
    // Use the file...

    // The CFile destructor closes the handle here. 
}
else
{
    DWORD err = GetLastError();
    // TODO: do your error handling...
}

另一种可能性是从CFile派生一个类,覆盖虚函数CFile::Open()。实现时可以复制MFC源代码,但省略_MAX_PATH检查。对于大型项目,该类可作为CFile的即插即用替代方案,以启用长路径。甚至可以在没有\\?\前缀的情况下添加它(但这更加复杂,因为前缀还会禁用从Win32路径到NT风格路径的常规转换,如将/转换为\,解析点等)。

1
@eryksun MSFT似乎仍然认为这是一个beta功能,因为“LongPathsEnabled”注册表键默认情况下未设置。我看不出其他原因需要这个双重选择要求。 - zett42
1
“最大路径长度限制”适用于Win32 API,而不适用于MFC。如果CFile::Open()仍然对_MAX_PATH进行硬编码检查,则无论如何表现或注册表调整都无法使用更长的路径。 - Remy Lebeau
@RemyLebeau是正确的,那是我的一个思维错误。在MFC代码中,长路径感知进程没有分支。我将编辑我的答案。 - zett42
1
要应用“\\?\"前缀,首先通过GetFullPathName规范化路径。如果它规范化为扩展的本地设备路径,则完成了。如果它是一个常规的本地设备路径(即以“\\.\"为前缀),则在前缀中将“.”替换为“?”。(为了完全正确,请检查它是否是现有目录中的DOS设备,例如“C:\spam\nul.txt”仅在“C:\spam”存在时才是“\\.\nul”)。如果它是UNC(例如“\\server\share\"),则将其转换为显式的UNC设备,例如“\\?\UNC\server\share\"。否则,只需将前缀添加到规范化的路径前面。 - Eryk Sun
谢谢,我明白了。 - zet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接