由 SUBST (Windows 10) 创建的驱动器未报告 QueryDosDevice() 的完整路径
Drive created by SUBST (Windows 10) Not Reporting Full Path to QueryDosDevice()
编辑 - 看起来我找到了问题
对不起。显然这都是我的错。有关详细信息,请参阅下面我自己写的答案。
我正在对一个 SUBST
-ed 驱动器进行测试,该驱动器包含在一个更大的方法中,该方法测试一个字符串以确保它(至少,可能)是一个有效路径。经过一些验证后,"encapsulating" 方法将字符串转换为 UNC 或绝对路径,具体取决于传入的路径,到其他地方的 return。
举几个例子:
对于映射到 \SERVERNAME\Share
的驱动器 U:\
,使用 Windows 注册表中的 \HKCU\Network\
键在本地计算机上查找网络驱动器映射, U:\PublicFolder\SomeFile.txt
变为 \SERVERNAME\Share\PublicFolder\SomeFile.txt
或者,C:\SomeFolder\SomeFile.txt
保持不变,因为(在方法中确定)它是本地物理驱动器的绝对路径。
到目前为止,其中大部分似乎都运行良好并且符合预期,但是我遇到了一个关于 Windows 10 中的 SUBST
命令创建的驱动器的问题(至少 - 我目前还没有 运行 在另一个 OS 下进行任何测试,因为我现在没有另一个 ).
老实说,我对 SUBST
命令没有太多经验,也不经常使用它,所以,一开始,我什至无法正确显示驱动器在 Windows。阅读 Microsoft 社区页面上的讨论后(Windows 10 issue "Subst" command doesn't work), I was finally able to get the drive set up "properly" (don't use an elevated command prompt, BTW), but the code I'm using to test for a SUBST
-ed drive - converted to VB.NET from this answer - 仍未正确解析完整路径。
这是我正在使用的转换后的代码(我打算稍后再做一些 "tweaking",但这是当前状态):
<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:84\CPI.TXT")
Dim SubstPath As String = String.Empty
Dim FullPath As String = String.Empty
If IsSubstPath(FullPath, SubstPath) Then
FullPath = SubstPath
End If
我的期望是 IsSubstPath()
方法 应该 return D:\substtest84\CPI.TXT
通过 realPath
变量。执行 SUBST
(没有附加参数)在命令提示符 (H:\: => D:\substtest
) 中正确显示映射。在调试时检查 TestFile
对象显示它是 Exists()
属性 returns True
,所以文件系统显然知道它在那里。
此时,每次我执行代码时,QueryDosDevice()
方法调用 return 的值为 0
,尽管我从 Marshal.GetLastWin32Error()
中得到不同的结果在我继续尝试使它正常工作时打电话。
在我的机器上安装 SUBST
-ed 驱动器 "properly" 后,我的第一次尝试导致了 Marshal.GetLastWin32Error()
returning 错误代码 1008 - ERROR_NO_TOKEN (“试图引用不存在的令牌”)。
进一步阅读链接的 MS 社区线程表明 运行ning SUBST
twice - once in一个普通的命令提示符,以及 again 在提升的命令提示符中 - should 使驱动器可供常规登录用户以及任何提升的用户使用用户操作。我在提升的命令提示符中重新 运行 SUBST
并使用与上面相同的测试代码再次尝试。这一次,Marshal.GetLastWin32Error()
returned 错误代码 6 - ERROR_INVALID_HANDLE ("The handle is invalid.").
认为此特定操作 可能 取决于系统上实际存在的 file/path(与 .NET IO.FileInfo
或 IO.DirectoryInfo
对象),我手动创建了特定的子文件夹和文件来表示我在代码中测试的内容(H:84\CPI.TXT
)并再次尝试(再次使用与上面相同的代码):
再一次,QueryDosDevice()
未能正确解析真实路径(returned 0
),但这次 Marshal.GetLastWin32Error()
方法 return ed 的值为 0 - ERROR_SUCCESS ("The operation completed successfully.")。考虑到,也许代码中有一些 "flaw" 可能无意中跳过了一个步骤或其他东西,我检查了 PathInformation
变量 - Text.StringBuilder
对象保存 QueryDosDevice()
- 处于中断模式,但是,唉,它也是空的。
注意:我也尝试使用目录而不是文件,但是 H:84\
导致 Marshal.GetLastWin32Error()
return 值为 0
而H:84
的结果为 6
。根据之前的测试,这一切都是有道理的,但它仍然会导致一个空的 PathInformation
变量(失败)。
阅读 Interwebz 的所有内容,似乎很多人都遇到了 Windows 10 下 SUBST
-ed 驱动器的各种问题,所以我现在想知道是否 这就是 这些意外结果的原因。还有其他人遇到过这些问题吗?如果遇到过,您是否能够用代码解决它们?
万一它很重要(我想它肯定会),这里有一些额外的细节:
- 我正在使用 Visual Studio 2017 CE,我的项目正在 .NET Framework 4.7.2 下编译
- 我正在创建
SUBST
路径的物理驱动器是一对 RAID 1 驱动器,采用 NTFS 格式并且有大量 space (178 GB)。
注意:我也曾尝试为我的 OS 驱动器 (C:\
) 创建一个 SUBST
-ed 路径用于测试,但这得到了与上面相同的结果。
如果我遗漏了什么,或者您需要进一步说明,请在评论中告诉我,我会根据需要更新问题。
DOUBLE/TRIPLE/QUADRUPLE 检查转换后的代码
看起来这一切都归结为我没有足够关注的一行代码(上面代码块中的第 14 行):
DriveLetter = IO.Path.GetPathRoot(PathToTest).Replace("\", "")
这里的问题是 Path.GetPathRoot()
方法 returns 格式为 H:\
的盘符 - 只有一个 反斜杠 (\
),所以 .Replace()
方法没有找到任何要替换的东西,并传递了一个 "invalid" 参数值(H:\
而不是 H:
)。如果尾随反斜杠存在,QueryDosDevice()
方法显然会失败,因此我进行了快速代码编辑:
DriveLetter = IO.Path.GetPathRoot(PathToTest).Replace("\", "") ' <--Replace ANY/ALL backslashes
我再次用我的 SUBST H: D:\substtest
驱动器和现有 file/directory 结构进行了测试。这次,QueryDosDevice()
方法返回了 19
的值,并正确解析了真实路径为 D:\substtest84\CPI.TXT
.
然后,我删除了我为测试创建的 subfolder/file 并再次尝试。同样,它正确返回了真实路径 D:\substtest84\CPI.TXT
。所以,显然这一切都归结为我忽略了在我将代码从 C# 转换为 VB.NET 期间引入的 "typo"。我很抱歉。转换后代码的完整修正版本
<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
正如我在问题中所说,我打算对这种方法做一些 "tweaking",但是这种 确实 有效(现在)。
编辑 - 看起来我找到了问题
对不起。显然这都是我的错。有关详细信息,请参阅下面我自己写的答案。
我正在对一个 SUBST
-ed 驱动器进行测试,该驱动器包含在一个更大的方法中,该方法测试一个字符串以确保它(至少,可能)是一个有效路径。经过一些验证后,"encapsulating" 方法将字符串转换为 UNC 或绝对路径,具体取决于传入的路径,到其他地方的 return。
举几个例子:
对于映射到
\SERVERNAME\Share
的驱动器U:\
,使用 Windows 注册表中的\HKCU\Network\
键在本地计算机上查找网络驱动器映射,U:\PublicFolder\SomeFile.txt
变为\SERVERNAME\Share\PublicFolder\SomeFile.txt
或者,
C:\SomeFolder\SomeFile.txt
保持不变,因为(在方法中确定)它是本地物理驱动器的绝对路径。
到目前为止,其中大部分似乎都运行良好并且符合预期,但是我遇到了一个关于 Windows 10 中的 SUBST
命令创建的驱动器的问题(至少 - 我目前还没有 运行 在另一个 OS 下进行任何测试,因为我现在没有另一个 ).
老实说,我对 SUBST
命令没有太多经验,也不经常使用它,所以,一开始,我什至无法正确显示驱动器在 Windows。阅读 Microsoft 社区页面上的讨论后(Windows 10 issue "Subst" command doesn't work), I was finally able to get the drive set up "properly" (don't use an elevated command prompt, BTW), but the code I'm using to test for a SUBST
-ed drive - converted to VB.NET from this answer - 仍未正确解析完整路径。
这是我正在使用的转换后的代码(我打算稍后再做一些 "tweaking",但这是当前状态):
<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:84\CPI.TXT")
Dim SubstPath As String = String.Empty
Dim FullPath As String = String.Empty
If IsSubstPath(FullPath, SubstPath) Then
FullPath = SubstPath
End If
我的期望是 IsSubstPath()
方法 应该 return D:\substtest84\CPI.TXT
通过 realPath
变量。执行 SUBST
(没有附加参数)在命令提示符 (H:\: => D:\substtest
) 中正确显示映射。在调试时检查 TestFile
对象显示它是 Exists()
属性 returns True
,所以文件系统显然知道它在那里。
此时,每次我执行代码时,QueryDosDevice()
方法调用 return 的值为 0
,尽管我从 Marshal.GetLastWin32Error()
中得到不同的结果在我继续尝试使它正常工作时打电话。
在我的机器上安装 SUBST
-ed 驱动器 "properly" 后,我的第一次尝试导致了 Marshal.GetLastWin32Error()
returning 错误代码 1008 - ERROR_NO_TOKEN (“试图引用不存在的令牌”)。
进一步阅读链接的 MS 社区线程表明 运行ning SUBST
twice - once in一个普通的命令提示符,以及 again 在提升的命令提示符中 - should 使驱动器可供常规登录用户以及任何提升的用户使用用户操作。我在提升的命令提示符中重新 运行 SUBST
并使用与上面相同的测试代码再次尝试。这一次,Marshal.GetLastWin32Error()
returned 错误代码 6 - ERROR_INVALID_HANDLE ("The handle is invalid.").
认为此特定操作 可能 取决于系统上实际存在的 file/path(与 .NET IO.FileInfo
或 IO.DirectoryInfo
对象),我手动创建了特定的子文件夹和文件来表示我在代码中测试的内容(H:84\CPI.TXT
)并再次尝试(再次使用与上面相同的代码):
再一次,QueryDosDevice()
未能正确解析真实路径(returned 0
),但这次 Marshal.GetLastWin32Error()
方法 return ed 的值为 0 - ERROR_SUCCESS ("The operation completed successfully.")。考虑到,也许代码中有一些 "flaw" 可能无意中跳过了一个步骤或其他东西,我检查了 PathInformation
变量 - Text.StringBuilder
对象保存 QueryDosDevice()
- 处于中断模式,但是,唉,它也是空的。
注意:我也尝试使用目录而不是文件,但是 H:84\
导致 Marshal.GetLastWin32Error()
return 值为 0
而H:84
的结果为 6
。根据之前的测试,这一切都是有道理的,但它仍然会导致一个空的 PathInformation
变量(失败)。
阅读 Interwebz 的所有内容,似乎很多人都遇到了 Windows 10 下 SUBST
-ed 驱动器的各种问题,所以我现在想知道是否 这就是 这些意外结果的原因。还有其他人遇到过这些问题吗?如果遇到过,您是否能够用代码解决它们?
万一它很重要(我想它肯定会),这里有一些额外的细节:
- 我正在使用 Visual Studio 2017 CE,我的项目正在 .NET Framework 4.7.2 下编译
- 我正在创建
SUBST
路径的物理驱动器是一对 RAID 1 驱动器,采用 NTFS 格式并且有大量 space (178 GB)。
注意:我也曾尝试为我的 OS 驱动器 (C:\
) 创建一个SUBST
-ed 路径用于测试,但这得到了与上面相同的结果。
如果我遗漏了什么,或者您需要进一步说明,请在评论中告诉我,我会根据需要更新问题。
DOUBLE/TRIPLE/QUADRUPLE 检查转换后的代码
看起来这一切都归结为我没有足够关注的一行代码(上面代码块中的第 14 行):
DriveLetter = IO.Path.GetPathRoot(PathToTest).Replace("\", "")
这里的问题是 Path.GetPathRoot()
方法 returns 格式为 H:\
的盘符 - 只有一个 反斜杠 (\
),所以 .Replace()
方法没有找到任何要替换的东西,并传递了一个 "invalid" 参数值(H:\
而不是 H:
)。如果尾随反斜杠存在,QueryDosDevice()
方法显然会失败,因此我进行了快速代码编辑:
DriveLetter = IO.Path.GetPathRoot(PathToTest).Replace("\", "") ' <--Replace ANY/ALL backslashes
我再次用我的 SUBST H: D:\substtest
驱动器和现有 file/directory 结构进行了测试。这次,QueryDosDevice()
方法返回了 19
的值,并正确解析了真实路径为 D:\substtest84\CPI.TXT
.
然后,我删除了我为测试创建的 subfolder/file 并再次尝试。同样,它正确返回了真实路径 D:\substtest84\CPI.TXT
。所以,显然这一切都归结为我忽略了在我将代码从 C# 转换为 VB.NET 期间引入的 "typo"。我很抱歉。转换后代码的完整修正版本
<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
正如我在问题中所说,我打算对这种方法做一些 "tweaking",但是这种 确实 有效(现在)。