编辑-看起来我发现了问题
对不起。显然都是我的错。有关详细信息,请参阅下面我自己写的答案。
我正在为一个SUBST
-ed驱动器进行测试,该驱动器是一个更大方法的一部分,该方法测试字符串以确保它(至少,可能)是一个有效路径。经过一点验证后,"封装"方法将字符串转换为UNC或绝对路径,具体取决于传入的路径,然后返回到其他位置。
举几个例子:
-
对于映射到
\SERVERNAMEShare
的驱动器U:
,使用Windows注册表中的HKCUNetwork
键在本地计算机上查找网络驱动器映射,U:PublicFolderSomeFile.txt
变为\SERVERNAMESharePublicFolderSomeFile.txt
-
或者,
C:SomeFolderSomeFile.txt
保持不变,因为(在方法中确定)它是到本地物理驱动器的绝对路径。
到目前为止,大部分操作似乎都能正常工作,正如预期的那样,但我在Windows 10中遇到了由SUBST
命令创建的驱动器的问题(至少-我现在还没有在另一个操作系统下运行任何测试,因为我现在没有另一个可用的操作系统)。
老实说,我对SUBST
命令没有太多经验,也不经常使用它,所以,一开始,我甚至很难让驱动器在Windows中正确显示。在阅读了Microsoft社区页面上的讨论(Windows 10问题"Subst"命令不起作用)后,我终于能够"正确"设置驱动器(不要使用提升的命令提示符,BTW),但我用来测试SUBST
-ed驱动器的代码-从这个答案转换为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: 984CPI.TXT")
Dim SubstPath As String = String.Empty
Dim FullPath As String = String.Empty
If IsSubstPath(FullPath, SubstPath) Then
FullPath = SubstPath
End If
我的期望是IsSubstPath()
方法应该通过realPath
变量返回D:substtest 984CPI.TXT
。执行SUBST
(不带附加参数)正确显示了命令提示符中的映射(H:: => D:substtest
)。在调试时检查TestFile
对象表明它的Exists()
属性返回True
,所以文件系统显然知道它在那里。
在这一点上,每次我执行代码时,QueryDosDevice()
方法调用都会返回值0
,尽管随着我继续尝试使其工作,我从Marshal.GetLastWin32Error()
调用中得到了不同的结果。
我在机器上"正确"设置SUBST
-ed驱动器后的第一次尝试导致Marshal.GetLastWin32Error()
返回错误代码1008-error_NO_TOKEN("试图引用不存在的令牌")。
在链接的MS社区线程中的进一步阅读表明,在正常命令提示符下运行SUBST
两次-一次,在提升的命令提示符下再次应使驱动器对常规登录用户和任何提升的用户操作都可用。我在提升的命令提示符中重新运行了SUBST
,并使用与上面相同的测试代码重试。这一次,Marshal.GetLastWin32Error()
返回错误代码6-error_INVALID_HANDLE("句柄无效。">)。
考虑到这个特定的操作可能依赖于系统上实际存在的文件/路径(而不是.NETIO.FileInfo
或IO.DirectoryInfo
对象),我手动创建了特定的子文件夹和文件来表示我在代码(H: 984CPI.TXT
)中测试的内容,并再次尝试(再次使用与上述相同的代码):
QueryDosDevice()
再次未能正确解析真实路径(返回0
),但这一次Marshal.GetLastWin32Error()
方法返回的值为0-ERROR_SUCCESS("操作成功完成。">),我在break模式下检查了PathInformation
变量,即保存QueryDosDevice()
结果的Text.StringBuilder
对象,但遗憾的是,它也是空的。
注意:我也尝试使用目录而不是文件,但H: 984
导致Marshal.GetLastWin32Error()
返回值0
,而H: 984
导致值6
。根据之前的测试,这一切都是有道理的,但它仍然会导致PathInformation
变量为空(失败)
阅读Interwebz的所有内容,似乎许多人在Windows 10下遇到了SUBST
-ed驱动器的各种问题,所以我现在想知道这是否是导致这些意外结果的原因。其他人遇到过这些问题吗?如果遇到了,您是否能够在代码中解决这些问题?
如果它很重要(我想它肯定会),这里有一些额外的细节:
- 我使用的是Visual Studio 2017 CE,我的项目是在.NET Framework 4.7.2下编译的
- 我创建
SUBST
路径的物理驱动器是一对用NTFS格式化的RAID 1驱动器,有足够的空间(178 GB)
注意:我也尝试创建一个指向操作系统驱动器(C:
)的SUBST
ed路径进行测试,但结果与上面相同
如果我遗漏了任何内容,或者您需要进一步澄清,请在评论中告诉我,我会根据需要更新问题。
双重/三重/四重检查转换代码
看起来这一切都归结为我没有足够关注的一行代码(上面代码块中的第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 984CPI.TXT
。
然后,我删除了为测试创建的子文件夹/文件,然后重试。同样,它正确地将真实路径返回为D:substtest 984CPI.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
正如我在问题中所说,我打算对这种方法进行一些"调整",但这种确实有效(现在)。