Как сделать длинную версию Unicode для пути UNC?

У меня сложилось впечатление, что если бы у меня был такой путь UNC:

\\SRVR-A\Home\UserA\Documents\TestFolder

и я хотел его расширить прошел ограничение MAX_PATH, я мог бы сделать это:

\\?\UNC\SRVR-A\Home\UserA\Documents\TestFolder

Но когда я запускаю следующее в Windows XP, происходит сбой с кодом ошибки ERROR_INVALID_NAME:

TCHAR buffDummy;
DWORD dwNeededLn = ::GetLongPathName(
    L"\\\\?\\UNC\\SRVR-A\\Home\UserA\\Documents\\TestFolder",
    &buffDummy, 0);
if(dwNeededLn == 0)
{
    //Error
    int nErrorCode = ::GetLastError();
}

Я что-то упускаю?

PS. Эта папка существует, и API работает нормально, если вместо этого я делаю \\SRVR-A\Home\UserA\Documents\TestFolder.


person c00000fd    schedule 06.06.2014    source источник
comment
Целью GetLongPathName является перевод короткого имени, например. c:\progra~2\MICROS~2.0 в длинное имя, например. C:\Program Files\Microsoft Visual Studio 10. Так что я не понимаю, каковы ваши намерения здесь...   -  person user1793036    schedule 06.06.2014
comment
@ user1793036: Вопрос не в этом.   -  person c00000fd    schedule 06.06.2014
comment
Вы видите эту проблему только с GetLongPathName или у вас также есть проблемы с другими функциями API, такими как CreateFile? Работает ли это на более поздних версиях Windows? (Может быть, это просто ошибка...)   -  person Harry Johnston    schedule 06.06.2014
comment
@HarryJohnston: я не экспериментировал со всеми из них, чтобы знать наверняка. Насколько я знаю, другие (ядерные) API, которые я пробовал, работают нормально. И да, это работает на Windows 7 и более поздних версиях. И даже если это ошибка, на самом деле нет другого API для работы с короткими именами 8.3, не так ли?   -  person c00000fd    schedule 06.06.2014
comment
Ну, нет никакой гарантии, что есть способ обойти ту или иную ошибку. Возможно, вы просто не сможете сделать это в Windows XP. (В конце концов, это уже конец жизни.)   -  person Harry Johnston    schedule 07.06.2014
comment
Этот подход может сработать.   -  person Harry Johnston    schedule 07.06.2014


Ответы (2)


Подсказка действительно содержится в цитате со страницы MSDN: «Во многих файловых системах короткое имя файла содержит символ тильды (~). Однако не все файловые системы следуют этому соглашению».

При работе с удаленной файловой системой вы не знаете лежащую в ее основе файловую систему. Вы не можете догадаться, каков его метод генерации коротких имен файлов, или даже есть ли такое понятие (в конце концов, это действительно специфичная для Windows концепция)

Таким образом, следует ожидать, что GetLongPathName вообще будет работать. Теперь может показаться, что в некоторых случаях это работает, но это, вероятно, несчастный случай - применение локальных правил к удаленному имени может работать, если системы достаточно похожи.

person MSalters    schedule 06.06.2014
comment
В ПОРЯДКЕ. Итак, следуя вашей логике... Мне нужен способ преобразовать путь из короткого формата 8.3 в длинный формат. Как бы Вы это сделали? PS. Я не знаю базового формата входного пути. - person c00000fd; 06.06.2014
comment
Я бы не стал этого делать. Если удаленный сервер называет файл \\10.0.0.1\Share\Foo~1.htm, то это его имя. Вы даже не можете понять, есть ли у него длинное имя, не говоря уже о том, каким оно будет. - person MSalters; 06.06.2014
comment
Я не говорю об удаленных серверах. Мне неизвестен базовый формат входного пути. - person c00000fd; 06.06.2014
comment
Что ж, по пути несложно определить, является ли он удаленным (начинается с \\server или \\?\UNC). В этом случае не пытайтесь преобразовывать имена. - person MSalters; 06.06.2014
comment
Да, возможно, это единственный способ справиться с этим. Знаете ли вы, что часть `\\?\UNC` чувствительна к регистру? - person c00000fd; 06.06.2014
comment
Вопрос не в том, чтобы угадать, какое длинное имя получило короткое имя. Это невозможно даже в локальной файловой системе! SMB включает поддержку запроса длинного имени файла, о чем свидетельствует тот факт, что вызов работает для OP, если он использует \\server\share вместо \\?\UNC\server\share. Проблема просто в том, что по какой-то причине функция не поддерживает последний формат. - person Harry Johnston; 07.06.2014
comment
@HarryJohnston: SMB может, но имя UNC не обязательно является общим ресурсом SMB. - person MSalters; 07.06.2014
comment
Может ли кто-нибудь подтвердить, что часть \\?\UNC чувствительна к регистру? - person c00000fd; 07.06.2014
comment
@MSalters: но в данном случае это так, поэтому, когда GetLongPathName запрашивает файловую систему преобразовать короткое имя в длинное, оно должно работать. GetLongPathName не имело бы смысла говорить, что я не уверен, что смогу получить длинное имя файла для этого пути, поэтому я даже не буду пытаться. (c00000fd: я смогу проверить это завтра, когда вернусь в офис.) - person Harry Johnston; 08.06.2014
comment
@ c00000fd: в Windows 7 \\?\UNC не учитывает регистр. Я также не могу найти в ядре Windows Research Kernel указания на то, что в Windows 2003 регистр чувствителен к регистру, хотя это не является окончательным. Могут быть частные случаи. - person Harry Johnston; 09.06.2014

Функция ::GetLongPathName() на самом деле состоит из двух функций: ::GetLongPathNameA() (ANSI) и ::GetLongPathNameW() (Wide).

Во включаемом файле fileapi.h есть код:

#ifdef UNICODE
#define GetLongPathName  GetLongPathNameW
#else
#define GetLongPathName  GetLongPathNameA
#endif // !UNICODE

Только GetLongPathNameW() обрабатывает более длинные имена путей.

Вам необходимо убедиться, что определено «UNICODE», иначе нужно специально вызывать GetLongPathNameW(), а не GetLongPathName()

Я провел несколько тестов в своей домашней локальной сети.

CALCITE — это внешний жесткий диск. Он запускает какой-то вариант Unix/Linux, но я не возился с ним. Он имеет IP-адрес 192.168.1.2. Я запускаю тест на рабочем столе Win7 Professional, используя VC Express 2013.

#include <iostream>
#include <string>
#include <Windows.h>

void Test(const std::wstring &sName)
{
    std::wcout << sName << L" ==> ";

    const size_t nBuffsize = 1024;
    wchar_t szBuff[nBuffsize] = { 0 };
    if (::GetLongPathNameW(sName.c_str(), szBuff, nBuffsize))
        std::wcout << szBuff << std::endl;
    else
        std::wcout << L"Error: " << ::GetLastError() << std::endl;
}

int main() 
{
    Test(L"\\\\CALCITE\\public\\x.txt");
    Test(L"\\\\?\\UNC\\CALCITE\\public\\x.txt");
    Test(L"\\\\?\\UNC\\192.168.1.2\\public\\x.txt");

    Test(L"\\\\CALCITE\\public\\bad name.txt");
    Test(L"\\\\CALCITE\\Bad path\\x.txt");

    return 0;
}

Результаты:

 \\CALCITE\public\x.txt ==> \\CALCITE\public\x.txt
 \\?\UNC\CALCITE\public\x.txt ==> \\?\UNC\CALCITE\public\x.txt
 \\?\UNC\192.168.1.2\public\x.txt ==> \\?\UNC\192.168.1.2\public\x.txt
 \\CALCITE\public\bad name.txt ==> Error: 2 
 \\CALCITE\Bad path\x.txt ==> Error: 67

Ошибка 2: ERROR_FILE_NOT_FOUND

Ошибка 67: ERROR_BAD_NET_NAME.

person Michael J    schedule 06.06.2014
comment
Да, я использую GetLongPathNameW. Извините, я думал, что это данность в эти дни. (Плюс часть L"" должна выдавать это.) Я не могу представить, чтобы кто-нибудь использовал ANSI или строки, отличные от Unicode. Итак, мне любопытно, можете ли вы воспроизвести такое же поведение на своем конце? - person c00000fd; 06.06.2014
comment
У меня такая проблема с Windows XP. Он отлично работает на Windows 7. - person c00000fd; 07.06.2014