diff --git a/Images/logo.png b/Images/logo.png new file mode 100644 index 0000000..89654d2 Binary files /dev/null and b/Images/logo.png differ diff --git a/Images/logo.jpg b/Images/logo_old.jpg similarity index 100% rename from Images/logo.jpg rename to Images/logo_old.jpg diff --git a/Images/screenshot.png b/Images/screenshot.png index d2441d7..3899405 100644 Binary files a/Images/screenshot.png and b/Images/screenshot.png differ diff --git a/Images/screenshot.v0.4.png b/Images/screenshot.v0.4.png new file mode 100644 index 0000000..d2441d7 Binary files /dev/null and b/Images/screenshot.v0.4.png differ diff --git a/Images/screenshot.v0.5.png b/Images/screenshot.v0.5.png new file mode 100644 index 0000000..572c262 Binary files /dev/null and b/Images/screenshot.v0.5.png differ diff --git a/Images/wiki/qq/1.png b/Images/wiki/qq/1.png new file mode 100644 index 0000000..17e355c Binary files /dev/null and b/Images/wiki/qq/1.png differ diff --git a/Images/wiki/qq/10.png b/Images/wiki/qq/10.png new file mode 100644 index 0000000..74d7f47 Binary files /dev/null and b/Images/wiki/qq/10.png differ diff --git a/Images/wiki/qq/11.png b/Images/wiki/qq/11.png new file mode 100644 index 0000000..e19b011 Binary files /dev/null and b/Images/wiki/qq/11.png differ diff --git a/Images/wiki/qq/12.png b/Images/wiki/qq/12.png new file mode 100644 index 0000000..bd2a73c Binary files /dev/null and b/Images/wiki/qq/12.png differ diff --git a/Images/wiki/qq/13.png b/Images/wiki/qq/13.png new file mode 100644 index 0000000..b982a96 Binary files /dev/null and b/Images/wiki/qq/13.png differ diff --git a/Images/wiki/qq/2.png b/Images/wiki/qq/2.png new file mode 100644 index 0000000..a488c07 Binary files /dev/null and b/Images/wiki/qq/2.png differ diff --git a/Images/wiki/qq/3.png b/Images/wiki/qq/3.png new file mode 100644 index 0000000..b17050a Binary files /dev/null and b/Images/wiki/qq/3.png differ diff --git a/Images/wiki/qq/4.png b/Images/wiki/qq/4.png new file mode 100644 index 0000000..93516c6 Binary files /dev/null and b/Images/wiki/qq/4.png differ diff --git a/Images/wiki/qq/5.png b/Images/wiki/qq/5.png new file mode 100644 index 0000000..b283f5f Binary files /dev/null and b/Images/wiki/qq/5.png differ diff --git a/Images/wiki/qq/6.png b/Images/wiki/qq/6.png new file mode 100644 index 0000000..ce52267 Binary files /dev/null and b/Images/wiki/qq/6.png differ diff --git a/Images/wiki/qq/7.png b/Images/wiki/qq/7.png new file mode 100644 index 0000000..c1a0ea9 Binary files /dev/null and b/Images/wiki/qq/7.png differ diff --git a/Images/wiki/qq/8.png b/Images/wiki/qq/8.png new file mode 100644 index 0000000..8e26d80 Binary files /dev/null and b/Images/wiki/qq/8.png differ diff --git a/Images/wiki/qq/9.png b/Images/wiki/qq/9.png new file mode 100644 index 0000000..80a38a4 Binary files /dev/null and b/Images/wiki/qq/9.png differ diff --git a/Images/wiki/wechat/10_mutex.png b/Images/wiki/wechat/10_mutex.png new file mode 100644 index 0000000..e34c7c8 Binary files /dev/null and b/Images/wiki/wechat/10_mutex.png differ diff --git a/Images/wiki/wechat/11_push_ebp_to_ret.png b/Images/wiki/wechat/11_push_ebp_to_ret.png new file mode 100644 index 0000000..73a2f68 Binary files /dev/null and b/Images/wiki/wechat/11_push_ebp_to_ret.png differ diff --git a/Images/wiki/wechat/12_push_ebp_to_ret.png b/Images/wiki/wechat/12_push_ebp_to_ret.png new file mode 100644 index 0000000..a235493 Binary files /dev/null and b/Images/wiki/wechat/12_push_ebp_to_ret.png differ diff --git a/Images/wiki/wechat/13_push_ebp_to_ret.png b/Images/wiki/wechat/13_push_ebp_to_ret.png new file mode 100644 index 0000000..8c86803 Binary files /dev/null and b/Images/wiki/wechat/13_push_ebp_to_ret.png differ diff --git a/Images/wiki/wechat/14_patch_dll.png b/Images/wiki/wechat/14_patch_dll.png new file mode 100644 index 0000000..ce8771b Binary files /dev/null and b/Images/wiki/wechat/14_patch_dll.png differ diff --git a/Images/wiki/wechat/1_start.png b/Images/wiki/wechat/1_start.png new file mode 100644 index 0000000..44b5c5a Binary files /dev/null and b/Images/wiki/wechat/1_start.png differ diff --git a/Images/wiki/wechat/2_click_attach.png b/Images/wiki/wechat/2_click_attach.png new file mode 100644 index 0000000..71dc8b8 Binary files /dev/null and b/Images/wiki/wechat/2_click_attach.png differ diff --git a/Images/wiki/wechat/3_attach_wechat_exe.png b/Images/wiki/wechat/3_attach_wechat_exe.png new file mode 100644 index 0000000..df7eff8 Binary files /dev/null and b/Images/wiki/wechat/3_attach_wechat_exe.png differ diff --git a/Images/wiki/wechat/4_wechatwin_dll.png b/Images/wiki/wechat/4_wechatwin_dll.png new file mode 100644 index 0000000..c329a75 Binary files /dev/null and b/Images/wiki/wechat/4_wechatwin_dll.png differ diff --git a/Images/wiki/wechat/5_search_string.png b/Images/wiki/wechat/5_search_string.png new file mode 100644 index 0000000..0714b02 Binary files /dev/null and b/Images/wiki/wechat/5_search_string.png differ diff --git a/Images/wiki/wechat/6_revokemsg_1.png b/Images/wiki/wechat/6_revokemsg_1.png new file mode 100644 index 0000000..883afe4 Binary files /dev/null and b/Images/wiki/wechat/6_revokemsg_1.png differ diff --git a/Images/wiki/wechat/7_je_to_jmp.png b/Images/wiki/wechat/7_je_to_jmp.png new file mode 100644 index 0000000..c54b78f Binary files /dev/null and b/Images/wiki/wechat/7_je_to_jmp.png differ diff --git a/Images/wiki/wechat/8_je_to_jmp.png b/Images/wiki/wechat/8_je_to_jmp.png new file mode 100644 index 0000000..41d3b4c Binary files /dev/null and b/Images/wiki/wechat/8_je_to_jmp.png differ diff --git a/Images/wiki/wechat/9_je_to_jmp.png b/Images/wiki/wechat/9_je_to_jmp.png new file mode 100644 index 0000000..d9caf2e Binary files /dev/null and b/Images/wiki/wechat/9_je_to_jmp.png differ diff --git a/README.md b/README.md index 8935108..547bd7e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@

- +

- + @@ -17,43 +17,49 @@

-# :eyes:微信防撤回补丁 -适用于 Windows 下PC版微信的防撤回补丁。 +# 👀微信/QQ/TIM防撤回补丁 +适用于 Windows 下 PC 版微信/QQ/TIM的防撤回补丁。 -支持最新测试版本:2.7.0.70 - -支持最新稳定版本:2.6.8.68 - -支持历史版本:2.6.6.28、2.6.7.32、2.6.7.40、2.6.7.57、2.6.8.37、2.6.8.51、2.6.8.52、2.6.8.65、2.7.0.65 - 下载地址: -[:zap:点我下载最新版本](https://github.com/huiyadanli/RevokeMsgPatcher/releases/download/0.4/RevokeMsgPatcher.v0.4.zip) +**[⚡️点我下载最新版本](https://github.com/huiyadanli/RevokeMsgPatcher/releases/download/0.6/RevokeMsgPatcher.v0.6.zip)** | +[☁备用下载-蓝奏云](https://www.lanzous.com/i75gffi) | +[☁备用下载-百度云](https://pan.baidu.com/s/1M8rNYrhS85pFjpAASZqKlw) 提取码:coco -## :camera:截图 +相关文档: +**[✔支持哪些版本](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81)** | +[❓常见问题](https://github.com/huiyadanli/RevokeMsgPatcher/wiki#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98) | +[📖查看完整文档](https://github.com/huiyadanli/RevokeMsgPatcher/wiki) + +原理与方法: +[📗微信](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/%E5%BE%AE%E4%BF%A1%E9%98%B2%E6%92%A4%E5%9B%9E%E4%B8%8E%E5%A4%9A%E5%BC%80%E6%95%99%E7%A8%8B) | +[📕QQ](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/QQ%E6%88%96TIM%E9%98%B2%E6%92%A4%E5%9B%9E%E6%95%99%E7%A8%8B) | +[📘TIM](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/QQ%E6%88%96TIM%E9%98%B2%E6%92%A4%E5%9B%9E%E6%95%99%E7%A8%8B) + +## 📷截图 ![Screenshot](https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/Images/screenshot.png) ## 🔨使用方法 -**请以管理员身份运行本程序,由于修改了微信的 `WeChatWin.dll` 文件,杀毒软件可能会弹出警告,放行即可。** +1. 首先,你的系统需要满足以下条件: -启动时会自动从注册表中获取微信的安装路径,如果没找到,需要手动选择微信路径。 + * Windows 7 或更高版本,**不支持XP**。 + * [.NET Framework 4.5](https://www.microsoft.com/zh-cn/download/details.aspx?id=30653) 或更高版本。**低于此版本在打开程序时可能无反应,或者直接报错**。 -环境要求: +2. 使用本程序前,先关闭微信/QQ/TIM。 -* Windows 7 或更高版本。 -* [.NET Framework 4.5](https://www.microsoft.com/zh-cn/download/details.aspx?id=30653) 或更高版本。 +3. **以管理员身份运行本程序**,等待右下角获取最新的补丁信息。 +4. 选择微信/QQ/TIM的安装路径。如果你用的安装版的微信/QQ/TIM,正常情况下本程序会自动从注册表中获取安装路径,绿色版需要手动选择路径。 +5. 点击防撤回。界面可能会出现一段时间的无响应,请耐心等待。**由于修改了微信的 WeChatWin.dll 文件、QQ/TIM的 IM.dll 文件,杀毒软件可能会弹出警告,放行即可。** -如果你想得到本软件最新版本的推送,请点击 Watch -> Release only -![Screenshot](https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/Images/watch_release.png) +## ❤️Thanks -## :heart:Thanks [wechat_anti_revoke](https://github.com/36huo/wechat_anti_revoke) -## 📃License +## 📄License GPLv3 ![](https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/Images/give_a_star.png) diff --git a/RevokeMsgPatcher.Assistant/App.config b/RevokeMsgPatcher.Assistant/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/RevokeMsgPatcher.Assistant/Data/0.6/patch.json b/RevokeMsgPatcher.Assistant/Data/0.6/patch.json new file mode 100644 index 0000000..b812451 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/Data/0.6/patch.json @@ -0,0 +1 @@ +{"Apps":{"Wechat":{"Name":"Wechat","FileTargetInfos":{"WeChatWin.dll":{"Name":"WeChatWin.dll","RelativePath":"WeChatWin.dll","Memo":null}},"FileModifyInfos":{"WeChatWin.dll":[{"Name":"WeChatWin.dll","Version":"2.7.1.88","SHA1Before":"034059bad50dd793140952391bfa7936133e69b4","SHA1After":"dd6d80c30ca9e0ea9f7d2f1add498fc9aa4bc7a0","Changes":[{"Position":2499465,"Content":[235]},{"Position":7952304,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.85","SHA1Before":"de0df4e138b72460450f66c029e33f4510f5e2df","SHA1After":"fbd35720aaff3cdcfd3ff18ea503dc06450e5c99","Changes":[{"Position":2499465,"Content":[235]},{"Position":7952000,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.82","SHA1Before":"20e111a18872bf6c7148a897c11da26c1ec95520","SHA1After":"1e0741d325ca6b1cd2402b829a3d13a2524af617","Changes":[{"Position":2499465,"Content":[235]},{"Position":7951952,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.74","SHA1Before":"b1eaf7edc074a88be5d0f89230436cc2084d24d2","SHA1After":"eb3d74ccd87a09059a005f4972861898fc3de463","Changes":[{"Position":2499465,"Content":[235]},{"Position":7951696,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.65","SHA1Before":"8346b97d264725da924d240c6eb77df3e693385e","SHA1After":"42bab2c9c79ef4f2088c00ea6d817973e14a5e6e","Changes":[{"Position":2495545,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.59","SHA1Before":"df954d403edaca89cd5394927a325a0023e93281","SHA1After":"6aa22460c91bb5c5e2f0ec1af99b8a5f6d4318c0","Changes":[{"Position":2496073,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.43","SHA1Before":"39cd9e09e1a3eac09e6808749bff525c9e3216ce","SHA1After":"7b829f1ff0217e346a80f9510fdd7634ddd49445","Changes":[{"Position":2494169,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.0.70","SHA1Before":"3b0601864aff3c1d792f812ad1ca05f02aa761e3","SHA1After":"1e8734d32b0a8c12758e30f99c77f729991fb071","Changes":[{"Position":2475657,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.0.65","SHA1Before":"063c2e05a0df1bdb8987c2d978d93499bd2052ba","SHA1After":"5ed4c09a4f18643b967f063a824d7e65d0567f8a","Changes":[{"Position":2475449,"Content":[117]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.68","SHA1Before":"2e9417f4276b12fe32ca7b4fee49272a4a2af334","SHA1After":"699602ee3cbb9ae5714f6e6ebc658c875a6c66e6","Changes":[{"Position":2454006,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.65","SHA1Before":"e01f6855a96c12c30808960903ed199a33e4952c","SHA1After":"d9120569cfd0433aebea107d7b90805cbbac7518","Changes":[{"Position":2454265,"Content":[117]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.52","SHA1Before":"88131302f664df6a657c9ca49d152da536fe5729","SHA1After":"8d1454b73831644181e962c1fa0ea4e2da4124a3","Changes":[{"Position":2453049,"Content":[117]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.51","SHA1Before":"d0a5517b1292a751501b00b4b1f0702db2d9fc30","SHA1After":"53e7b1525d49bf2c3250a8131ff0ba2510779b78","Changes":[{"Position":2452614,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.37","SHA1Before":"7e01f8b04a158a4a50bc5a6e67c2fb8b02233170","SHA1After":"a1895004415fe9bcd7e690bd6e482b833b515599","Changes":[{"Position":2452614,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.7.57","SHA1Before":"80a91aaf941bcb1c24a7d672838ac73e9ebb2e40","SHA1After":"a0d3f9a45a835f97aef7fe0872387d8cfb5c25a4","Changes":[{"Position":2433413,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.7.40","SHA1Before":"04bd0cb28df6630b518f42a3f9c2caa4a9359fbc","SHA1After":"13c91cf1d4609959771fd137b9a86a5ca365e1b6","Changes":[{"Position":2432934,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.7.32","SHA1Before":"a02519c1007ee6723947c262c720d63c619f633e","SHA1After":"f3007471ca8734c29783c25f0bb49949a783a44","Changes":[{"Position":2432806,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.6.28","SHA1Before":"0b19cb17a62c3ea0efce0fb675a1d3b17845cba3","SHA1After":"260948656725446b818ea668273ceff02ddfb44d","Changes":[{"Position":2401678,"Content":[116]}]}]}},"QQ":{"Name":"QQ","FileTargetInfos":{"IM.dll":{"Name":"IM.dll","RelativePath":"Bin\\IM.dll","Memo":null}},"FileModifyInfos":{"IM.dll":[{"Name":"IM.dll","Version":"9.2.2.26569","SHA1Before":"434254e76c520789558e075af677821258536311","SHA1After":"237c9e489a97858a175f0f7c72ade4ebcbac7a69","Changes":[{"Position":371146,"Content":[235,9,144,144,144]},{"Position":371687,"Content":[235,9,144,144,144]},{"Position":372117,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.2.1.26546","SHA1Before":"8d8ea2c2cbf43f5acf8d684b153e90035352d5f5","SHA1After":"7d194dd5be03982b533d7375c93d9a72587fe28d","Changes":[{"Position":369545,"Content":[235,9,144,144,144]},{"Position":370086,"Content":[235,9,144,144,144]},{"Position":370516,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.2.0.26453","SHA1Before":"c1935ca6347b0c2a7e6108a7f8ee0643d39deb66","SHA1After":"42811188a7e7b346a6a3c1066936b98c747acaf6","Changes":[{"Position":353794,"Content":[235,9,144,144,144]},{"Position":354335,"Content":[235,9,144,144,144]},{"Position":354767,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.2.0.26389","SHA1Before":"6f8855fb80acfa456f8f69989fe949308fe4d154","SHA1After":"f6b8e05a178b9b10ba17c597fa0a44b7a2a966a8","Changes":[{"Position":356808,"Content":[235,9,144,144,144]},{"Position":357349,"Content":[235,9,144,144,144]},{"Position":357781,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.9.26361","SHA1Before":"022d3433d13d07a354c38816f61cb0b7ac60d3fd","SHA1After":"873a57c1fb51cdd099c8cb7108b5ab5cb4459557","Changes":[{"Position":354270,"Content":[235,9,144,144,144]},{"Position":354811,"Content":[235,9,144,144,144]},{"Position":355243,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.9.26346","SHA1Before":"895eb70f707b8222e6460c91492b1281e525059b","SHA1After":"0bb83990e2b5b5f23b7b43249941ff638201af54","Changes":[{"Position":354270,"Content":[235,9,144,144,144]},{"Position":354811,"Content":[235,9,144,144,144]},{"Position":355243,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.8.26211","SHA1Before":"a950d3cf5e8925f7775624271105ef78d9c5cb57","SHA1After":"dffc1cb87b91e6467e13c935611f2f7fd76b9a8d","Changes":[{"Position":337135,"Content":[235,9,144,144,144]},{"Position":337676,"Content":[235,9,144,144,144]},{"Position":338108,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.7.25980","SHA1Before":"c6632339fbe675312a70ae4620e70699c258cd36","SHA1After":"e9ddc5cc681950796fc8fe4c55f580428c890b51","Changes":[{"Position":327839,"Content":[235,9,144,144,144]},{"Position":328380,"Content":[235,9,144,144,144]},{"Position":328812,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.0.4.23786","SHA1Before":"69a714f4eadb09f1453f6f022d4adbcd801cfab8","SHA1After":"b48e77a924076b3ebdffc4af514c868c551d2bca","Changes":[{"Position":318321,"Content":[235,7,144,144,144]},{"Position":318862,"Content":[235,7,144,144,144]},{"Position":319379,"Content":[235,7,144,144,144]}]}]}},"TIM":{"Name":"TIM","FileTargetInfos":{"IM.dll":{"Name":"IM.dll","RelativePath":"Bin\\IM.dll","Memo":null}},"FileModifyInfos":{"IM.dll":[{"Name":"IM.dll","Version":"2.3.2.21173","SHA1Before":"ecf3e69f3fb100ffe2fee095ffded591b9781024","SHA1After":"0514d1304e7ac46b4d33386ec3313888f5ae7171","Changes":[{"Position":317322,"Content":[235,9,144,144,144]},{"Position":317863,"Content":[235,9,144,144,144]},{"Position":318295,"Content":[235,8,144,144,144,144]}]}]}},"QQLite":{"Name":"QQLite","FileTargetInfos":{"IM.dll":{"Name":"IM.dll","RelativePath":"Bin\\IM.dll","Memo":null}},"FileModifyInfos":{"IM.dll":[{"Name":"IM.dll","Version":"7.9.14314.0","SHA1Before":"2e97d7671963fa148a1beeda6ce4964314310593","SHA1After":"723c008fb53435ead20fa6f2e951c9a4a8ff46da","Changes":[{"Position":148741,"Content":[235,2,144,144]},{"Position":149689,"Content":[235,2,144,144]}]},{"Name":"IM.dll","Version":"7.9.14308.0","SHA1Before":"b8a7a873178706b97be11c25f13bcf09e9e578a2","SHA1After":"c5bf533c7af6996b42d1fb2a0fb3f26dfd52f8bf","Changes":[{"Position":148741,"Content":[235,2,144,144]},{"Position":149689,"Content":[235,2,144,144]}]}]}}},"LatestVersion":"0.6","Notice":""} \ No newline at end of file diff --git a/RevokeMsgPatcher.Assistant/FormAssisant.Designer.cs b/RevokeMsgPatcher.Assistant/FormAssisant.Designer.cs new file mode 100644 index 0000000..6ddb778 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/FormAssisant.Designer.cs @@ -0,0 +1,61 @@ +namespace RevokeMsgPatcher.Assistant +{ + partial class FormAssisant + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows 窗体设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + this.txtInfo = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // txtInfo + // + this.txtInfo.Location = new System.Drawing.Point(12, 12); + this.txtInfo.Multiline = true; + this.txtInfo.Name = "txtInfo"; + this.txtInfo.Size = new System.Drawing.Size(484, 238); + this.txtInfo.TabIndex = 0; + // + // FormAssisant + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(508, 262); + this.Controls.Add(this.txtInfo); + this.Name = "FormAssisant"; + this.Text = "冷血无情的助手界面"; + this.Load += new System.EventHandler(this.FormMain_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox txtInfo; + } +} + diff --git a/RevokeMsgPatcher.Assistant/FormAssisant.cs b/RevokeMsgPatcher.Assistant/FormAssisant.cs new file mode 100644 index 0000000..d5e189c --- /dev/null +++ b/RevokeMsgPatcher.Assistant/FormAssisant.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace RevokeMsgPatcher.Assistant +{ + public partial class FormAssisant : Form + { + public FormAssisant() + { + InitializeComponent(); + } + + private void FormMain_Load(object sender, EventArgs e) + { + JsonData obj = new JsonData(); + string json = obj.BagJson(); + Console.WriteLine(json); + + DirectoryInfo directory = new DirectoryInfo("../../Data/" + obj.Bag().LatestVersion); + if (!directory.Exists) + { + directory.Create(); + } + string path = Path.Combine(directory.FullName, "patch.json"); + File.WriteAllText(path, json); + + txtInfo.AppendText("生成完毕!位置:" + path); + } + } +} diff --git a/RevokeMsgPatcher.Assistant/FormAssisant.resx b/RevokeMsgPatcher.Assistant/FormAssisant.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/FormAssisant.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/RevokeMsgPatcher.Assistant/JsonData.cs b/RevokeMsgPatcher.Assistant/JsonData.cs new file mode 100644 index 0000000..1520b65 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/JsonData.cs @@ -0,0 +1,539 @@ +using RevokeMsgPatcher.Model; +using System.Collections.Generic; +using System.Web.Script.Serialization; + +namespace RevokeMsgPatcher +{ + /// + /// 补丁信息 + /// + public class JsonData + { + + public Bag Bag() + { + return new Bag + { + Apps = AppConfig(), + LatestVersion = "0.6", + Notice = "" + }; + } + + public string BagJson() + { + JavaScriptSerializer serializer = new JavaScriptSerializer(); + return serializer.Serialize(Bag()); + } + + public Dictionary AppConfig() + { + return new Dictionary + { + { "Wechat" , Wechat() }, + { "QQ" , QQ() }, + { "TIM" , TIM() }, + { "QQLite" , QQLite() } + }; + } + + public string AppConfigJson() + { + JavaScriptSerializer serializer = new JavaScriptSerializer(); + return serializer.Serialize(AppConfig()); + } + + public App Wechat() + { + return new App + { + Name = "Wechat", + FileTargetInfos = new Dictionary + { + { + "WeChatWin.dll", + new TargetInfo + { + Name = "WeChatWin.dll", + RelativePath = "WeChatWin.dll" + } + } + }, + FileModifyInfos = new Dictionary> + { + { + "WeChatWin.dll", + new List + { + new ModifyInfo { + Name="WeChatWin.dll", + Version="2.7.1.88", + SHA1Before="034059bad50dd793140952391bfa7936133e69b4", + SHA1After="dd6d80c30ca9e0ea9f7d2f1add498fc9aa4bc7a0", + Changes = new List + { + new Change + { + Position =0x00262389, + Content =new byte[] { 0xEB } + }, + new Change + { + Position =0x007957B0, + Content =new byte[] { 0xC3 } + } + } + }, + new ModifyInfo { + Name="WeChatWin.dll", + Version="2.7.1.85", + SHA1Before="de0df4e138b72460450f66c029e33f4510f5e2df", + SHA1After="fbd35720aaff3cdcfd3ff18ea503dc06450e5c99", + Changes = new List + { + new Change + { + Position =0x00262389, + Content =new byte[] { 0xEB } + }, + new Change + { + Position =0x00795680, + Content =new byte[] { 0xC3 } + } + } + }, + new ModifyInfo { + Name="WeChatWin.dll", + Version="2.7.1.82", + SHA1Before="20e111a18872bf6c7148a897c11da26c1ec95520", + SHA1After="1e0741d325ca6b1cd2402b829a3d13a2524af617", + Changes = new List + { + new Change + { + Position =0x00262389, + Content =new byte[] { 0xEB } + }, + new Change + { + Position =0x00795650, + Content =new byte[] { 0xC3 } + } + } + }, + new ModifyInfo { + Name="WeChatWin.dll", + Version="2.7.1.74", + SHA1Before="b1eaf7edc074a88be5d0f89230436cc2084d24d2", + SHA1After="eb3d74ccd87a09059a005f4972861898fc3de463", + Changes = new List + { + new Change + { + Position =0x00262389, + Content =new byte[] { 0xEB } + }, + new Change + { + Position =0x00795550, + Content =new byte[] { 0xC3 } + } + } + }, + new ModifyInfo { + Name="WeChatWin.dll", + Version="2.7.1.65", + SHA1Before="8346b97d264725da924d240c6eb77df3e693385e", + SHA1After="42bab2c9c79ef4f2088c00ea6d817973e14a5e6e", + Changes = new List + { + new Change + { + Position =2495545, + Content =new byte[] { 235} + } + } + }, + new ModifyInfo {Name="WeChatWin.dll",Version="2.7.1.59",SHA1Before="df954d403edaca89cd5394927a325a0023e93281",SHA1After="6aa22460c91bb5c5e2f0ec1af99b8a5f6d4318c0",Changes = new List { new Change {Position=2496073,Content=new byte[] { 235} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.7.1.43",SHA1Before="39cd9e09e1a3eac09e6808749bff525c9e3216ce",SHA1After="7b829f1ff0217e346a80f9510fdd7634ddd49445",Changes = new List { new Change {Position=2494169,Content=new byte[] { 235} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.7.0.70",SHA1Before="3b0601864aff3c1d792f812ad1ca05f02aa761e3",SHA1After="1e8734d32b0a8c12758e30f99c77f729991fb071",Changes = new List { new Change {Position=2475657,Content=new byte[] { 235} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.7.0.65",SHA1Before="063c2e05a0df1bdb8987c2d978d93499bd2052ba",SHA1After="5ed4c09a4f18643b967f063a824d7e65d0567f8a",Changes = new List { new Change {Position=2475449,Content=new byte[] { 117} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.8.68",SHA1Before="2e9417f4276b12fe32ca7b4fee49272a4a2af334",SHA1After="699602ee3cbb9ae5714f6e6ebc658c875a6c66e6",Changes = new List { new Change {Position=2454006,Content=new byte[] { 116} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.8.65",SHA1Before="e01f6855a96c12c30808960903ed199a33e4952c",SHA1After="d9120569cfd0433aebea107d7b90805cbbac7518",Changes = new List { new Change {Position=2454265,Content=new byte[] { 117} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.8.52",SHA1Before="88131302f664df6a657c9ca49d152da536fe5729",SHA1After="8d1454b73831644181e962c1fa0ea4e2da4124a3",Changes = new List { new Change {Position=2453049,Content=new byte[] { 117} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.8.51",SHA1Before="d0a5517b1292a751501b00b4b1f0702db2d9fc30",SHA1After="53e7b1525d49bf2c3250a8131ff0ba2510779b78",Changes = new List { new Change {Position=2452614,Content=new byte[] { 116} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.8.37",SHA1Before="7e01f8b04a158a4a50bc5a6e67c2fb8b02233170",SHA1After="a1895004415fe9bcd7e690bd6e482b833b515599",Changes = new List { new Change {Position=2452614,Content=new byte[] { 116} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.7.57",SHA1Before="80a91aaf941bcb1c24a7d672838ac73e9ebb2e40",SHA1After="a0d3f9a45a835f97aef7fe0872387d8cfb5c25a4",Changes = new List { new Change {Position=2433413,Content=new byte[] { 116} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.7.40",SHA1Before="04bd0cb28df6630b518f42a3f9c2caa4a9359fbc",SHA1After="13c91cf1d4609959771fd137b9a86a5ca365e1b6",Changes = new List { new Change {Position=2432934,Content=new byte[] { 116} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.7.32",SHA1Before="a02519c1007ee6723947c262c720d63c619f633e",SHA1After="f3007471ca8734c29783c25f0bb49949a783a44",Changes = new List { new Change {Position=2432806,Content=new byte[] { 116} } } },new ModifyInfo {Name="WeChatWin.dll",Version="2.6.6.28",SHA1Before="0b19cb17a62c3ea0efce0fb675a1d3b17845cba3",SHA1After="260948656725446b818ea668273ceff02ddfb44d",Changes = new List { new Change {Position=2401678,Content=new byte[] { 116} } } } + } + } + } + }; + } + + public App QQ() + { + return new App + { + Name = "QQ", + FileTargetInfos = new Dictionary + { + { + "IM.dll", + new TargetInfo + { + Name = "IM.dll", + RelativePath = @"Bin\IM.dll" + } + } + }, + FileModifyInfos = new Dictionary> + { + { + "IM.dll", + new List + { + new ModifyInfo + { + Name = "IM.dll", + Version = "9.2.2.26569", + SHA1Before = "434254e76c520789558e075af677821258536311", + SHA1After = "237c9e489a97858a175f0f7c72ade4ebcbac7a69", + Changes = new List + { + new Change + { + Position = 0x0005A9CA, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0005ABE7, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0005AD95, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.2.1.26546", + SHA1Before = "8d8ea2c2cbf43f5acf8d684b153e90035352d5f5", + SHA1After = "7d194dd5be03982b533d7375c93d9a72587fe28d", + Changes = new List + { + new Change + { + Position = 0x0005A389, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0005A5A6, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0005A754, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.2.0.26453", + SHA1Before = "c1935ca6347b0c2a7e6108a7f8ee0643d39deb66", + SHA1After = "42811188a7e7b346a6a3c1066936b98c747acaf6", + Changes = new List + { + new Change + { + Position = 0x00056602, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0005681F, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x000569CF, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.2.0.26389", + SHA1Before = "6f8855fb80acfa456f8f69989fe949308fe4d154", + SHA1After = "f6b8e05a178b9b10ba17c597fa0a44b7a2a966a8", + Changes = new List + { + new Change + { + Position = 0x000571C8, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x000573E5, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x00057595, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.1.9.26361", + SHA1Before = "022d3433d13d07a354c38816f61cb0b7ac60d3fd", + SHA1After = "873a57c1fb51cdd099c8cb7108b5ab5cb4459557", + Changes = new List + { + new Change + { + Position = 0x000567DE, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x000569FB, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x00056BAB, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.1.9.26346", + SHA1Before = "895eb70f707b8222e6460c91492b1281e525059b", + SHA1After = "0bb83990e2b5b5f23b7b43249941ff638201af54", + Changes = new List + { + new Change + { + Position = 0x000567DE, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x000569FB, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x00056BAB, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.1.8.26211", + SHA1Before = "a950d3cf5e8925f7775624271105ef78d9c5cb57", + SHA1After = "dffc1cb87b91e6467e13c935611f2f7fd76b9a8d", + Changes = new List + { + new Change + { + Position = 0x000524EF, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0005270C, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x000528BC, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.1.7.25980", + SHA1Before = "c6632339fbe675312a70ae4620e70699c258cd36", + SHA1After = "e9ddc5cc681950796fc8fe4c55f580428c890b51", + Changes = new List + { + new Change + { + Position = 0x0005009F, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x000502BC, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0005046C, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "9.0.4.23786", + SHA1Before = "69a714f4eadb09f1453f6f022d4adbcd801cfab8", + SHA1After = "b48e77a924076b3ebdffc4af514c868c551d2bca", + Changes = new List + { + new Change + { + Position = 0x0004DB71, + Content = new byte[] { 0xEB, 0x07, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0004DD8E, + Content = new byte[] { 0xEB, 0x07, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0004DF93, + Content = new byte[] { 0xEB, 0x07, 0x90, 0x90, 0x90 } + } + } + } + } + } + } + }; + } + + public App TIM() + { + return new App + { + Name = "TIM", + FileTargetInfos = new Dictionary + { + { + "IM.dll", + new TargetInfo + { + Name = "IM.dll", + RelativePath = @"Bin\IM.dll" + } + } + }, + FileModifyInfos = new Dictionary> + { + { + "IM.dll", + new List + { + new ModifyInfo + { + Name = "IM.dll", + Version = "2.3.2.21173", + SHA1Before = "ecf3e69f3fb100ffe2fee095ffded591b9781024", + SHA1After = "0514d1304e7ac46b4d33386ec3313888f5ae7171", + Changes = new List + { + new Change + { + Position = 0x0004D78A, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0004D9A7, + Content = new byte[] { 0xEB, 0x09, 0x90, 0x90, 0x90 } + }, + new Change + { + Position = 0x0004DB57, + Content = new byte[] { 0xEB, 0x08, 0x90, 0x90, 0x90, 0x90 } + } + } + } + } + } + } + }; + } + + public App QQLite() + { + return new App + { + Name = "QQLite", + FileTargetInfos = new Dictionary + { + { + "IM.dll", + new TargetInfo + { + Name = "IM.dll", + RelativePath = @"Bin\IM.dll" + } + } + }, + FileModifyInfos = new Dictionary> + { + { + "IM.dll", + new List + { + new ModifyInfo + { + Name = "IM.dll", + Version = "7.9.14314.0", + SHA1Before = "2e97d7671963fa148a1beeda6ce4964314310593", + SHA1After = "723c008fb53435ead20fa6f2e951c9a4a8ff46da", + Changes = new List + { + new Change + { + Position = 0x00024505, + Content = new byte[] { 0xEB, 0x02, 0x90, 0x90 } + }, + new Change + { + Position = 0x000248B9, + Content = new byte[] { 0xEB, 0x02, 0x90, 0x90 } + } + } + }, + new ModifyInfo + { + Name = "IM.dll", + Version = "7.9.14308.0", + SHA1Before = "b8a7a873178706b97be11c25f13bcf09e9e578a2", + SHA1After = "c5bf533c7af6996b42d1fb2a0fb3f26dfd52f8bf", + Changes = new List + { + new Change + { + Position = 0x00024505, + Content = new byte[] { 0xEB, 0x02, 0x90, 0x90 } + }, + new Change + { + Position = 0x000248B9, + Content = new byte[] { 0xEB, 0x02, 0x90, 0x90 } + } + } + } + } + } + } + }; + } + } +} diff --git a/RevokeMsgPatcher.Assistant/Program.cs b/RevokeMsgPatcher.Assistant/Program.cs new file mode 100644 index 0000000..7509010 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace RevokeMsgPatcher.Assistant +{ + static class Program + { + /// + /// 应用程序的主入口点。 + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new FormAssisant()); + } + } +} diff --git a/RevokeMsgPatcher.Assistant/Properties/AssemblyInfo.cs b/RevokeMsgPatcher.Assistant/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b3a3ffb --- /dev/null +++ b/RevokeMsgPatcher.Assistant/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("RevokeMsgPatcher.Assistant")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RevokeMsgPatcher.Assistant")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("6992004f-17e6-45bf-8d72-180a31e9c23c")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +// 方法是按如下所示使用“*”: : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RevokeMsgPatcher.Assistant/Properties/Resources.Designer.cs b/RevokeMsgPatcher.Assistant/Properties/Resources.Designer.cs new file mode 100644 index 0000000..92146a4 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本: 4.0.30319.42000 +// +// 对此文件的更改可能导致不正确的行为,如果 +// 重新生成代码,则所做更改将丢失。 +// +//------------------------------------------------------------------------------ + +namespace RevokeMsgPatcher.Assistant.Properties +{ + + + /// + /// 强类型资源类,用于查找本地化字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// 返回此类使用的缓存 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RevokeMsgPatcher.Assistant.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 覆盖当前线程的 CurrentUICulture 属性 + /// 使用此强类型的资源类的资源查找。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/RevokeMsgPatcher.Assistant/Properties/Resources.resx b/RevokeMsgPatcher.Assistant/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/RevokeMsgPatcher.Assistant/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/RevokeMsgPatcher.Assistant/Properties/Settings.Designer.cs b/RevokeMsgPatcher.Assistant/Properties/Settings.Designer.cs new file mode 100644 index 0000000..50ea233 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RevokeMsgPatcher.Assistant.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/RevokeMsgPatcher.Assistant/Properties/Settings.settings b/RevokeMsgPatcher.Assistant/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/RevokeMsgPatcher.Assistant/RevokeMsgPatcher.Assistant.csproj b/RevokeMsgPatcher.Assistant/RevokeMsgPatcher.Assistant.csproj new file mode 100644 index 0000000..bc77f12 --- /dev/null +++ b/RevokeMsgPatcher.Assistant/RevokeMsgPatcher.Assistant.csproj @@ -0,0 +1,113 @@ + + + + + Debug + AnyCPU + {6992004F-17E6-45BF-8D72-180A31E9C23C} + WinExe + RevokeMsgPatcher.Assistant + RevokeMsgPatcher.Assistant + v4.5 + 512 + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + Form + + + FormAssisant.cs + + + + + + FormAssisant.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {977bf781-ced8-4389-9404-0fa08fdf21df} + RevokeMsgPatcher + + + + + False + .NET Framework 3.5 SP1 + false + + + + \ No newline at end of file diff --git a/RevokeMsgPatcher.MultiInstance/App.config b/RevokeMsgPatcher.MultiInstance/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.Designer.cs b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.Designer.cs new file mode 100644 index 0000000..11b3e4a --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.Designer.cs @@ -0,0 +1,186 @@ +namespace RevokeMsgPatcher.MultiInstance +{ + partial class FormMultiInstance + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows 窗体设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + this.txtPath = new System.Windows.Forms.TextBox(); + this.lblPathTag = new System.Windows.Forms.Label(); + this.btnChoosePath = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.startNum = new System.Windows.Forms.NumericUpDown(); + this.btnStart = new System.Windows.Forms.Button(); + this.linkLabel1 = new System.Windows.Forms.LinkLabel(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.startNum)).BeginInit(); + this.SuspendLayout(); + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(86, 60); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(332, 21); + this.txtPath.TabIndex = 7; + // + // lblPathTag + // + this.lblPathTag.AutoSize = true; + this.lblPathTag.Location = new System.Drawing.Point(15, 63); + this.lblPathTag.Name = "lblPathTag"; + this.lblPathTag.Size = new System.Drawing.Size(65, 12); + this.lblPathTag.TabIndex = 6; + this.lblPathTag.Text = "微信路径:"; + // + // btnChoosePath + // + this.btnChoosePath.Location = new System.Drawing.Point(424, 59); + this.btnChoosePath.Name = "btnChoosePath"; + this.btnChoosePath.Size = new System.Drawing.Size(36, 23); + this.btnChoosePath.TabIndex = 8; + this.btnChoosePath.Text = "..."; + this.btnChoosePath.UseVisualStyleBackColor = true; + this.btnChoosePath.Click += new System.EventHandler(this.btnChoosePath_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(84, 151); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(293, 24); + this.label1.TabIndex = 9; + this.label1.Text = "只要我打开微信的速度足够快,我就多开无限个微信。\r\n我为什么能手动双开微信,别问,问就是手速快。"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(15, 95); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(65, 12); + this.label2.TabIndex = 11; + this.label2.Text = "多开个数:"; + // + // startNum + // + this.startNum.Location = new System.Drawing.Point(86, 91); + this.startNum.Maximum = new decimal(new int[] { + 1000, + 0, + 0, + 0}); + this.startNum.Minimum = new decimal(new int[] { + 2, + 0, + 0, + 0}); + this.startNum.Name = "startNum"; + this.startNum.Size = new System.Drawing.Size(55, 21); + this.startNum.TabIndex = 12; + this.startNum.Value = new decimal(new int[] { + 2, + 0, + 0, + 0}); + // + // btnStart + // + this.btnStart.Location = new System.Drawing.Point(369, 91); + this.btnStart.Name = "btnStart"; + this.btnStart.Size = new System.Drawing.Size(91, 23); + this.btnStart.TabIndex = 13; + this.btnStart.Text = "点击启动!"; + this.btnStart.UseVisualStyleBackColor = true; + this.btnStart.Click += new System.EventHandler(this.btnStart_Click); + // + // linkLabel1 + // + this.linkLabel1.AutoSize = true; + this.linkLabel1.Location = new System.Drawing.Point(284, 95); + this.linkLabel1.Name = "linkLabel1"; + this.linkLabel1.Size = new System.Drawing.Size(41, 12); + this.linkLabel1.TabIndex = 15; + this.linkLabel1.TabStop = true; + this.linkLabel1.Text = "GitHub"; + this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(177, 95); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(101, 12); + this.label3.TabIndex = 14; + this.label3.Text = "软件主页(开源):"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 9); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(323, 36); + this.label4.TabIndex = 16; + this.label4.Text = "注意:\r\n1. 使用本程序多开前不能存在正在运行的微信进程。\r\n2. 多开的成功率取决于你的机器性能,不保证每次都能成功"; + // + // FormMultiInstance + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(472, 133); + this.Controls.Add(this.label4); + this.Controls.Add(this.linkLabel1); + this.Controls.Add(this.label3); + this.Controls.Add(this.btnStart); + this.Controls.Add(this.startNum); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.lblPathTag); + this.Controls.Add(this.btnChoosePath); + this.Name = "FormMultiInstance"; + this.Text = "微信多开小工具"; + ((System.ComponentModel.ISupportInitialize)(this.startNum)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox txtPath; + private System.Windows.Forms.Label lblPathTag; + private System.Windows.Forms.Button btnChoosePath; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.NumericUpDown startNum; + private System.Windows.Forms.Button btnStart; + private System.Windows.Forms.LinkLabel linkLabel1; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + } +} + diff --git a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs new file mode 100644 index 0000000..1f566ef --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs @@ -0,0 +1,111 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace RevokeMsgPatcher.MultiInstance +{ + public partial class FormMultiInstance : Form + { + public FormMultiInstance() + { + InitializeComponent(); + string installFolder = FindInstallPathFromRegistry("Wechat"); + if (!string.IsNullOrEmpty(installFolder)) + { + string wechatPath = Path.Combine(installFolder, "WeChat.exe"); + if (File.Exists(wechatPath)) + { + txtPath.Text = wechatPath; + } + } + } + + private void btnChoosePath_Click(object sender, EventArgs e) + { + OpenFileDialog dialog = new OpenFileDialog + { + Multiselect = false, + Title = "请选择微信启动主程序", + Filter = "微信主程序|WeChat.exe" + }; + if (dialog.ShowDialog() == DialogResult.OK) + { + txtPath.Text = dialog.FileName; + } + } + + private void btnStart_Click(object sender, EventArgs e) + { + if (File.Exists(txtPath.Text)) + { + // 检测微信进程是否存在 + Process[] ps = Process.GetProcessesByName("WeChat"); + if (ps.Length > 0) + { + DialogResult result = MessageBox.Show("当前存在运行中的微信进程,请先关闭当前微信才能使用该功能。点击【确定】强制关闭当前所有微信进程并进行多开,点击【取消】不做任何处理。", "当前存在运行中的微信进程", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (result == DialogResult.OK) + { + foreach (Process p in ps) + p.Kill(); + } + else + { + return; + } + } + // 启动多个实例 + for (int i = 0; i < startNum.Value; i++) + { + Process.Start(txtPath.Text); + } + } + } + + private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start("https://github.com/huiyadanli/RevokeMsgPatcher"); + } + + /// + /// 从注册表中寻找安装路径 + /// + /// + /// 安装信息的注册表键名 + /// 微信:WeChat + /// QQ:{052CFB79-9D62-42E3-8A15-DE66C2C97C3E} + /// TIM:TIM + /// + /// 安装路径 + public static string FindInstallPathFromRegistry(string uninstallKeyName) + { + try + { + RegistryKey key = Registry.LocalMachine.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{uninstallKeyName}"); + if (key == null) + { + return null; + } + object installLocation = key.GetValue("InstallLocation"); + key.Close(); + if (installLocation != null && !string.IsNullOrEmpty(installLocation.ToString())) + { + return installLocation.ToString(); + } + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + return null; + } + } +} diff --git a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/RevokeMsgPatcher.MultiInstance/Program.cs b/RevokeMsgPatcher.MultiInstance/Program.cs new file mode 100644 index 0000000..e94f7ef --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace RevokeMsgPatcher.MultiInstance +{ + static class Program + { + /// + /// 应用程序的主入口点。 + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new FormMultiInstance()); + } + } +} diff --git a/RevokeMsgPatcher.MultiInstance/Properties/AssemblyInfo.cs b/RevokeMsgPatcher.MultiInstance/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ed57d68 --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("RevokeMsgPatcher.MultiInstance")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RevokeMsgPatcher.MultiInstance")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("73043ca8-af54-4591-9174-40fb6e0a3d36")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +// 方法是按如下所示使用“*”: : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RevokeMsgPatcher.MultiInstance/Properties/Resources.Designer.cs b/RevokeMsgPatcher.MultiInstance/Properties/Resources.Designer.cs new file mode 100644 index 0000000..6847c43 --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本: 4.0.30319.42000 +// +// 对此文件的更改可能导致不正确的行为,如果 +// 重新生成代码,则所做更改将丢失。 +// +//------------------------------------------------------------------------------ + +namespace RevokeMsgPatcher.MultiInstance.Properties +{ + + + /// + /// 强类型资源类,用于查找本地化字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// 返回此类使用的缓存 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RevokeMsgPatcher.MultiInstance.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 覆盖当前线程的 CurrentUICulture 属性 + /// 使用此强类型的资源类的资源查找。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/RevokeMsgPatcher.MultiInstance/Properties/Resources.resx b/RevokeMsgPatcher.MultiInstance/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/RevokeMsgPatcher.MultiInstance/Properties/Settings.Designer.cs b/RevokeMsgPatcher.MultiInstance/Properties/Settings.Designer.cs new file mode 100644 index 0000000..46fb148 --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RevokeMsgPatcher.MultiInstance.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/RevokeMsgPatcher.MultiInstance/Properties/Settings.settings b/RevokeMsgPatcher.MultiInstance/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj b/RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj new file mode 100644 index 0000000..2ed2fcd --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {73043CA8-AF54-4591-9174-40FB6E0A3D36} + WinExe + RevokeMsgPatcher.MultiInstance + RevokeMsgPatcher.MultiInstance + v4.5 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Form + + + FormMultiInstance.cs + + + + + FormMultiInstance.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/RevokeMsgPatcher.sln b/RevokeMsgPatcher.sln index e632b93..829e586 100644 --- a/RevokeMsgPatcher.sln +++ b/RevokeMsgPatcher.sln @@ -5,6 +5,10 @@ VisualStudioVersion = 15.0.28010.2016 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevokeMsgPatcher", "RevokeMsgPatcher\RevokeMsgPatcher.csproj", "{977BF781-CED8-4389-9404-0FA08FDF21DF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevokeMsgPatcher.Assistant", "RevokeMsgPatcher.Assistant\RevokeMsgPatcher.Assistant.csproj", "{6992004F-17E6-45BF-8D72-180A31E9C23C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevokeMsgPatcher.MultiInstance", "RevokeMsgPatcher.MultiInstance\RevokeMsgPatcher.MultiInstance.csproj", "{73043CA8-AF54-4591-9174-40FB6E0A3D36}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +19,14 @@ Global {977BF781-CED8-4389-9404-0FA08FDF21DF}.Debug|Any CPU.Build.0 = Debug|Any CPU {977BF781-CED8-4389-9404-0FA08FDF21DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {977BF781-CED8-4389-9404-0FA08FDF21DF}.Release|Any CPU.Build.0 = Release|Any CPU + {6992004F-17E6-45BF-8D72-180A31E9C23C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6992004F-17E6-45BF-8D72-180A31E9C23C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6992004F-17E6-45BF-8D72-180A31E9C23C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6992004F-17E6-45BF-8D72-180A31E9C23C}.Release|Any CPU.Build.0 = Release|Any CPU + {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/RevokeMsgPatcher/BusinessException.cs b/RevokeMsgPatcher/BusinessException.cs new file mode 100644 index 0000000..2b865e3 --- /dev/null +++ b/RevokeMsgPatcher/BusinessException.cs @@ -0,0 +1,14 @@ +using System; + +namespace RevokeMsgPatcher +{ + class BusinessException : ApplicationException + { + public string ErrorCode { get; protected set; } + + public BusinessException(string errcode, string message) : base(message) + { + ErrorCode = errcode; + } + } +} diff --git a/RevokeMsgPatcher/FormMain.Designer.cs b/RevokeMsgPatcher/FormMain.Designer.cs index b8c0d0f..99c35d8 100644 --- a/RevokeMsgPatcher/FormMain.Designer.cs +++ b/RevokeMsgPatcher/FormMain.Designer.cs @@ -29,7 +29,7 @@ private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormMain)); - this.label2 = new System.Windows.Forms.Label(); + this.lblPathTag = new System.Windows.Forms.Label(); this.btnPatch = new System.Windows.Forms.Button(); this.txtPath = new System.Windows.Forms.TextBox(); this.btnChoosePath = new System.Windows.Forms.Button(); @@ -37,21 +37,29 @@ this.linkLabel1 = new System.Windows.Forms.LinkLabel(); this.btnRestore = new System.Windows.Forms.Button(); this.lblUpdatePachJson = new System.Windows.Forms.Label(); + this.lblVersion = new System.Windows.Forms.Label(); + this.lblVersionTag = new System.Windows.Forms.Label(); + this.rbtWechat = new System.Windows.Forms.RadioButton(); + this.rbtQQ = new System.Windows.Forms.RadioButton(); + this.rbtTIM = new System.Windows.Forms.RadioButton(); + this.label5 = new System.Windows.Forms.Label(); + this.panelMask = new System.Windows.Forms.Panel(); + this.rbtQQLite = new System.Windows.Forms.RadioButton(); this.SuspendLayout(); // - // label2 + // lblPathTag // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(17, 15); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(65, 12); - this.label2.TabIndex = 1; - this.label2.Text = "微信路径:"; + this.lblPathTag.AutoSize = true; + this.lblPathTag.Location = new System.Drawing.Point(12, 34); + this.lblPathTag.Name = "lblPathTag"; + this.lblPathTag.Size = new System.Drawing.Size(65, 12); + this.lblPathTag.TabIndex = 1; + this.lblPathTag.Text = "应用路径:"; // // btnPatch // this.btnPatch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnPatch.Location = new System.Drawing.Point(409, 10); + this.btnPatch.Location = new System.Drawing.Point(372, 56); this.btnPatch.Name = "btnPatch"; this.btnPatch.Size = new System.Drawing.Size(102, 23); this.btnPatch.TabIndex = 3; @@ -63,15 +71,16 @@ // this.txtPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.txtPath.Location = new System.Drawing.Point(88, 12); + this.txtPath.Location = new System.Drawing.Point(82, 29); this.txtPath.Name = "txtPath"; - this.txtPath.Size = new System.Drawing.Size(275, 21); + this.txtPath.Size = new System.Drawing.Size(352, 21); this.txtPath.TabIndex = 4; + this.txtPath.TextChanged += new System.EventHandler(this.txtPath_TextChanged); // // btnChoosePath // this.btnChoosePath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnChoosePath.Location = new System.Drawing.Point(369, 10); + this.btnChoosePath.Location = new System.Drawing.Point(440, 27); this.btnChoosePath.Name = "btnChoosePath"; this.btnChoosePath.Size = new System.Drawing.Size(34, 23); this.btnChoosePath.TabIndex = 5; @@ -82,7 +91,7 @@ // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(17, 44); + this.label1.Location = new System.Drawing.Point(12, 87); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(101, 12); this.label1.TabIndex = 6; @@ -91,7 +100,7 @@ // linkLabel1 // this.linkLabel1.AutoSize = true; - this.linkLabel1.Location = new System.Drawing.Point(112, 44); + this.linkLabel1.Location = new System.Drawing.Point(119, 87); this.linkLabel1.Name = "linkLabel1"; this.linkLabel1.Size = new System.Drawing.Size(41, 12); this.linkLabel1.TabIndex = 7; @@ -102,7 +111,7 @@ // btnRestore // this.btnRestore.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnRestore.Location = new System.Drawing.Point(409, 39); + this.btnRestore.Location = new System.Drawing.Point(264, 56); this.btnRestore.Name = "btnRestore"; this.btnRestore.Size = new System.Drawing.Size(102, 23); this.btnRestore.TabIndex = 8; @@ -112,32 +121,122 @@ // // lblUpdatePachJson // - this.lblUpdatePachJson.AutoSize = true; + this.lblUpdatePachJson.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.lblUpdatePachJson.Cursor = System.Windows.Forms.Cursors.Hand; - this.lblUpdatePachJson.Location = new System.Drawing.Point(198, 44); + this.lblUpdatePachJson.ForeColor = System.Drawing.SystemColors.Highlight; + this.lblUpdatePachJson.Location = new System.Drawing.Point(166, 89); this.lblUpdatePachJson.Name = "lblUpdatePachJson"; - this.lblUpdatePachJson.Size = new System.Drawing.Size(131, 12); + this.lblUpdatePachJson.Size = new System.Drawing.Size(308, 12); this.lblUpdatePachJson.TabIndex = 9; - this.lblUpdatePachJson.Text = "获取最新补丁信息中..."; + this.lblUpdatePachJson.Text = "[ 获取最新补丁信息中... ]"; + this.lblUpdatePachJson.TextAlign = System.Drawing.ContentAlignment.TopRight; this.lblUpdatePachJson.Click += new System.EventHandler(this.lblUpdatePachJson_Click); // + // lblVersion + // + this.lblVersion.AutoSize = true; + this.lblVersion.Location = new System.Drawing.Point(83, 61); + this.lblVersion.Name = "lblVersion"; + this.lblVersion.Size = new System.Drawing.Size(0, 12); + this.lblVersion.TabIndex = 10; + // + // lblVersionTag + // + this.lblVersionTag.AutoSize = true; + this.lblVersionTag.Location = new System.Drawing.Point(12, 61); + this.lblVersionTag.Name = "lblVersionTag"; + this.lblVersionTag.Size = new System.Drawing.Size(65, 12); + this.lblVersionTag.TabIndex = 9; + this.lblVersionTag.Text = "当前版本:"; + // + // rbtWechat + // + this.rbtWechat.AutoSize = true; + this.rbtWechat.Checked = true; + this.rbtWechat.Location = new System.Drawing.Point(82, 7); + this.rbtWechat.Name = "rbtWechat"; + this.rbtWechat.Size = new System.Drawing.Size(47, 16); + this.rbtWechat.TabIndex = 12; + this.rbtWechat.TabStop = true; + this.rbtWechat.Text = "微信"; + this.rbtWechat.UseVisualStyleBackColor = true; + this.rbtWechat.CheckedChanged += new System.EventHandler(this.radioButtons_CheckedChanged); + // + // rbtQQ + // + this.rbtQQ.AutoSize = true; + this.rbtQQ.Location = new System.Drawing.Point(140, 7); + this.rbtQQ.Name = "rbtQQ"; + this.rbtQQ.Size = new System.Drawing.Size(35, 16); + this.rbtQQ.TabIndex = 13; + this.rbtQQ.Text = "QQ"; + this.rbtQQ.UseVisualStyleBackColor = true; + this.rbtQQ.CheckedChanged += new System.EventHandler(this.radioButtons_CheckedChanged); + // + // rbtTIM + // + this.rbtTIM.AutoSize = true; + this.rbtTIM.Location = new System.Drawing.Point(186, 7); + this.rbtTIM.Name = "rbtTIM"; + this.rbtTIM.Size = new System.Drawing.Size(41, 16); + this.rbtTIM.TabIndex = 14; + this.rbtTIM.Text = "TIM"; + this.rbtTIM.UseVisualStyleBackColor = true; + this.rbtTIM.CheckedChanged += new System.EventHandler(this.radioButtons_CheckedChanged); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(12, 9); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(65, 12); + this.label5.TabIndex = 16; + this.label5.Text = "选择应用:"; + // + // panelMask + // + this.panelMask.Location = new System.Drawing.Point(140, 127); + this.panelMask.Name = "panelMask"; + this.panelMask.Size = new System.Drawing.Size(157, 58); + this.panelMask.TabIndex = 17; + this.panelMask.Visible = false; + // + // rbtQQLite + // + this.rbtQQLite.AutoSize = true; + this.rbtQQLite.Location = new System.Drawing.Point(235, 7); + this.rbtQQLite.Name = "rbtQQLite"; + this.rbtQQLite.Size = new System.Drawing.Size(71, 16); + this.rbtQQLite.TabIndex = 18; + this.rbtQQLite.Text = "QQ轻聊版"; + this.rbtQQLite.UseVisualStyleBackColor = true; + // // FormMain // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(523, 69); + this.ClientSize = new System.Drawing.Size(490, 110); + this.Controls.Add(this.rbtQQLite); this.Controls.Add(this.lblUpdatePachJson); + this.Controls.Add(this.panelMask); + this.Controls.Add(this.lblVersion); + this.Controls.Add(this.lblVersionTag); + this.Controls.Add(this.label5); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.rbtTIM); + this.Controls.Add(this.lblPathTag); + this.Controls.Add(this.rbtQQ); this.Controls.Add(this.btnRestore); + this.Controls.Add(this.rbtWechat); + this.Controls.Add(this.btnPatch); + this.Controls.Add(this.btnChoosePath); this.Controls.Add(this.linkLabel1); this.Controls.Add(this.label1); - this.Controls.Add(this.btnChoosePath); - this.Controls.Add(this.txtPath); - this.Controls.Add(this.btnPatch); - this.Controls.Add(this.label2); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(506, 149); this.Name = "FormMain"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "微信防撤回补丁"; + this.Text = "微信/QQ/TIM防撤回补丁"; this.Load += new System.EventHandler(this.FormMain_Load); this.ResumeLayout(false); this.PerformLayout(); @@ -145,7 +244,7 @@ } #endregion - private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lblPathTag; private System.Windows.Forms.Button btnPatch; private System.Windows.Forms.TextBox txtPath; private System.Windows.Forms.Button btnChoosePath; @@ -153,6 +252,14 @@ private System.Windows.Forms.LinkLabel linkLabel1; private System.Windows.Forms.Button btnRestore; private System.Windows.Forms.Label lblUpdatePachJson; + private System.Windows.Forms.Label lblVersion; + private System.Windows.Forms.Label lblVersionTag; + private System.Windows.Forms.RadioButton rbtWechat; + private System.Windows.Forms.RadioButton rbtQQ; + private System.Windows.Forms.RadioButton rbtTIM; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Panel panelMask; + private System.Windows.Forms.RadioButton rbtQQLite; } } diff --git a/RevokeMsgPatcher/FormMain.cs b/RevokeMsgPatcher/FormMain.cs index a7a9649..08f183e 100644 --- a/RevokeMsgPatcher/FormMain.cs +++ b/RevokeMsgPatcher/FormMain.cs @@ -1,126 +1,205 @@ -using System; +using RevokeMsgPatcher.Model; +using RevokeMsgPatcher.Modifier; +using RevokeMsgPatcher.Utils; +using System; +using System.Collections.Generic; using System.IO; using System.Net; using System.Threading.Tasks; +using System.Web.Script.Serialization; using System.Windows.Forms; namespace RevokeMsgPatcher { public partial class FormMain : Form { - Patcher patcher = null; + // 当前使用的修改者 + private AppModifier modifier = null; + + private WechatModifier wechatModifier = null; + private QQModifier qqModifier = null; + private TIMModifier timModifier = null; + private QQLiteModifier qqLiteModifier = null; + + private string thisVersion; + private bool needUpdate = false; + + private GAHelper ga = new GAHelper(); // Google Analytics 记录 + + public void InitModifier() + { + // 从配置文件中读取配置 + JavaScriptSerializer serializer = new JavaScriptSerializer(); + Bag bag = serializer.Deserialize(Properties.Resources.PatchJson); + + // 初始化每个应用对应的修改者 + wechatModifier = new WechatModifier(bag.Apps["Wechat"]); + qqModifier = new QQModifier(bag.Apps["QQ"]); + timModifier = new TIMModifier(bag.Apps["TIM"]); + qqLiteModifier = new QQLiteModifier(bag.Apps["QQLite"]); + + rbtWechat.Tag = wechatModifier; + rbtQQ.Tag = qqModifier; + rbtTIM.Tag = timModifier; + rbtQQLite.Tag = qqLiteModifier; + + // 默认微信 + rbtWechat.Enabled = true; + modifier = wechatModifier; + } public FormMain() { InitializeComponent(); - patcher = new Patcher(); - txtPath.Text = Util.AutoFindInstallPath(); - if (!string.IsNullOrEmpty(txtPath.Text)) - { - patcher.IntallPath = txtPath.Text; - btnRestore.Enabled = File.Exists(patcher.BakPath); - } + // 标题加上版本号 string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); if (currentVersion.Length > 3) { - currentVersion = " v" + currentVersion.Substring(0, 3); + thisVersion = currentVersion.Substring(0, 3); + currentVersion = " v" + thisVersion; } this.Text += currentVersion; + + InitModifier(); + InitControls(); + + ga.RequestPageView($"/main/{thisVersion}", $"进入{thisVersion}版本主界面"); + } + + private void InitControls() + { + // 自动获取应用安装路径 + txtPath.Text = modifier.FindInstallPath(); + btnRestore.Enabled = false; + // 显示是否能够备份还原 + if (!string.IsNullOrEmpty(txtPath.Text)) + { + modifier.InitEditors(txtPath.Text); + lblVersion.Text = modifier.GetVersion(); + btnRestore.Enabled = modifier.BackupExists(); + } } private void btnPatch_Click(object sender, EventArgs e) { - if (string.IsNullOrEmpty(txtPath.Text) || !Util.IsWechatInstallPath(txtPath.Text)) + if (!modifier.IsAllFilesExist(txtPath.Text)) { - MessageBox.Show("请选择微信安装路径!"); + MessageBox.Show("请选择正确的安装路径!"); return; } - patcher.IntallPath = txtPath.Text; + // 记录点了什么应用的防撤回 + string enName = GetCheckedRadioButtonNameEn(); // 应用英文名 + string version = modifier.GetVersion(); // 应用版本 + ga.RequestPageView($"{enName}/{version}/patch", "点击防撤回"); + + EnableAllButton(false); + // a.重新初始化编辑器 + modifier.InitEditors(txtPath.Text); + // b.计算SHA1,验证文件完整性,寻找对应的补丁信息 try { - btnPatch.Enabled = false; - string version = patcher.JudgeVersion(); - if (!string.IsNullOrEmpty(version)) - { - if (version == "done") - { - MessageBox.Show("已经安装过防撤回补丁了"); - btnPatch.Enabled = true; - return; - } - - if (patcher.Patch()) - { - MessageBox.Show("成功安装防撤回补丁!原 WeChatWin.dll 文件已经备份到 " + patcher.BakPath + " ,如果有问题可以手动覆盖恢复"); - } - else - { - MessageBox.Show("防撤回补丁安装失败!"); - } - btnRestore.Enabled = File.Exists(patcher.BakPath); - } - else - { - MessageBox.Show("当前微信版本不被支持:" + Util.GetFileVersion(patcher.DllPath)); - } + modifier.ValidateAndFindModifyInfo(); + } + catch (BusinessException ex) + { + ga.RequestPageView($"{enName}/{version}/patch/sha1/ex/{ex.ErrorCode}", ex.Message); + MessageBox.Show(ex.Message); + return; + } + catch (IOException ex) + { + ga.RequestPageView($"{enName}/{version}/patch/sha1/ex/{ex.HResult.ToString("x4")}", ex.Message); + MessageBox.Show(ex.Message + " 请以管理员权限启动本程序,并确认当前应用(微信/QQ/TIM)处于关闭状态。"); + return; + } + catch (Exception ex) + { + ga.RequestPageView($"{enName}/{version}/patch/sha1/ex/{ex.HResult.ToString("x4")}", ex.Message); + MessageBox.Show(ex.Message); + return; + } + finally + { + EnableAllButton(true); + btnRestore.Enabled = modifier.BackupExists(); + } + // c.打补丁 + try + { + modifier.Patch(); + ga.RequestPageView($"{enName}/{version}/patch/succ", "防撤回成功"); + MessageBox.Show("补丁安装成功!"); } catch (Exception ex) { Console.WriteLine(ex.Message); - MessageBox.Show(ex.Message + " 请以管理员权限启动本程序,并确认微信处于关闭状态。"); + ga.RequestPageView($"{enName}/{version}/patch/ex/{ex.HResult.ToString("x4")}", ex.Message); + MessageBox.Show(ex.Message + " 请以管理员权限启动本程序,并确认当前应用(微信/QQ/TIM)处于关闭状态。"); } - btnPatch.Enabled = true; + finally + { + EnableAllButton(true); + btnRestore.Enabled = modifier.BackupExists(); + } + } private void txtPath_TextChanged(object sender, EventArgs e) { - if (!string.IsNullOrEmpty(txtPath.Text)) + if (modifier.IsAllFilesExist(txtPath.Text)) { - patcher.IntallPath = txtPath.Text; - btnRestore.Enabled = File.Exists(patcher.BakPath); + modifier.InitEditors(txtPath.Text); + btnRestore.Enabled = modifier.BackupExists(); + } + else + { + btnPatch.Enabled = false; + btnRestore.Enabled = false; } } private void btnChoosePath_Click(object sender, EventArgs e) { FolderBrowserDialog dialog = new FolderBrowserDialog(); - dialog.Description = "请选择微信安装路径"; + dialog.Description = "请选择安装路径"; if (dialog.ShowDialog() == DialogResult.OK) { - if (string.IsNullOrEmpty(dialog.SelectedPath) || !Util.IsWechatInstallPath(dialog.SelectedPath)) + if (string.IsNullOrEmpty(dialog.SelectedPath) || !modifier.IsAllFilesExist(dialog.SelectedPath)) { - MessageBox.Show("无法找到微信关键文件,请选择正确的微信安装路径!"); + MessageBox.Show("无法找到此应用的关键文件,请选择正确的安装路径!"); } else { txtPath.Text = dialog.SelectedPath; + btnRestore.Enabled = false; + // 显示是否能够备份还原 + if (!string.IsNullOrEmpty(txtPath.Text)) + { + modifier.InitEditors(txtPath.Text); + lblVersion.Text = modifier.GetVersion(); + btnRestore.Enabled = modifier.BackupExists(); + } } } } private void btnRestore_Click(object sender, EventArgs e) { - btnRestore.Enabled = false; + EnableAllButton(false); try { - if (File.Exists(patcher.BakPath)) - { - File.Copy(patcher.BakPath, patcher.DllPath, true); - MessageBox.Show("还原成功"); - } - else - { - MessageBox.Show("备份文件不存在"); - } + modifier.Restore(); + MessageBox.Show("还原成功!"); } catch (Exception ex) { Console.WriteLine(ex.Message); MessageBox.Show(ex.Message); } - btnRestore.Enabled = File.Exists(patcher.BakPath); + EnableAllButton(true); + btnRestore.Enabled = modifier.BackupExists(); } private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) @@ -131,32 +210,127 @@ namespace RevokeMsgPatcher private async void FormMain_Load(object sender, EventArgs e) { // 异步获取最新的补丁信息 - Task t = new Task(() => + string json = await HttpUtil.GetPatchJsonAsync(); + if (string.IsNullOrEmpty(json)) { - return new WebClient().DownloadString("https://huiyadanli.coding.me/i/patch.json"); - }); - t.Start(); - string json = await t; - if(string.IsNullOrEmpty(json)) - { - lblUpdatePachJson.Text = "获取失败"; - - } else - { - patcher.SetNewPatchJson(json); - lblUpdatePachJson.Text = "获取成功"; + lblUpdatePachJson.Text = "[ 获取最新补丁信息失败 ]"; } + else + { + try + { + JavaScriptSerializer serializer = new JavaScriptSerializer(); + Bag bag = serializer.Deserialize(json); + wechatModifier.Config = bag.Apps["Wechat"]; + qqModifier.Config = bag.Apps["QQ"]; + timModifier.Config = bag.Apps["TIM"]; + qqLiteModifier.Config = bag.Apps["QQLite"]; + + if (Convert.ToDecimal(bag.LatestVersion) > Convert.ToDecimal(thisVersion)) + { + needUpdate = true; + lblUpdatePachJson.Text = $"[ 存在最新版本 {bag.LatestVersion} ]"; + } + else + { + needUpdate = false; + lblUpdatePachJson.Text = "[ 获取成功,点击查看更多信息 ]"; + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + lblUpdatePachJson.Text = "[ 更换新配置时异常 ]"; + } + } } private void lblUpdatePachJson_Click(object sender, EventArgs e) { - string versions = ""; - patcher.TargetFiles.ForEach(t => + string tips = ""; + if (needUpdate) { - versions += t.Version + Environment.NewLine; - }); - MessageBox.Show("当前所支持的微信版本:" + Environment.NewLine + versions); + tips += "【当前存在最新版本,点击确定进入软件主页下载最新版本。】" + Environment.NewLine + Environment.NewLine; + } + tips += "支持以下版本" + Environment.NewLine; + tips += " ➯ 微信:" + wechatModifier.Config.GetSupportVersionStr() + Environment.NewLine; + tips += " ➯ QQ:" + qqModifier.Config.GetSupportVersionStr() + Environment.NewLine; + tips += " ➯ QQ轻聊版:" + qqLiteModifier.Config.GetSupportVersionStr() + Environment.NewLine; + tips += " ➯ TIM:" + timModifier.Config.GetSupportVersionStr() + Environment.NewLine; + + DialogResult dr = MessageBox.Show(tips, "当前支持防撤回的版本", MessageBoxButtons.OKCancel); + if (dr == DialogResult.OK && needUpdate) + { + System.Diagnostics.Process.Start("https://github.com/huiyadanli/RevokeMsgPatcher/releases"); + } + } + + private void radioButtons_CheckedChanged(object sender, EventArgs e) + { + EnableAllButton(false); + RadioButton radioButton = sender as RadioButton; + // 切换使用不同的防撤回对象 + if (rbtWechat.Checked) + { + modifier = (WechatModifier)rbtWechat.Tag; + } + else if (rbtQQ.Checked) + { + modifier = (QQModifier)rbtQQ.Tag; + } + else if (rbtTIM.Checked) + { + modifier = (TIMModifier)rbtTIM.Tag; + } + else if (rbtQQLite.Checked) + { + modifier = (QQLiteModifier)rbtQQLite.Tag; + } + txtPath.Text = modifier.FindInstallPath(); + ga.RequestPageView($"{GetCheckedRadioButtonNameEn()}/{lblVersion.Text}/switch", "切换标签页"); + EnableAllButton(true); + lblVersion.Text = ""; + btnRestore.Enabled = false; + // 显示是否能够备份还原 + if (!string.IsNullOrEmpty(txtPath.Text)) + { + modifier.InitEditors(txtPath.Text); + lblVersion.Text = modifier.GetVersion(); + btnRestore.Enabled = modifier.BackupExists(); + } + } + + private string GetCheckedRadioButtonNameEn() + { + if (rbtWechat.Checked) + { + return "wechat"; + } + else if (rbtQQ.Checked) + { + return "qq"; + } + else if (rbtTIM.Checked) + { + return "tim"; + } + else if (rbtQQLite.Checked) + { + return "qqlite"; + } + return "none"; + } + + private void EnableAllButton(bool state) + { + foreach (Control c in this.Controls) + { + if (c is Button) + { + c.Enabled = state; + } + } } } } diff --git a/RevokeMsgPatcher/FormMain.resx b/RevokeMsgPatcher/FormMain.resx index 97c6724..629e234 100644 --- a/RevokeMsgPatcher/FormMain.resx +++ b/RevokeMsgPatcher/FormMain.resx @@ -120,78 +120,289 @@ - AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA - AADn7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/W2t//zdHW/+bt8f/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5u3y/6Omqf/N1Nb/sbW4/83R1v/m7fH/5+3y/+ft - 8v/m7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5Oru/7O3uf/O0tf/y9DU/7C0 - t/+5vcD/s7i6/9TZ3f/n7PL/5+zy/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+zy/8jN - 0f+1ubz/6Ozx/8LGyf/Bxsn/ioyN/56ho/+us7f/19zg/+bt8v/m7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5u3y/9/l6f+foqT/qq2w/6isrf/e5Of/rLCy/7vAw/+bnqD/rrK0/83S1v/m7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/ur7C/6eqrf/Axcj/5u3y/8PHy/+anZ//mJqc/87S1f/m7fL/tru+/6isr//n7fH/xcrN/6yw - s//d4uf/5+3x/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+zy/7a6vf+6v8L/5evx/66ytf/S2Nz/5+zy/7GzuP9vcHH/hoiI/5+jpP/L0NP/wMTI/+ft - 8v/m7PH/5uzx/7a5vf+xtbj/5Orv/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7PL/pKmr/7m9wP+7wMT/tbm7/7e8v//n7PL/5+zy/9HU2f+fo6X/ztTX/62w - s/+Hi43/zdLX/+bt8f/m7fL/5+zy/+Hn7f+vsrb/tbm9/+fs8f/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+fs8v+5vcH/4efr/+Tp7v/n7PL/t7u//+fs8v/n7fL/5+3y/7i8 - v/+ipaj/5u3x/9bc4P/Cx8v/dHd5/+Lo7f/n7fH/5+3y/+bt8v/d4+b/q6+y/8DFyf/m7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/9/l6f+8wcT/293x/+To8f+3u77/5+zy/+ft - 8v/n7fH/5u3x/9DV2f+nqq3/0tbb/+bt8v/M0db/kZWY/7zAxP/h5+z/5+3y/+fs8v/m7PL/0Nbb/6mt - sP/W2+D/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3x/7a5vf+7se3/0M7w/7a7 - vv/n7PH/5+3y/+ft8v/n7fL/5u3y/+ft8f+9wMT/qq6x/+Lo7P/g5+z/vcLG/2FkZv/S19z/4efr/+ft - 8v/m7fL/6Ozy/7q+wf+zt7r/5uzx/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/m7fH/trm9/+fs - 8f/n7fL/uL2//+ft8f/n7fL/6Ozy/+Xr8v+2u7//s7e6/+br8f/k6e7/q6+y/7i9wP/m7fH/4Obq/5OW - mf+nq6//3ePn/+bs8f/n7fL/5+3y/9zi5/+prrH/p6uu/+Hl6f/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/Gys//2t/j/+bt8v+4vcD/5evw/+bs8f+5vcH/rbK1/97k6v/T2Nz/pqms/+Dl6v/m7fL/1tve/6er - rv/N0tb/5+3y/+ft8v+kqKz/4+nu/97k6P/m7PL/5+3y/9LX2/+5vcH/o6ap/+fs8//n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8f+xtbj/trq8/7e8v//Axcn/rbK1/9vh5f/n7fL/5u3x/8LGyv/m7PH/qa2v/9je - 4v/n7PL/6Ozx/8DFyP+qrrH/5Orv/+bs8v/Q1dr/zNHW/83T1/+tsLP/s7e5/6yvsv/Z3+P/6Ozy/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+fs8f/Y3eH/rK+z/9PX3P/n7fL/5+3y/+ft8v/m7fH/wMXI/+bs - 8v/m7PL/sLW3/9LY2//m7fH/6Ozy/+Ho7P+orK//xcrN/+Po7f+1ubz/rLCy/6+ytf/P1Nj/5+3x/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/7a6vv/n7PH/5+3y/+ft8v/n7fL/5+3y/+ft - 8f/Cx8r/5uzx/+ft8v/m7PL/q6+y/+bs8v/n7fL/5+3y/+bt8v/JztL/oaSn/6irrv/Hy8//5+3x/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/2+Hm/7y/wv/n7fL/5+3y/+jt - 8f/m7PD/ztTY/9Xa3v/m7fL/5+3y/+fs8f+wtbf/5uzy/+ft8v/n7fL/5+3y/+fs8v/m7PH/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/sba4/+Xq - 7//Z3uL/sbW4/7C0uP/X29//4ujs/8DEx//m7PH/4ufr/7W5vP/n7fH/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/k6e3/rbCz/7G1t/+1ubv/oKKl/+Xq7//n7PL/4+jt/7S4uv+tsbT/5evv/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/P1Nj/k5aY/7zBxP/m7PL/5+zz/+Tp7f+yt7r/r7O2/8jN0f/n7fH/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+bt8v/h5+v/rLCz/7q/wv+vsrb/rrO2/+Ln7P/m7fL/5u3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/29/k/+Xq7//m7PP/5u3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+bt8v/m7PL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5u3y/+bt - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft8v/n7fL/5+3y/+ft - 8v/n7fL/5+3y/+ft8v/n7fL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOAJPRTgOT0U4KE5EN1BPRTh2T0U4jU9FN5FPRTiRT0U4kU9F + OJFPRTiRT0Q4j09FN35PRTdcT0U4ME9FOBRPRTgET0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTcATkQ4Ek9FOEZORTiHT0U3yU9FN+VPRDf3T0Q3/09F + Nv9PRDb/T0Q1/09ENf9PRDX/T0Q1/09FNv9PRDf/T0Q3+09FN+tPRTfTTkU4m09FOFhPRTgcT0U4Ak9F + OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOAJPRTcqT0U4eE9FOM1ORDf1T0Q2/09E + Nv9ORTn/TkZD/05IVf9NSmP/TUxt/01Ndv9NTnr/TU13/01Mb/9NS2X/TklY/05HR/9ORTr/T0U2/09E + Nv9PRTf7T0U4209FOJVPRTg8T0Q4CE9FOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Ak5FOCRPRTiNT0Q4409F + N/1PRDX/TkU8/01JVv9MTn//S1Ol/0pWvP9KWM7/SlnU/0pZ1/9KWtj/SlrZ/0pa2P9KWtf/SlnV/0pZ + 0f9KV8L/S1Ss/0xQi/9NSl//TkZC/09ENv9PRDb/T0U48U9FOKtPRTg6T0U4BAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTcATkU3Ek5F + OHJPRTjfT0Q3/05FN/9OR0n/TE59/0tUrv9KWdD/SlrY/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa2P9KWtj/SlnU/0pWuv9MUIz/TUhV/05FOf9PRDb/T0Q3709F + OJdPRTgkT0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgCT0U4Mk9FOLtPRDf7T0Q2/05HR/9MT4f/SlfC/0pZ1/9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtj/SlnY/0pY + y/9MUZj/TUhV/05FN/9PRDf9T0U4105FN1RPRTgGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgGTkU3XE9FN+FPRDf/TkU8/01Mc/9KVr3/SlnX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrY/0pYyv9MT4j/TkZF/05ENv9ORTfxTkU3h09FOA5PRTcAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgIT0U4ek9FN/FPRTb/TkdM/0tSn/9KWdT/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnX/0tVtf9NSl//TkU3/09F + N/tPRTelT0U4GE5ENwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgKT0U4iU9FN/dORTj/TUpg/0pW + vP9KWdj/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtj/SljK/01NeP9ORTn/T0Q3/U9FOLdPRTgcT0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgIT0U4hU9F + N/lPRTf/TkdJ/0pWv/9KWtn/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdP/TE+L/05FO/9PRDf/T0U4s09FNxgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgCT0U4bE9FN/VPRTf/T0U3/05FOP9NTXP/SljO/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1f9MT4z/TkU7/09E + N/1PRTihT0U3DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgAT0U4Rk9FN+1ORTb/TUtn/0xNe/9ORTn/TkU3/01Mcf9KWM3/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlnV/0xPiP9ORTj/T0U3+09FOHxPRTgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Ik9FONFPRTb/TUlX/0pXxP9KWdL/TE6A/05F + Of9ORTf/TUxv/0pYzf9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdL/TUxz/05FN/9PRTftT0U4SE9FOAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATkU4Bk5FOJ1PRDf9TkZD/0tU + sP9KWtj/SlrX/0pZ0v9MToD/TkU5/05FN/9NTHD/SljN/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pXx/9NSVn/T0Q2/09F + OM1PRTgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4AE9F + OEhPRTfzTkU4/0xQjP9KWdf/SlrX/0pa1/9KWtf/SlnS/0xOgP9ORTn/TkU3/01McP9KWM3/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtj/S1St/05GQP9PRTf9T0U4g09FOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE9FOA5PRTi9T0Q2/01JWv9KWM3/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdL/TE6B/05F + OP9ORTf/TUxx/0pYzf9KWdf/SlrX/0pa1/9KWtf/SlrX/0pZ1P9KWcz/SlrW/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0pZzf9MTXv/TkQ2/09FN+VPRDgsT0U4AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOABORThYT0U4905FPP9LU6X/SlrY/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ0/9MToL/TkU5/05FN/9NTG//SljN/0pa1/9KWtf/SlrX/0pZ1P9LToL/TlOQ/0pa + 1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYzf9QUHH/Sla//05HTP9PRDf/T0U4k09F + OAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgKT0U4vU9ENv9NSmT/SlnS/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnS/0xOgv9ORTj/TkU2/01Mb/9KWMz/SlrX/0pZ + 0/9LToP/TkQ1/01Tlv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ0v9NTXL/T0xh/0pa + 1v9MT4n/TkU3/09FN+VPRTgmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgAT0U4PE9F + N/FORTr/S1Ol/0pZ2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdP/TE+C/05F + Of9ORTf/TUxt/0pXyf9LTX//TkQ2/05ENP9NUpb/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrW/0pZ + 0v9PUoX/T0U2/05Mav9KWtf/SlfC/05HSv9PRTf9T0U4eE9FOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAATkU3Ak9FOI1PRTf/TkhV/0pYzv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ0v9MToL/TkU5/05FNv9NR0//TkQ1/09FNf9ORDT/TVKW/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pYzv9OUHr/T0U3/05ENf9MTXX/SlrX/0pZ1/9NTXX/T0Q2/05FOMVORTgOAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FNxRPRTfRTkQ2/0xOgP9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnS/0xOgP9NRDb/TkQ1/09FNf9PRTX/TkQ0/01S + lv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0tYw/9NSmH/TkU1/09FNf9ORDX/T1Wb/0pa1/9KWtj/S1Oq/05F + Ov9PRTfzT0U4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTg8T0U38U5FPP9LVLH/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ0v9MTHT/TkQ1/09F + Nf9PRTX/T0U1/05ENP9NU5b/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtb/SlnP/01SlP9PR0b/TkQ0/09FNf9PRTX/TkQ3/0xX + uf9KWtf/SlrX/0pYyv9OSE7/T0Q3/U9FOHpPRTcAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTgAT0U4eE5E + N/1OSFD/SljK/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ + 0v9MTHP/TkQ1/09FNf9PRTX/T0U1/09FNf9ORDT/TVKV/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWc3/TFOc/09KVv9PRTb/T0U1/09F + Nf9PRTX/T0U1/09KUv9LWcz/SlrX/0pa1/9KWdf/TExz/09ENv9PRTixT0U4CAAAAAAAAAAAAAAAAAAA + AAAAAAAAT0U4Bk9FOKVPRTb/TUxt/0pZ1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ0P9MS2//TkQ1/09FNf9PRTX/T0U1/09FNf9PRTX/TkQ0/09MZP9KWMv/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1v9KWdP/S1nO/0tUp/9UVof/iYaO/7u3 + sv9ORDT/T0U1/2phVP/Mycb/YllL/05ENP9OTnP/SlrW/0pa1/9KWtf/SlrZ/0tQkP9ORTb/T0U41U9F + OBoAAAAAAAAAAAAAAAAAAAAAAAAAAE5FOBBPRTjFTkQ1/0xPhP9KWtn/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZz/9MS27/TkQ1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/05E + NP9PRTf/UU1g/0xRlP9LUpv/TFat/01Yuv9NWcH/TVnB/01Zvv9NV7L/S1Oh/0tRlv9PUon/Tkpb/09I + Sf9iWk7/zsvH/9jW0/+qpZ7/T0U1/8K/u//Kx8T/0c/M/1NKOv9ORDT/TVex/0pa1/9KWtf/SlrX/0pa + 2P9LVLD/TkU8/09FN/FPRTg4AAAAAAAAAAAAAAAAAAAAAAAAAABPRTgkTkU35U5FN/9LU6H/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYz/9MS2r/TkQ1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0Q0/05ENP9ORDT/TkQ1/09GOf9PRj7/T0dA/09HQP9PRz//T0Y7/05F + Nv9ORDT/TkQ0/05ENP9PRTX/mJKJ/6unoP+HgXb/zcrG/2ZeUP/Ewb3/nJeP/66qo/9waFz/T0pX/0pZ + zv9KWtf/SlrX/0pa1/9KWtf/SlfC/05GRP9PRTf5TkU4TgAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Mk9F + N+9ORTv/S1W0/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYzv9MSmf/TkQ1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/6Kdlf9waFv/T0U1/8TBvP9vZ1r/o56X/09F + Nf+gm5P/cmpf/0xSl/9KWtb/SlrX/0pa1/9KWtf/SlrX/0pYyv9OR0z/T0U3+09FOFgAAAAAAAAAAAAA + AAAAAAAAAAAAAE9FODpPRTfzTkU+/0tVuv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pY + zf9MSmX/TkQ1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf+QioD/jIZ8/09F + Nf/DwLv/YFdI/7y5s/9PRTX/sa2m/2FcZv9KWMr/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWM7/TkhT/09E + NvtPRThYAAAAAAAAAAAAAAAAAAAAAAAAAABPRTdCT0U39U5GQP9KVr7/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pYzP94eJT/XFNE/09FNf9+d2z/VEo7/09FNf9SSTn/f3ht/4mDeP9bUkP/T0U1/1NJ + Of+AeW7/gntw/09FNf9yal3/YFdJ/09FNf91bmH/XVRF/1ZNPv+HgHb/c2tf/3t0af9PRTX/Ukk5/4iB + d/9qYVT/WVBB/7Wxq/+uqaL/enJn/09FNf+inZX/qaWe/5yXkf9MU5z/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlnQ/01JV/9PRDb7T0U4WAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U3Qk9FN/VORkD/Sla+/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYy/9UUWn/9PT0/6GclP9dU0X/+Pj4/4mDeP9PRTX/raih/9XT + 0P+3s63/wL24/09FNf/LyMT/1NHO/8fEwP95cWX/trKt/4N8cf9PRTX/wb25/3lxZf+tqaL/zszI/9nX + 1f/T0c7/T0U1/4uEev/d3Nr/nJaO/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/05ENP9OTGX/SlnT/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ0P9NSVf/T0Q2+09FOFgAAAAAAAAAAAAAAAAAAAAAAAAAAE9F + ODpPRTfzTkY+/0tWuv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pYyf9MSVz/h4B2/8XCvv/PzMn/koyC/9LQ + zf/Bvrn/T0U1/+Xj4v+2sq3/qqWf/6mknf9kW03/5+bl/1BGNv9PRTX/T0U1/7ayrf+DfHH/T0U1/8G9 + uf95cWX/rKeg/7ayrf+dmI//09HO/09FNf+Ykon/opyV/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09E + NP9PSUr/S1jD/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWM7/TkhT/09ENvtPRThYAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTcyT0Q3705FO/9LVbT/SlrY/0pa1/9KWtf/SlrX/0pXyP9NSVv/TkQ1/8G+ + uf+IgXf/ycbC/97c2/94cWX/6Ofm/1VMPP/f3dv/qaSd/5SOhf/x8fH/YFdJ/+rp6f9WTD3/T0U1/09F + Nf+2sq3/mZSL/09FNf/IxcD/eXFl/1hPQP+WkIf/wLy3/9PRzv9PRTX/mJKJ/6Kclf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9ORTr/TFOg/0pa1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SljK/05H + TP9PRTf7T0U4WAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4JE9FOOdORDj/S1Kj/0pa2f9KWtf/SlrX/0pZ + z/9NSVj/TkQ1/1hOP//o5+b/U0o6/4uEev/r6un/UEY2/7y4s/+Ignf/hn90/+fl5P/b2db/sq2n/09F + Nf+uqqP/5uXk/9LQzf95cWX/trKt/+vq6f/Qzcr/7e3s/1xSRP9kXE7/5ePi/93b2f+rpp//W1JD/+De + 3P/i4d//m5WN/09FNf9PRTX/T0U1/09FNf9ORTn/TlOX/0pZ1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pXw/9ORkT/T0U3+U9FOE4AAAAAAAAAAAAAAAAAAAAAAAAAAE9EOBBPRTjHTkQ1/0xP + hf9KWtn/SlrX/0pa1/9KWdP/TE5+/05FNv9SSDn/aF9S/09FNf9USjv/Zl1Q/09FNf9dVEX/XVRF/09F + Nf9fVkf/aWFT/09FNf9PRTX/T0U1/2deUf9uZln/UEY2/7ayrf+EfXL/bmZZ/1lQQf9PRTX/T0U1/15V + R/9uZln/UUc4/1JIOP+qpZ7/sq6o/2JZS/9PRTX/T0U1/09FNf9ORTj/T1ON/0pa1f9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa2P9LVbL/TkU9/09FN/NPRTg6AAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgGT0U4pU9FNv9NTG7/SlrX/0pa1/9KWtf/SlrX/0pZ0/9LToL/TkU2/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf+2sqz/g3xx/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/XVRF/2lgU/9PRTX/T0U1/09FNf9ORTr/T1SU/0pZ + 1P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtn/TFGR/05FN/9PRTjXT0U4HAAA + AAAAAAAAAAAAAAAAAAAAAAAAT0U4AE9FOHpPRDf9TkhS/0pYyv9KWtf/SlrX/0pa1/9KWtf/SlnV/0tP + hv9ORTf/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/05E + NP9ORkH/TVSh/0pa1v9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnX/01N + dP9PRDb/T0U4tU9FOAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTg+TkQ3805FPf9LVbP/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWdT/S0+J/05FN/9PRTX/T0U1/09FNf9PRTX/xMG9/2JZS/9WTD3/2dfU/46H + fv+EfXL/gHlu/5+akv+Nh33/m5aN/4uFev9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/05ENP9QTF3/S1a3/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pYy/9OSFD/T0Q2/U9FOHxPRTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U3Fk9F + N9VPRDb/TE+E/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1f9LT4v/TkU4/09FNf9PRTX/T0U1/9XT + 0P9lXE7/T0U1/7i0r/9aUUL/i4V6/4qDef/g393/1NLP/6ijnP+UjoT/T0U1/09FNf9PRTX/T0U1/09F + Nf9PRTX/T0U1/09FNf9ORDT/TkQ0/05ENf9QSk7/S1e//0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa2P9LVK7/TkU7/09FN/VPRTg6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE9FOAJPRTiRT0U2/05JVv9KWM7/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0tQ + j/9ORTj/T0U1/09FNf/V09D/ZVxO/09FNf+4tK//WlFC/4uFev/Fwr7/p6Ka/7Svqf/V0s//lI6E/09F + Nf9PRTX/T0U1/09FNf9PRTX/T0U1/09FNf9ORDX/TkdB/05MYf9PRjz/TkU2/01LZv9KWMn/SlrY/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdf/TU13/09ENv9PRTjJT0U4EAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4QE9FN/NORTv/S1Oo/0pa2P9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdX/S1CS/05FOP9TSjr/1tTR/2lhU/9QRjb/uLSv/1pRQv+LhXr/+Pj4/2Rb + Tf9xaV3/+vn6/5SOhP9PRTX/T0U1/05ENP9ORDT/TkU1/05ENv9PSlX/TVCF/0tXwv9KWdT/TE+J/05F + Ov9ORTb/TUto/0pXyf9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlfE/05H + TP9PRTf9T0U4fE9FOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOAxPRTjBT0Q2/01L + Z/9KWdL/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1f9LUZT/19bY/+Xk4//l5OP/fXZq/9jW + 1P+Oh37/g3xy/7OvqP9PRTX/TkU1/7y4s/+LhXz/TkU4/05FOv9ORkL/T01o/01PgP9MV7b/SlnP/0pa + 1/9KWtf/SlrX/0pZ1P9MUIv/TkU7/09FNv9NS2b/SlfK/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrY/0xQjf9ORTf/T0U3509FOCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgAT0U4Xk9FN/lORT3/S1Op/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0tR + mP9ORTn/T0U1/09FNf9PRTX/T0U1/09FNf9PRTX/T0U1/0xLZf9NUYv/TlSY/05Ysf9KV8H/SljH/0pZ + 1P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnU/0xPiv9ORTr/TkQ2/01LaP9KV8n/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pXxf9OSE7/T0U3/09FOJtPRTgGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOBBPRTjBTkQ2/01JXf9KWc7/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdb/S1Gb/05FO/9PRTX/T0U1/09FNf9PRTX/T0U1/05ENP9NUpf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdX/TFCM/05F + PP9ORTb/TUpl/0pXyP9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1/9MTn//TkU2/09FN+lPRTgwT0U4AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPRTcAT0U4Tk9FN/VORTn/TFCR/0pa + 2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1v9KUp3/TkU7/09FNf9PRTX/T0U1/09F + Nf9ORDT/TVKW/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ1P9MUIv/TkU7/09FNv9NSmb/SlfH/0pa2P9KWtf/SlrX/0pa2P9LVbL/TkZC/09F + N/1PRDiJT0U4AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5F + OAhPRDihT0Q3/05GRf9LVbP/SlnY/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0tS + oP9ORT3/T0U1/09FNf9PRTX/TkQ0/01Slv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0xPjP9ORTz/TkU2/01KZf9KV8j/SlrX/0pa + 1/9KWMn/TUlb/05FNv9PRTjRT0U4HgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAT0U3Jk9FONdORDb/TUlb/0pXyP9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdb/SlOj/05FPv9PRTX/T0U1/05ENP9NUpb/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdT/TFCK/05F + O/9ORTb/TUpl/0pXyP9KWdT/TE15/05FN/9PRTfxT0U4UE9FOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FNwBPRTdOT0U3705FN/9NS2z/SljO/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1v9KU6b/TkU+/05FNf9ORDT/TVKW/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pZ1f9MUI3/TkU7/05FNv9NS2b/TE+D/05FOv9PRTf7T0U4g09FOAQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4BE9F + N3RPRTf3TkU3/01Ndf9KWM//SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnW/0pT + qP9ORkD/TkQ0/01Slv9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlnV/0xQjf9ORj7/T0Q3/09FN/9PRTf9T0U4qU9F + OBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgIT0U4jU9FN/tORTj/TUxy/0pYzP9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWdb/SlSr/01FP/9NU5b/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWdH/TUtn/09F + Nv9PRTf/T0U3u09FOBxPRTcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOA5PRTiVT0U3+U5FNv9NS2b/SlbA/0pZ + 2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pZ1v9KU6f/TlOT/0pa1v9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 2P9KWM3/TE6A/05FPP9PRDf9T0U4v09FOCBPRTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4Ck9F + OINPRTf1TkU2/05IT/9LU6X/SlnW/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa2P9KVrr/TUpk/05EOP9PRDf9T0U3r09FNx5ORDcAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABPRTgGT0U4ZE9FN+VPRDb/TkU+/0xNe/9KV8H/SlrY/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrY/0pYzf9MUJD/TkdI/05ENv9PRTfzT0U4kU9FOBJPRTcAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FNwJPRTc6T0U4w09EN/tPRDb/TkdK/0xQ + jf9KV8X/SlrY/0pa2P9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtj/SljO/0tSn/9OSVr/TkU4/09EN/9PRTjdT0U3XE9F + OAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4AE9F + NxRPRTd+T0U35U9FN/9ORDf/TkdO/0xOg/9KVbf/SlnR/0pa2f9KWtj/SlrX/0pa1/9KWtf/SlrX/0pa + 1/9KWtf/SlrX/0pa1/9KWtf/SlrX/0pa1/9KWtj/SlrZ/0pZ1f9KV8D/S1GU/01JWv9ORTn/T0Q2/09F + N/FPRTehT0U4Kk9FOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAT0U4Ak9FOCxORTeVT0U36U9FN/9PRDb/TkY+/01KXf9MT4P/S1St/0pX + xP9KWM7/SlnV/0pZ2P9KWtj/SlnY/0pZ2P9KWtj/SlnW/0pZ0P9KV8f/S1W2/0xQjv9NS2j/TkZD/09E + N/9PRTf/T0U3809FN7NPRThET0U3BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0U4BE9FODBPRTiHT0U31U9F + N/dPRDb/T0U2/05FO/9ORkb/TUhV/01Mbf9NTXr/TE6A/0xPhP9MToH/TE58/01Mcf9NSVz/TkdJ/05G + Pf9ORTf/T0Q2/09EN/tORDflT0U4nU9EOEZPRTgKT0U4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAT0U4Ak9FNxZPRTdMT0U4mU9FOMtPRTfvT0U3+U9ENv1PRDb/T0U2/09FNv9PRTb/T0U2/09F + Nv9PRDb/T0Q2/09EN/lPRTfzT0U32U9FOKdPRTdgT0U4Ik9FOARPRTgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9FOARPRTgST0U4ME9FN1JPRTh+TkU3o09F + OLFPRTi9T0U4wU9FOL9PRTi1T0U4qU9FOItPRThaT0U4Ok5FNxhPRTcGT0U4AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABPRTgAT0U4Ak9FOARPRTgET0U4Bk9FOAZPRTgGT0U4BE9FOARPRTgCT0U4AE9FOAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////////////////////////////////////8B////////4AAP//////+AAAH//////AAAAH + /////4AAAAH////+AAAAAP////wAAAAAP///+AAAAAAf///gAAAAAA///8AAAAAAB///wAAAAAAD//+A + AAAAAAP//wAAAAAAAf/+AAAAAAAA//4AAAAAAAB//AAAAAAAAH/8AAAAAAAAP/gAAAAAAAA/+AAAAAAA + AD/wAAAAAAAAH/AAAAAAAAAf8AAAAAAAAB/wAAAAAAAAD+AAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AA + AAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAAAA + AA/gAAAAAAAAD/AAAAAAAAAP8AAAAAAAAB/wAAAAAAAAH/AAAAAAAAAf+AAAAAAAAD/4AAAAAAAAP/wA + AAAAAAA//AAAAAAAAH/+AAAAAAAAf/4AAAAAAAD//wAAAAAAAf//gAAAAAAB///AAAAAAAP//8AAAAAA + B///4AAAAAAP///wAAAAAB////wAAAAAP////gAAAAD/////gAAAAf/////AAAAH//////AAAB////// + /gAA////////8A////////////////////////////////////8= \ No newline at end of file diff --git a/RevokeMsgPatcher/Model/App.cs b/RevokeMsgPatcher/Model/App.cs new file mode 100644 index 0000000..cbeaf54 --- /dev/null +++ b/RevokeMsgPatcher/Model/App.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model +{ + public class App + { + public string Name { get; set; } + + public Dictionary FileTargetInfos { get; set; } + + public Dictionary> FileModifyInfos { get; set; } + + public HashSet GetSupportVersions() + { + // 使用 HashSet 防重 + HashSet versions = new HashSet(); + foreach (List modifyInfos in FileModifyInfos.Values) + { + foreach (ModifyInfo modifyInfo in modifyInfos) + { + versions.Add(modifyInfo.Version); + } + } + return versions; + } + + public string GetSupportVersionStr() + { + string str = ""; + foreach (string v in GetSupportVersions()) + { + str += v + "、"; + } + if (str.Length > 1) + { + str = str.Substring(0, str.Length - 1); + } + return str; + } + } +} diff --git a/RevokeMsgPatcher/Model/Bag.cs b/RevokeMsgPatcher/Model/Bag.cs new file mode 100644 index 0000000..c84dd6a --- /dev/null +++ b/RevokeMsgPatcher/Model/Bag.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model +{ + public class Bag + { + public Dictionary Apps { get; set; } + + public string LatestVersion { get; set; } + + public string Notice { get; set; } + } +} diff --git a/RevokeMsgPatcher/Model/Change.cs b/RevokeMsgPatcher/Model/Change.cs new file mode 100644 index 0000000..df3c99f --- /dev/null +++ b/RevokeMsgPatcher/Model/Change.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model +{ + public class Change + { + public long Position { get; set; } + + public byte[] Content { get; set; } + + public Change Clone() + { + Change o = new Change(); + o.Position = Position; + o.Content = Content; + return o; + } + } +} diff --git a/RevokeMsgPatcher/Model/ModifyInfo.cs b/RevokeMsgPatcher/Model/ModifyInfo.cs new file mode 100644 index 0000000..c83a39b --- /dev/null +++ b/RevokeMsgPatcher/Model/ModifyInfo.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model +{ + public class ModifyInfo + { + public string Name { get; set; } + + //public string RelativePath { get; set; } + + public string Version { get; set; } + + public string SHA1Before { get; set; } + + public string SHA1After { get; set; } + + public List Changes { get; set; } + + public ModifyInfo Clone() + { + ModifyInfo o = new ModifyInfo(); + o.Name = Name; + o.Version = Version; + o.SHA1Before = SHA1Before; + o.SHA1After = SHA1After; + List cs = new List(); + foreach(Change c in Changes) + { + cs.Add(c.Clone()); + } + o.Changes = cs; + return o; + } + } +} diff --git a/RevokeMsgPatcher/Model/TargetFile.cs b/RevokeMsgPatcher/Model/TargetFile.cs deleted file mode 100644 index f4c8819..0000000 --- a/RevokeMsgPatcher/Model/TargetFile.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RevokeMsgPatcher.Model -{ - public class TargetFile - { - public string FileName { get; set; } - - public string Version { get; set; } - - public string SHA1Before { get; set; } - - public string SHA1After { get; set; } - - public long Position { get; set; } - - public byte Content { get; set; } - } -} diff --git a/RevokeMsgPatcher/Model/TargetInfo.cs b/RevokeMsgPatcher/Model/TargetInfo.cs new file mode 100644 index 0000000..28a1dd2 --- /dev/null +++ b/RevokeMsgPatcher/Model/TargetInfo.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model +{ + public class TargetInfo + { + public string Name { get; set; } + + public string RelativePath { get; set; } + + public string Memo { get; set; } + + public TargetInfo Clone() + { + TargetInfo o = new TargetInfo(); + o.Name = Name; + o.RelativePath = RelativePath; + o.Memo = Memo; + return o; + } + } +} diff --git a/RevokeMsgPatcher/Modifier/AppModifier.cs b/RevokeMsgPatcher/Modifier/AppModifier.cs new file mode 100644 index 0000000..827520e --- /dev/null +++ b/RevokeMsgPatcher/Modifier/AppModifier.cs @@ -0,0 +1,215 @@ +using RevokeMsgPatcher.Model; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Modifier +{ + /// + /// 1. 自动获取安装目录(已验证) 或者手动填写安装目录 + /// 2. 验证安装目录 验证要修改的 dll 是否存在 + /// 3. 判断所有 dll 是否符合防撤回要求 + /// 4. 备份所有 dll // *.h.bak + /// 5. 根据每个 dll 匹配的 修改信息 循环修改 + /// + public abstract class AppModifier + { + protected App config; + + public App Config { set { config = value; } get { return config; } } + + protected List editors; + + public string InstallPath { get; set; } + + /// + /// 自动搜索应用安装路径 + /// + /// 应用安装路径 + public abstract string FindInstallPath(); + + //public abstract bool ValidateAndInitialize(string installPath); + + /// + /// 获取版本号 + /// + /// + public abstract string GetVersion(); + + /// + /// 判断APP安装路径内是否都存在要修改的文件 + /// + /// APP安装路径 + /// + public bool IsAllFilesExist(string installPath) + { + if (string.IsNullOrEmpty(installPath)) + { + return false; + } + int success = 0; + foreach (TargetInfo info in config.FileTargetInfos.Values) + { + string filePath = Path.Combine(installPath, info.RelativePath); + if (File.Exists(filePath)) + { + success++; + } + } + if (success == config.FileTargetInfos.Count) + { + return true; + } + else + { + return false; + } + } + + /// + /// a.初始化修改器 + /// + /// APP安装路径 + public void InitEditors(string installPath) + { + // 初始化文件修改器 + editors = new List(); + foreach (TargetInfo info in config.FileTargetInfos.Values) + { + FileHexEditor editor = new FileHexEditor(installPath, info); + editors.Add(editor); + } + + } + + /// + /// b.验证文件完整性,寻找对应的补丁信息 + /// + public void ValidateAndFindModifyInfo() + { + // 寻找对应文件版本与SHA1的修改信息 + foreach (FileHexEditor editor in editors) // 多种文件 + { + // 通过SHA1和文件版本判断是否可以打补丁 根据不同结果返回不同的提示 + ModifyInfo matchingSHA1Before = null, matchingSHA1After = null, matchingVersion = null; + foreach (ModifyInfo modifyInfo in config.FileModifyInfos[editor.FileName]) // 多个版本信息 + { + if (modifyInfo.Name == editor.FileName) // 保险用的无用判断 + { + if (editor.FileSHA1 == modifyInfo.SHA1After) + { + matchingSHA1After = modifyInfo; + } + else if (editor.FileSHA1 == modifyInfo.SHA1Before) + { + matchingSHA1Before = modifyInfo; + } + + if (editor.FileVersion == modifyInfo.Version) + { + matchingVersion = modifyInfo; + } + } + } + + // 补丁前SHA1匹配上,肯定是正确的dll + if (matchingSHA1Before != null) + { + editor.FileModifyInfo = matchingSHA1Before; + continue; + } + // 补丁后SHA1匹配上,肯定已经打过补丁 + if (matchingSHA1After != null) + { + throw new BusinessException("installed", $"你已经安装过此补丁,文件路径:{editor.FilePath}"); + } + // 全部不匹配,说明不支持 + if (matchingSHA1Before == null && matchingSHA1After == null && matchingVersion == null) + { + throw new BusinessException("not_support", $"不支持此版本:{editor.FileVersion},文件路径:{editor.FilePath}"); + } + // SHA1不匹配,版本匹配,可能dll已经被其他补丁程序修改过 + if ((matchingSHA1Before == null && matchingSHA1After == null) && matchingVersion != null) + { + throw new BusinessException("maybe_modified", $"程序支持此版本:{editor.FileVersion}。但是文件校验不通过,请确认是否使用过其他补丁程序。文件路径:{editor.FilePath}"); + } + } + } + + /// + /// c.根据补丁信息,安装补丁 + /// + /// + public bool Patch() + { + // 首先验证文件修改器是否没问题 + foreach (FileHexEditor editor in editors) + { + if (editor.FileModifyInfo == null) + { + throw new Exception("补丁安装失败,原因:文件修改器初始化失败!"); + } + } + // 再备份所有文件 + foreach (FileHexEditor editor in editors) + { + editor.Backup(); + } + // 打补丁! + List done = new List(); // 已经打上补丁的 + try + { + foreach (FileHexEditor editor in editors) + { + bool success = editor.Patch(); + if (!success) + { + editor.Restore(); + } + done.Add(editor); + } + } + catch (Exception ex) + { + // 恢复所有已经打上补丁的文件 + foreach (FileHexEditor editor in done) + { + editor.Restore(); + } + throw ex; + } + return true; + } + + public bool BackupExists() + { + foreach (FileHexEditor editor in editors) + { + if (!File.Exists(editor.FileBakPath)) + { + return false; + } + } + return true; + } + + public bool Restore() + { + if (BackupExists()) + { + foreach (FileHexEditor editor in editors) + { + editor.Restore(); + } + return true; + } + else + { + throw new Exception("备份文件不存在,还原失败!"); + } + } + } +} diff --git a/RevokeMsgPatcher/Modifier/FileHexEditor.cs b/RevokeMsgPatcher/Modifier/FileHexEditor.cs new file mode 100644 index 0000000..d4ecb16 --- /dev/null +++ b/RevokeMsgPatcher/Modifier/FileHexEditor.cs @@ -0,0 +1,74 @@ +using RevokeMsgPatcher.Model; +using RevokeMsgPatcher.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Modifier +{ + public class FileHexEditor + { + public string FileName { get; set; } + + public string FilePath { get; set; } + + public string FileBakPath { get; set; } + + private string version; + public string FileVersion + { + get + { + if (version == null) + { + version = FileUtil.GetFileVersion(FilePath); + } + return version; + } + } + + public string sha1; + public string FileSHA1 + { + get + { + if (sha1 == null) + { + sha1 = FileUtil.ComputeFileSHA1(FilePath); + } + return sha1; + } + } + + public TargetInfo FileTargetInfo { get; set; } + + public ModifyInfo FileModifyInfo { get; set; } + + public FileHexEditor(string installPath, TargetInfo target) + { + FileTargetInfo = target.Clone(); + FileName = FileTargetInfo.Name; + FilePath = Path.Combine(installPath, FileTargetInfo.RelativePath); + FileBakPath = FilePath + ".h.bak"; + } + + public void Backup() + { + File.Copy(FilePath, FileBakPath, true); + } + + public bool Patch() + { + FileUtil.EditMultiHex(FilePath, FileModifyInfo.Changes); + return true; + } + + public void Restore() + { + File.Copy(FileBakPath, FilePath, true); + } + } +} diff --git a/RevokeMsgPatcher/Modifier/QQLiteModifier.cs b/RevokeMsgPatcher/Modifier/QQLiteModifier.cs new file mode 100644 index 0000000..45af711 --- /dev/null +++ b/RevokeMsgPatcher/Modifier/QQLiteModifier.cs @@ -0,0 +1,56 @@ +using RevokeMsgPatcher.Model; +using RevokeMsgPatcher.Utils; + +namespace RevokeMsgPatcher.Modifier +{ + class QQLiteModifier : AppModifier + { + public QQLiteModifier(App config) + { + this.config = config; + } + + /// + /// 自动寻找获取微信安装路径 + /// + /// + public override string FindInstallPath() + { + string installPath = PathUtil.FindInstallPathFromRegistry("QQLite"); + if (!IsAllFilesExist(installPath)) + { + foreach (string defaultPath in PathUtil.GetDefaultInstallPaths(@"Tencent\QQLite")) + { + if (IsAllFilesExist(defaultPath)) + { + return defaultPath; + } + } + } + else + { + return installPath; + } + return null; + } + + /// + /// 获取整个APP的当前版本 + /// + /// + public override string GetVersion() + { + if (editors != null && editors.Count > 0) + { + foreach (FileHexEditor editor in editors) + { + if (editor.FileName == "IM.dll") + { + return editor.FileVersion; + } + } + } + return ""; + } + } +} diff --git a/RevokeMsgPatcher/Modifier/QQModifier.cs b/RevokeMsgPatcher/Modifier/QQModifier.cs new file mode 100644 index 0000000..e52dde8 --- /dev/null +++ b/RevokeMsgPatcher/Modifier/QQModifier.cs @@ -0,0 +1,56 @@ +using RevokeMsgPatcher.Model; +using RevokeMsgPatcher.Utils; + +namespace RevokeMsgPatcher.Modifier +{ + class QQModifier : AppModifier + { + public QQModifier(App config) + { + this.config = config; + } + + /// + /// 自动寻找获取微信安装路径 + /// + /// + public override string FindInstallPath() + { + string installPath = PathUtil.FindInstallPathFromRegistry("{052CFB79-9D62-42E3-8A15-DE66C2C97C3E}"); + if (!IsAllFilesExist(installPath)) + { + foreach (string defaultPath in PathUtil.GetDefaultInstallPaths(@"Tencent\QQ")) + { + if (IsAllFilesExist(defaultPath)) + { + return defaultPath; + } + } + } + else + { + return installPath; + } + return null; + } + + /// + /// 获取整个APP的当前版本 + /// + /// + public override string GetVersion() + { + if (editors != null && editors.Count > 0) + { + foreach (FileHexEditor editor in editors) + { + if (editor.FileName == "IM.dll") + { + return editor.FileVersion; + } + } + } + return ""; + } + } +} diff --git a/RevokeMsgPatcher/Modifier/TIMModifier.cs b/RevokeMsgPatcher/Modifier/TIMModifier.cs new file mode 100644 index 0000000..37938b8 --- /dev/null +++ b/RevokeMsgPatcher/Modifier/TIMModifier.cs @@ -0,0 +1,56 @@ +using RevokeMsgPatcher.Model; +using RevokeMsgPatcher.Utils; + +namespace RevokeMsgPatcher.Modifier +{ + class TIMModifier : AppModifier + { + + public TIMModifier(App config) + { + this.config = config; + } + /// + /// 自动寻找获取微信安装路径 + /// + /// + public override string FindInstallPath() + { + string installPath = PathUtil.FindInstallPathFromRegistry("TIM"); + if (!IsAllFilesExist(installPath)) + { + foreach (string defaultPath in PathUtil.GetDefaultInstallPaths(@"Tencent\TIM")) + { + if (IsAllFilesExist(defaultPath)) + { + return defaultPath; + } + } + } + else + { + return installPath; + } + return null; + } + + /// + /// 获取整个APP的当前版本 + /// + /// + public override string GetVersion() + { + if (editors != null && editors.Count > 0) + { + foreach (FileHexEditor editor in editors) + { + if (editor.FileName == "IM.dll") + { + return editor.FileVersion; + } + } + } + return ""; + } + } +} diff --git a/RevokeMsgPatcher/Modifier/WechatModifier.cs b/RevokeMsgPatcher/Modifier/WechatModifier.cs new file mode 100644 index 0000000..ace78e8 --- /dev/null +++ b/RevokeMsgPatcher/Modifier/WechatModifier.cs @@ -0,0 +1,72 @@ +using RevokeMsgPatcher.Model; +using RevokeMsgPatcher.Utils; + +namespace RevokeMsgPatcher.Modifier +{ + class WechatModifier : AppModifier + { + + public WechatModifier(App config) + { + this.config = config; + } + + /// + /// 自动寻找获取微信安装路径 + /// + /// + public override string FindInstallPath() + { + string installPath = PathUtil.FindInstallPathFromRegistry("Wechat"); + if (!IsAllFilesExist(installPath)) + { + foreach (string defaultPath in PathUtil.GetDefaultInstallPaths(@"Tencent\Wechat")) + { + if (IsAllFilesExist(defaultPath)) + { + return defaultPath; + } + } + } + else + { + return installPath; + } + return null; + } + + /// + /// 获取整个APP的当前版本 + /// + /// + public override string GetVersion() + { + if (editors != null && editors.Count > 0) + { + foreach (FileHexEditor editor in editors) + { + if (editor.FileName == "WeChatWin.dll") + { + return editor.FileVersion; + } + } + } + return ""; + } + + //public override bool ValidateAndInitialize(string installPath) + //{ + // // 判断是否是安装路径 + // if (!IsAllBinaryFilesExist(installPath)) + // { + // return false; + // } + + // // 初始化十六进制文件编辑器 + // // 并寻找与之配对的版本修改信息 + // InitEditors(installPath); + + // return true; + //} + } +} diff --git a/RevokeMsgPatcher/Patcher.cs b/RevokeMsgPatcher/Patcher.cs deleted file mode 100644 index 8e69281..0000000 --- a/RevokeMsgPatcher/Patcher.cs +++ /dev/null @@ -1,91 +0,0 @@ -using RevokeMsgPatcher.Model; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Web.Script.Serialization; - -namespace RevokeMsgPatcher -{ - public class Patcher - { - private string intallPath; - public string IntallPath - { - get { return intallPath; } - set - { - intallPath = value; - if (!string.IsNullOrEmpty(intallPath) && Util.IsWechatInstallPath(intallPath)) - { - dllPath = Path.Combine(intallPath, "WeChatWin.dll"); - bakPath = Path.Combine(intallPath, "WeChatWin.dll.h.bak"); - } - else - { - intallPath = null; - dllPath = null; - bakPath = null; - } - } - } - - private string dllPath; - private string bakPath; - - public string DllPath { get => dllPath; } - public string BakPath { get => bakPath; } - - private List targetFiles; - private TargetFile currentFile; - - public List TargetFiles { get => targetFiles; } - - public Patcher() - { - JavaScriptSerializer serializer = new JavaScriptSerializer(); - targetFiles = serializer.Deserialize>(Properties.Resources.PatchJson); - } - - public void SetNewPatchJson(string json) - { - JavaScriptSerializer serializer = new JavaScriptSerializer(); - targetFiles = serializer.Deserialize>(json); - } - - public string JudgeVersion() - { - string sha1 = Util.ComputeFileSHA1(dllPath); - Debug.WriteLine(sha1); - - foreach (TargetFile t in targetFiles) - { - if (sha1 == t.SHA1After) - { - return "done"; - } - if (sha1 == t.SHA1Before) - { - currentFile = t; - return t.Version; - } - } - return null; - } - - public bool Patch() - { - if (currentFile != null) - { - File.Copy(dllPath, bakPath, true); - bool done = Util.EditHex(dllPath, currentFile.Position, currentFile.Content); - if (!done) - { - File.Copy(bakPath, dllPath, true); - } - return done; - } - return false; - } - - } -} diff --git a/RevokeMsgPatcher/Program.cs b/RevokeMsgPatcher/Program.cs index 7c19d17..5046c55 100644 --- a/RevokeMsgPatcher/Program.cs +++ b/RevokeMsgPatcher/Program.cs @@ -14,14 +14,18 @@ namespace RevokeMsgPatcher [STAThread] static void Main() { + + +#if DEBUG + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new FormMain()); +#else try { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); -#if DEBUG - Application.Run(new FormMain()); -#else //当前用户是管理员的时候,直接启动应用程序 //如果不是管理员,则使用启动对象启动程序,以确保使用管理员身份运行 //获得当前登录的Windows用户标示 @@ -53,12 +57,13 @@ namespace RevokeMsgPatcher //退出 Application.Exit(); } -#endif + } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } +#endif } diff --git a/RevokeMsgPatcher/Properties/AssemblyInfo.cs b/RevokeMsgPatcher/Properties/AssemblyInfo.cs index 8154376..dd4e1df 100644 --- a/RevokeMsgPatcher/Properties/AssemblyInfo.cs +++ b/RevokeMsgPatcher/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 // 方法是按如下所示使用“*”: : //[assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.4")] -[assembly: AssemblyFileVersion("0.4")] +[assembly: AssemblyVersion("0.6")] +[assembly: AssemblyFileVersion("0.6")] diff --git a/RevokeMsgPatcher/Properties/Resources.Designer.cs b/RevokeMsgPatcher/Properties/Resources.Designer.cs index a9f5581..23c3ae0 100644 --- a/RevokeMsgPatcher/Properties/Resources.Designer.cs +++ b/RevokeMsgPatcher/Properties/Resources.Designer.cs @@ -61,7 +61,7 @@ namespace RevokeMsgPatcher.Properties { } /// - /// 查找类似 [{"FileName":"WeChatWin.dll","Version":"2.6.8.65","SHA1Before":"e01f6855a96c12c30808960903ed199a33e4952c","SHA1After":"d9120569cfd0433aebea107d7b90805cbbac7518","Position":2454265,"Content":117},{"FileName":"WeChatWin.dll","Version":"2.6.8.52","SHA1Before":"88131302f664df6a657c9ca49d152da536fe5729","SHA1After":"8d1454b73831644181e962c1fa0ea4e2da4124a3","Position":2453049,"Content":117},{"FileName":"WeChatWin.dll","Version":"2.6.8.51","SHA1Before":"d0a5517b1292a751501b00b4b1f0702db2d9fc30","SHA1After":"53e7b [字符串的其余部分被截断]"; 的本地化字符串。 + /// 查找类似 {"Apps":{"Wechat":{"Name":"Wechat","FileTargetInfos":{"WeChatWin.dll":{"Name":"WeChatWin.dll","RelativePath":"WeChatWin.dll","Memo":null}},"FileModifyInfos":{"WeChatWin.dll":[{"Name":"WeChatWin.dll","Version":"2.7.1.88","SHA1Before":"034059bad50dd793140952391bfa7936133e69b4","SHA1After":"dd6d80c30ca9e0ea9f7d2f1add498fc9aa4bc7a0","Changes":[{"Position":2499465,"Content":[235]},{"Position":7952304,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.85","SHA1Before":"de0df4e138b72460450f66c029e33f4510f [字符串的其余部分被截断]"; 的本地化字符串。 /// internal static string PatchJson { get { diff --git a/RevokeMsgPatcher/Properties/Resources.resx b/RevokeMsgPatcher/Properties/Resources.resx index 166c0e1..84120cf 100644 --- a/RevokeMsgPatcher/Properties/Resources.resx +++ b/RevokeMsgPatcher/Properties/Resources.resx @@ -118,6 +118,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - [{"FileName":"WeChatWin.dll","Version":"2.6.8.65","SHA1Before":"e01f6855a96c12c30808960903ed199a33e4952c","SHA1After":"d9120569cfd0433aebea107d7b90805cbbac7518","Position":2454265,"Content":117},{"FileName":"WeChatWin.dll","Version":"2.6.8.52","SHA1Before":"88131302f664df6a657c9ca49d152da536fe5729","SHA1After":"8d1454b73831644181e962c1fa0ea4e2da4124a3","Position":2453049,"Content":117},{"FileName":"WeChatWin.dll","Version":"2.6.8.51","SHA1Before":"d0a5517b1292a751501b00b4b1f0702db2d9fc30","SHA1After":"53e7b1525d49bf2c3250a8131ff0ba2510779b78","Position":2452614,"Content":116},{"FileName":"WeChatWin.dll","Version":"2.6.8.37","SHA1Before":"7e01f8b04a158a4a50bc5a6e67c2fb8b02233170","SHA1After":"a1895004415fe9bcd7e690bd6e482b833b515599","Position":2452614,"Content":116},{"FileName":"WeChatWin.dll","Version":"2.6.7.57","SHA1Before":"80a91aaf941bcb1c24a7d672838ac73e9ebb2e40","SHA1After":"a0d3f9a45a835f97aef7fe0872387d8cfb5c25a4","Position":2433413,"Content":116},{"FileName":"WeChatWin.dll","Version":"2.6.7.40","SHA1Before":"04bd0cb28df6630b518f42a3f9c2caa4a9359fbc","SHA1After":"13c91cf1d4609959771fd137b9a86a5ca365e1b6","Position":2432934,"Content":116},{"FileName":"WeChatWin.dll","Version":"2.6.7.32","SHA1Before":"a02519c1007ee6723947c262c720d63c619f633e","SHA1After":"f3007471ca8734c29783c25f0bb49949a783a44","Position":2432806,"Content":116},{"FileName":"WeChatWin.dll","Version":"2.6.6.28","SHA1Before":"0b19cb17a62c3ea0efce0fb675a1d3b17845cba3","SHA1After":"260948656725446b818ea668273ceff02ddfb44d","Position":2401678,"Content":116}] + {"Apps":{"Wechat":{"Name":"Wechat","FileTargetInfos":{"WeChatWin.dll":{"Name":"WeChatWin.dll","RelativePath":"WeChatWin.dll","Memo":null}},"FileModifyInfos":{"WeChatWin.dll":[{"Name":"WeChatWin.dll","Version":"2.7.1.88","SHA1Before":"034059bad50dd793140952391bfa7936133e69b4","SHA1After":"dd6d80c30ca9e0ea9f7d2f1add498fc9aa4bc7a0","Changes":[{"Position":2499465,"Content":[235]},{"Position":7952304,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.85","SHA1Before":"de0df4e138b72460450f66c029e33f4510f5e2df","SHA1After":"fbd35720aaff3cdcfd3ff18ea503dc06450e5c99","Changes":[{"Position":2499465,"Content":[235]},{"Position":7952000,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.82","SHA1Before":"20e111a18872bf6c7148a897c11da26c1ec95520","SHA1After":"1e0741d325ca6b1cd2402b829a3d13a2524af617","Changes":[{"Position":2499465,"Content":[235]},{"Position":7951952,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.74","SHA1Before":"b1eaf7edc074a88be5d0f89230436cc2084d24d2","SHA1After":"eb3d74ccd87a09059a005f4972861898fc3de463","Changes":[{"Position":2499465,"Content":[235]},{"Position":7951696,"Content":[195]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.65","SHA1Before":"8346b97d264725da924d240c6eb77df3e693385e","SHA1After":"42bab2c9c79ef4f2088c00ea6d817973e14a5e6e","Changes":[{"Position":2495545,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.59","SHA1Before":"df954d403edaca89cd5394927a325a0023e93281","SHA1After":"6aa22460c91bb5c5e2f0ec1af99b8a5f6d4318c0","Changes":[{"Position":2496073,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.1.43","SHA1Before":"39cd9e09e1a3eac09e6808749bff525c9e3216ce","SHA1After":"7b829f1ff0217e346a80f9510fdd7634ddd49445","Changes":[{"Position":2494169,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.0.70","SHA1Before":"3b0601864aff3c1d792f812ad1ca05f02aa761e3","SHA1After":"1e8734d32b0a8c12758e30f99c77f729991fb071","Changes":[{"Position":2475657,"Content":[235]}]},{"Name":"WeChatWin.dll","Version":"2.7.0.65","SHA1Before":"063c2e05a0df1bdb8987c2d978d93499bd2052ba","SHA1After":"5ed4c09a4f18643b967f063a824d7e65d0567f8a","Changes":[{"Position":2475449,"Content":[117]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.68","SHA1Before":"2e9417f4276b12fe32ca7b4fee49272a4a2af334","SHA1After":"699602ee3cbb9ae5714f6e6ebc658c875a6c66e6","Changes":[{"Position":2454006,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.65","SHA1Before":"e01f6855a96c12c30808960903ed199a33e4952c","SHA1After":"d9120569cfd0433aebea107d7b90805cbbac7518","Changes":[{"Position":2454265,"Content":[117]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.52","SHA1Before":"88131302f664df6a657c9ca49d152da536fe5729","SHA1After":"8d1454b73831644181e962c1fa0ea4e2da4124a3","Changes":[{"Position":2453049,"Content":[117]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.51","SHA1Before":"d0a5517b1292a751501b00b4b1f0702db2d9fc30","SHA1After":"53e7b1525d49bf2c3250a8131ff0ba2510779b78","Changes":[{"Position":2452614,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.8.37","SHA1Before":"7e01f8b04a158a4a50bc5a6e67c2fb8b02233170","SHA1After":"a1895004415fe9bcd7e690bd6e482b833b515599","Changes":[{"Position":2452614,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.7.57","SHA1Before":"80a91aaf941bcb1c24a7d672838ac73e9ebb2e40","SHA1After":"a0d3f9a45a835f97aef7fe0872387d8cfb5c25a4","Changes":[{"Position":2433413,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.7.40","SHA1Before":"04bd0cb28df6630b518f42a3f9c2caa4a9359fbc","SHA1After":"13c91cf1d4609959771fd137b9a86a5ca365e1b6","Changes":[{"Position":2432934,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.7.32","SHA1Before":"a02519c1007ee6723947c262c720d63c619f633e","SHA1After":"f3007471ca8734c29783c25f0bb49949a783a44","Changes":[{"Position":2432806,"Content":[116]}]},{"Name":"WeChatWin.dll","Version":"2.6.6.28","SHA1Before":"0b19cb17a62c3ea0efce0fb675a1d3b17845cba3","SHA1After":"260948656725446b818ea668273ceff02ddfb44d","Changes":[{"Position":2401678,"Content":[116]}]}]}},"QQ":{"Name":"QQ","FileTargetInfos":{"IM.dll":{"Name":"IM.dll","RelativePath":"Bin\\IM.dll","Memo":null}},"FileModifyInfos":{"IM.dll":[{"Name":"IM.dll","Version":"9.2.2.26569","SHA1Before":"434254e76c520789558e075af677821258536311","SHA1After":"237c9e489a97858a175f0f7c72ade4ebcbac7a69","Changes":[{"Position":371146,"Content":[235,9,144,144,144]},{"Position":371687,"Content":[235,9,144,144,144]},{"Position":372117,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.2.1.26546","SHA1Before":"8d8ea2c2cbf43f5acf8d684b153e90035352d5f5","SHA1After":"7d194dd5be03982b533d7375c93d9a72587fe28d","Changes":[{"Position":369545,"Content":[235,9,144,144,144]},{"Position":370086,"Content":[235,9,144,144,144]},{"Position":370516,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.2.0.26453","SHA1Before":"c1935ca6347b0c2a7e6108a7f8ee0643d39deb66","SHA1After":"42811188a7e7b346a6a3c1066936b98c747acaf6","Changes":[{"Position":353794,"Content":[235,9,144,144,144]},{"Position":354335,"Content":[235,9,144,144,144]},{"Position":354767,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.2.0.26389","SHA1Before":"6f8855fb80acfa456f8f69989fe949308fe4d154","SHA1After":"f6b8e05a178b9b10ba17c597fa0a44b7a2a966a8","Changes":[{"Position":356808,"Content":[235,9,144,144,144]},{"Position":357349,"Content":[235,9,144,144,144]},{"Position":357781,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.9.26361","SHA1Before":"022d3433d13d07a354c38816f61cb0b7ac60d3fd","SHA1After":"873a57c1fb51cdd099c8cb7108b5ab5cb4459557","Changes":[{"Position":354270,"Content":[235,9,144,144,144]},{"Position":354811,"Content":[235,9,144,144,144]},{"Position":355243,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.9.26346","SHA1Before":"895eb70f707b8222e6460c91492b1281e525059b","SHA1After":"0bb83990e2b5b5f23b7b43249941ff638201af54","Changes":[{"Position":354270,"Content":[235,9,144,144,144]},{"Position":354811,"Content":[235,9,144,144,144]},{"Position":355243,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.8.26211","SHA1Before":"a950d3cf5e8925f7775624271105ef78d9c5cb57","SHA1After":"dffc1cb87b91e6467e13c935611f2f7fd76b9a8d","Changes":[{"Position":337135,"Content":[235,9,144,144,144]},{"Position":337676,"Content":[235,9,144,144,144]},{"Position":338108,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.1.7.25980","SHA1Before":"c6632339fbe675312a70ae4620e70699c258cd36","SHA1After":"e9ddc5cc681950796fc8fe4c55f580428c890b51","Changes":[{"Position":327839,"Content":[235,9,144,144,144]},{"Position":328380,"Content":[235,9,144,144,144]},{"Position":328812,"Content":[235,8,144,144,144,144]}]},{"Name":"IM.dll","Version":"9.0.4.23786","SHA1Before":"69a714f4eadb09f1453f6f022d4adbcd801cfab8","SHA1After":"b48e77a924076b3ebdffc4af514c868c551d2bca","Changes":[{"Position":318321,"Content":[235,7,144,144,144]},{"Position":318862,"Content":[235,7,144,144,144]},{"Position":319379,"Content":[235,7,144,144,144]}]}]}},"TIM":{"Name":"TIM","FileTargetInfos":{"IM.dll":{"Name":"IM.dll","RelativePath":"Bin\\IM.dll","Memo":null}},"FileModifyInfos":{"IM.dll":[{"Name":"IM.dll","Version":"2.3.2.21173","SHA1Before":"ecf3e69f3fb100ffe2fee095ffded591b9781024","SHA1After":"0514d1304e7ac46b4d33386ec3313888f5ae7171","Changes":[{"Position":317322,"Content":[235,9,144,144,144]},{"Position":317863,"Content":[235,9,144,144,144]},{"Position":318295,"Content":[235,8,144,144,144,144]}]}]}},"QQLite":{"Name":"QQLite","FileTargetInfos":{"IM.dll":{"Name":"IM.dll","RelativePath":"Bin\\IM.dll","Memo":null}},"FileModifyInfos":{"IM.dll":[{"Name":"IM.dll","Version":"7.9.14314.0","SHA1Before":"2e97d7671963fa148a1beeda6ce4964314310593","SHA1After":"723c008fb53435ead20fa6f2e951c9a4a8ff46da","Changes":[{"Position":148741,"Content":[235,2,144,144]},{"Position":149689,"Content":[235,2,144,144]}]},{"Name":"IM.dll","Version":"7.9.14308.0","SHA1Before":"b8a7a873178706b97be11c25f13bcf09e9e578a2","SHA1After":"c5bf533c7af6996b42d1fb2a0fb3f26dfd52f8bf","Changes":[{"Position":148741,"Content":[235,2,144,144]},{"Position":149689,"Content":[235,2,144,144]}]}]}}},"LatestVersion":"0.6","Notice":""} \ No newline at end of file diff --git a/RevokeMsgPatcher/RevokeMsgPatcher.csproj b/RevokeMsgPatcher/RevokeMsgPatcher.csproj index 5b77683..561f33e 100644 --- a/RevokeMsgPatcher/RevokeMsgPatcher.csproj +++ b/RevokeMsgPatcher/RevokeMsgPatcher.csproj @@ -37,6 +37,7 @@ + @@ -49,17 +50,31 @@ + Form FormMain.cs - - + + + + + + + + + + + + + + + + - FormMain.cs diff --git a/RevokeMsgPatcher/Util.cs b/RevokeMsgPatcher/Util.cs deleted file mode 100644 index 3b40939..0000000 --- a/RevokeMsgPatcher/Util.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Microsoft.Win32; -using System; -using System.Diagnostics; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace RevokeMsgPatcher -{ - public class Util - { - - /// - /// 自动查找安装路径 - /// - /// 安装路径 - public static string AutoFindInstallPath() - { - // 微信的注册表路径 - try - { - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\WeChat"); - object installLocation = key.GetValue("InstallLocation"); - key.Close(); - if (installLocation == null || string.IsNullOrEmpty(installLocation.ToString()) || !IsWechatInstallPath(installLocation.ToString())) - { - // 从默认安装目录查找 - string[] drives = Environment.GetLogicalDrives(); //获取当前计算机逻辑磁盘名称列表 - foreach (string d in drives) - { - string assertPath = Path.Combine(d, @"Program Files (x86)\Tencent\WeChat"); - if (IsWechatInstallPath(assertPath)) - { - return assertPath; - } - } - } - else - { - return installLocation.ToString(); - } - } - catch(Exception e) - { - Console.WriteLine(e.Message); - } - return null; - } - - /// - /// 通过文件是否存在判断是否是安装目录 - /// - /// 安装目录 - /// - public static bool IsWechatInstallPath(string path) - { - return File.Exists(Path.Combine(path, "WeChatWin.dll")) && File.Exists(Path.Combine(path, "WeChat.exe")); - } - - /// - /// 获取文件版本 - /// - /// - /// - public static string GetFileVersion(string path) - { - FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(path); - return fileVersionInfo.FileVersion; - } - - /// - /// 计算文件SHA1 - /// - /// - /// - public static string ComputeFileSHA1(string s) - { - FileStream file = new FileStream(s, FileMode.Open); - SHA1 sha1 = new SHA1CryptoServiceProvider(); - byte[] retval = sha1.ComputeHash(file); - file.Close(); - - StringBuilder sc = new StringBuilder(); - for (int i = 0; i < retval.Length; i++) - { - sc.Append(retval[i].ToString("x2")); - } - return sc.ToString(); - } - - /// - /// 修改文件指定位置的字节 - /// - /// WeChatWin.dll 的路径 - /// 偏移位置 - /// 修改后的值 - /// - public static bool EditHex(string path, long position, byte after) - { - using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) - { - stream.Position = position; - stream.WriteByte(after); - } - return true; - - } - } -} diff --git a/RevokeMsgPatcher/Utils/Device.cs b/RevokeMsgPatcher/Utils/Device.cs new file mode 100644 index 0000000..be4d155 --- /dev/null +++ b/RevokeMsgPatcher/Utils/Device.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Net; +using System.Security.Cryptography; +using System.Text; + +namespace RevokeMsgPatcher.Utils +{ + public class Device + { + private static string macID = null; + private static string osVersion = null; + + private static string fingerPrint = null; + + #region PROP, get it only once + + public static string MacID + { + get + { + if (macID == null) + { + macID = ObtainMacID(); + } + return macID; + } + } + + public static string OSVersion + { + get + { + if (osVersion == null) + { + var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().Cast() + select x.GetPropertyValue("Caption")).FirstOrDefault(); + osVersion = name != null ? name.ToString() : "Unknown"; + } + return osVersion; + } + } + #endregion + + /// + /// Calculate GUID + /// + /// GUID + public static string Value() + { + if (fingerPrint == null) + { + fingerPrint = GetHash( + "MAC >> " + MacID + ); + } + return fingerPrint; + } + + private static string GetHash(string s) + { + MD5 sec = new MD5CryptoServiceProvider(); + ASCIIEncoding enc = new ASCIIEncoding(); + byte[] bt = enc.GetBytes(s); + return GetHexString(sec.ComputeHash(bt)); + } + + private static string GetHexString(byte[] bt) + { + string s = string.Empty; + for (int i = 0; i < bt.Length; i++) + { + byte b = bt[i]; + int n, n1, n2; + n = (int)b; + n1 = n & 15; + n2 = (n >> 4) & 15; + if (n2 > 9) + s += ((char)(n2 - 10 + (int)'A')).ToString(); + else + s += n2.ToString(); + if (n1 > 9) + s += ((char)(n1 - 10 + (int)'A')).ToString(); + else + s += n1.ToString(); + if ((i + 1) != bt.Length && (i + 1) % 2 == 0) s += "-"; + } + return s; + } + + + #region Original Device ID Getting Code + + public static string ObtainMacID() + { + return Identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled"); + } + + private static string Identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue) + { + string result = ""; + try + { + ManagementClass mc = new ManagementClass(wmiClass); + ManagementObjectCollection moc = mc.GetInstances(); + foreach (ManagementObject mo in moc) + { + if (mo[wmiMustBeTrue].ToString() == "True") + { + //Only get the first one + if (result == "") + { + result = mo[wmiProperty].ToString(); + break; + } + } + } + } + catch + { + } + return result; + } + + private static string Identifier(string wmiClass, string wmiProperty) + { + string result = ""; + try + { + ManagementClass mc = new ManagementClass(wmiClass); + ManagementObjectCollection moc = mc.GetInstances(); + foreach (ManagementObject mo in moc) + { + //Only get the first one + if (result == "") + { + result = mo[wmiProperty].ToString(); + break; + } + } + } + catch + { + } + return result; + } + #endregion + } +} diff --git a/RevokeMsgPatcher/Utils/FileUtil.cs b/RevokeMsgPatcher/Utils/FileUtil.cs new file mode 100644 index 0000000..e7bb263 --- /dev/null +++ b/RevokeMsgPatcher/Utils/FileUtil.cs @@ -0,0 +1,78 @@ +using RevokeMsgPatcher.Model; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace RevokeMsgPatcher.Utils +{ + public class FileUtil + { + /// + /// 获取文件版本 + /// + /// + /// + public static string GetFileVersion(string path) + { + FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(path); + return fileVersionInfo.FileVersion; + } + + /// + /// 计算文件SHA1 + /// + /// 文件路径 + /// + public static string ComputeFileSHA1(string s) + { + FileStream file = new FileStream(s, FileMode.Open); + SHA1 sha1 = new SHA1CryptoServiceProvider(); + byte[] retval = sha1.ComputeHash(file); + file.Close(); + + StringBuilder sc = new StringBuilder(); + for (int i = 0; i < retval.Length; i++) + { + sc.Append(retval[i].ToString("x2")); + } + return sc.ToString(); + } + + /// + /// 修改文件指定位置的字节 + /// + /// 文件对象的路径 + /// 偏移位置 + /// 修改后的值 + /// + public static bool EditHex(string path, long position, byte after) + { + using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) + { + stream.Position = position; + stream.WriteByte(after); + } + return true; + + } + + /// + /// 修改文件多个指定位置的多个字节 + /// + /// 文件对象的路径 + /// 需要修改的位置和内容 + public static void EditMultiHex(string path, List changes) + { + using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) + { + foreach (Change change in changes) + { + stream.Seek(change.Position, SeekOrigin.Begin); + stream.Write(change.Content, 0, change.Content.Length); + } + } + } + } +} diff --git a/RevokeMsgPatcher/Utils/GAHelper.cs b/RevokeMsgPatcher/Utils/GAHelper.cs new file mode 100644 index 0000000..fa8fd79 --- /dev/null +++ b/RevokeMsgPatcher/Utils/GAHelper.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace RevokeMsgPatcher.Utils +{ + /// + /// 用于软件的 Google Analytics 实现 By huiyadanli + /// 相关文档: + /// 指南 https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide + /// 参数 https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters + /// 测试 https://ga-dev-tools.appspot.com/hit-builder/ + /// + public class GAHelper + { + // 根据实际情况修改 + private static readonly HttpClient client = HttpUtil.Client; + + private const string GAUrl = "https://www.google-analytics.com/collect"; + + // 根据实际使用分析账号设置 + private const string tid = "UA-80358493-2"; // GA Tracking ID / Property ID. + + private static readonly string cid = Device.Value(); // Anonymous Client ID. // Guid.NewGuid().ToString() + + // 屏幕分辨率(可选) + private static readonly string sr = Screen.PrimaryScreen.Bounds.Width + "x" + Screen.PrimaryScreen.Bounds.Height; + + public string UserAgent { get; set; } + + public GAHelper() + { + UserAgent = string.Format("Hui Google Analytics Tracker/1.0 ({0}; {1}; {2})", Environment.OSVersion.Platform.ToString(), Environment.OSVersion.Version.ToString(), Environment.OSVersion.VersionString); + } + + public async Task RequestPageViewAsync(string page, string title = null) + { + try + { + if (!page.StartsWith("/")) + { + page = "/" + page; + } + // 请求参数 + var values = new Dictionary + { + { "v", "1" }, // 当前必填1 + { "tid", tid }, + { "cid", cid }, + { "ua", UserAgent }, + { "t", "pageview" }, + { "sr", sr }, + { "dp", page }, + { "dt", title }, + }; + var content = new FormUrlEncodedContent(values); + var response = await client.PostAsync(GAUrl, content); + } + catch (Exception ex) + { + + Console.WriteLine("GAHelper:" + ex.Message); + } + } + + public void RequestPageView(string page, string title = null) + { + Task.Run(() => RequestPageViewAsync(page, title)); + } + } +} diff --git a/RevokeMsgPatcher/Utils/HttpUtil.cs b/RevokeMsgPatcher/Utils/HttpUtil.cs new file mode 100644 index 0000000..90972d2 --- /dev/null +++ b/RevokeMsgPatcher/Utils/HttpUtil.cs @@ -0,0 +1,55 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Utils +{ + public class HttpUtil + { + public static HttpClient Client { get; } = new HttpClient(); + + static HttpUtil() + { + Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"); + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; + } + + /// + /// 补丁路径 + /// 已经弃用的路径 + /// https://swordmaker-hauls-51508.netlify.com/i/revokemsg/05.json + /// https://huiyadanli.github.io/i/revokemsg/05.json + /// + private static readonly string[] urls = new string[] + { + "https://coding.net/u/huiyadanli/p/RevokeMsgPatcher/git/raw/master/RevokeMsgPatcher.Assistant/Data/0.6/patch.json", + "https://gitee.com/huiyadanli/RevokeMsgPatcher/raw/master/RevokeMsgPatcher.Assistant/Data/0.6/patch.json", + "https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/RevokeMsgPatcher.Assistant/Data/0.6/patch.json" + }; + + private static int i = 0; + + public static async Task GetPatchJsonAsync() + { + try + { + return await Client.GetStringAsync(urls[i]); + } + catch (Exception ex) + { + Console.WriteLine("第" + (i + 1) + "次请求异常:[" + ex.Message + "]\nURL:" + urls[i]); + i++; + if (i > urls.Length) + { + i = 0; + return null; + } + else + { + return await GetPatchJsonAsync(); + } + } + } + } +} diff --git a/RevokeMsgPatcher/Utils/PathUtil.cs b/RevokeMsgPatcher/Utils/PathUtil.cs new file mode 100644 index 0000000..61c547c --- /dev/null +++ b/RevokeMsgPatcher/Utils/PathUtil.cs @@ -0,0 +1,81 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Utils +{ + public class PathUtil + { + public static void DisplayAllProgram() + { + RegistryKey uninstallKey, programKey; + uninstallKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"); + string[] programKeys = uninstallKey.GetSubKeyNames(); + foreach (string keyName in programKeys) + { + programKey = uninstallKey.OpenSubKey(keyName); + Console.WriteLine(keyName + " , " + programKey.GetValue("DisplayName") + " , " + programKey.GetValue("InstallLocation")); + programKey.Close(); + } + uninstallKey.Close(); + } + + /// + /// 从注册表中寻找安装路径 + /// + /// + /// 安装信息的注册表键名 + /// 微信:WeChat + /// QQ:{052CFB79-9D62-42E3-8A15-DE66C2C97C3E} + /// TIM:TIM + /// + /// 安装路径 + public static string FindInstallPathFromRegistry(string uninstallKeyName) + { + try + { + RegistryKey key = Registry.LocalMachine.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{uninstallKeyName}"); + if (key == null) + { + return null; + } + object installLocation = key.GetValue("InstallLocation"); + key.Close(); + if (installLocation != null && !string.IsNullOrEmpty(installLocation.ToString())) + { + return installLocation.ToString(); + } + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + return null; + } + + /// + /// 获取所有可能的默认安装路径 + /// + /// Tencent\* + /// + public static List GetDefaultInstallPaths(string relativePath) + { + List list = new List(); + // 从默认安装目录查找 + string[] drives = Environment.GetLogicalDrives(); //获取当前计算机逻辑磁盘名称列表 + foreach (string d in drives) + { + string path = Path.Combine(d, $@"Program Files (x86)\{relativePath}"); + if (Directory.Exists(path)) + { + list.Add(path); + } + } + return list; + } + } +} diff --git a/RevokeMsgPatcher/icon.ico b/RevokeMsgPatcher/icon.ico index cf59834..28f14bd 100644 Binary files a/RevokeMsgPatcher/icon.ico and b/RevokeMsgPatcher/icon.ico differ