Доступ к файлам с длинными путями (более 260)

Я использую Microsoft Scripting Runtime (FSO) для анализа папок и создания списка всего их содержимого, папки находятся в сети, и результирующие пути в конечном итоге длиннее 260. Минимальный код, который у меня есть, выглядит следующим образом: -

Private Sub ProcessFolder(ByVal StrFolder As String)
Dim Fl              As File
Dim Fldr            As Folder
Dim RootFldr        As Folder

Set RootFldr = FS.GetFolder(StrFolder)

    For Each Fl In RootFldr.Files
        Debug.Print Fl.Path
    Next

    For Each Fldr In RootFldr.SubFolders
        DoEvents
        ProcessFolder Fldr.Path
    Next

Set RootFldr = nothing    
End sub

На определенном уровне длина StrFolder стала 259, строка папки Set RootFldr ... работала, но For Each Fl In RootFldr.Files выдавала ошибку 76: Path not found, предположительно из-за содержимого, из-за которого путь превышал ограничение 260.

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

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


person Gary Evans    schedule 18.07.2016    source источник
comment
Возможный повтор: ссылка   -  person Kerry White    schedule 23.07.2016
comment
Оба похожи, и мне жаль, что я не предоставил больше информации с самого начала (и я думал, что задал хороший вопрос!). Я обновлю свои дальнейшие требования, в настоящее время я использую shortpath, чтобы пройти через это. Это разрушает путь (делает его нечитаемым/пригодным для использования человеком), но я думаю, что мог бы написать что-нибудь, чтобы это исправить.   -  person Gary Evans    schedule 25.07.2016
comment
Просто идея для обходного пути: после того, как вы достигли, вероятно, 250 символов в пути к файлу, вы можете сопоставить с ним новую букву диска и повторить ее, а другую, когда изначально достигло 500 символов. Но вам нужно изменить код, чтобы понять, что нужно восстановить полный путь. Таким образом теоретически вы можете увеличить размер до 25*250 символов.   -  person PatricK    schedule 26.07.2016
comment
@PatricK Это вероятная идея, и я думаю, что пробовал это в прошлом проекте (давным-давно). Проблема заключалась в том, что ограничение находится на результирующем пути, то есть после разрешения сопоставленного диска на его путь и добавления префикса к пути, к которому вы пытаетесь добраться.   -  person Gary Evans    schedule 26.07.2016
comment
@Patrick Решение, о котором я думал, было похожим. Я думал запустить PUSHD и POPD в WshSell, потому что команда DIR очень хорошо оптимизирована для поиска в каталогах (намного быстрее, чем использование FSO). Однако, как только я увидел ответ Ганса, это было бессмысленно. Его намного лучше.   -  person Tim    schedule 26.07.2016


Ответы (3)


Метод преобразования загроможденных имен путей DOS в формате MAXFILE в исходные имена путей ОС хорошо известен и задокументировано. Резюмируя:

  • Укажите префикс пути, который использует букву диска, с префиксом \\?\, например \\?\C:\foo\bar\baz.txt.
  • Укажите префикс пути, который использует общую папку, с префиксом '\\?\UNC\, например \\?\UNC\server\share\baz.txt.

Хорошо работает и с FileSystemObject, по крайней мере, когда я тестировал ваш код в Windows 10. Это может не обязательно иметь место в более старых версиях Windows или с сетевым перенаправителем на вашем сервере. Протестировано с помощью файлового менеджера FAR для создания подкаталогов с длинными именами и проверено с помощью:

Dim path = "\\?\C:\temp\LongNameTest"
ProcessFolder path

Произведено:

\\?\c:\temp\LongNameTest\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\Chrysanthemum.jpg

Что составляет 488 символов. Что нужно иметь в виду:

  • Собственные имена путей должны быть полными путями, они не могут быть относительными путями. Другими словами, они всегда должны начинаться с буквы диска или имени общего ресурса и начинаться с корня диска/общего ресурса.
  • Вы возвращаете исходное имя пути, не забудьте снова удалить префикс, если вы его отображаете.
  • Не тестировалось, но должно завершиться ошибкой, все еще существует ограничение на длину самого имени файла (без имен каталогов), не может быть длиннее 259 символов. Это не должно быть проблемой, поскольку пользователь также не может их создавать.
person Hans Passant    schedule 23.07.2016
comment
Как вы проверили это на FSO? Я не могу заставить его работать, мне нужно получить размер файла, дату изменения и создания, я использовал Set FSFldr = FS.GetFolder("\\?\lo3uppesaapp001\pesa_cmc....) - person Gary Evans; 25.07.2016
comment
Это недопустимый путь, проверьте сообщение еще раз. Скорее всего \\?\UNC\lo3uppesaapp001\pesa_cmc... - person Hans Passant; 25.07.2016
comment
Привет, я знаю, что это недопустимый путь, я не хотел публиковать путь к активной папке и засорять сообщение. Я исхожу из того, что прочитал в ответе - person Gary Evans; 25.07.2016
comment
To specify such a path using UNC, use the "\\?\UNC\" prefix. For example, "\\?\UNC\server\share", where "server" is the name of the computer and "share" is the name of the shared folder. Таким образом, ваш пример пути будет \\?\UNC\lo3uppesaapp001\pesa_cmc.... - person Tim; 26.07.2016

Это потребовало некоторого творческого программирования, но использование ShortPath было ответом.

Этот инструмент должен был создать список каждой папки и файла в корневой папке, файлы также отображали их размер и даты создания/изменения. Проблема заключалась в том, что когда результирующий путь к файлу или папке превышал 260, возникала ошибка Error 76: Path Not Found, и код не захватывал содержимое этой области.

Использование Microsoft Scripting Runtime (FSO) ShortPath помогло бы обойти эту проблему, но путь пошел от удобочитаемого к закодированному:

Полный путь \\ServerName00000\Root_Root_contentmanagement\DPT\STANDARDS_GUIDELINES\VENDOR_CERTIFICATION_FILES\PDFX_CERTIFICATION_ALL\2006_2007\DPT\CompantName0\Approved\Quark\India under Colonial Rule_structure sample\058231738X\Douglas M. Peers_01_058231738X\SUPPORT\ADDITIONAL INFORMATION\IUC-XTG & XML file

Короткий путь \\lo3uppesaapp001\pesa_cmcoe_contentmanagement\CTS\S4SJ05~5\V275SE~8\PDM5D9~G\2N52EQ~5\HPE\GS9C6L~U\Approved\Quark\IQPSJ5~F\0CWHH1~G\DOFNHA~8\SUPPORT\A6NO7S~K\IUC-XTG & XML file

(Обратите внимание, что я изменил полный путь для защиты IP и информации о компании, но размер остался прежним)

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

Короткий ответ: ShortPath в FSO решит проблему, но путь будет некрасивым.

Dim FS              As New FileSystemObject
Dim LngRow          As Long
Dim StrFolderPath   As String
Dim WkBk            As Excel.Workbook
Dim WkSht           As Excel.Worksheet

Public Sub Run_Master()

Set WkBk = Application.Workbooks.Add

    WkBk.SaveAs ThisWorkbook.Path & "\Data.xlsx"

    Set WkSht = WkBk.Worksheets(1)

        WkSht.Range("A1") = "Path"
        WkSht.Range("B1") = "File Name"
        WkSht.Range("C1") = "Size (KB)"
        WkSht.Range("D1") = "Created"
        WkSht.Range("E1") = "Modified"

        LngRow = 2

        Run "\\ServerName00000\AREA_DEPT0_TASK000"

    Set WkSht = Nothing

    WkBk.Close 1
Set WkBk = Nothing

MsgBox "Done!"

End Sub

Private Sub Run(ByVal StrVolumeToCheck As String)
Dim Fldr            As Folder
Dim Fldr2           As Folder

Set Fldr = FS.GetFolder(StrVolumeToCheck)

    'This is the variable that follows the full path name
    StrFolderPath = Fldr.Path

    WkSht.Range("A" & LngRow) = StrFolderPath
    LngRow = LngRow +1

    For Each Fldr2 In Fldr.SubFolders
        If (Left(Fldr2.Name, 1) <> ".") And (UCase(Trim(Fldr2.Name)) <> "LOST+FOUND") Then
            ProcessFolder Fldr2.Path
        End If
    Next

Set Fldr = Nothing

End Sub

Private Sub ProcessFolder(ByVal StrFolder As String)
'This is the one that will will be called recursively to list all files and folders
Dim Fls             As Files
Dim Fl              As File
Dim Fldrs           As Folders
Dim Fldr            As Folder
Dim RootFldr        As Folder

Set RootFldr = FS.GetFolder(StrFolder)

    If (RootFldr.Name <> "lost+found") And (Left(RootFldr.Name, 1) <> ".") Then

        'Add to my full folder path
        StrFolderPath = StrFolderPath & "\" & RootFldr.Name

        WkSht.Range("A" & LngRow) = StrFolderPath
        WkSht.Range("D1") = RootFldr.DateCreated
        WkSht.Range("E1") = RootFldr.DateLastModified
        Lngrow = LngRow + 1

        'This uses the short path to get the files in FSO
        Set Fls = FS.GetFolder(RootFldr.ShortPath).Files

            For Each Fl In Fls
                'This output our string variable of the path (i.e. not the short path)
                WkSht.Range("A" & LngRow) = StrFolderPath
                WkSht.Range("B" & LngRow) = Fl.Name
                WkSht.Range("C" & LngRow) = Fl.Size /1024 '(bytes to kilobytes)
                WkSht.Range("D" & LngRow) = Fl.DateCreated
                WkSht.Range("E" & LngRow) = Fl.DateLastModified

                LngRow = LngRow + 1

            Next
        Set Fls = Nothing

        'This uses the short path to get the sub-folders in FSO
        Set Fldrs = FS.GetFolder(RootFldr.ShortPath).SubFolders
            For Each Fldr In Fldrs
                'Recurse this Proc
                ProcessFolder Fldr.Path
                DoEvents
            Next
        Set Fldrs = Nothing

        'Now we have processed this folder, trim the folder name off of the string
        StrFolderPath = Left(StrFolderPath, Len(StrFolderPath) - Len(RootFldr.Name)+1)

    End If
Set RootFldr = Nothing

End Sub

Как уже упоминалось, это урезанная версия кода, которая работает для меня, чтобы проиллюстрировать метод, используемый для преодоления этого ограничения. На самом деле кажется довольно элементарным, как только я это сделал.

person Gary Evans    schedule 25.07.2016

Однажды мне удалось обойти это с помощью команды subst командной оболочки. Он позволяет назначить букву диска локальному пути (что-то вроде общего сетевого ресурса).

person Mister 832    schedule 26.07.2016