├── Auto.py ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── QProcess.py ├── README.md ├── ResourcePath.py ├── SettingsManage.py ├── UI.py ├── WSL2.py ├── WinCmd.py ├── dist └── wsl2.exe ├── images ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── logo.ico └── logo.png └── lib ├── logo.ico ├── wsl.vbs └── wsl2.ui /Auto.py: -------------------------------------------------------------------------------- 1 | # ================================================================== 2 | # 文 件 名: Auto.py 3 | # 概 要: 开机启动脚本 4 | # 作 者: IT小强 5 | # 创建时间: 2020/1/25 22:51 6 | # 修改时间: 7 | # copyright (c) 2016 - 2020 mail@xqitw.cn 8 | # ================================================================== 9 | from SettingsManage import SettingsManage 10 | from WinCmd import WinCmd 11 | 12 | if __name__ == "__main__": 13 | # 实例化配置管理类 14 | settings_manage = SettingsManage() 15 | settings = settings_manage.get() 16 | 17 | # 获取需要转发的端口 18 | ports = settings.get('ports', []) 19 | 20 | # 实例化windows命令处理类 21 | win_cmd = WinCmd() 22 | 23 | # 获取wsl2 IP 24 | wsl2_ip = win_cmd.get_wsl2_ip() 25 | 26 | # 端口转发处理 27 | port_str = '' 28 | for port in ports: 29 | port_str += (',' + port if port_str else port) 30 | # 删除端口 31 | win_cmd.port_del(wsl_port=port, exec_run=True) 32 | # 添加端口 33 | win_cmd.port_add(wsl_ip=wsl2_ip, wsl_port=port, exec_run=True) 34 | 35 | # 清除防已有火墙 36 | win_cmd.fire_wall_rule_del(wall_type=win_cmd.FireWallRuleIn, exec_run=True) 37 | win_cmd.fire_wall_rule_del(wall_type=win_cmd.FireWallRuleOut, exec_run=True) 38 | 39 | # 添加新的防火墙 40 | win_cmd.fire_wall_rule_add(wsl_port=port_str, wall_type=win_cmd.FireWallRuleIn, exec_run=True) 41 | win_cmd.fire_wall_rule_add(wsl_port=port_str, wall_type=win_cmd.FireWallRuleOut, exec_run=True) 42 | 43 | # 启动wsl2 44 | win_cmd.start_wsl(exec_run=True) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://mirrors.aliyun.com/pypi/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | pyside2 = "*" 10 | pyinstaller = "*" 11 | 12 | [requires] 13 | python_version = "3.7" 14 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "9c33208fa925ccb17724b2a8ae84c4a5e382f4f530971ea917c8e5aad79c91fc" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://mirrors.aliyun.com/pypi/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "altgraph": { 20 | "hashes": [ 21 | "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa", 22 | "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe" 23 | ], 24 | "version": "==0.17" 25 | }, 26 | "future": { 27 | "hashes": [ 28 | "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" 29 | ], 30 | "version": "==0.18.2" 31 | }, 32 | "pefile": { 33 | "hashes": [ 34 | "sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645" 35 | ], 36 | "version": "==2019.4.18" 37 | }, 38 | "pyinstaller": { 39 | "hashes": [ 40 | "sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7" 41 | ], 42 | "index": "pypi", 43 | "version": "==3.6" 44 | }, 45 | "pyside2": { 46 | "hashes": [ 47 | "sha256:11bba54a62bcd9d7879d3e74cc54c0054c8c6dcdf011ecee9b47c5229cbd7af9", 48 | "sha256:578b727a5a254cfd509ea2f1fa31779f217a2a1d765c770727662dac950d60eb", 49 | "sha256:72feeb655958791383085bcb3154f6b3e193c1d66b6aa771c4244a6cafd62b7e", 50 | "sha256:77474e11c0bb3efa2d7e8506fe0f36049585ba911b8242e070b5f8978e5ba6f7", 51 | "sha256:c9f59e8c49a9a3b0cca04d8468becd8a562eb9ad0ac1d4d9a8622d2dfa3ce4c9", 52 | "sha256:ce43f98333443242cd3fe976d72fcb3acf6bb7fa40dd5949e59947a501d5dd72" 53 | ], 54 | "index": "pypi", 55 | "version": "==5.14.0" 56 | }, 57 | "pywin32-ctypes": { 58 | "hashes": [ 59 | "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", 60 | "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98" 61 | ], 62 | "version": "==0.2.0" 63 | }, 64 | "shiboken2": { 65 | "hashes": [ 66 | "sha256:101fc366798f88cbff13586907f3755bebbd9304e66626fb6b0f6b28e0c9a5d2", 67 | "sha256:47c7c2652f578b37588e8b6daff3a852b3c88ae0f83be13886e4a74859e81763", 68 | "sha256:4f138656fc755399776062c89492d61f887d4e5fe7c78cded73917e80afcf2f5", 69 | "sha256:676fef81e4d95b02816fde7359c1f2604efa3edd34b05ab0da42c57c7555f7d7", 70 | "sha256:a88267c7cc17501effc6b1b36d85e7ab28173af939b975ea42716ed12493b478", 71 | "sha256:ab3ba84784c9641a11a21a8c64d494fa8b57be25e081e77f76747d543699f03c" 72 | ], 73 | "version": "==5.14.0" 74 | } 75 | }, 76 | "develop": {} 77 | } 78 | -------------------------------------------------------------------------------- /QProcess.py: -------------------------------------------------------------------------------- 1 | # ================================================================== 2 | # 文 件 名: QProcess.py 3 | # 概 要: QProcess 封装 4 | # 作 者: IT小强 5 | # 创建时间: 2020/1/22 21:37 6 | # 修改时间: 7 | # copyright (c) 2016 - 2020 mail@xqitw.cn 8 | # ================================================================== 9 | from PySide2.QtCore import QProcess as PySide2QProcess 10 | 11 | 12 | class QProcess: 13 | """ 14 | QProcess 封装 15 | """ 16 | 17 | @staticmethod 18 | def get_out_put(cmd): 19 | """ 20 | 执行命令,并返回结果 21 | :param cmd: 要执行的命令 22 | :return: 23 | """ 24 | process = PySide2QProcess() 25 | process.start(cmd) 26 | process.waitForFinished() 27 | out_put = process.readAllStandardOutput() 28 | out_put = out_put.data() 29 | try: 30 | out_put = out_put.decode('utf-8') 31 | except UnicodeDecodeError: 32 | out_put = out_put.decode('gbk') 33 | finally: 34 | return out_put 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WSL2端口自动转发工具 2 | 3 | ### 打包 4 | 5 | ``` 6 | pyinstaller wsl2.py -F --noconsole --hidden-import PySide2.QtXml --icon="lib/logo.ico" --add-data="lib\logo.ico;lib" --add-data="lib\wsl2.ui;lib" --add-data="lib\wsl.vbs;lib" 7 | ``` 8 | 9 | ### 界面 10 | 11 | + 查询WSL2当前IP 12 | 13 | ![查询WSL2当前IP](./images/4.png) 14 | 15 | + 查询端口 16 | 17 | ![查询端口](./images/2.png) 18 | 19 | + 添加端口 20 | 21 | ![添加端口](./images/1.png) 22 | 23 | + 删除端口 24 | 25 | ![删除端口](./images/3.png) 26 | 27 | + 保存配置信息及启动脚本 28 | 29 | ![删除端口](./images/5.png) -------------------------------------------------------------------------------- /ResourcePath.py: -------------------------------------------------------------------------------- 1 | # ================================================================== 2 | # 文 件 名: ResourcePath.py 3 | # 概 要: 资源路径处理类 4 | # 作 者: IT小强 5 | # 创建时间: 2020/1/22 14:51 6 | # 修改时间: 7 | # copyright (c) 2016 - 2020 mail@xqitw.cn 8 | # ================================================================== 9 | import sys 10 | from os import environ, mkdir 11 | from os.path import expanduser, expandvars, join 12 | from pathlib import Path 13 | 14 | 15 | class ResourcePath: 16 | """ 17 | 资源路径处理类 18 | """ 19 | 20 | @staticmethod 21 | def get_user_home_path(): 22 | """ 23 | 获取用户主目录 24 | :return: 25 | """ 26 | user_home_path = '' 27 | try: 28 | user_home_path = environ['HOME'] 29 | except KeyError: 30 | user_home_path = expanduser('~') 31 | if not user_home_path: 32 | user_home_path = expandvars('$HOME') 33 | finally: 34 | return user_home_path 35 | 36 | @staticmethod 37 | def resource_path(relative_path): 38 | """ 39 | 获取 资源绝对路径 40 | :param relative_path: 41 | :return: 42 | """ 43 | if hasattr(sys, '_MEIPASS'): 44 | # PyInstaller会创建临时文件夹temp 45 | # 并把路径存储在_MEIPASS中 46 | base_path = sys._MEIPASS 47 | else: 48 | base_path = '.' 49 | return join(base_path, relative_path) 50 | 51 | @classmethod 52 | def create_settings_path(cls, path_name='.wsl2_auto_port_forward'): 53 | """ 54 | 创建并返回配置目录 55 | :param path_name: 56 | :return: 57 | """ 58 | user_home_path = cls.get_user_home_path() 59 | settings_path = join(user_home_path, path_name) 60 | if not Path(settings_path).is_dir(): 61 | mkdir(settings_path) 62 | return settings_path 63 | -------------------------------------------------------------------------------- /SettingsManage.py: -------------------------------------------------------------------------------- 1 | # ================================================================== 2 | # 文 件 名: SettingsManage.py 3 | # 概 要: 配置操作类,获取、保存配置 4 | # 作 者: IT小强 5 | # 创建时间: 2019/12/24 18:14 6 | # 修改时间: 7 | # copyright (c) 2016 - 2019 mail@xqitw.cn 8 | # ================================================================== 9 | from os.path import join, isfile 10 | from json import loads, dumps 11 | 12 | from ResourcePath import ResourcePath 13 | 14 | 15 | class SettingsManage: 16 | """ 17 | 配置操作类,获取、保存配置 18 | """ 19 | 20 | # 默认配置数据 21 | __settings = { 22 | 'auto_start_wsl': False, 23 | 'fire_wall_open': False, 24 | 'fire_wall_close': False, 25 | 'ports': [], 26 | 'wsl_bat_content': r"""@echo off 27 | wsl.exe -u root""" 28 | } 29 | 30 | def __init__(self): 31 | """ 32 | 初始化 33 | """ 34 | self.settingsDir = ResourcePath.create_settings_path() 35 | self.settingsFile = join(self.settingsDir, 'settings.json') 36 | self.__get_file_content() 37 | 38 | def set(self, name, value): 39 | """ 40 | 设置配置 41 | @param name: 配置名称 42 | @param value: 配置值 43 | """ 44 | self.__settings[name] = value 45 | self.save_file_content(self.settingsFile, dumps(self.__settings)) 46 | 47 | def get(self, name=None, default_value=None): 48 | """ 49 | 获取配置 50 | @param name: 配置名称 None 会返回全部配置 51 | @param default_value: 不存在时返回的默认值 52 | @return: 返回配置值 53 | """ 54 | if not name: 55 | return self.__settings 56 | return self.__settings.get(name, default_value) 57 | 58 | def __get_file_content(self): 59 | """ 60 | 读取json文件的内容并转为字典 61 | """ 62 | 63 | # 如果文件不存在则创建文件 64 | if not isfile(self.settingsFile): 65 | f = open(self.settingsFile, 'w', encoding='utf8') 66 | f.write(dumps(self.__settings)) 67 | f.close() 68 | 69 | # 读取文件内容 70 | f = open(self.settingsFile, 'r', encoding='utf8') 71 | content = f.read() 72 | f.close() 73 | 74 | # 转为json 75 | self.__settings.update(loads(content)) 76 | 77 | @staticmethod 78 | def save_file_content(file, content): 79 | """ 80 | 保存配置到json文件 81 | """ 82 | f = open(file, 'w', encoding='utf8') 83 | f.write(content) 84 | f.close() 85 | -------------------------------------------------------------------------------- /UI.py: -------------------------------------------------------------------------------- 1 | # ================================================================== 2 | # 文 件 名: UI.py 3 | # 概 要: WSL2 端口自动转发 4 | # 作 者: IT小强 5 | # 创建时间: 2019/12/22 9:51 6 | # 修改时间: 7 | # copyright (c) 2016 - 2019 mail@xqitw.cn 8 | # ================================================================== 9 | from os.path import isfile 10 | from shutil import copyfile 11 | 12 | from PySide2.QtGui import QIcon 13 | from PySide2.QtUiTools import QUiLoader 14 | from PySide2.QtWidgets import QMessageBox, QAction, QSystemTrayIcon, QMenu 15 | from PySide2.QtCore import QProcess 16 | 17 | from ResourcePath import ResourcePath 18 | from SettingsManage import SettingsManage 19 | from WinCmd import WinCmd 20 | 21 | 22 | class UI: 23 | """ 24 | WSL2 端口自动转发 25 | """ 26 | 27 | def __init__(self, qt_application=None): 28 | self.qt_application = qt_application 29 | # 实例化配置管理类 30 | self.settings_manage = SettingsManage() 31 | self.__setting = self.settings_manage.get() 32 | 33 | # 实例化windows命令处理类 34 | self.wsl2 = WinCmd() 35 | 36 | # 初始化启动脚本 37 | if not isfile(self.wsl2.WSL_VBS_PATH): 38 | copyfile(self.wsl2.WSL_VBS_PATH_TEMP, self.wsl2.WSL_VBS_PATH) 39 | if not isfile(self.wsl2.WSL_BAT_PATH): 40 | self.settings_manage.save_file_content( 41 | self.wsl2.WSL_BAT_PATH, 42 | self.__setting.get('wsl_bat_content', '') 43 | ) 44 | # 加载UI文件 45 | self.ui = QUiLoader().load(ResourcePath.resource_path('lib/wsl2.ui')) 46 | 47 | # 设置界面图标 48 | app_icon = QIcon(ResourcePath.resource_path("lib/logo.ico")) 49 | self.ui.setWindowIcon(app_icon) 50 | 51 | # 设置选中状态 52 | self.ui.auto_start_wsl.setChecked(self.__setting.get('auto_start_wsl', False)) 53 | self.ui.fire_wall_open.setChecked(self.__setting.get('fire_wall_open', False)) 54 | self.ui.fire_wall_close.setChecked(self.__setting.get('fire_wall_close', False)) 55 | 56 | # 设置文本框的值 57 | self.ui.port_text.appendPlainText('\n'.join(self.__setting.get('ports', []))) 58 | self.ui.bat_text.appendPlainText(self.wsl2.get_bat_script()) 59 | 60 | # 按钮监听 61 | self.ui.get_wsl2_ip.clicked.connect(self.__get_wsl2_ip) 62 | self.ui.port_add.clicked.connect(self.__port_add) 63 | self.ui.port_del.clicked.connect(self.__port_del) 64 | self.ui.port_info.clicked.connect(self.__port_info) 65 | self.ui.wsl_l_v.clicked.connect(self.__wsl_l_v) 66 | self.ui.port_reset.clicked.connect(self.__port_reset) 67 | self.ui.end_wsl.clicked.connect(self.__end_wsl) 68 | self.ui.start_wsl.clicked.connect(self.__start_wsl) 69 | self.ui.start_wsl_all.clicked.connect(self.start_wsl_all) 70 | self.ui.save_settings.clicked.connect(self.__save_settings) 71 | self.ui.save_settings_ports.clicked.connect(self.__save_settings) 72 | 73 | # 设置系统托盘图标的菜单 74 | tp_icon = QIcon(ResourcePath.resource_path("lib/logo.ico")) 75 | self.tp = QSystemTrayIcon(self.ui) 76 | self.tp.setIcon(tp_icon) 77 | 78 | self.ui_hide = QAction(icon=tp_icon, text='隐藏(Hide)', triggered=self.ui.hide) 79 | self.ui_show = QAction(icon=tp_icon, text='显示(Show)', triggered=self.ui.show) 80 | self.ui_exit = QAction(icon=tp_icon, text='退出(Exit)', triggered=self.__quit_app) 81 | self.tp_menu = QMenu() 82 | self.tp_menu.addAction(self.ui_hide) 83 | self.tp_menu.addAction(self.ui_show) 84 | self.tp_menu.addAction(self.ui_exit) 85 | self.tp.setContextMenu(self.tp_menu) 86 | self.tp.activated.connect(self.__tp_connect_action) 87 | self.tp.show() 88 | self.tp.showMessage( 89 | 'WSL2AutoPortForward', 90 | 'WSL2端口自动转发工具已启动', 91 | QSystemTrayIcon.MessageIcon.Information 92 | ) 93 | 94 | def __tp_connect_action(self, activation_reason): 95 | """ 96 | 监听托盘图标点击 97 | :param activation_reason: 点击类型 98 | :return: 99 | """ 100 | if activation_reason == QSystemTrayIcon.ActivationReason.Trigger: 101 | # 左单击 102 | if self.ui.isHidden(): 103 | self.ui.show() 104 | else: 105 | self.ui.hide() 106 | elif activation_reason == QSystemTrayIcon.ActivationReason.Context: 107 | # 右单击 108 | self.tp_menu.show() 109 | elif activation_reason == QSystemTrayIcon.ActivationReason.DoubleClick: 110 | # 双击 111 | self.ui.show() 112 | 113 | def __quit_app(self): 114 | """ 115 | 退出APP 116 | :return: 117 | """ 118 | re = QMessageBox.question( 119 | self.ui, 120 | "提示", 121 | "退出系统", 122 | QMessageBox.Yes | QMessageBox.No, 123 | QMessageBox.No 124 | ) 125 | if re == QMessageBox.Yes: 126 | # 关闭窗体程序 127 | self.qt_application.quit() 128 | # 在应用程序全部关闭后,TrayIcon其实还不会自动消失, 129 | # 直到你的鼠标移动到上面去后,才会消失, 130 | # 这是个问题,(如同你terminate一些带TrayIcon的应用程序时出现的状况), 131 | # 这种问题的解决我是通过在程序退出前将其setVisible(False)来完成的。 132 | self.tp.setVisible(False) 133 | 134 | def __wsl_l_v(self): 135 | """ 136 | 获取wsl信息 137 | :return: 138 | """ 139 | wsl_l_v_txt = ' ' + self.wsl2.wsl_l_v(exec_run=True).replace('\x00', '').strip() 140 | if not wsl_l_v_txt: 141 | # 未查询到wsl信息提示 142 | wsl_l_v_txt = '未查询到wsl信息!' 143 | QMessageBox.information(self.ui, '系统提示', wsl_l_v_txt) 144 | self.ui.wsl_l_v_text.setPlainText(wsl_l_v_txt) 145 | 146 | def __get_wsl2_ip(self): 147 | wsl2_ip_info = self.wsl2.get_wsl2_ip() 148 | if not wsl2_ip_info: 149 | # 未查询到端口转发信息提示 150 | QMessageBox.information(self.ui, '系统提示', '未查询到IP信息!') 151 | else: 152 | wsl2_ip_info = 'WSL2当前IP为:' + wsl2_ip_info 153 | self.ui.wsl_l_v_text.setPlainText(wsl2_ip_info) 154 | 155 | def __port_add(self): 156 | wsl2_ip_info = self.wsl2.get_wsl2_ip() 157 | if not wsl2_ip_info: 158 | # 未查询到端口转发信息提示 159 | QMessageBox.information(self.ui, '系统提示', '未查询到IP信息!') 160 | else: 161 | self.ui.result_text.clear() 162 | ports = self.ui.port_text.toPlainText() 163 | port_str = '' 164 | for port in ports.splitlines(): 165 | if not port.strip(): 166 | continue 167 | self.__port_add_one(port, wsl2_ip_info) 168 | port_str += (',' + port if port_str else port) 169 | self.__fire_wall_rule_add(port_str) 170 | self.ui.result_text.appendPlainText('Succeed!') 171 | 172 | def __port_del(self, del_port=True, del_fire=True): 173 | self.ui.result_text.clear() 174 | ports = self.ui.port_text.toPlainText() 175 | if del_port: 176 | for port in ports.splitlines(): 177 | if not port.strip(): 178 | continue 179 | self.__port_del_one(port) 180 | if del_fire: 181 | self.__fire_wall_rule_del() 182 | self.ui.result_text.appendPlainText('Succeed!') 183 | 184 | def __port_reset(self): 185 | port_info = self.wsl2.port_info() 186 | self.ui.result_text.clear() 187 | for port in port_info: 188 | self.__port_del_one(port['port']) 189 | self.__fire_wall_rule_del() 190 | self.ui.result_text.appendPlainText('Succeed!') 191 | 192 | def __port_info(self): 193 | """ 194 | 获取端口转发信息 195 | :return: 196 | """ 197 | info_str = self.wsl2.port_info(True).strip() 198 | if not info_str: 199 | # 未查询到端口转发信息提示 200 | info_str = '未查询到端口转发信息!' 201 | QMessageBox.information(self.ui, '系统提示', info_str) 202 | self.ui.result_text.setPlainText(info_str) 203 | 204 | def __wsl2_auto_port_forward(self): 205 | """ 206 | 一键自动转发 207 | @return: 208 | """ 209 | 210 | self.__port_del(del_port=False, del_fire=True) 211 | self.__port_add() 212 | 213 | def __end_wsl(self): 214 | """ 215 | 停止wsl 216 | :return: 217 | """ 218 | self.start_qt_process(self.wsl2.end_wsl(exec_run=False)) 219 | info_str = 'wsl 已全部停止' 220 | QMessageBox.information(self.ui, '系统提示', info_str) 221 | 222 | def __start_wsl(self): 223 | """ 224 | 启动wsl 225 | :return: 226 | """ 227 | self.start_qt_process(self.wsl2.start_wsl(exec_run=False)) 228 | 229 | def start_wsl_all(self): 230 | """ 231 | 启动wsl并转发端口 232 | :return: 233 | """ 234 | self.__start_wsl() 235 | self.__wsl2_auto_port_forward() 236 | 237 | def __save_settings(self): 238 | """ 239 | 保存全部配置 240 | :return: 241 | """ 242 | # 保存脚本 243 | self.__save_bat_script() 244 | 245 | # 保存配置信息 246 | self.settings_manage.set('fire_wall_open', self.ui.fire_wall_open.isChecked()) 247 | self.settings_manage.set('fire_wall_close', self.ui.fire_wall_close.isChecked()) 248 | self.settings_manage.set('auto_start_wsl', self.ui.auto_start_wsl.isChecked()) 249 | self.settings_manage.set('ports', self.ui.port_text.toPlainText().splitlines()) 250 | 251 | # 保存成功提示 252 | QMessageBox.information(self.ui, '系统提示', '配置保存成功!') 253 | 254 | def __save_bat_script(self): 255 | """ 256 | 保存启动脚本 257 | :return: 258 | """ 259 | content = self.ui.bat_text.toPlainText() 260 | self.settings_manage.set('wsl_bat_content', content) 261 | self.wsl2.save_bat_script(content) 262 | 263 | def __fire_wall_rule_add(self, port): 264 | """ 265 | 添加防火墙 266 | :param port: 端口号,多个端口逗号隔开 267 | :return: 268 | """ 269 | if self.ui.fire_wall_open.isChecked(): 270 | self.start_qt_process( 271 | self.wsl2.fire_wall_rule_add( 272 | wsl_port=port, 273 | wall_type=self.wsl2.FireWallRuleIn, 274 | exec_run=False 275 | ) 276 | ) 277 | self.ui.result_text.appendPlainText('>>> 添加防火墙:【' + self.wsl2.FireWallRuleIn + '】...') 278 | self.start_qt_process( 279 | self.wsl2.fire_wall_rule_add( 280 | wsl_port=port, 281 | wall_type=self.wsl2.FireWallRuleOut, 282 | exec_run=False 283 | ) 284 | ) 285 | self.ui.result_text.appendPlainText('>>> 添加防火墙:【' + self.wsl2.FireWallRuleOut + '】...') 286 | 287 | def __fire_wall_rule_del(self): 288 | """ 289 | 删除防火墙 290 | :return: 291 | """ 292 | if self.ui.fire_wall_close.isChecked(): 293 | self.start_qt_process( 294 | self.wsl2.fire_wall_rule_del( 295 | wall_type=self.wsl2.FireWallRuleIn, 296 | exec_run=False 297 | ) 298 | ) 299 | self.ui.result_text.appendPlainText('>>> 删除防火墙:【' + self.wsl2.FireWallRuleIn + '】...') 300 | self.start_qt_process( 301 | self.wsl2.fire_wall_rule_del( 302 | wall_type=self.wsl2.FireWallRuleOut, 303 | exec_run=False 304 | ) 305 | ) 306 | self.ui.result_text.appendPlainText('>>> 删除防火墙:【' + self.wsl2.FireWallRuleOut + '】...') 307 | 308 | def __port_add_one(self, port, wsl2_ip_info): 309 | """ 310 | 添加单个端口 311 | :param port: 端口号 312 | :param wsl2_ip_info: 转发的IP 313 | :return: 314 | """ 315 | self.start_qt_process(self.wsl2.port_add(wsl_ip=wsl2_ip_info, wsl_port=port, exec_run=False)) 316 | self.ui.result_text.appendPlainText('>>> 添加端口:【' + port + '】...') 317 | 318 | def __port_del_one(self, port): 319 | """ 320 | 删除单个端口 321 | :param port: 端口号 322 | :return: 323 | """ 324 | self.start_qt_process(self.wsl2.port_del(wsl_port=port, exec_run=False)) 325 | self.ui.result_text.appendPlainText('>>> 删除端口:【' + port + '】...') 326 | 327 | def start_qt_process(self, cmd): 328 | """ 329 | 启动子进程执行耗时命令 330 | :param cmd: 331 | :return: 332 | """ 333 | process = QProcess(self.ui) 334 | process.start(cmd) 335 | result = process.waitForStarted() 336 | return result 337 | -------------------------------------------------------------------------------- /WSL2.py: -------------------------------------------------------------------------------- 1 | # ================================================================== 2 | # 文 件 名: WSL2.py 3 | # 概 要: 启动文件 4 | # 作 者: IT小强 5 | # 创建时间: 2020/1/22 15:52 6 | # 修改时间: 7 | # copyright (c) 2016 - 2020 mail@xqitw.cn 8 | # ================================================================== 9 | from sys import exit, argv 10 | 11 | from PySide2.QtWidgets import QApplication 12 | 13 | from UI import UI 14 | 15 | if __name__ == "__main__": 16 | app = QApplication([]) 17 | ui = UI(app) 18 | if (len(argv) == 2 and argv[1] == 'start') or ui.ui.auto_start_wsl.isChecked(): 19 | ui.start_wsl_all() 20 | else: 21 | ui.ui.show() 22 | exit(app.exec_()) 23 | -------------------------------------------------------------------------------- /WinCmd.py: -------------------------------------------------------------------------------- 1 | # ================================================================== 2 | # 文 件 名: WinCmd.py 3 | # 概 要: WSL2 端口转发处理类 4 | # 作 者: IT小强 5 | # 创建时间: 2019/12/22 9:48 6 | # 修改时间: 7 | # copyright (c) 2016 - 2019 mail@xqitw.cn 8 | # ================================================================== 9 | from os.path import join 10 | from re import search 11 | 12 | from QProcess import QProcess 13 | from SettingsManage import SettingsManage 14 | from ResourcePath import ResourcePath 15 | 16 | 17 | class WinCmd: 18 | """ 19 | WSL2 端口转发处理类 20 | """ 21 | BASH_EXE = r'bash.exe' 22 | WSCRIPT_EXE = r'wscript.exe' 23 | WSL_EXE = r'wsl.exe' 24 | WSL_VBS_PATH_TEMP = ResourcePath.resource_path('lib/wsl.vbs') 25 | WSL_VBS_PATH = join(ResourcePath.create_settings_path(), 'wsl.vbs') 26 | WSL_BAT_PATH = join(ResourcePath.create_settings_path(), 'wsl.bat') 27 | POWER_SHELL = r'PowerShell.exe' 28 | FireWallRuleOut = 'Outbound' 29 | FireWallRuleIn = 'Inbound' 30 | FireWallRuleDisplayName = 'WSL 2 Firewall Unlock' 31 | 32 | @classmethod 33 | def wsl_l_v(cls, exec_run=True): 34 | """ 35 | 查询wsl信息 36 | :return: 37 | """ 38 | cmd = cls.WSL_EXE 39 | cmd += ' -l -v' 40 | return cls.read_cmd(cmd, exec_run) 41 | 42 | @classmethod 43 | def get_wsl2_ip(cls, exec_run=True): 44 | """ 45 | 该方法用于获取WSL2的IP 46 | :param exec_run: 47 | :return: str 48 | """ 49 | pattern = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" 50 | cmd = cls.BASH_EXE + ' -c "ifconfig eth0 | grep \'inet \'"' 51 | if not exec_run: 52 | return cmd 53 | try: 54 | result = cls.read_cmd(cmd) 55 | wsl_ip = search(pattern, result).group(0) 56 | except AttributeError: 57 | wsl_ip = '' 58 | return wsl_ip 59 | 60 | @classmethod 61 | def start_wsl(cls, exec_run=True): 62 | """ 63 | 启动wsl子系统 64 | :return: 65 | """ 66 | cmd = cls.WSCRIPT_EXE 67 | cmd += ' ' + cls.WSL_VBS_PATH + ' wsl' 68 | return cls.read_cmd(cmd, exec_run) 69 | 70 | @classmethod 71 | def end_wsl(cls, exec_run=True): 72 | """ 73 | 停止wsl子系统 74 | :return: 75 | """ 76 | cmd = cls.WSL_EXE 77 | cmd += ' --shutdown' 78 | return cls.read_cmd(cmd, exec_run) 79 | 80 | @classmethod 81 | def port_add(cls, wsl_ip, wsl_port, addr='0.0.0.0', exec_run=True): 82 | """ 83 | 添加端口转发 84 | :param addr: 监听地址 85 | :param wsl_ip: 待转发的IP地址 86 | :param wsl_port: 待添加端口号 87 | :param exec_run: 88 | :return: 89 | """ 90 | cmd = cls.POWER_SHELL 91 | cmd += ' netsh interface portproxy add v4tov4' 92 | cmd += ' listenport=' + wsl_port 93 | cmd += ' listenaddress=' + addr 94 | cmd += ' connectport=' + wsl_port 95 | cmd += ' connectaddress=' + wsl_ip 96 | return cls.read_cmd(cmd, exec_run) 97 | 98 | @classmethod 99 | def port_del(cls, wsl_port, addr='0.0.0.0', exec_run=True): 100 | """ 101 | 删除端口转发 102 | :param exec_run: 103 | :param addr: 监听地址 104 | :param wsl_port: 待删除的端口号 105 | :return: 106 | """ 107 | cmd = cls.POWER_SHELL 108 | cmd += ' netsh interface portproxy delete v4tov4 listenport=' + wsl_port + ' listenaddress=' + addr 109 | return cls.read_cmd(cmd, exec_run) 110 | 111 | @classmethod 112 | def port_reset(cls, exec_run=True): 113 | """ 114 | 清除所有的端口转发 115 | :param exec_run 116 | :return: 117 | """ 118 | cmd = cls.POWER_SHELL 119 | cmd += ' netsh interface portproxy reset' 120 | return cls.read_cmd(cmd, exec_run) 121 | 122 | @classmethod 123 | def port_info(cls, is_str=False, exec_run=True): 124 | """ 125 | 查看端口转发情况 126 | :param exec_run: 127 | :param is_str: 128 | :return: 129 | """ 130 | cmd = cls.POWER_SHELL 131 | cmd += ' netsh interface portproxy show all' 132 | info_str = cls.read_cmd(cmd, exec_run) 133 | if is_str: 134 | return info_str 135 | return cls.port_info_to_list(info_str) 136 | 137 | @classmethod 138 | def fire_wall_rule_add(cls, wsl_port, wall_type, exec_run=True): 139 | """ 140 | 添加防火墙规则 141 | :param exec_run: 142 | :param wsl_port: str 端口号 143 | :param wall_type: str 防火墙类型 Outbound | Inbound 144 | :return: 145 | """ 146 | "New-NetFireWallRule -DisplayName 'test1008611' -Direction 'Inbound' -Action Allow -Protocol TCP" 147 | wall_name = cls.FireWallRuleDisplayName + '-' + wall_type 148 | cmd = cls.POWER_SHELL 149 | cmd += " New-NetFireWallRule -DisplayName '" + wall_name + "'" 150 | cmd += " -Direction '" + wall_type + "'" 151 | cmd += " -LocalPort " + wsl_port + " -Action Allow -Protocol TCP" 152 | return cls.read_cmd(cmd, exec_run) 153 | 154 | @classmethod 155 | def fire_wall_rule_del(cls, wall_type, exec_run=True): 156 | """ 157 | 删除防火墙规则 158 | :param exec_run: 159 | :param wall_type: str 防火墙类型 Outbound | Inbound 160 | :return: 161 | """ 162 | wall_name = cls.FireWallRuleDisplayName + '-' + wall_type 163 | cmd = cls.POWER_SHELL 164 | cmd += " Remove-NetFireWallRule -DisplayName '" + wall_name + "'" 165 | return cls.read_cmd(cmd, exec_run) 166 | 167 | @classmethod 168 | def save_bat_script(cls, content): 169 | SettingsManage.save_file_content(cls.WSL_BAT_PATH, content) 170 | 171 | @classmethod 172 | def get_bat_script(cls): 173 | f = open(cls.WSL_BAT_PATH, 'r', encoding='utf8') 174 | content = f.read() 175 | f.close() 176 | return content 177 | 178 | @staticmethod 179 | def read_cmd(cmd, exec_run=True): 180 | """ 181 | 执行命令,并返回输出结果 182 | :param exec_run: 183 | :param cmd: 184 | :return: 185 | """ 186 | if not exec_run: 187 | return cmd 188 | result = QProcess.get_out_put(cmd) 189 | return result 190 | 191 | @staticmethod 192 | def port_info_to_list(info_str): 193 | """ 194 | 格式化话端口信息 195 | :param info_str: 196 | :return: 以字典形式返回 197 | """ 198 | info = [] 199 | pattern = r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(.*?)(\d+)(.*?)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" 200 | for line in info_str.splitlines(): 201 | line_info = search(pattern, line) 202 | if line_info: 203 | info.append({ 204 | 'addr': line_info.group(1), 205 | 'port': line_info.group(3), 206 | 'ip': line_info.group(5), 207 | }) 208 | return info 209 | -------------------------------------------------------------------------------- /dist/wsl2.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/dist/wsl2.exe -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/images/1.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/images/2.png -------------------------------------------------------------------------------- /images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/images/3.png -------------------------------------------------------------------------------- /images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/images/4.png -------------------------------------------------------------------------------- /images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/images/5.png -------------------------------------------------------------------------------- /images/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/images/logo.ico -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/images/logo.png -------------------------------------------------------------------------------- /lib/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itxq/wslm-gui/179d64b1c6547f7e0ff77e33095ac56e15c9f607/lib/logo.ico -------------------------------------------------------------------------------- /lib/wsl.vbs: -------------------------------------------------------------------------------- 1 | If WScript.Arguments.Count <= 0 Then 2 | WScript.Quit 3 | End If 4 | 5 | bat = Left(WScript.ScriptFullName, InStrRev(WScript.ScriptFullName, "\")) & WScript.Arguments(0) & ".bat" 6 | arg = "" 7 | 8 | If WScript.Arguments.Count > 1 Then 9 | arg = WScript.Arguments(1) 10 | End If 11 | 12 | CreateObject("WScript.Shell").Run """" & bat & """ """ & arg & """", 0, False -------------------------------------------------------------------------------- /lib/wsl2.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | form 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 790 13 | 594 14 | 15 | 16 | 17 | 18 | 733 19 | 542 20 | 21 | 22 | 23 | WSL2端口自动转发器 24 | 25 | 26 | true 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 0 42 | 43 | 44 | 45 | 46 | 47 | 48 | 端口转发 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 0 60 | 0 61 | 62 | 63 | 64 | PointingHandCursor 65 | 66 | 67 | height: 38px; 68 | line-height: 38px; 69 | padding: 0 18px; 70 | background-color: #1E9FFF; 71 | color: #fff; 72 | white-space: nowrap; 73 | text-align: center; 74 | font-size: 14px; 75 | border: none; 76 | border-radius: 2px; 77 | cursor: pointer; 78 | border-radius: 100px; 79 | 80 | 81 | 端口转发查询 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 0 90 | 0 91 | 92 | 93 | 94 | PointingHandCursor 95 | 96 | 97 | height: 38px; 98 | line-height: 38px; 99 | padding: 0 18px; 100 | background-color: #009688; 101 | color: #fff; 102 | white-space: nowrap; 103 | text-align: center; 104 | font-size: 14px; 105 | border: none; 106 | border-radius: 2px; 107 | cursor: pointer; 108 | border-radius: 100px; 109 | 110 | 111 | 添加端口 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 0 120 | 0 121 | 122 | 123 | 124 | PointingHandCursor 125 | 126 | 127 | height: 38px; 128 | line-height: 38px; 129 | padding: 0 18px; 130 | background-color: #FFB800; 131 | color: #fff; 132 | white-space: nowrap; 133 | text-align: center; 134 | font-size: 14px; 135 | border: none; 136 | border-radius: 2px; 137 | cursor: pointer; 138 | border-radius: 100px; 139 | 140 | 141 | 删除端口 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 0 150 | 0 151 | 152 | 153 | 154 | PointingHandCursor 155 | 156 | 157 | height: 38px; 158 | line-height: 38px; 159 | padding: 0 18px; 160 | background-color: #FF5722; 161 | color: #fff; 162 | white-space: nowrap; 163 | text-align: center; 164 | font-size: 14px; 165 | border: none; 166 | border-radius: 2px; 167 | cursor: pointer; 168 | border-radius: 100px; 169 | 170 | 171 | 清除所有端口 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 0 182 | 0 183 | 184 | 185 | 186 | 此处输入需要转发的端口,一行一个;添加、删除端口均需要以管理员身份运行本程序 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 0 195 | 0 196 | 197 | 198 | 199 | true 200 | 201 | 202 | 添加【端口转发查询】按钮即可查看当前端口转发详情 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 0 211 | 0 212 | 213 | 214 | 215 | PointingHandCursor 216 | 217 | 218 | height: 38px; 219 | alternate-background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(0, 0, 0, 255), stop:0.05 rgba(14, 8, 73, 255), stop:0.36 rgba(28, 17, 145, 255), stop:0.6 rgba(126, 14, 81, 255), stop:0.75 rgba(234, 11, 11, 255), stop:0.79 rgba(244, 70, 5, 255), stop:0.86 rgba(255, 136, 0, 255), stop:0.935 rgba(239, 236, 55, 255)); 220 | selection-background-color: qradialgradient(spread:repeat, cx:0.5, cy:0.5, radius:0.077, fx:0.5, fy:0.5, stop:0 rgba(0, 169, 255, 147), stop:0.497326 rgba(0, 0, 0, 147), stop:1 rgba(0, 169, 255, 147)); 221 | line-height: 38px; 222 | padding: 0 18px; 223 | background-color: #1E9FFF; 224 | color: #fff; 225 | white-space: nowrap; 226 | text-align: center; 227 | font-size: 14px; 228 | border: none; 229 | border-radius: 2px; 230 | cursor: pointer; 231 | border-radius: 100px; 232 | 233 | 234 | 一键启动WSL并转发端口 235 | 236 | 237 | false 238 | 239 | 240 | false 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 0 249 | 0 250 | 251 | 252 | 253 | PointingHandCursor 254 | 255 | 256 | height: 38px; 257 | alternate-background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(0, 0, 0, 255), stop:0.05 rgba(14, 8, 73, 255), stop:0.36 rgba(28, 17, 145, 255), stop:0.6 rgba(126, 14, 81, 255), stop:0.75 rgba(234, 11, 11, 255), stop:0.79 rgba(244, 70, 5, 255), stop:0.86 rgba(255, 136, 0, 255), stop:0.935 rgba(239, 236, 55, 255)); 258 | selection-background-color: qradialgradient(spread:repeat, cx:0.5, cy:0.5, radius:0.077, fx:0.5, fy:0.5, stop:0 rgba(0, 169, 255, 147), stop:0.497326 rgba(0, 0, 0, 147), stop:1 rgba(0, 169, 255, 147)); 259 | line-height: 38px; 260 | padding: 0 18px; 261 | background-color: #009688; 262 | color: #fff; 263 | white-space: nowrap; 264 | text-align: center; 265 | font-size: 14px; 266 | border: none; 267 | border-radius: 2px; 268 | cursor: pointer; 269 | border-radius: 100px; 270 | 271 | 272 | 保存端口转发配置信息 273 | 274 | 275 | false 276 | 277 | 278 | false 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | WSL管理 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 0 300 | 0 301 | 302 | 303 | 304 | PointingHandCursor 305 | 306 | 307 | font: 14px Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif; 308 | height: 38px; 309 | line-height: 38px; 310 | padding: 0 18px; 311 | background-color: #009688; 312 | color: #fff; 313 | white-space: nowrap; 314 | text-align: center; 315 | font-size: 14px; 316 | border: none; 317 | border-radius: 2px; 318 | cursor: pointer; 319 | border-radius: 100px; 320 | 321 | 322 | 启动WSL 323 | 324 | 325 | false 326 | 327 | 328 | false 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 0 337 | 0 338 | 339 | 340 | 341 | PointingHandCursor 342 | 343 | 344 | height: 38px; 345 | line-height: 38px; 346 | padding: 0 18px; 347 | background-color: #FF5722; 348 | color: #fff; 349 | white-space: nowrap; 350 | text-align: center; 351 | font-size: 14px; 352 | border: none; 353 | border-radius: 2px; 354 | cursor: pointer; 355 | border-radius: 100px; 356 | 357 | 358 | 停止WSL 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 0 367 | 0 368 | 369 | 370 | 371 | PointingHandCursor 372 | 373 | 374 | height: 38px; 375 | alternate-background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(0, 0, 0, 255), stop:0.05 rgba(14, 8, 73, 255), stop:0.36 rgba(28, 17, 145, 255), stop:0.6 rgba(126, 14, 81, 255), stop:0.75 rgba(234, 11, 11, 255), stop:0.79 rgba(244, 70, 5, 255), stop:0.86 rgba(255, 136, 0, 255), stop:0.935 rgba(239, 236, 55, 255)); 376 | selection-background-color: qradialgradient(spread:repeat, cx:0.5, cy:0.5, radius:0.077, fx:0.5, fy:0.5, stop:0 rgba(0, 169, 255, 147), stop:0.497326 rgba(0, 0, 0, 147), stop:1 rgba(0, 169, 255, 147)); 377 | line-height: 38px; 378 | padding: 0 18px; 379 | background-color: #1E9FFF; 380 | color: #fff; 381 | white-space: nowrap; 382 | text-align: center; 383 | font-size: 14px; 384 | border: none; 385 | border-radius: 2px; 386 | cursor: pointer; 387 | border-radius: 100px; 388 | 389 | 390 | 查询WSL状态信息 391 | 392 | 393 | false 394 | 395 | 396 | false 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 0 405 | 0 406 | 407 | 408 | 409 | PointingHandCursor 410 | 411 | 412 | height: 38px; 413 | line-height: 38px; 414 | padding: 0 18px; 415 | background-color: #1E9FFF; 416 | color: #fff; 417 | white-space: nowrap; 418 | text-align: center; 419 | font-size: 14px; 420 | border: none; 421 | border-radius: 2px; 422 | cursor: pointer; 423 | border-radius: 100px; 424 | 425 | 426 | WSL IP 查询 427 | 428 | 429 | false 430 | 431 | 432 | false 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 0 443 | 0 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | ArrowCursor 458 | 459 | 460 | <html><head/><body><p>配置管理</p></body></html> 461 | 462 | 463 | <html><head/><body><p>配置管理</p></body></html> 464 | 465 | 466 | 配置管理 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | PointingHandCursor 475 | 476 | 477 | color: #009688; 478 | font: 75 12pt "Agency FB"; 479 | 480 | 481 | 添加端口时是否同时开放防火墙 482 | 483 | 484 | true 485 | 486 | 487 | 488 | 489 | 490 | 491 | PointingHandCursor 492 | 493 | 494 | color: #FF5722; 495 | font: 75 12pt "Agency FB"; 496 | 497 | 498 | 删除端口时是否同时关闭防火墙 499 | 500 | 501 | true 502 | 503 | 504 | false 505 | 506 | 507 | 508 | 509 | 510 | 511 | PointingHandCursor 512 | 513 | 514 | color:#FFB800; 515 | font: 75 12pt "Agency FB"; 516 | 517 | 518 | 启动程序时,是否自动启动WSL并转发端口 519 | 520 | 521 | true 522 | 523 | 524 | false 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 0 533 | 0 534 | 535 | 536 | 537 | 启动脚本内容 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 0 546 | 0 547 | 548 | 549 | 550 | 此处填写启动脚本内容 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 0 559 | 0 560 | 561 | 562 | 563 | PointingHandCursor 564 | 565 | 566 | height: 38px; 567 | alternate-background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(0, 0, 0, 255), stop:0.05 rgba(14, 8, 73, 255), stop:0.36 rgba(28, 17, 145, 255), stop:0.6 rgba(126, 14, 81, 255), stop:0.75 rgba(234, 11, 11, 255), stop:0.79 rgba(244, 70, 5, 255), stop:0.86 rgba(255, 136, 0, 255), stop:0.935 rgba(239, 236, 55, 255)); 568 | selection-background-color: qradialgradient(spread:repeat, cx:0.5, cy:0.5, radius:0.077, fx:0.5, fy:0.5, stop:0 rgba(0, 169, 255, 147), stop:0.497326 rgba(0, 0, 0, 147), stop:1 rgba(0, 169, 255, 147)); 569 | line-height: 38px; 570 | padding: 0 18px; 571 | background-color: #009688; 572 | color: #fff; 573 | white-space: nowrap; 574 | text-align: center; 575 | font-size: 14px; 576 | border: none; 577 | border-radius: 2px; 578 | cursor: pointer; 579 | border-radius: 100px; 580 | 581 | 582 | 保存配置信息及启动脚本 583 | 584 | 585 | false 586 | 587 | 588 | false 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | --------------------------------------------------------------------------------