0

编辑 - 看起来我发现了问题

对不起。显然这都是我的错。有关详细信息,请参阅下面的我自己写的答案。


我正在对一个SUBST-ed 驱动器进行测试,该驱动器包含在一个更大的方法中,该方法测试一个字符串以确保它(至少,可能)是一个有效的路径。经过一些验证后,“封装”方法将字符串转换为 UNC 或绝对路径,具体取决于传入的路径,以在其他地方返回。

几个例子:

  • 对于U:\映射到的驱动器\\SERVERNAME\Share,使用\HKCU\Network\Windows 注册表中的键查找本地计算机上的网络驱动器映射,U:\PublicFolder\SomeFile.txt变为\\SERVERNAME\Share\PublicFolder\SomeFile.txt

  • 或者,C:\SomeFolder\SomeFile.txt保持不变,因为(在方法中确定)它是本地物理驱动器的绝对路径。

到目前为止,其中大部分似乎运行良好且符合预期,但我遇到了与SUBSTWindows 10 中的命令创建的驱动器有关的问题(至少 - 我目前尚未在另一个操作系统下运行任何测试因为我现在没有其他可用的)。

老实说,我对这个SUBST命令没有太多经验,也不经常使用它,所以一开始,我什至无法让驱动器在 Windows 中正确显示。在阅读了 Microsoft 社区页面上的讨论后(Windows 10 问题“Subst”命令不起作用),我终于能够“正确”设置驱动器(不要使用提升的命令提示符,顺便说一句),但是我用来测试 -ed 驱动器的代码- 从这个答案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()因为我继续尝试让它工作。

在我的机器上“正确”设置了 -ed 驱动器后,我的第一次尝试SUBST导致Marshal.GetLastWin32Error()返回错误代码1008 - ERROR_NO_TOKEN(“尝试引用不存在的令牌”)。

在链接的 MS 社区线程中的进一步阅读表明,运行SUBST 两次-一次在正常命令提示符下,再次在提升的命令提示符下 -应该使驱动器可用于常规登录用户以及任何提升的用户操作。我在提升的命令提示符下重新运行SUBST,并使用与上面相同的测试代码再次尝试。这一次,Marshal.GetLastWin32Error()返回错误代码6 - ERROR_INVALID_HANDLE ( "The handle is invalid." )。

考虑到这个特定的操作可能取决于系统上实际存在的文件/路径(而不是 .NETIO.FileInfoIO.DirectoryInfo对象),我手动创建了特定的子文件夹和文件来代表我在代码中测试的内容(H:\0984\CPI.TXT)并尝试了再一次(再次,使用与上面相同的代码):

再次,QueryDosDevice()未能正确解析真实路径(返回0),但这次Marshal.GetLastWin32Error()方法返回值0 - ERROR_SUCCESS“操作成功完成。”)。考虑到,也许代码中有一些“缺陷”可能会无意中跳过一个步骤或其他什么,我检查了PathInformation变量 -Text.StringBuilder保存结果的对象QueryDosDevice()- 在中断模式下,但是,可惜它也是空的。

注意:我也尝试使用目录而不是文件,但H:\0984\导致Marshal.GetLastWin32Error()返回值0whileH:\0984导致值6. 根据之前的测试,这一切都是有道理的,但它仍然会导致一个空PathInformation变量(失败)。

阅读所有关于 Interwebz 的文章,似乎很多人SUBST在 Windows 10 下遇到了 -ed 驱动器的各种问题,所以我现在想知道这是否这些意外结果的原因。有没有其他人遇到过这些问题,如果有,您是否能够在代码中解决它们?

如果它很重要(我想它肯定可能),这里有一些额外的细节:

  • 我正在使用 Visual Studio 2017 CE,我的项目正在 .NET Framework 4.7.2 下编译
  • 我要创建SUBST路径的物理驱动器是一对用 NTFS 格式化的 RAID 1 驱动器,并且有足够的空间 (178 GB)。

    注意:我还尝试SUBST为我的 OS 驱动器 ( ) 创建一个 -ed 路径以C:\进行测试,但这会得到与上述相同的结果。

如果我遗漏了任何内容,或者您​​需要进一步澄清,请在评论中告诉我,我会根据需要更新问题。

4

1 回答 1

1

双/三/四检查转换代码

看起来这一切都归结为我没有足够关注的一行代码(上面代码块中的第 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

正如我在问题中所说,我打算对这种方法进行一些“调整”,但这确实有效(现在)。

于 2019-10-15T15:13:52.117 回答