登录时盘符出现了,随后却打开成空文件夹,或者显示红叉并提示网络资源不可用。权限通常没问题。真正发生的是:映射命令在 VPN 客户端已存在、但到文件服务器还没有路由的那个窗口里执行了,Windows 把这次失败记成了驱动器的状态。修法是不要再把映射当成登录时该发生的事,而是把它当成连接序列的最后一步。

为什么映射执行得太早

登录脚本在会话开始时启动。在登录过程中连接的 VPN 客户端会把自己的时间线叠加上去:进程可能已在运行,虚拟适配器可能已存在,而到办公子网的路由可能仍然缺失。这三种状态从脚本角度看几乎一样,只有一种能用。

有些环境的应对是每次登录都重复同一条失败的命令行,结果培养出一个学会点两次驱动器的用户,以及一个从未靠自己成功过的脚本。

驱动器本身让情况更糟。映射的驱动器是会话对象,一旦 Windows 把它标记为不可用,即使隧道后来恢复正常,图标也可能长期保持损坏。这就是为什么手动重新映射有效,而脚本看起来什么也没做。会话记住的是失败,而不是背后的意图。

“已连接”状态不是路径

托盘里的 VPN 图标只报告隧道是否升起,对是否存在通往某台特定服务器的路由、DNS 是否把该名称解析成隧道地址、服务器自己的防火墙是否放行,一概不说。

这是三种独立故障,可见结果却相同;以托盘图标为准,就把它们并成一个不可靠的信号。

点名你真正需要的那台服务器的就绪检查能把它们区分开,因为它会一直失败到具体路径可用为止,而不是等到某个接口自称已连接。

以服务器为准,而不是以客户端为准

挑一台只能经隧道到达的主机,先等它通,再做任何映射。下面的片段会连接已保存的 Windows VPN 条目,轮询文件服务器,然后映射驱动器。

@echo off
set server=fileserver.corp.example
rasdial "Office VPN" /disconnect
rasdial "Office VPN"
:wait
ping -n 1 -w 1000 %server% >nul || (
  timeout /t 5 /nobreak >nul
  goto wait
)
net use Z: \\%server%\share /persistent:no

ping 是就绪探测,不是权限测试。如果你的网络过滤了 ICMP,就改为探测你真正在用的服务:快速检查用 net view \\%server%,或者在 PowerShell 里对 445 端口做一次短 TCP 测试。两者都以真正重要的东西为准。

给尝试次数设上限。没有天花板的等待循环会把坏掉的隧道变成永远登录不完的会话,那比缺少盘符更糟。

让它可重复执行并感知会话

在用户自己的会话里运行。以 SYSTEM 运行的任务会把驱动器放进完全不同的会话,已登录用户永远看不到结果。这样你得到的是一个报告成功的脚本,和一个没有驱动器的桌面。

映射前先检查,并且只删除过期的条目。盘符未被占用时 net use Z: /delete 会返回错误,这个错误可以安全忽略,而不是放弃脚本其余部分的理由。保留 /persistent:no,以免失败的一次尝试日后变成永久的重连尝试。

也不要把密码写进文件。rasdial 可以复用为 VPN 条目保存的凭据,共享应当以已登录用户的身份认证。如果共享确实需要不同凭据,用 cmdkey 按用户注册一次,而不是把它们嵌进放在登录共享上的批处理文件。任何能读那个共享的人都能读到密码,这就把便利变成了事故。

重跑整个脚本应当是无害的,因为那正是会发生的事。用户会重连,笔记本会唤醒,同一段代码路径会以不同的初始状态再跑一遍。

记录足够的信息,证明是哪一半迟到了

把时间戳、就绪检查结果和映射的退出码写进用户配置文件下的文件。下一次收到驱动器缺失的报告时,那份日志能回答唯一重要的问题:是隧道来晚了,还是共享拒绝了?

没有它,每次复发都要从零排查,而通常的结论是一次什么也修不好的权限变更。

从冷启动测一次,从睡眠恢复再测一次。顺序最容易出问题的是唤醒:会话比网络先恢复,脚本看到的是一台已经登录了好几个小时的机器。

相关文章