Диск, созданный SUBST (Windows 10), не сообщающий полный путь к QueryDosDevice () - PullRequest
0 голосов
/ 11 октября 2019

РЕДАКТИРОВАТЬ - СМОТРЕТЬ, КАК Я НАШЕЛ ПРОБЛЕМУ

Извините. Видимо, это была моя вина. Смотрите подробности в моем собственном ответе ниже.


Я работаю над тестом для диска SUBST, который входит в состав более крупного метода, которыйпроверяет строку, чтобы убедиться, что это (по крайней мере, возможно) правильный путь. После небольшой проверки метод «encapsulation» преобразует строку в UNC или абсолютный путь, в зависимости от переданного пути, для возврата в другое место.

Пара примеров:

  • Для диска U:\, сопоставленного с \\SERVERNAME\Share, с помощью ключа \HKCU\Network\ в реестре Windows для поиска сопоставлений сетевых дисков на локальном компьютере U:\PublicFolder\SomeFile.txt становится \\SERVERNAME\Share\PublicFolder\SomeFile.txt

  • С другой стороны, C:\SomeFolder\SomeFile.txt остается без изменений, поскольку (как определено в методе) это абсолютный путь к локальному физическому диску.

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

Если честно, у меня нет большого опыта работы с командой SUBST и я не очень часто ее использую,так что сначала у меня были проблемы дажезаставить диск правильно отображаться в Windows. Прочитав обсуждение на странице сообщества Microsoft ( Проблема Windows 10, команда «Subst» не работает ), я наконец-то смог настроить диск «правильно» ( не работаетиспользуйте командную строку с повышенными правами, BTW ), но код, который я использую для проверки диска SUBST, преобразованного в VB.NET из этого ответа , все еще не разрешаетполный путь правильно.

Вот преобразованный код, который я использую (я собираюсь сделать некоторую «подстройку» позже, когда у меня все работает, но это текущее состояние):

<DllImport("kernel32.dll", SetLastError:=True)>
Private Shared Function QueryDosDevice(ByVal lpDeviceName As String, ByVal lpTargetPath As System.Text.StringBuilder, ByVal ucchMax As Integer) As UInteger
End Function

Private Shared Function IsSubstPath(ByVal pathToTest As String, <Out> ByRef realPath As String) As Boolean
    Dim PathInformation As System.Text.StringBuilder = New System.Text.StringBuilder(260)
    Dim DriveLetter As String = Nothing
    Dim WinApiResult As UInteger = 0

    realPath = Nothing

    Try
        ' Get the drive letter of the path
        DriveLetter = IO.Path.GetPathRoot(pathToTest).Replace("\\", "")
    Catch ex As ArgumentException
        Return False
    End Try

    WinApiResult = QueryDosDevice(DriveLetter, PathInformation, 260)

    If WinApiResult = 0 Then
        Dim LastWinError As Integer = Marshal.GetLastWin32Error()

        Return False
    End If

    ' If the drive is SUBST'ed, the result will be in the format of "\??\C:\realPath\".
    If PathInformation.ToString().StartsWith("\??\") Then
        Dim RealRoot As String = PathInformation.ToString().Remove(0, 4)

        RealRoot += If(PathInformation.ToString().EndsWith("\"), "", "\")
        realPath = IO.Path.Combine(RealRoot, pathToTest.Replace(IO.Path.GetPathRoot(pathToTest), ""))

        Return True
    End If

    realPath = pathToTest
    Return False
End Function

Который я так называю для диска, который я создал с помощью SUBST H: D:\substtest:

Dim TestFile As New IO.FileInfo("H:\0984\CPI.TXT")
Dim SubstPath As String = String.Empty
Dim FullPath As String = String.Empty

If IsSubstPath(FullPath, SubstPath) Then
    FullPath = SubstPath
End If

Я ожидаю, что метод IsSubstPath() должен вернуть D:\substtest\0984\CPI.TXT через realPathпеременная. Выполнение SUBST (без дополнительных параметров) правильно показало отображение в командной строке (H:\: => D:\substtest). Проверка объекта TestFile во время отладки показывает, что его свойство Exists() возвращает True, поэтому файловая система, по-видимому, знает, что он есть.

На этом этапе каждый раз, когда я выполняю код,вызов метода QueryDosDevice() возвращает значение 0, хотя я получаю различные результаты от вызова Marshal.GetLastWin32Error(), так как продолжаю пытаться заставить это работать.

Моя первая попытка после получения SUBSTПривод "правильно" на моей машине привел к возвращению Marshal.GetLastWin32Error() кода ошибки 1008 - ERROR_NO_TOKEN (" Была сделана попытка сослаться на токен, который не существует ").

Дальнейшее чтение в связанной ветке сообщества MS показало, что SUBST дважды - один раз в обычной командной строке и снова в командной строке с повышенными привилегиями - должен сделать диск доступным как обычному вошедшему в систему пользователю, так и любым повышенным действиям пользователя. Я перезапустил SUBST в командной строке с повышенными правами и попытался снова, используя тот же код тестирования, что и выше. На этот раз Marshal.GetLastWin32Error() вернул код ошибки 6 - ERROR_INVALID_HANDLE ( "Неверный дескриптор." ).

Думая об этой конкретной операции может будучи зависимым от файла / пути, реально существующего в системе (в отличие от объектов .NET IO.FileInfo или IO.DirectoryInfo), я вручную создал определенную подпапку и файл, чтобы представить то, что я проверял в своем коде (H:\0984\CPI.TXT) и попробовал еще раз (снова используя тот же код, что и выше):

Еще раз, QueryDosDevice() не удалось правильно проанализировать реальный путь (возвращается 0), но на этот раз метод Marshal.GetLastWin32Error() вернул значение 0 - ERROR_SUCCESS ( "Операция завершена успешно. "). Думая, что, возможно, в коде был какой-то «недостаток», который мог непреднамеренно пропустить шаг или что-то еще, я проверил переменную PathInformation - объект Text.StringBuilder, который содержит результаты QueryDosDevice() - в режиме прерывания, ноувы, он также пуст.

ПРИМЕЧАНИЕ. Я также попытался использовать каталог вместо файла, но H:\0984\ привел к Marshal.GetLastWin32Error() возвращаемому значению 0, тогда как H:\0984 привел кзначение 6. Основываясь на предыдущем тестировании, все это имеет смысл, но, тем не менее, приводит к пустой переменной PathInformation (ошибка).

Читая все вокруг Interwebz, кажется, что многие люди испытывают различные проблемыс SUBST -дисками под Windows 10, так что мне остается только задуматься, а не является ли причиной этих неожиданных результатов. Кто-нибудь еще сталкивался с этими проблемами и, если да, вам удалось разрешить их в коде?

В случае, если это имеет значение (как я полагаю, это может иметь место), вот некоторые дополнительные подробности:

  • Я использую Visual Studio 2017 CE, и мой проект компилируется в .NET Framework 4.7.2
  • Физический диск, на который я создаю путь SUBST, представляет собой пару RAID 1дисков, отформатированных в NTFS и имеющих достаточно места (178 ГБ).

    ПРИМЕЧАНИЕ. Я также попытался создать путь SUBST на моем диске ОС (C:\) для тестирования, но получаются те же результаты, что и выше.

Если я что-то пропустил или если вам потребуются дополнительные разъяснения, пожалуйста, дайте мне знать в комментариях, и я обновлювопрос по необходимости.

1 Ответ

0 голосов
/ 15 октября 2019

DOUBLE / TRIPLE / QUADRUPLE CHECK CONVERTED CODE

Похоже, все сводится к одной строке кода, на которую я не обращал достаточно пристального внимания ( строка 14 в блоке кода выше):

DriveLetter = IO.Path.GetPathRoot(PathToTest).Replace("\\", "")

Проблема здесь в том, что метод Path.GetPathRoot() возвращает букву диска в формате H:\ - есть только одна обратная косая черта (\)поэтому метод .Replace() не нашел ничего для замены и передавал «неверное» значение параметра (H:\ вместо H:). Метод QueryDosDevice(), по-видимому, потерпит неудачу, если есть обратная косая черта, поэтому я сделал быстрое редактирование кода:

DriveLetter = IO.Path.GetPathRoot(PathToTest).Replace("\", "") ' <--Replace ANY/ALL backslashes

Я снова протестировал на своем SUBST H: D:\substtest диске с существующей структурой файлов / каталогов, как указано выше,На этот раз метод QueryDosDevice() возвратил значение 19 и правильно проанализировал реальный путь как D:\substtest\0984\CPI.TXT.

Затем я удалил подпапку / файл, который я создал для тестирования, и повторил попытку. Опять же, он правильно вернул реальный путь как D:\substtest\0984\CPI.TXT. Итак, по-видимому, все сводится к тому, что я упускаю из виду «опечатку», появившуюся во время преобразования кода из C # в VB.NET. Мои извенения. Полная, исправленная версия преобразованного кода

<DllImport("kernel32.dll", SetLastError:=True)>
Private Shared Function QueryDosDevice(ByVal lpDeviceName As String, ByVal lpTargetPath As System.Text.StringBuilder, ByVal ucchMax As Integer) As UInteger
End Function

Private Shared Function IsSubstPath(ByVal pathToTest As String, <Out> ByRef realPath As String) As Boolean
    Dim PathInformation As System.Text.StringBuilder = New System.Text.StringBuilder(260)
    Dim DriveLetter As String = Nothing
    Dim WinApiResult As UInteger = 0

    realPath = Nothing

    Try
        ' Get the drive letter of the path without the trailing backslash
        DriveLetter = IO.Path.GetPathRoot(pathToTest).Replace("\", "")
    Catch ex As ArgumentException
        Return False
    End Try

    WinApiResult = QueryDosDevice(DriveLetter, PathInformation, 260)

    If WinApiResult = 0 Then
        Dim LastWinError As Integer = Marshal.GetLastWin32Error()

        Return False
    End If

    ' If the drive is SUBST'ed, the result will be in the format of "\??\C:\realPath\".
    If PathInformation.ToString().StartsWith("\??\") Then
        Dim RealRoot As String = PathInformation.ToString().Remove(0, 4)

        RealRoot += If(PathInformation.ToString().EndsWith("\"), "", "\")
        realPath = IO.Path.Combine(RealRoot, pathToTest.Replace(IO.Path.GetPathRoot(pathToTest), ""))

        Return True
    End If

    realPath = pathToTest
    Return False
End Function

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

...