使用任务计划程序,根据网络环境自动开关代理软件

住处的路由器经过一番折腾,自带访问国际互联网的环境,而其他地方的网络当然不会有这个东西了,这就需要在电脑上启动代理。每次搬电脑都要开关代理很麻烦,于是心生一计,使用 Windows 自带的十分完善的任务计划功能,来实现根据网络环境的代理软件自动开关功能。理论上这同时适用于 Clash、ShadowsocksR 等代理软件。

在 Windows 11 build 22000.282 测试通过,理论上近几个大版本的 Windows 10 也同样适用。

自动打开代理软件

这一部分讲述了在连接到某一特定 SSID 的 WiFi 网络时如何启动代理,以 Shadowsocks 为例。

使用 Windows 搜索 “计划任务” 或者 “taskschd” 可以找到任务计划程序。

点击右侧 “创建任务”,随便起个名字。

切换到 “触发器” 选项卡,新建 “发生事件时” 触发器,“开始任务”选择 “发生事件时”,“日志” 选择 “Microsoft-Windows-NetworkProfile/Operational”,“源” 选择 “NetworkProfile”,“事件 ID” 填写“10000”,其他不变,如图。

触发器设定

切换到 “操作” 选项卡,新建操作,在 “程序或脚本” 栏里浏览并选择你的 Shadowsocks 主程序,其他不变,点击确定。

再切换到 “条件” 选项卡,选中 “只有在以下网络连接可用时才启动”,选择你希望打开 Shadowsocks 的网络环境。某些时候,网络 SSID 并不等于环境名称,这时最好查看“找到真正的网络名称” 部分以确保选择正确。这样相当于监听每一次网络环境改变事件,再检查当前网络环境,如果符合条件则启动。同时还需要关闭”只有在计算机使用交流电源时才启动此任务“,毕竟能拿着电脑到处跑的人应该会经常用电池吧。

点击确定,即可完成设置。

找到真正的网络名称

有些时候,你可能会遇到和我一样的情况:明明想找一个名为 xxx 的 WiFi,但在 “只有在以下网络连接可用时才启动” 下拉框中却发现了形如 “xxx 1”“xxx 2” 之类的网络。如果没有这种情况,那很好,你不用看这一小节了;否则,需要打开 “适配器选项” 窗口来确定真正的网络名称。

适配器选项

这里吐槽一下小米互传:就不能固定一个 SSID 吗?

首先要连接目标网络,然后根据操作系统不同,有不同的步骤。

对于 Windows 10,在任务栏网络图标右键,选择 “打开网络和 Internet 设置”,然后点击 “更改适配器选项”;而对 Windows 11,要打开设置 - 网络 & Internet - 高级网络设置 - 更多网络适配器选项。会弹出如下图的窗口。

更多适配器选项

这里再骂一次微软,Windows 10 适配器选项页面的高分屏优化都比 11 好,起码字体没毛边。

这里根据你的情况寻找你的网络名称,比如我的是 “PDCN_5G 7”。

自动关闭代理软件

仍然以 Shadowsocks 为例。这个实现起来相对麻烦些,因为任务计划没有设定关闭程序的操作。好在我们有 PowerShell,所以我们可以使用 PoweShell 脚本来结束进程。

新建一个文本文档,将扩展名改为. ps1,文件名任取。然后,在里面粘贴以下内容(假设你的 Shadowsocks 设定的是 PAC 模式):

powershell
1
2
3
4
Write-Output "Stopping Shadowsocks"
Get-Process | Where-Object {$_.ProcessName.Contains("Shadowsocks")} | Stop-Process
Write-Output "Trying to delete proxy registry"
Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -Name "AutoConfigURL"

然后保存。

按上面的步骤创建一个新任务。“触发器” 与上面相同。

在 “操作”-“程序或脚本” 的“添加参数”栏填入刚刚新建的 PowerShell 脚本路径,“程序或脚本”填入“pwsh.exe”(如果你安装了新版 PowerShell Core)或“powershell.exe”(通用,但是是老版本),其他不变,确定。如下图:

编辑操作

编辑执行脚本操作

“条件”-“只有在以下网络连接可用时” 改为你希望自动关闭 Shadowsocks 的网络环境。

点击确定,即可完成设置。不想了解细节的读者,不需要读本节中下面的部分。

我们都知道,要关闭 Shadowsocks,当然要结束 Shadowsocks.exe。它运行的时候,还会同时启动 Privoxy 透明代理,但结束主程序的时候它也会一起关闭,所以没有必要考虑这个。

如果你使用的是 PAC 代理模式而不是全局代理,有时系统设置 - 网络 & Internet - 代理 - 使用设置脚本的开关并不会关闭,部分影响网络浏览。这个开关由注册表键值 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\AutoConfigURL 所管理,如果存在则代表这个开关打开,并将 PAC 代理指向它的值,键值字符串代表设置的代理服务器地址。那么,我们增加一条命令将这个键删掉,就可以保证彻底关闭了。

进阶:自动检测网络环境,开关代理

这部分以 v2rayN 为例。如果你经常在多个网络环境之间穿梭,每个网络条件不同(甚至同一个网络路由器端的梯子都有可能挂掉),上面的那个办法就不是那么好用了。既然我们写了 PowerShell 脚本,不妨一步到位,直接检测网络环境。

首先你需要新建一个在网络环境改变的时候就调用的任务计划,而不需要特定网络连接条件,可以参照本文的 “自动打开代理” 部分。

然后新建一个 PowerShell 脚本,少废话,上代码。

powershell
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name ProxyEnable -Value 0 #首先禁用系统代理,防止对检测造成影响
$url = "http://clients3.google.com/generate_204"
$req = [system.Net.WebRequest]::Create($url)
$proxyActive = Get-Process v2rayN -ErrorAction SilentlyContinue #检测 v2rayN 是否运行,后面那个 param 是在它没有运行的情况下继续执行脚本
try {
    $res = $req.GetResponse() #获取 HTTP 状态码
} 
catch [System.Net.WebException] {
    $res = $_.Exception.Response
}
if ($res.StatusCode -eq"NoContent") {
    #能够直连国际互联网
    Write-Output "Google Is Connected, Stopping Proxy"
    Get-Process | Where-Object {$_.ProcessName.Contains("v2ray")} | Stop-Process
}
else {
    #不能直连国际互联网
    if ($null -eq $proxyActive) {
        # 代理未运行,打开代理
        Write-Output "Cannot connect to Google, Starting Proxy"
        &"Your\Path\To\v2rayN.exe"
    }
    else {
        # 代理软件已运行,启用系统代理即可
        Write-Output "Re-enabling Proxy"
        Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name ProxyEnable -Value 1
    }
}    

将里面的 Your\Path\To\v2rayN.exe 替换为 v2rayN.exe 路径,然后让那个任务计划在满足条件时执行它。

v2rayN 采用的是系统代理方案,把 v2rayN.exe 结束掉,会自动结束 xray.exe,但要手动控制系统代理。控制它的注册表项是 HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyEnable,类型为 DWORD,1 为开启,0 为关闭。

显而易见,http://clients3.google.com/generate_204 是在墙外的网站。如果访问正常,这个页面会返回一个 HTTP 204 NoContent,否则 res 会是 $null。

参考文献

windows - How to launch a command on network connection/disconnection? - Super User

利用 Windows 计划任务定时关闭程序 - 蜂鸣器 (fmqcloud.com)

一键打开关闭 ie 代理 - 问题求助 - 小众软件官方论坛 (appinn.net)

How to Get, Edit, Create and Delete Registry Keys with PowerShell (netwrix.com)

如何在 powershell 中让运行的程序停止 _百度知道 (baidu.com)

设定 Windows 计划任务定期执行 PowerShell 脚本【图文】_StanlyCheng_51CTO 博客

如何在 PowerShell 中获取数字 HTTP 状态码? - 问答 - 云 + 社区 - 腾讯云 (tencent.com)

Check if a process is running (microsoft.com)