Having a little trouble while accessing an alternate ntfs stream from a file with a very long name (more that MAX_PATH characters long, created using "\\?\" prefix according to this). For the first time i thought it's my mistake in code, but then I tried a cmd command:

more < "\\?\c:\!!!Long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long filename!!!.png:streamname"

and it fails with an error that file is not found. The file exists and it's contents can be read, but I cannot access the stream I need for read and write. I don't want my software to be unable to work with long filenames, so I'm searching for any workarounds for this situation.

I know that I can use BackupRead function, but I'm not sure if this solution can be fast on a large files and it will not work in 2000.

GetShortPathName gives me the same fail result, are there any other APIs that can shorten a filename? I really dont want to do this using a temporary junction with a short filename. Any thoughts?

有帮助吗?

解决方案

As the very helpful page on CreateFile says referring to the lpFileName parameter which specifies the filename:

In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\?\" to the path.

Since you are contemplating BackupRead obviously you are wanting to access this stream programatically. If so, test things programatically. Trying all these operations from the command prompt is a crap-shoot and will not establish anything more than the ability to perform such operations from the command prompt.

With that in mind, let's try this simple program - boilerplate code removed:

#include "stdafx.h"

int APIENTRY _tWinMain(HINSTANCE,
                       HINSTANCE,
                       LPTSTR,
                       int)
{
    /* This is the name of the file that we will try to create. Please note that
     * I have hardcoded the path to my user directory (C:\Users\nikb), and since 
     * it's unlikely that path exists on your computer, you should probably put
     * something there that makes sense.
     */
    LPCWSTR lpszFileName = L"\\\\?\\c:\\users\\nikb\\!!!Long long long long "
                           L"long long long long long long long long long long "
                           L"long long long long long long long long long long "
                           L"long long long long long long filename!!!.png:streamname";

    HANDLE hFile = CreateFileW(lpszFileName, GENERIC_WRITE, 0, NULL, 
        CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

    if(hFile != INVALID_HANDLE_VALUE)
    {
        BYTE bBuffer[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'N', 'i', 'k', '!' };
        DWORD dwSize = 10;

        if(WriteFile(hFile, bBuffer, dwSize, &dwSize, NULL))
            ::MessageBoxW(GetDesktopWindow(), L"Success", L"WriteFile", MB_OK);
        else
            ::MessageBoxW(GetDesktopWindow(), L"Failure", L"WriteFile", MB_OK);

        CloseHandle(hFile);
    }

    return 0;

}

This should work just fine. So now, let's make that filename longer by adding a few more words to it. Again, be sure to properly update the path to something valid on your machine.

LPCWSTR lpszFileName = L"\\\\?\\c:\\users\\nikb\\!!!Long long long long long long long "
                       L"long long long long long long long long long long long "
                       L"long long long long long long long long long long long "
                       L"long long long long long long long long long long long "
                       L"long long long long long long long long long long long "
                       L"long long long long long long long long long long long "
                       L"long long long long long long long long long long long "
                       L"long long filename!!!.png:streamname";

Now that's long. And it will fail. Weird... If we check the output of GetLastError() we will get ERROR_INVALID_NAME so what gives? This should work because it clearly isn't longer then 32,767 characters and we're using the fancy \\?\ syntax to let us specify reeeeeally long paths. Right?

Well... sort of. The MSDN page on CreateFile links to the very helpful page titled Naming Files, Paths and Namespaces. As a matter of fact, even you linked to that page in your question. Which is interesting, because it would have answered your question before you even asked it:

The Windows API has many functions that also have Unicode versions to permit an extended-length path for a maximum total path length of 32,767 characters. This type of path is composed of components separated by backslashes, each up to the value returned in the lpMaximumComponentLength parameter of the GetVolumeInformation function (this value is commonly 255 characters). To specify an extended-length path, use the "\?\" prefix. For example, "\?\D:\very long path".

So although the path itself may be 32,767 characters long no individual component (i.e. "part") of the path may exceed the largest value allowed by the filesystem. Indeed, if you try to find out what the maximum component length that NTFS reports back, it will be 255.

So the filename is a single component, and a single component may not be longer than 255 characters. The filename which you specified was:

!!!Long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long filename!!!.png:streamname

A quick test shows that this is 264 characters long. And 264, it should come as no surprise, is greater than 255.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top