├── .gitattributes ├── .gitignore ├── About.Designer.cs ├── About.cs ├── About.en.resx ├── About.resx ├── About.zh-CN.resx ├── App.config ├── AutoTask ├── BaseJob.cs ├── SelectDevice.Designer.cs ├── SelectDevice.cs ├── SelectDevice.resx ├── TaskEdit.Designer.cs ├── TaskEdit.cs ├── TaskEdit.resx ├── TaskEditItem.Designer.cs ├── TaskEditItem.cs ├── TaskEditItem.resx ├── TaskJson.cs ├── TaskList.Designer.cs ├── TaskList.cs ├── TaskList.resx ├── TaskRun.cs ├── TaskRunWindow.Designer.cs ├── TaskRunWindow.cs ├── TaskRunWindow.resx ├── TaskShowItem.Designer.cs ├── TaskShowItem.cs └── TaskShowItem.resx ├── Base ├── ADB.cs ├── ADBKey.cs ├── AdbParse.cs ├── CMD.cs ├── FireWallHelper.cs ├── GlobalHotkey.cs ├── ProcessWindowController.cs ├── Scrcpy.cs ├── ScrcpyParam.cs └── ScrcpyParamAttribute.cs ├── BaseControl.Designer.cs ├── BaseControl.cs ├── BaseControl.resx ├── BaseForm.cs ├── BaseForm.resx ├── Docs ├── Task.md ├── Update.en.md ├── Update.md ├── autohotkey.ahk └── task.json ├── HotKeyControl.Designer.cs ├── HotKeyControl.cs ├── HotKeyControl.resx ├── HotKeySetting.Designer.cs ├── HotKeySetting.cs ├── HotKeySetting.en.resx ├── HotKeySetting.resx ├── HotKeySetting.zh-CN.resx ├── IPConnect.Designer.cs ├── IPConnect.cs ├── IPConnect.en.resx ├── IPConnect.resx ├── IPConnect.zh-CN.resx ├── IPPair.Designer.cs ├── IPPair.cs ├── IPPair.en.resx ├── IPPair.resx ├── LICENSE ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.en.resx ├── MainForm.resx ├── MainForm.zh-CN.resx ├── MainForm.zh-Hans.resx ├── MobileControlGuru.csproj ├── MobileControlGuru.sln ├── MoblieControl.Designer.cs ├── MoblieControl.cs ├── MoblieControl.en.resx ├── MoblieControl.resx ├── MoblieControl.zh-Hans.resx ├── Model ├── Device.cs └── KeyItem.cs ├── Preview ├── api.png ├── hotkeysetting.en.png ├── hotkeysetting.png ├── ipconnect.en.png ├── ipconnect.png ├── main.en.png ├── main.png ├── taskedit.png └── taskmain.png ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── README.en.md ├── README.md ├── Resources ├── fanhui.png ├── favicon.ico ├── jietu.png ├── nosee.png ├── ontop.png ├── ontop_blur.png ├── scrcpy-win64-v2.4.zip ├── scrcpy_params.json ├── see.bmp ├── see.png ├── shouye.png ├── to_top.png └── to_top_blur.png ├── ScrcpyConrtrolPanel.Designer.cs ├── ScrcpyConrtrolPanel.cs ├── ScrcpyConrtrolPanel.resx ├── ScrcpyForm.Designer.cs ├── ScrcpyForm.cs ├── ScrcpyForm.resx ├── ScrcpySetting.Designer.cs ├── ScrcpySetting.cs ├── ScrcpySetting.en.resx ├── ScrcpySetting.resx ├── ScrcpySetting.zh-CN.resx ├── Src ├── DeviceADB.cs ├── DeviceManager.cs ├── MyHotKey.cs ├── ScrcpyMonitor.cs └── TaskManager.cs ├── Tools ├── Common.cs ├── ConfigHelp.cs ├── JsonHelp.cs ├── LanguageHelper.cs ├── LogHelper.cs ├── QuartzJobUtil.cs └── ZipHelper.cs ├── WebAPI ├── Controllers │ ├── DeviceController.cs │ └── HomeController.cs ├── Startup.cs └── WebHelper.cs ├── adb_cmds.txt └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | *.vcxproj.filters 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # .NET Core 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | **/Properties/launchSettings.json 51 | 52 | *_i.c 53 | *_p.c 54 | *_i.h 55 | *.ilk 56 | *.meta 57 | *.obj 58 | *.pch 59 | *.pdb 60 | *.pgc 61 | *.pgd 62 | *.rsp 63 | *.sbr 64 | *.tlb 65 | *.tli 66 | *.tlh 67 | *.tmp 68 | *.tmp_proj 69 | *.log 70 | *.vspscc 71 | *.vssscc 72 | .builds 73 | *.pidb 74 | *.svclog 75 | *.scc 76 | 77 | # Chutzpah Test files 78 | _Chutzpah* 79 | 80 | # Visual C++ cache files 81 | ipch/ 82 | *.aps 83 | *.ncb 84 | *.opendb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | *.VC.db 89 | *.VC.VC.opendb 90 | 91 | # Visual Studio profiler 92 | *.psess 93 | *.vsp 94 | *.vspx 95 | *.sap 96 | 97 | # TFS 2012 Local Workspace 98 | $tf/ 99 | 100 | # Guidance Automation Toolkit 101 | *.gpState 102 | 103 | # ReSharper is a .NET coding add-in 104 | _ReSharper*/ 105 | *.[Rr]e[Ss]harper 106 | *.DotSettings.user 107 | 108 | # JustCode is a .NET coding add-in 109 | .JustCode 110 | 111 | # TeamCity is a build add-in 112 | _TeamCity* 113 | 114 | # DotCover is a Code Coverage Tool 115 | *.dotCover 116 | 117 | # Visual Studio code coverage results 118 | *.coverage 119 | *.coveragexml 120 | 121 | # NCrunch 122 | _NCrunch_* 123 | .*crunch*.local.xml 124 | nCrunchTemp_* 125 | 126 | # MightyMoose 127 | *.mm.* 128 | AutoTest.Net/ 129 | 130 | # Web workbench (sass) 131 | .sass-cache/ 132 | 133 | # Installshield output folder 134 | [Ee]xpress/ 135 | 136 | # DocProject is a documentation generator add-in 137 | DocProject/buildhelp/ 138 | DocProject/Help/*.HxT 139 | DocProject/Help/*.HxC 140 | DocProject/Help/*.hhc 141 | DocProject/Help/*.hhk 142 | DocProject/Help/*.hhp 143 | DocProject/Help/Html2 144 | DocProject/Help/html 145 | 146 | # Click-Once directory 147 | publish/ 148 | 149 | # Publish Web Output 150 | *.[Pp]ublish.xml 151 | *.azurePubxml 152 | # TODO: Comment the next line if you want to checkin your web deploy settings 153 | # but database connection strings (with potential passwords) will be unencrypted 154 | *.pubxml 155 | *.publishproj 156 | 157 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 158 | # checkin your Azure Web App publish settings, but sensitive information contained 159 | # in these scripts will be unencrypted 160 | PublishScripts/ 161 | 162 | # NuGet Packages 163 | *.nupkg 164 | # The packages folder can be ignored because of Package Restore 165 | **/packages/* 166 | # except build/, which is used as an MSBuild target. 167 | !**/packages/build/ 168 | # Uncomment if necessary however generally it will be regenerated when needed 169 | #!**/packages/repositories.config 170 | # NuGet v3's project.json files produces more ignoreable files 171 | *.nuget.props 172 | *.nuget.targets 173 | 174 | # Microsoft Azure Build Output 175 | csx/ 176 | *.build.csdef 177 | 178 | # Microsoft Azure Emulator 179 | ecf/ 180 | rcf/ 181 | 182 | # Windows Store app package directories and files 183 | AppPackages/ 184 | BundleArtifacts/ 185 | Package.StoreAssociation.xml 186 | _pkginfo.txt 187 | 188 | # Visual Studio cache files 189 | # files ending in .cache can be ignored 190 | *.[Cc]ache 191 | # but keep track of directories ending in .cache 192 | !*.[Cc]ache/ 193 | 194 | # Others 195 | ClientBin/ 196 | ~$* 197 | *~ 198 | *.dbmdl 199 | *.dbproj.schemaview 200 | *.jfm 201 | *.pfx 202 | *.publishsettings 203 | node_modules/ 204 | orleans.codegen.cs 205 | 206 | # Since there are multiple workflows, uncomment next line to ignore bower_components 207 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 208 | #bower_components/ 209 | 210 | # RIA/Silverlight projects 211 | Generated_Code/ 212 | 213 | # Backup & report files from converting an old project file 214 | # to a newer Visual Studio version. Backup files are not needed, 215 | # because we have git ;-) 216 | _UpgradeReport_Files/ 217 | Backup*/ 218 | UpgradeLog*.XML 219 | UpgradeLog*.htm 220 | 221 | # SQL Server files 222 | *.mdf 223 | *.ldf 224 | 225 | # Business Intelligence projects 226 | *.rdl.data 227 | *.bim.layout 228 | *.bim_*.settings 229 | 230 | # Microsoft Fakes 231 | FakesAssemblies/ 232 | 233 | # GhostDoc plugin setting file 234 | *.GhostDoc.xml 235 | 236 | # Node.js Tools for Visual Studio 237 | .ntvs_analysis.dat 238 | 239 | # Visual Studio 6 build log 240 | *.plg 241 | 242 | # Visual Studio 6 workspace options file 243 | *.opt 244 | 245 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 246 | *.vbw 247 | 248 | # Visual Studio LightSwitch build output 249 | **/*.HTMLClient/GeneratedArtifacts 250 | **/*.DesktopClient/GeneratedArtifacts 251 | **/*.DesktopClient/ModelManifest.xml 252 | **/*.Server/GeneratedArtifacts 253 | **/*.Server/ModelManifest.xml 254 | _Pvt_Extensions 255 | 256 | # Paket dependency manager 257 | .paket/paket.exe 258 | paket-files/ 259 | 260 | # FAKE - F# Make 261 | .fake/ 262 | 263 | # JetBrains Rider 264 | .idea/ 265 | *.sln.iml 266 | 267 | # CodeRush 268 | .cr/ 269 | 270 | # Python Tools for Visual Studio (PTVS) 271 | __pycache__/ 272 | *.pyc 273 | 274 | # Cake - Uncomment if you are using it 275 | # tools/ 276 | -------------------------------------------------------------------------------- /About.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru 2 | { 3 | partial class About 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(About)); 32 | this.linkLabel1 = new System.Windows.Forms.LinkLabel(); 33 | this.label1 = new AntdUI.Label(); 34 | this.linkLabel2 = new System.Windows.Forms.LinkLabel(); 35 | this.linkLabel3 = new System.Windows.Forms.LinkLabel(); 36 | this.SuspendLayout(); 37 | // 38 | // linkLabel1 39 | // 40 | resources.ApplyResources(this.linkLabel1, "linkLabel1"); 41 | this.linkLabel1.Name = "linkLabel1"; 42 | this.linkLabel1.TabStop = true; 43 | this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); 44 | // 45 | // label1 46 | // 47 | resources.ApplyResources(this.label1, "label1"); 48 | this.label1.Name = "label1"; 49 | // 50 | // linkLabel2 51 | // 52 | resources.ApplyResources(this.linkLabel2, "linkLabel2"); 53 | this.linkLabel2.Name = "linkLabel2"; 54 | this.linkLabel2.TabStop = true; 55 | this.linkLabel2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel2_LinkClicked); 56 | // 57 | // linkLabel3 58 | // 59 | resources.ApplyResources(this.linkLabel3, "linkLabel3"); 60 | this.linkLabel3.Name = "linkLabel3"; 61 | this.linkLabel3.TabStop = true; 62 | this.linkLabel3.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); 63 | // 64 | // About 65 | // 66 | resources.ApplyResources(this, "$this"); 67 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 68 | this.Controls.Add(this.linkLabel3); 69 | this.Controls.Add(this.linkLabel2); 70 | this.Controls.Add(this.label1); 71 | this.Controls.Add(this.linkLabel1); 72 | this.Name = "About"; 73 | this.ResumeLayout(false); 74 | this.PerformLayout(); 75 | 76 | } 77 | 78 | #endregion 79 | 80 | private System.Windows.Forms.LinkLabel linkLabel1; 81 | private AntdUI.Label label1; 82 | private System.Windows.Forms.LinkLabel linkLabel2; 83 | private System.Windows.Forms.LinkLabel linkLabel3; 84 | } 85 | } -------------------------------------------------------------------------------- /About.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace MobileControlGuru 12 | { 13 | public partial class About : Form 14 | { 15 | public About() 16 | { 17 | InitializeComponent(); 18 | } 19 | 20 | private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 21 | { 22 | System.Diagnostics.Process.Start("https://github.com/yclown/MobileControlGuru"); 23 | } 24 | 25 | private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 26 | { 27 | System.Diagnostics.Process.Start("https://wwo.lanzouo.com/b0064zc4ve?pwd=7njs"); 28 | } 29 | 30 | private void linkLabel3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 31 | { 32 | System.Diagnostics.Process.Start("https://github.com/yclown/MobileControlGuru/releases"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 0, 0 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /AutoTask/BaseJob.cs: -------------------------------------------------------------------------------- 1 | using Quartz; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MobileControlGuru.AutoTask 9 | { 10 | public class BaseJob : IJob 11 | { 12 | public Task Execute(IJobExecutionContext context) 13 | { 14 | JobKey key = context.JobDetail.Key; 15 | JobDataMap dataMap = context.JobDetail.JobDataMap; 16 | //string devicename = dataMap.GetString("devicename"); 17 | TaskJson.TaskInfo taskInfo = (TaskJson.TaskInfo)dataMap.Get("taskInfo"); 18 | 19 | return Task.Factory.StartNew(() => 20 | { 21 | TaskRun taskRun = new TaskRun(taskInfo.DeviceName, taskInfo); 22 | taskRun.Run(); 23 | }); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AutoTask/SelectDevice.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru.AutoTask 2 | { 3 | partial class SelectDevice 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.select1 = new AntdUI.Select(); 32 | this.button1 = new AntdUI.Button(); 33 | this.SuspendLayout(); 34 | // 35 | // select1 36 | // 37 | this.select1.Location = new System.Drawing.Point(12, 12); 38 | this.select1.Name = "select1"; 39 | this.select1.Size = new System.Drawing.Size(180, 39); 40 | this.select1.TabIndex = 0; 41 | this.select1.Text = "select1"; 42 | // 43 | // button1 44 | // 45 | this.button1.Location = new System.Drawing.Point(98, 66); 46 | this.button1.Name = "button1"; 47 | this.button1.Size = new System.Drawing.Size(94, 35); 48 | this.button1.TabIndex = 1; 49 | this.button1.Text = "确定"; 50 | this.button1.Click += new System.EventHandler(this.button1_Click); 51 | // 52 | // SelectDevice 53 | // 54 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 55 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 56 | this.ClientSize = new System.Drawing.Size(201, 108); 57 | this.Controls.Add(this.button1); 58 | this.Controls.Add(this.select1); 59 | this.Name = "SelectDevice"; 60 | this.Text = "SelectDevice"; 61 | this.Load += new System.EventHandler(this.SelectDevice_Load); 62 | this.ResumeLayout(false); 63 | 64 | } 65 | 66 | #endregion 67 | 68 | public AntdUI.Select select1; 69 | public AntdUI.Button button1; 70 | } 71 | } -------------------------------------------------------------------------------- /AutoTask/SelectDevice.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Src; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Data; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace MobileControlGuru.AutoTask 13 | { 14 | public partial class SelectDevice : Form 15 | { 16 | public SelectDevice() 17 | { 18 | InitializeComponent(); 19 | } 20 | 21 | private void SelectDevice_Load(object sender, EventArgs e) 22 | { 23 | this.select1.Items =new AntdUI.BaseCollection(); 24 | foreach(var d in DeviceManager.Instance.devices) 25 | { 26 | this.select1.Items.Add(d.Name); 27 | } 28 | this.select1.SelectedIndex = 0; 29 | 30 | 31 | } 32 | 33 | private void button1_Click(object sender, EventArgs e) 34 | { 35 | 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /AutoTask/SelectDevice.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /AutoTask/TaskEdit.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualBasic; 2 | using MobileControlGuru.Tools; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | 13 | namespace MobileControlGuru.AutoTask 14 | { 15 | public partial class TaskEdit : BaseForm 16 | { 17 | 18 | public TaskJson.TaskInfo taskInfo; 19 | 20 | public TaskEdit() 21 | { 22 | InitializeComponent(); 23 | } 24 | 25 | public TaskEdit(TaskJson.TaskInfo taskInfo) 26 | { 27 | this.taskInfo = taskInfo; 28 | InitializeComponent(); 29 | } 30 | private void TaskEdit_Load(object sender, EventArgs e) 31 | { 32 | Init(); 33 | } 34 | 35 | private void Init() 36 | { 37 | 38 | if (taskInfo != null) 39 | { 40 | this.task_name.Text = taskInfo.Name; 41 | this.devicename_input.Text = taskInfo.DeviceName; 42 | this.cornexp_input.Text = taskInfo.Corn; 43 | 44 | foreach (var item in taskInfo.TaskItems) 45 | { 46 | var edit = new TaskEditItem(item); 47 | edit.deleteEevnt += ItemDelete; 48 | this.flowLayoutPanel1.Controls.Add(edit); 49 | } 50 | 51 | } 52 | 53 | 54 | } 55 | 56 | private void button1_Click(object sender, EventArgs e) 57 | { 58 | var edit = new TaskEditItem(); 59 | edit.deleteEevnt += ItemDelete; 60 | 61 | this.flowLayoutPanel1.Controls.Add(edit); 62 | UpdateItemIndex(); 63 | } 64 | private void UpdateItemIndex() 65 | { 66 | int i = 0; 67 | foreach (TaskEditItem c in this.flowLayoutPanel1.Controls) 68 | { 69 | c.SetIndex(++i); 70 | } 71 | } 72 | private void button3_Click(object sender, EventArgs e) 73 | { 74 | this.flowLayoutPanel1.Controls.Clear(); 75 | } 76 | 77 | private void button2_Click(object sender, EventArgs e) 78 | { 79 | 80 | var t= GetTaksInfo(); 81 | if(t.id == 0) 82 | { 83 | TaskJson.AddTask(t); 84 | } 85 | else 86 | { 87 | TaskJson.EditTask(t); 88 | } 89 | this.Close(); 90 | 91 | } 92 | 93 | 94 | public TaskJson.TaskInfo GetTaksInfo() 95 | { 96 | List list = new List(); 97 | 98 | foreach (TaskEditItem c in this.flowLayoutPanel1.Controls) 99 | { 100 | list.Add(c.GetTaskInfo()); 101 | } 102 | 103 | if(taskInfo != null) 104 | { 105 | taskInfo.TaskItems=list; 106 | taskInfo.Name=this.task_name.Text; 107 | taskInfo.DeviceName = this.devicename_input.Text; 108 | taskInfo.Corn = this.cornexp_input.Text; 109 | return taskInfo; 110 | } 111 | else 112 | { 113 | return new TaskJson.TaskInfo() { 114 | Name = task_name.Text, 115 | TaskItems = list, 116 | DeviceName=devicename_input.Text, 117 | Corn = cornexp_input.Text 118 | }; 119 | } 120 | 121 | } 122 | 123 | 124 | public void ItemDelete(int index) 125 | { 126 | UpdateItemIndex(); 127 | } 128 | 129 | 130 | SelectDevice selectDevice; 131 | private void button4_Click(object sender, EventArgs e) 132 | { 133 | var taskinfo = GetTaksInfo(); 134 | TaskRunWindow tr = new TaskRunWindow(taskinfo, true); 135 | tr.Text = "debug on:" + taskinfo.DeviceName; 136 | tr.ShowDialog(this); 137 | 138 | 139 | //selectDevice = new SelectDevice(); 140 | //selectDevice.button1.Click += SureDevice; 141 | //selectDevice.ShowDialog(this); 142 | //tr.Run(); 143 | 144 | 145 | } 146 | private void SureDevice(object sender, EventArgs e) 147 | { 148 | var name= this.selectDevice.select1.SelectedValue.ToString(); 149 | selectDevice.Dispose(); 150 | TaskRunWindow tr = new TaskRunWindow(GetTaksInfo(),true); 151 | tr.Text = "debug on:"+name; 152 | tr.ShowDialog(this); 153 | } 154 | 155 | private void select1_SelectedIndexChanged(object sender, int value) 156 | { 157 | 158 | } 159 | 160 | private void button5_Click(object sender, EventArgs e) 161 | { 162 | //var time= QuartzJobUtil.GetNextTime(this.cornexp_input.Text, DateTime.Now); 163 | 164 | List five_runtime = new List(); 165 | DateTime? lastTime = DateTime.Now; 166 | for (int i = 0; i < 5; i++) 167 | { 168 | var time = QuartzJobUtil.GetNextTime(this.cornexp_input.Text, lastTime); 169 | 170 | if (time != null) 171 | { 172 | five_runtime.Add(((DateTime)time).ToString("yyyy-MM-dd HH:mm:ss")); 173 | } 174 | lastTime = time; 175 | } 176 | MessageBox.Show(string.Join("\n", five_runtime),"最近5次运行时间"); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /AutoTask/TaskEditItem.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru.AutoTask 2 | { 3 | partial class TaskEditItem 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region 组件设计器生成的代码 24 | 25 | /// 26 | /// 设计器支持所需的方法 - 不要修改 27 | /// 使用代码编辑器修改此方法的内容。 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.label1 = new AntdUI.Label(); 32 | this.button1 = new AntdUI.Button(); 33 | this.input1 = new AntdUI.Input(); 34 | this.select1 = new AntdUI.Select(); 35 | this.SuspendLayout(); 36 | // 37 | // label1 38 | // 39 | this.label1.Location = new System.Drawing.Point(4, 4); 40 | this.label1.Name = "label1"; 41 | this.label1.Size = new System.Drawing.Size(73, 37); 42 | this.label1.TabIndex = 1; 43 | this.label1.Text = "操作"; 44 | // 45 | // button1 46 | // 47 | this.button1.Location = new System.Drawing.Point(431, 4); 48 | this.button1.Name = "button1"; 49 | this.button1.Size = new System.Drawing.Size(68, 37); 50 | this.button1.TabIndex = 2; 51 | this.button1.Text = "删 除"; 52 | this.button1.Click += new System.EventHandler(this.button1_Click); 53 | // 54 | // input1 55 | // 56 | this.input1.Location = new System.Drawing.Point(194, 4); 57 | this.input1.Name = "input1"; 58 | this.input1.Size = new System.Drawing.Size(231, 37); 59 | this.input1.TabIndex = 3; 60 | // 61 | // select1 62 | // 63 | this.select1.Location = new System.Drawing.Point(74, 3); 64 | this.select1.Name = "select1"; 65 | this.select1.Size = new System.Drawing.Size(114, 37); 66 | this.select1.TabIndex = 4; 67 | this.select1.Text = "select1"; 68 | this.select1.SelectedIndexChanged += new AntdUI.IntEventHandler(this.select1_SelectedIndexChanged); 69 | // 70 | // TaskEditItem 71 | // 72 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 73 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 74 | this.Controls.Add(this.select1); 75 | this.Controls.Add(this.input1); 76 | this.Controls.Add(this.button1); 77 | this.Controls.Add(this.label1); 78 | this.Name = "TaskEditItem"; 79 | this.Size = new System.Drawing.Size(502, 48); 80 | this.Load += new System.EventHandler(this.TaskEditItem_Load); 81 | this.ResumeLayout(false); 82 | 83 | } 84 | 85 | #endregion 86 | private AntdUI.Label label1; 87 | private AntdUI.Button button1; 88 | private AntdUI.Input input1; 89 | private AntdUI.Select select1; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /AutoTask/TaskEditItem.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Data; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace MobileControlGuru.AutoTask 13 | { 14 | public partial class TaskEditItem : UserControl 15 | { 16 | 17 | public delegate void Delete(int index); 18 | public int Index; 19 | public Delete deleteEevnt { get; set; } 20 | 21 | public TaskJson.TaskItem taskItem; 22 | public TaskEditItem() 23 | { 24 | InitializeComponent(); 25 | } 26 | public TaskEditItem(TaskJson.TaskItem taskItem) 27 | { 28 | this.taskItem = taskItem; 29 | InitializeComponent(); 30 | 31 | } 32 | public void SetIndex(int index) 33 | { 34 | Index=index; 35 | this.label1.Text ="操作:"+ index.ToString(); 36 | } 37 | 38 | public TaskJson.TaskItem GetTaskInfo() 39 | { 40 | return new TaskJson.TaskItem() 41 | { 42 | Oprate = ((TaskJson.OpType)this.select1.SelectedValue).Oprate, 43 | Param = input1.Text, 44 | IsAdb = ((TaskJson.OpType)this.select1.SelectedValue).IsAdb, 45 | }; 46 | 47 | } 48 | 49 | public bool CheckValue() 50 | { 51 | 52 | return false; 53 | } 54 | 55 | private void TaskEditItem_Load(object sender, EventArgs e) 56 | { 57 | this.select1.Items = TaskJson.Configs; 58 | this.select1.SelectedIndex = 0; 59 | 60 | if (taskItem != null) 61 | { 62 | //this.label1.Text = "操作:" + Index; 63 | var d= TaskJson.OpTypes.Where(n => n.Oprate == taskItem.Oprate).FirstOrDefault(); 64 | int i= TaskJson.OpTypes.IndexOf(d); 65 | if(i != -1) 66 | { 67 | this.select1.SelectedIndex = i; 68 | } 69 | this.input1.Text = taskItem.Param; 70 | } 71 | 72 | } 73 | 74 | private void select1_SelectedIndexChanged(object sender, int value) 75 | { 76 | 77 | } 78 | 79 | private void button1_Click(object sender, EventArgs e) 80 | { 81 | deleteEevnt(Index); 82 | this.Dispose(); 83 | 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /AutoTask/TaskList.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru.AutoTask 2 | { 3 | partial class TaskList 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.flowLayoutPanel1 = new AntdUI.In.FlowLayoutPanel(); 32 | this.button1 = new AntdUI.Button(); 33 | this.button2 = new AntdUI.Button(); 34 | this.start_quatrz = new AntdUI.Button(); 35 | this.shutdown = new AntdUI.Button(); 36 | this.flowPanel1 = new AntdUI.FlowPanel(); 37 | this.flowPanel1.SuspendLayout(); 38 | this.SuspendLayout(); 39 | // 40 | // flowLayoutPanel1 41 | // 42 | this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 43 | | System.Windows.Forms.AnchorStyles.Left) 44 | | System.Windows.Forms.AnchorStyles.Right))); 45 | this.flowLayoutPanel1.AutoScroll = true; 46 | this.flowLayoutPanel1.Location = new System.Drawing.Point(12, 52); 47 | this.flowLayoutPanel1.Name = "flowLayoutPanel1"; 48 | this.flowLayoutPanel1.Size = new System.Drawing.Size(623, 433); 49 | this.flowLayoutPanel1.TabIndex = 0; 50 | // 51 | // button1 52 | // 53 | this.button1.Location = new System.Drawing.Point(3, 3); 54 | this.button1.Name = "button1"; 55 | this.button1.Size = new System.Drawing.Size(75, 36); 56 | this.button1.TabIndex = 1; 57 | this.button1.Text = "添加任务"; 58 | this.button1.Type = AntdUI.TTypeMini.Primary; 59 | this.button1.Click += new System.EventHandler(this.button1_Click); 60 | // 61 | // button2 62 | // 63 | this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 64 | this.button2.Location = new System.Drawing.Point(560, 12); 65 | this.button2.Name = "button2"; 66 | this.button2.Size = new System.Drawing.Size(75, 34); 67 | this.button2.TabIndex = 2; 68 | this.button2.Text = "刷新"; 69 | this.button2.Click += new System.EventHandler(this.button2_Click); 70 | // 71 | // start_quatrz 72 | // 73 | this.start_quatrz.Location = new System.Drawing.Point(165, 3); 74 | this.start_quatrz.Name = "start_quatrz"; 75 | this.start_quatrz.Size = new System.Drawing.Size(75, 36); 76 | this.start_quatrz.TabIndex = 3; 77 | this.start_quatrz.Text = "开启任务"; 78 | this.start_quatrz.Type = AntdUI.TTypeMini.Success; 79 | this.start_quatrz.Click += new System.EventHandler(this.button3_Click); 80 | // 81 | // shutdown 82 | // 83 | this.shutdown.Location = new System.Drawing.Point(84, 3); 84 | this.shutdown.Name = "shutdown"; 85 | this.shutdown.Size = new System.Drawing.Size(75, 36); 86 | this.shutdown.TabIndex = 4; 87 | this.shutdown.Text = "关闭任务"; 88 | this.shutdown.Type = AntdUI.TTypeMini.Error; 89 | this.shutdown.Click += new System.EventHandler(this.button4_Click); 90 | // 91 | // flowPanel1 92 | // 93 | this.flowPanel1.Controls.Add(this.start_quatrz); 94 | this.flowPanel1.Controls.Add(this.shutdown); 95 | this.flowPanel1.Controls.Add(this.button1); 96 | this.flowPanel1.Location = new System.Drawing.Point(12, 4); 97 | this.flowPanel1.Name = "flowPanel1"; 98 | this.flowPanel1.Size = new System.Drawing.Size(260, 42); 99 | this.flowPanel1.TabIndex = 5; 100 | this.flowPanel1.Text = "flowPanel1"; 101 | // 102 | // TaskList 103 | // 104 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 105 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 106 | this.ClientSize = new System.Drawing.Size(647, 497); 107 | this.Controls.Add(this.flowPanel1); 108 | this.Controls.Add(this.button2); 109 | this.Controls.Add(this.flowLayoutPanel1); 110 | this.Name = "TaskList"; 111 | this.Text = "TaskList"; 112 | this.Load += new System.EventHandler(this.TaskList_Load); 113 | this.flowPanel1.ResumeLayout(false); 114 | this.ResumeLayout(false); 115 | 116 | } 117 | 118 | #endregion 119 | 120 | private AntdUI.In.FlowLayoutPanel flowLayoutPanel1; 121 | private AntdUI.Button button1; 122 | private AntdUI.Button button2; 123 | private AntdUI.Button start_quatrz; 124 | private AntdUI.Button shutdown; 125 | private AntdUI.FlowPanel flowPanel1; 126 | } 127 | } -------------------------------------------------------------------------------- /AutoTask/TaskList.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Tools; 2 | using Quartz; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | using static MobileControlGuru.AutoTask.TaskJson; 13 | 14 | namespace MobileControlGuru.AutoTask 15 | { 16 | public partial class TaskList : BaseForm 17 | { 18 | public TaskList() 19 | { 20 | InitializeComponent(); 21 | } 22 | 23 | private void TaskList_Load(object sender, EventArgs e) 24 | { 25 | Init(); 26 | } 27 | 28 | public void Init() 29 | { 30 | TaskJson.LoadJsonData(); 31 | this.flowLayoutPanel1.Controls.Clear(); 32 | foreach (var task in TaskJson.Instance.tasks) 33 | { 34 | var item = new TaskShowItem(task); 35 | item.taskList = this; 36 | item.Tag = task; 37 | 38 | this.flowLayoutPanel1.Controls.Add(item); 39 | } 40 | start_quatrz.Visible = !QuartzJobUtil.scheduler.IsStarted; 41 | shutdown.Visible = QuartzJobUtil.scheduler.IsStarted; 42 | 43 | 44 | } 45 | private void button1_Click(object sender, EventArgs e) 46 | { 47 | ShowEdit(null); 48 | } 49 | 50 | private void EditFormClosed(object sender, FormClosedEventArgs e) 51 | { 52 | Init(); 53 | } 54 | 55 | public void ShowEdit(TaskJson.TaskInfo taskinfo) 56 | { 57 | TaskEdit diglog = new TaskEdit(taskinfo); 58 | diglog.StartPosition = FormStartPosition.CenterParent; 59 | diglog.FormClosed += EditFormClosed; 60 | diglog.Show(this); 61 | 62 | } 63 | 64 | public void DelTask (int id) 65 | { 66 | 67 | TaskJson.DelTask(id); 68 | Init(); 69 | } 70 | 71 | private void button2_Click(object sender, EventArgs e) 72 | { 73 | Init(); 74 | } 75 | 76 | private void button3_Click(object sender, EventArgs e) 77 | { 78 | QuartzJobUtil.StartScheduler(); 79 | foreach (var task in TaskJson.Instance.tasks) 80 | { 81 | 82 | try 83 | { 84 | Dictionary a = new Dictionary 85 | { 86 | {"taskInfo", task } 87 | }; 88 | QuartzJobUtil.AddJob 89 | (task.id.ToString(), task.Corn, new JobDataMap(a)); 90 | 91 | if (!task.IsRun) 92 | { 93 | QuartzJobUtil.PauseJob(task.id.ToString()); 94 | } 95 | 96 | } 97 | catch (Exception ex) { 98 | LogHelper.Error(ex); 99 | MessageBox.Show($"添加任务{task.Name}出错 任务id:{task.id}"); 100 | } 101 | 102 | 103 | 104 | } 105 | 106 | start_quatrz.Visible = false; 107 | shutdown.Visible = true; 108 | 109 | } 110 | 111 | private void button4_Click(object sender, EventArgs e) 112 | { 113 | QuartzJobUtil.ShutdownScheduler(); 114 | start_quatrz.Visible = true; 115 | shutdown.Visible = false; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /AutoTask/TaskRun.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Base; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Threading; 9 | using MobileControlGuru.Tools; 10 | using MobileControlGuru.Src; 11 | using static MobileControlGuru.AutoTask.TaskRun; 12 | namespace MobileControlGuru.AutoTask 13 | { 14 | public class TaskRun 15 | { 16 | TaskJson.TaskInfo taskInfo; 17 | string DeviceName; 18 | bool Debug=false; 19 | 20 | public delegate void TaskStartDelegate(); 21 | public delegate void TaskFinishedDelegate(); 22 | 23 | public delegate void TaskSingleEndDelegate(string cmd); 24 | public delegate void TaskSingleStartDelegate(string cmd); 25 | 26 | //任务开始委托 27 | public TaskStartDelegate taskStartDelegate; 28 | //任务结束委托 29 | public TaskFinishedDelegate taskFinishedDelegate; 30 | 31 | //单条指令开始 32 | public TaskSingleStartDelegate singleStartDelegate; 33 | //单条指令结束 34 | public TaskSingleEndDelegate singleEndDelegate; 35 | public CancellationTokenSource cts = new CancellationTokenSource(); 36 | public TaskRun(string deviceName, TaskJson.TaskInfo taskInfo, bool debug=false) 37 | { 38 | DeviceName = deviceName; 39 | this.taskInfo = taskInfo; 40 | Debug = debug; 41 | } 42 | 43 | /// 44 | /// 任务核心运行方法 45 | /// 46 | /// 47 | /// 48 | public string R(TaskJson.TaskItem taskItem) 49 | { 50 | try 51 | { 52 | 53 | if (singleStartDelegate != null) 54 | { 55 | singleStartDelegate(taskItem.Oprate + " " + taskItem.Param); 56 | } 57 | 58 | string res = ""; 59 | if (taskItem.IsAdb) 60 | { 61 | 62 | var adb= ADB.Exec( 63 | (!string.IsNullOrEmpty(DeviceName)?"-s "+ DeviceName : "")+ " " 64 | +taskItem.Oprate + " " + taskItem.Param); 65 | res= adb.Message; 66 | } 67 | else 68 | { 69 | if (taskItem.Oprate == "sleep") 70 | { 71 | var times= taskItem.Param.Split(' '); 72 | if (times.Count() == 1) 73 | { 74 | Thread.Sleep(Convert.ToInt32(times[0])); 75 | } 76 | else 77 | { 78 | int time = new Random().Next(Convert.ToInt32(times[0]), Convert.ToInt32(times[1])); 79 | Thread.Sleep(time); 80 | } 81 | 82 | } 83 | else 84 | { 85 | res= CMD.Exec(taskItem.Param); 86 | 87 | } 88 | } 89 | 90 | if (singleEndDelegate != null) 91 | { 92 | singleEndDelegate(res); 93 | } 94 | 95 | return res; 96 | } 97 | catch (Exception ex) 98 | { 99 | LogHelper.Error(ex, taskItem.Oprate+""+ taskItem.Param); 100 | singleEndDelegate("run error"+ ex.Message); 101 | return ex.Message; 102 | } 103 | 104 | 105 | } 106 | 107 | public void Run() 108 | { 109 | 110 | if (taskStartDelegate != null) 111 | { 112 | taskStartDelegate(); 113 | } 114 | if (Debug) 115 | { 116 | foreach (var t in taskInfo.TaskItems) 117 | { 118 | if (cts.Token.IsCancellationRequested) 119 | { 120 | break; 121 | } 122 | R(t); 123 | } 124 | } 125 | else 126 | { 127 | int i = taskInfo.RunTimes; 128 | while (i != 0) 129 | { 130 | foreach (var t in taskInfo.TaskItems) 131 | { 132 | if (cts.Token.IsCancellationRequested) 133 | { 134 | break; 135 | } 136 | R(t); 137 | } 138 | i--; 139 | } 140 | 141 | } 142 | 143 | 144 | if (taskFinishedDelegate != null) 145 | { 146 | taskFinishedDelegate(); 147 | } 148 | } 149 | 150 | public async Task RunAsync() 151 | { 152 | //using (HttpClient client = new HttpClient()) 153 | //{ 154 | // string website = "https://www.example.com"; 155 | // string content = await client.GetStringAsync(website); 156 | // Console.WriteLine("下载内容长度:" + content.Length); 157 | //} 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /AutoTask/TaskRunWindow.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru.AutoTask 2 | { 3 | partial class TaskRunWindow 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.button1 = new AntdUI.Button(); 32 | this.textBox1 = new System.Windows.Forms.TextBox(); 33 | this.button2 = new AntdUI.Button(); 34 | this.flowLayoutPanel1 = new AntdUI.In.FlowLayoutPanel(); 35 | this.flowLayoutPanel1.SuspendLayout(); 36 | this.SuspendLayout(); 37 | // 38 | // button1 39 | // 40 | this.button1.Location = new System.Drawing.Point(3, 3); 41 | this.button1.Name = "button1"; 42 | this.button1.Size = new System.Drawing.Size(75, 38); 43 | this.button1.TabIndex = 1; 44 | this.button1.Text = "运行"; 45 | this.button1.Click += new System.EventHandler(this.button1_Click); 46 | // 47 | // textBox1 48 | // 49 | this.textBox1.Location = new System.Drawing.Point(12, 54); 50 | this.textBox1.Multiline = true; 51 | this.textBox1.Name = "textBox1"; 52 | this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 53 | this.textBox1.Size = new System.Drawing.Size(348, 469); 54 | this.textBox1.TabIndex = 2; 55 | // 56 | // button2 57 | // 58 | this.button2.Location = new System.Drawing.Point(84, 3); 59 | this.button2.Name = "button2"; 60 | this.button2.Size = new System.Drawing.Size(75, 36); 61 | this.button2.TabIndex = 3; 62 | this.button2.Text = "停止"; 63 | this.button2.Type = AntdUI.TTypeMini.Error; 64 | this.button2.Click += new System.EventHandler(this.button2_Click); 65 | // 66 | // flowLayoutPanel1 67 | // 68 | this.flowLayoutPanel1.Controls.Add(this.button1); 69 | this.flowLayoutPanel1.Controls.Add(this.button2); 70 | this.flowLayoutPanel1.Location = new System.Drawing.Point(12, 3); 71 | this.flowLayoutPanel1.Name = "flowLayoutPanel1"; 72 | this.flowLayoutPanel1.Size = new System.Drawing.Size(200, 45); 73 | this.flowLayoutPanel1.TabIndex = 4; 74 | // 75 | // TaskRunWindow 76 | // 77 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 78 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 79 | this.ClientSize = new System.Drawing.Size(371, 532); 80 | this.Controls.Add(this.flowLayoutPanel1); 81 | this.Controls.Add(this.textBox1); 82 | this.Name = "TaskRunWindow"; 83 | this.Text = "t"; 84 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.TaskRunWindow_FormClosing); 85 | this.Load += new System.EventHandler(this.TaskRunWindow_Load); 86 | this.flowLayoutPanel1.ResumeLayout(false); 87 | this.ResumeLayout(false); 88 | this.PerformLayout(); 89 | 90 | } 91 | 92 | #endregion 93 | private AntdUI.Button button1; 94 | private System.Windows.Forms.TextBox textBox1; 95 | private AntdUI.Button button2; 96 | private AntdUI.In.FlowLayoutPanel flowLayoutPanel1; 97 | } 98 | } -------------------------------------------------------------------------------- /AutoTask/TaskRunWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | using static Microsoft.WindowsAPICodePack.Shell.PropertySystem.SystemProperties.System; 12 | using static MobileControlGuru.AutoTask.TaskRun; 13 | 14 | namespace MobileControlGuru.AutoTask 15 | { 16 | public partial class TaskRunWindow : BaseForm 17 | { 18 | public string DeviceName; 19 | public TaskJson.TaskInfo TaskInfo; 20 | public TaskRun taskRun; 21 | bool Debug=false; 22 | public System.Threading.Tasks.Task task; 23 | 24 | //任务开始委托 25 | public TaskStartDelegate taskStartDelegate; 26 | //任务结束委托 27 | public TaskFinishedDelegate taskFinishedDelegate; 28 | 29 | //单条指令开始 30 | public TaskSingleStartDelegate singleStartDelegate; 31 | //单条指令结束 32 | public TaskSingleEndDelegate singleEndDelegate; 33 | 34 | public TaskRunWindow( TaskJson.TaskInfo taskInfo, bool debug=false) 35 | { 36 | DeviceName = taskInfo.DeviceName; 37 | TaskInfo = taskInfo; 38 | Debug = debug; 39 | InitializeComponent(); 40 | 41 | } 42 | 43 | public void RunTask() 44 | { 45 | taskRun = new TaskRun(DeviceName, TaskInfo, Debug); 46 | taskRun.singleStartDelegate += TaskItemStart; 47 | taskRun.singleEndDelegate += TaskItemEnd; 48 | taskRun.taskStartDelegate += TaskStart; 49 | taskRun.taskFinishedDelegate += TaskFinished; 50 | 51 | taskRun.taskStartDelegate += taskStartDelegate; 52 | taskRun.taskFinishedDelegate += taskFinishedDelegate; 53 | taskRun.singleStartDelegate += singleStartDelegate; 54 | taskRun.singleEndDelegate += singleEndDelegate; 55 | 56 | task = new System.Threading.Tasks.Task(() => 57 | { 58 | 59 | taskRun.Run(); 60 | 61 | }, taskRun.cts.Token); 62 | task.Start(); 63 | 64 | } 65 | 66 | private void TaskStart() 67 | { 68 | 69 | UpdateButtonStart(false); 70 | UpdateButtonEnd(true); 71 | AddMessage("task Start :"+DateTime.Now.ToString()); 72 | } 73 | 74 | private void TaskFinished() 75 | { 76 | UpdateButtonStart(true); 77 | UpdateButtonEnd( false); 78 | AddMessage("task End :"+ DateTime.Now.ToString()); 79 | } 80 | private void UpdateButtonStart(bool visible) 81 | { 82 | if (button1.InvokeRequired) 83 | { 84 | button1.Invoke(new Action(() => 85 | { 86 | this.button1.Visible = visible; 87 | 88 | })); 89 | } 90 | else 91 | { 92 | this.button1.Visible = visible; 93 | } 94 | } 95 | 96 | private void UpdateButtonEnd(bool visible) 97 | { 98 | if (button2.InvokeRequired) 99 | { 100 | button2.Invoke(new Action(() => 101 | { 102 | this.button2.Visible = visible; 103 | 104 | })); 105 | } 106 | else 107 | { 108 | this.button2.Visible = visible; 109 | } 110 | } 111 | 112 | private void TaskItemEnd(string output) 113 | { 114 | AddMessage("output:"+output); 115 | 116 | } 117 | 118 | private void TaskItemStart(string cmd) 119 | { 120 | AddMessage("cmd:" + cmd); 121 | // throw new NotImplementedException(); 122 | } 123 | 124 | public void AddMessage(string msg) 125 | { 126 | 127 | // 假设这是你在后台线程中执行的代码 128 | if (textBox1.InvokeRequired) 129 | { 130 | textBox1.Invoke(new Action(() => 131 | { 132 | this.textBox1.Text += msg + Environment.NewLine; 133 | this.textBox1.Refresh(); 134 | textBox1.SelectionStart = textBox1.Text.Length; // 设置选区开始位置为文本末尾 135 | textBox1.ScrollToCaret(); // 滚动到插入符号位置(即文本末尾) 136 | })); 137 | } 138 | else 139 | { 140 | // 如果当前线程已经是UI线程,则直接更新 141 | this.textBox1.Text += msg + Environment.NewLine; 142 | this.textBox1.Refresh(); 143 | textBox1.SelectionStart = textBox1.Text.Length; // 设置选区开始位置为文本末尾 144 | textBox1.ScrollToCaret(); // 滚动到插入符号位置(即文本末尾) 145 | } 146 | } 147 | 148 | private void button1_Click(object sender, EventArgs e) 149 | { 150 | RunTask(); 151 | } 152 | 153 | private void TaskRunWindow_Load(object sender, EventArgs e) 154 | { 155 | 156 | } 157 | 158 | private void TaskRunWindow_FormClosing(object sender, FormClosingEventArgs e) 159 | { 160 | 161 | } 162 | 163 | private void button2_Click(object sender, EventArgs e) 164 | { 165 | //task.ki(); 166 | taskRun.cts.Cancel(); 167 | //TaskFinished(); 168 | } 169 | 170 | 171 | 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /AutoTask/TaskShowItem.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Src; 2 | using MobileControlGuru.Tools; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace MobileControlGuru.AutoTask 15 | { 16 | public partial class TaskShowItem : UserControl 17 | { 18 | public TaskShowItem() 19 | { 20 | InitializeComponent(); 21 | } 22 | public TaskJson.TaskInfo taskInfo; 23 | public TaskList taskList; 24 | TaskRunWindow runWindow; 25 | 26 | public TaskShowItem(TaskJson.TaskInfo taskInfo) 27 | { 28 | this.taskInfo = taskInfo; 29 | InitializeComponent(); 30 | } 31 | 32 | private void TaskShowItem_Load(object sender, EventArgs e) 33 | { 34 | if (taskInfo != null) 35 | { 36 | this.label1.Text = taskInfo.Name; 37 | this.inputNumber1.Value=taskInfo.RunTimes; 38 | 39 | run_able.Text = taskInfo.IsRun?"启用中":"禁用中"; 40 | run_able.Type = taskInfo.IsRun ? AntdUI.TTypeMini.Success: AntdUI.TTypeMini.Warn; 41 | } 42 | 43 | 44 | } 45 | 46 | private void button1_Click(object sender, EventArgs e) 47 | { 48 | var taskinfo= (TaskJson.TaskInfo)this.Tag; 49 | 50 | taskList.ShowEdit(taskinfo); 51 | } 52 | 53 | private void button2_Click(object sender, EventArgs e) 54 | { 55 | var taskinfo = (TaskJson.TaskInfo)this.Tag; 56 | DialogResult AF = MessageBox.Show("确定删除"+ taskinfo.Name+ "任务吗?", "确认框", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); 57 | if (AF == DialogResult.OK) 58 | { 59 | taskList.DelTask(taskinfo.id); 60 | } 61 | else 62 | { 63 | 64 | } 65 | 66 | } 67 | 68 | private void button3_Click(object sender, EventArgs e) 69 | { 70 | 71 | //AntdUI.Button button = (AntdUI.Button)sender; 72 | 73 | //DeviceManager.Instance.devices.ForEach(n => 74 | //{ 75 | 76 | 77 | //}); 78 | runWindow = new TaskRunWindow(taskInfo); 79 | runWindow.Show(); 80 | //button.Visible = false; 81 | //runWindow.taskStartDelegate += taskStart; 82 | //runWindow.taskFinishedDelegate += taskFinished; 83 | runWindow.RunTask(); 84 | } 85 | 86 | private void taskStart() 87 | { 88 | UpdateButton(run, false); 89 | UpdateButton(del, false); 90 | UpdateButton(edit, false); 91 | 92 | UpdateButton(button4, true); 93 | UpdateButton(run_able, true); 94 | } 95 | 96 | private void taskFinished() 97 | { 98 | UpdateButton(run,true); 99 | UpdateButton(del,true); 100 | UpdateButton(edit,true); 101 | 102 | UpdateButton(button4,false); 103 | UpdateButton(run_able, false); 104 | 105 | runWindow=null; 106 | } 107 | 108 | private void UpdateButton(AntdUI.Button button, bool visible) 109 | { 110 | if (button.InvokeRequired) 111 | { 112 | button.Invoke(new Action(() => 113 | { 114 | button.Visible = visible; 115 | 116 | })); 117 | } 118 | else 119 | { 120 | button.Visible = visible; 121 | } 122 | } 123 | 124 | private void inputNumber1_ValueChanged(object sender, decimal value) 125 | { 126 | taskInfo.RunTimes=Convert.ToInt32(value); 127 | TaskJson.EditTask(taskInfo); 128 | } 129 | 130 | private void button4_Click(object sender, EventArgs e) 131 | { 132 | runWindow.Show(); 133 | } 134 | 135 | private void button5_Click(object sender, EventArgs e) 136 | { 137 | //runWindow.taskRun.cts.Cancel(); 138 | 139 | var btn = (AntdUI.Button)sender; 140 | this.taskInfo.IsRun = !taskInfo.IsRun; 141 | 142 | if (taskInfo.IsRun) 143 | { 144 | btn.Text = "启用中"; 145 | btn.Type = AntdUI.TTypeMini.Success; 146 | 147 | QuartzJobUtil.ResumeJob(taskInfo.id.ToString()); 148 | } 149 | else 150 | { 151 | btn.Text = "禁用中"; 152 | btn.Type = AntdUI.TTypeMini.Warn; 153 | QuartzJobUtil.PauseJob(taskInfo.id.ToString()); 154 | } 155 | TaskJson.EditTask(taskInfo); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Base/ADB.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Src; 2 | using MobileControlGuru.Tools; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Drawing; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace MobileControlGuru.Base 14 | { 15 | public class ADB 16 | { 17 | 18 | public static string ADB_PATH = Path.Combine(ConfigHelp.GetConfig("ScrcpyPath"), "adb.exe"); 19 | 20 | public static AdbParse Exec(string Arguments) 21 | { 22 | try 23 | { 24 | LogHelper.Info("exc [adb " + Arguments+"]"); 25 | var p = Process.Start(new System.Diagnostics.ProcessStartInfo(ADB_PATH) 26 | { 27 | Arguments = Arguments, 28 | CreateNoWindow = true, 29 | UseShellExecute = false, 30 | RedirectStandardOutput = true, 31 | RedirectStandardError = true, 32 | StandardOutputEncoding = Encoding.UTF8, 33 | StandardErrorEncoding = Encoding.UTF8, 34 | }); 35 | 36 | string output = p.StandardOutput.ReadToEnd(); 37 | LogHelper.Info("adb output: " + output); 38 | 39 | p.Close(); 40 | p.Dispose(); 41 | return new AdbParse("adb "+ Arguments, output); 42 | } 43 | catch(Exception e) 44 | { 45 | LogHelper.Error(e, "exc error:[adb " + Arguments + "]\n"); 46 | return new AdbParse("adb " + Arguments, false,e.Message); 47 | 48 | } 49 | 50 | 51 | 52 | } 53 | 54 | public async Task ExecuteCommandsInParallel(List commands) 55 | { 56 | var tasks = new List(); 57 | 58 | foreach (var command in commands) 59 | { 60 | // 创建并启动一个新的任务,传入当前命令 61 | tasks.Add(Task.Run(() => Exec(command))); 62 | } 63 | 64 | // 等待所有任务完成 65 | await Task.WhenAll(tasks); 66 | } 67 | 68 | 69 | 70 | 71 | #region adb 操作 72 | 73 | public static AdbParse Connect(string device) 74 | { 75 | 76 | var res = Exec($"connect {device}"); 77 | 78 | DeviceManager.Instance.UpdateDevices(); 79 | return res; 80 | } 81 | 82 | public static AdbParse Pair(string device,int port,int code) 83 | { 84 | 85 | var res = Exec($"pair {device}:{port} {code}"); 86 | DeviceManager.Instance.UpdateDevices(); 87 | return res; 88 | } 89 | public static AdbParse SendKeyEvent(string device, ADBKey.Key key) 90 | { 91 | 92 | var res = Exec($"-s {device} shell input keyevent {key}"); 93 | return res; 94 | } 95 | public static AdbParse SendKeyEvent(string device, string key) 96 | { 97 | 98 | var res = Exec($"-s {device} shell input keyevent {key}"); 99 | return res; 100 | 101 | } 102 | public static AdbParse Screencap(string device, string path) 103 | { 104 | 105 | var res = Exec($"-s {device} shell screencap -p >{path}"); 106 | return res; 107 | 108 | } 109 | 110 | #endregion 111 | public static List GetDeiveces() 112 | { 113 | var str = Exec("devices"); 114 | var devices = str.Message.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries) 115 | .Skip(1).Select(line => line.Split('\t')).ToArray(); 116 | List items = new List(); 117 | foreach (var deviceInfo in devices) 118 | { 119 | if (deviceInfo.Length == 2) 120 | { 121 | string deviceId = deviceInfo[0]; 122 | string deviceStatus = deviceInfo[1]; 123 | items.Add(new Model.Device() 124 | { 125 | Name = deviceId, 126 | Status = deviceStatus, 127 | IsTcpIP= Tools.Common.IsValidIP(deviceId) 128 | 129 | }); 130 | } 131 | } 132 | items= items.Where(n=>n.Status=="device").ToList(); 133 | return items; 134 | } 135 | 136 | public static async Task Lock(List deviceItems) 137 | { 138 | 139 | 140 | var tasks = new List(); 141 | foreach (var d in deviceItems) 142 | { 143 | // 创建并启动一个新的任务,传入当前命令 144 | tasks.Add(Task.Run( 145 | () =>new DeviceADB(d.Name).SendKeyEvent( ADBKey.Key.KEYCODE_POWER) 146 | ) 147 | ); 148 | } 149 | 150 | // 等待所有任务完成 151 | await Task.WhenAll(tasks); 152 | 153 | } 154 | 155 | public static void Lock(string device) 156 | { 157 | new DeviceADB(device).SendKeyEvent(ADBKey.Key.KEYCODE_POWER); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Base/ADBKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MobileControlGuru.Base 8 | { 9 | public class ADBKey 10 | { 11 | //https://blog.csdn.net/h_bpdwn/article/details/91425599 12 | public enum Key 13 | { 14 | 15 | KEYCODE_HOME=3, 16 | KEYCODE_MENU=82, 17 | KEYCODE_BACK=4, 18 | 19 | KEYCODE_VOLUME_UP = 24, 20 | KEYCODE_VOLUME_DOWN = 25, 21 | KEYCODE_POWER = 26, 22 | 23 | KEYCODE_ENTER = 66, 24 | KEYCODE_ESCAPE = 111, 25 | 26 | KEYCODE_VOLUME_MUTE = 164, 27 | KEYCODE_PAGE_UP = 92, 28 | KEYCODE_PAGE_DOWN = 93, 29 | 30 | 31 | #region MEDIA 32 | KEYCODE_MEDIA_PLAY, 33 | KEYCODE_MEDIA_PAUSE, 34 | KEYCODE_MEDIA_STOP, 35 | KEYCODE_MEDIA_PLAY_PAUSE, 36 | KEYCODE_MEDIA_FAST_FORWARD, 37 | KEYCODE_MEDIA_REWIND, 38 | KEYCODE_MEDIA_NEXT, 39 | KEYCODE_MEDIA_PREVIOUS, 40 | KEYCODE_MEDIA_CLOSE, 41 | KEYCODE_MEDIA_EJECT, 42 | KEYCODE_MEDIA_RECORD, 43 | #endregion 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Base/AdbParse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MobileControlGuru.Base 8 | { 9 | /// 10 | /// adb 返回处理 11 | /// 12 | public class AdbParse 13 | { 14 | public bool IsSuccess { get; set; } 15 | public string Message { set; get; } 16 | public string Cmd { set; get; } 17 | 18 | public AdbParse(string Cmd, string adbres) 19 | { 20 | this.Message= adbres; 21 | this.Cmd = Cmd; 22 | this.IsSuccess = true; 23 | 24 | 25 | } 26 | 27 | public AdbParse(string Cmd, bool isSuccess, string message) 28 | { 29 | IsSuccess = isSuccess; 30 | Message = message; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Base/CMD.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Tools; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace MobileControlGuru.Base 10 | { 11 | public class CMD 12 | { 13 | 14 | public static string Exec(string command) { 15 | 16 | LogHelper.Info("cmd run:" + command); 17 | // 创建一个新的ProcessStartInfo对象来配置进程启动信息 18 | ProcessStartInfo startInfo = new ProcessStartInfo(); 19 | startInfo.FileName = "cmd.exe"; // 指定要运行的命令解释器(CMD) 20 | startInfo.Arguments = "/C " + command; // 指定要运行的命令和参数(/C 表示执行完命令后关闭命令窗口) 21 | startInfo.RedirectStandardOutput = true; // 重定向标准输出以便捕获输出 22 | startInfo.UseShellExecute = false; // 设置为false以便能够重定向输出 23 | startInfo.CreateNoWindow = true; // 设置为true以隐藏命令窗口(如果不需要看到窗口) 24 | 25 | // 使用ProcessStartInfo对象启动进程 26 | using (Process process = new Process { StartInfo = startInfo }) 27 | { 28 | // 启动进程 29 | process.Start(); 30 | 31 | // 读取命令的输出 32 | string output = process.StandardOutput.ReadToEnd(); 33 | 34 | // 等待进程结束 35 | process.WaitForExit(); 36 | LogHelper.Info("cmd output:" + output); 37 | return output; 38 | } 39 | 40 | 41 | 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Base/FireWallHelper.cs: -------------------------------------------------------------------------------- 1 | using NetFwTypeLib; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MobileControlGuru.Base 11 | { 12 | public class FireWallHelper 13 | { 14 | public static void AddOrUpdateInboundRule(string name, int port) 15 | { 16 | // 获取防火墙策略对象 17 | INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2")); 18 | 19 | // 检查规则是否存在 20 | INetFwRule existingRule = firewallPolicy.Rules.Item(name); 21 | if (existingRule != null) 22 | { 23 | // 更新现有规则 24 | existingRule.LocalPorts = port.ToString(); 25 | // 你可以根据需要更新其他属性,比如 Action, Enabled 等 26 | 27 | } 28 | else 29 | { 30 | // 创建新规则 31 | INetFwRule firewallRule = (INetFwRule)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwRule")); 32 | 33 | // 设置规则属性 34 | firewallRule.Name = name; 35 | firewallRule.Description = "Allow inbound traffic to my service on port " + port; 36 | firewallRule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN; 37 | firewallRule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW; 38 | firewallRule.Enabled = true; 39 | firewallRule.Profiles = (int)(NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_ALL); 40 | firewallRule.LocalPorts = port.ToString(); 41 | firewallRule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP; 42 | firewallRule.Grouping = "@firewallapi.dll,-28752"; // 可以自定义分组,或者留空 43 | firewallRule.EdgeTraversal = false; 44 | 45 | // 添加新规则到防火墙策略 46 | firewallPolicy.Rules.Add(firewallRule); 47 | 48 | } 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Base/GlobalHotkey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Forms; 9 | 10 | namespace MobileControlGuru.Base 11 | { 12 | public class GlobalHotkey 13 | { 14 | #region Hotkey 15 | //如果函数执行成功,返回值不为0。 16 | //如果函数执行失败,返回值为0。要得到扩展错误信息,调用GetLastError。 17 | [DllImport("user32.dll", SetLastError = true)] 18 | public static extern bool RegisterHotKey( 19 | IntPtr hWnd, //要定义热键的窗口的句柄 20 | int id, //定义热键ID(不能与其它ID重复) 21 | KeyModifiers fsModifiers, //标识热键是否在按Alt、Ctrl、Shift、Windows等键时才会生效 22 | Keys vk //定义热键的内容 23 | ); 24 | [DllImport("user32.dll", SetLastError = true)] 25 | public static extern bool UnregisterHotKey( 26 | IntPtr hWnd, //要取消热键的窗口的句柄 27 | int id //要取消热键的ID 28 | ); 29 | //定义了辅助键的名称(将数字转变为字符以便于记忆,也可去除此枚举而直接使用数值) 30 | [Flags()] 31 | public enum KeyModifiers 32 | { 33 | None = 0, 34 | Alt = 1, 35 | Ctrl = 2, 36 | Shift = 4, 37 | WindowsKey = 8 38 | } 39 | #endregion 40 | 41 | 42 | 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Base/ProcessWindowController.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | 11 | namespace MobileControlGuru.Base 12 | { 13 | public class ProcessWindowController 14 | { 15 | [DllImport("user32.dll")] 16 | public static extern bool SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 17 | [DllImport("user32.dll")] 18 | public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); 19 | [DllImport("user32.dll")] 20 | public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); 21 | 22 | [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 23 | public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); 24 | [DllImport("user32.dll")] 25 | public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 26 | 27 | [DllImport("user32.dll")] 28 | public static extern int GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 29 | 30 | [DllImport("user32.dll")] 31 | [return: MarshalAs(UnmanagedType.Bool)] 32 | public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); 33 | 34 | [DllImport("user32.dll")] 35 | public static extern bool UnhookWinEvent(IntPtr hWinEventHook); 36 | [StructLayout(LayoutKind.Sequential)] 37 | public struct RECT 38 | { 39 | public int Left; //最左坐标 40 | public int Top; //最上坐标 41 | public int Right; //最右坐标 42 | public int Bottom; //最下坐标 43 | } 44 | 45 | public const uint WINEVENT_OUTOFCONTEXT = 0; 46 | public const uint EVENT_SYSTEM_FOREGROUND = 3; 47 | public const int SW_MINIMIZE = 6; 48 | public const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B; 49 | public const uint EVENT_SYSTEM_DESKTOPSWITCH = 0x0020; 50 | 51 | public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); 52 | 53 | 54 | 55 | 56 | public static void MinimizeProcessWindow(int processId) 57 | { 58 | // 获取进程的主窗口句柄 59 | IntPtr hWnd = Process.GetProcessById(processId).MainWindowHandle; 60 | 61 | // 如果找到了窗口句柄 62 | if (hWnd != IntPtr.Zero) 63 | { 64 | // 将窗口最小化 65 | ShowWindow(hWnd, SW_MINIMIZE); 66 | } 67 | else 68 | { 69 | Console.WriteLine($"无法找到进程ID {processId} 的主窗口"); 70 | } 71 | } 72 | public static string GetWindowTitle(int processId) 73 | { 74 | // 获取进程的主窗口句柄 75 | IntPtr hWnd = Process.GetProcessById(processId).MainWindowHandle; 76 | 77 | // 创建一个StringBuilder来存储窗口标题 78 | var sb = new StringBuilder(256); 79 | 80 | // 如果找到了窗口句柄 81 | if (hWnd != IntPtr.Zero) 82 | { 83 | // 获取窗口标题 84 | int length = GetWindowText(hWnd, sb, sb.Capacity); 85 | if (length > 0) 86 | { 87 | return sb.ToString(); 88 | } 89 | else 90 | { 91 | Console.WriteLine("无法获取窗口标题"); 92 | } 93 | } 94 | else 95 | { 96 | Console.WriteLine($"无法找到进程ID {processId} 的主窗口"); 97 | } 98 | 99 | return null; 100 | } 101 | 102 | 103 | 104 | 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Base/Scrcpy.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Tools; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MobileControlGuru.Base 11 | { 12 | public class Scrcpy 13 | { 14 | public static string PATH = Path.Combine(ConfigHelp.GetConfig("ScrcpyPath"), "scrcpy.exe"); 15 | 16 | public static bool ChcekScrcpyPATH() 17 | { 18 | 19 | return File.Exists(PATH); 20 | } 21 | public static void ExtractScrcpyZip(string BasePath) 22 | { 23 | 24 | ZipHelper.ExtractZipToDirectory(BasePath); 25 | string basapath = Path.Combine(BasePath, "scrcpy-win64-v2.4"); 26 | Tools.ConfigHelp.SetSetting("ScrcpyPath", basapath); 27 | ADB.ADB_PATH = Path.Combine(basapath, "adb.exe"); 28 | Scrcpy.PATH = Path.Combine(basapath, "scrcpy.exe"); 29 | } 30 | 31 | public static Process Exec(string Arguments) 32 | { 33 | LogHelper.Info("exc [scrcpy " + Arguments+"]"); 34 | var p = Process.Start(new System.Diagnostics.ProcessStartInfo(PATH) 35 | { 36 | Arguments = Arguments, 37 | CreateNoWindow = true, 38 | UseShellExecute = false, 39 | RedirectStandardOutput = true, 40 | RedirectStandardError = true, 41 | StandardOutputEncoding = Encoding.UTF8, 42 | StandardErrorEncoding = Encoding.UTF8, 43 | }); 44 | p.EnableRaisingEvents = true; 45 | return p; 46 | } 47 | 48 | #region 49 | public static Process IPConnet(string device,string port) 50 | { 51 | string device_ip = $"{device}:{port}"; 52 | var p= IPPut(device_ip); 53 | return p; 54 | } 55 | public static Process IPPut(string device_ip) 56 | { 57 | var adbinfo = ADB.Connect(device_ip); 58 | var p = Exec($"-s {device_ip} --window-title={device_ip} "+Scrcpy.GetDefaultSetting()); 59 | return p; 60 | } 61 | 62 | 63 | public static Process UsbPut(string device) 64 | { 65 | var p = Exec($" -s {device} --window-title={device} " + Scrcpy.GetDefaultSetting()); 66 | return p; 67 | } 68 | 69 | public static Process Put(string device,string CustomSetting="") 70 | { 71 | Process p = null; 72 | if (!string.IsNullOrEmpty(CustomSetting)) 73 | { 74 | p = Exec($" -s {device} --window-title={device} " + CustomSetting); 75 | } 76 | else 77 | { 78 | p = Exec($" -s {device} --window-title={device} " + Scrcpy.GetDefaultSetting()); 79 | } 80 | 81 | return p; 82 | } 83 | #endregion 84 | 85 | 86 | public static string GetDefaultSetting() 87 | { 88 | var scrcpyParam = JsonHelp.Str2Obj(ConfigHelp.GetConfig("ScrcpyParamObj")); 89 | 90 | if (scrcpyParam != null) 91 | { 92 | return scrcpyParam.ToString(); 93 | } 94 | 95 | return ""; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Base/ScrcpyParam.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MobileControlGuru.Base 9 | { 10 | public class ScrcpyParam 11 | { 12 | #region audio params 13 | [ScrcpyParam("--no-audio")] 14 | public bool NoAudio { get; set; } 15 | 16 | 17 | 18 | #endregion 19 | 20 | #region video params https://github.com/Genymobile/scrcpy/blob/master/doc/video.md 21 | [ScrcpyParam("--no-video")] 22 | public bool NoVideo { get; set; } 23 | 24 | #endregion 25 | 26 | 27 | #region window params https://github.com/Genymobile/scrcpy/blob/master/doc/window.md 28 | //[ScrcpyParam("--window-title")] 29 | //public string WindowTitle { get; set; } 30 | [ScrcpyParam("--window-x")] 31 | public string WindowX { get; set; } 32 | [ScrcpyParam("--window-y")] 33 | public string WindowY { get; set; } 34 | [ScrcpyParam("--window-width")] 35 | public string WindowWidth { get; set; } 36 | [ScrcpyParam("--window-height")] 37 | public string WindowHeight { get; set; } 38 | 39 | [ScrcpyParam("--window-borderless")] 40 | public bool WindowBorderless { get; set; } 41 | 42 | [ScrcpyParam("--always-on-top")] 43 | public bool AlwaysOnTop { get; set; } 44 | [ScrcpyParam("--fullscreen")] 45 | public bool Fullscreen { get; set; } 46 | [ScrcpyParam("--disable-screensaver")] 47 | public bool DisableScreensaver { get; set; } 48 | 49 | 50 | #endregion 51 | 52 | 53 | #region 54 | [ScrcpyParam("--keyboard=uhid")] 55 | public bool KeyboardUhid { get; set; } 56 | [ScrcpyParam("--stay-awake")] 57 | public bool StayAwake { get; set; } 58 | [ScrcpyParam("--turn-screen-off")] 59 | public bool TurnScreenOff { get; set; } 60 | #endregion 61 | 62 | public override string ToString() 63 | { 64 | StringBuilder sb = new StringBuilder(); 65 | var properties = typeof(ScrcpyParam).GetProperties(); 66 | 67 | foreach (var property in properties) 68 | { 69 | Type propertyType = property.PropertyType; 70 | 71 | ScrcpyParamAttribute attr = property.GetCustomAttribute(); 72 | 73 | switch (propertyType.Name) 74 | { 75 | case "Boolean": 76 | if (((bool)property.GetValue(this))) { 77 | sb.Append($" {attr.ParamName}"); 78 | } 79 | break; 80 | case "String": 81 | if (!string.IsNullOrEmpty(property.GetValue(this).ToString())) { 82 | sb.Append($" {attr.ParamName}{attr.ValueSymbol}{property.GetValue(this)}"); 83 | } 84 | break; 85 | default: 86 | 87 | break; 88 | } 89 | 90 | } 91 | 92 | return sb.ToString(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Base/ScrcpyParamAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MobileControlGuru.Base 8 | { 9 | public class ScrcpyParamAttribute : Attribute 10 | { 11 | public string ParamName { get; set; } 12 | public string ValueSymbol { get; set; } 13 | 14 | public ScrcpyParamAttribute(string paramName, string valueSymbol="=") 15 | { 16 | ParamName = paramName; 17 | ValueSymbol = valueSymbol; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BaseControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru 2 | { 3 | partial class BaseControl 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region 组件设计器生成的代码 24 | 25 | /// 26 | /// 设计器支持所需的方法 - 不要修改 27 | /// 使用代码编辑器修改此方法的内容。 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.SuspendLayout(); 32 | // 33 | // BaseControl 34 | // 35 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 37 | this.Name = "BaseControl"; 38 | this.Size = new System.Drawing.Size(305, 50); 39 | this.Load += new System.EventHandler(this.BaseControl_Load); 40 | this.ResumeLayout(false); 41 | 42 | } 43 | 44 | #endregion 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /BaseControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace MobileControlGuru 13 | { 14 | public partial class BaseControl : UserControl 15 | { 16 | 17 | public ComponentResourceManager resources; 18 | public BaseControl() 19 | { 20 | InitializeComponent(); 21 | } 22 | 23 | public void ChangeLang(string lang) 24 | { 25 | Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang); 26 | 27 | } 28 | public void ApplyResource() 29 | { 30 | foreach (Control ctl in this.Controls) 31 | { 32 | resources.ApplyResources(ctl, ctl.Name); 33 | } 34 | this.ResumeLayout(false); 35 | this.PerformLayout(); 36 | resources.ApplyResources(this, "$this"); 37 | } 38 | 39 | private void BaseControl_Load(object sender, EventArgs e) 40 | { 41 | var lang = Tools.ConfigHelp.GetConfig("Lang"); 42 | if (!string.IsNullOrEmpty(lang)) 43 | { 44 | ChangeLang(lang); 45 | ApplyResource(); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BaseControl.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /BaseForm.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Properties; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace MobileControlGuru 12 | { 13 | public class BaseForm : Form 14 | { 15 | public ComponentResourceManager resources; 16 | public BaseForm() 17 | { 18 | //resources = new ComponentResourceManager(); 19 | } 20 | 21 | public void ChangeLang(string lang) 22 | { 23 | Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang); 24 | 25 | } 26 | 27 | private void InitializeComponent() 28 | { 29 | this.SuspendLayout(); 30 | // 31 | // BaseForm 32 | // 33 | this.ClientSize = new System.Drawing.Size(284, 261); 34 | this.Name = "BaseForm"; 35 | this.Load += new System.EventHandler(this.BaseForm_Load); 36 | this.ResumeLayout(false); 37 | 38 | } 39 | 40 | private void BaseForm_Load(object sender, EventArgs e) 41 | { 42 | 43 | } 44 | 45 | public void ApplyResource() 46 | { 47 | //foreach (Control ctl in this.Controls) 48 | //{ 49 | // resources.ApplyResources(ctl, ctl.Name); 50 | 51 | // if (ctl.Controls.Count>0 ) 52 | // { 53 | 54 | 55 | // } 56 | //} 57 | Apply(this.Controls); 58 | this.ResumeLayout(false); 59 | this.PerformLayout(); 60 | resources.ApplyResources(this, "$this"); 61 | 62 | } 63 | 64 | 65 | public void Apply(Control.ControlCollection controls) 66 | { 67 | foreach (Control ctl in controls) 68 | { 69 | resources.ApplyResources(ctl, ctl.Name); 70 | if(ctl.Controls.Count>0) 71 | { 72 | Apply(ctl.Controls); 73 | } 74 | 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /BaseForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Docs/Task.md: -------------------------------------------------------------------------------- 1 | # 任务说明 2 | 通过corn表达式来进行任务调度 3 | 多任务尚未测试,如果bug及时反馈 4 | 5 | # 操作说明 6 | 1. 进入任务列表 7 | 2. 编辑任务信息 8 | 3. 开启任务(默认启动都要手动开启) 9 | 4. 任务保存在程序目录中的 task.json 10 | 11 | # 操作和参数 12 | 13 | | 操作 | 参数示例| 备注 | 14 | | --- | --- | --- | 15 | | 点击 | 500 500| 输入 x y 坐标,坐标可通过调试打开显示坐标显示,有些手机需要开启模拟点击才能运行 | 16 | | 滑动 | 540 1300 540 500 1000 | 5个数字,表示从哪里滑动到哪里, 最后一个是时间,时间越长滑动越慢 | 17 | | 按键 | KEYCODE_HOME(或3) |字符或者数字 可搜索安卓keyevent 找到对应的键码 | 18 | | 启动APP | com.alibaba.android.rimet/com.alibaba.android.rimet.biz.LaunchHomeActivity | 启动app需要获取到app的Activity 自行搜索 安卓启动xxx app | 19 | | 睡眠 | 500 1500(或 500) | 单位毫秒 填一个数字就是固定睡眠,两个数字,随机在两个值之间时长 | 20 | | 自定义命令 | 无 | 自定义cmd指令,可以运行控制台执行 | 21 | 22 | # corn表达式 23 | 自行搜索corn表达式 24 | 25 | # 任务示例 26 | ```json 27 | [ 28 | { 29 | "Name": "每5秒下滑", 30 | "TaskItems": [ 31 | { 32 | "Oprate": "shell input swipe", 33 | "Param": "540 1300 540 500 1000", 34 | "IsAdb": true 35 | } 36 | ], 37 | "id": 1, 38 | "RunTimes": 1, 39 | "DeviceName": "419218f7", 40 | "Corn": "0/5 * * * * ?", 41 | "IsRun": true 42 | } 43 | ] 44 | ``` 45 | 46 | ## 界面预览 47 | 48 | 任务列表 49 | 50 | ![image](/Preview/taskmain.png) 51 | 52 | 任务编辑 53 | 54 | ![image](/Preview/taskedit.png) -------------------------------------------------------------------------------- /Docs/Update.en.md: -------------------------------------------------------------------------------- 1 | # Update Log 2 | 3 | ## V 1.1 4 | 1. Add automated tasks 5 | 6 | 7 | ## V 1.0.8.1 8 | 1. Add window top and screen top 9 | 10 | ## V 1.0.8.0 11 | 1. Add the function of custom shortcut keys (the entry is set icon in the upper right corner) 12 | 2. Add wireless authorization (not via usb authorization) 13 | 3. Add wireless authorization API 14 | 15 | 16 | ## V 1.0.7 17 | 1. Add English support 18 | 19 | 20 | ## V 1.0.6 21 | 1. Add screen casting parameters to set wake up and screen after screen casting 22 | 23 | ## V 1.0.5 24 | 1. Add more on-screen buttons for easy multi-control 25 | 2. IP After the connection, save the last connection IP address 26 | 27 | 28 | ## V 1.0.4 29 | 1. Add screen click shortcut, Alt+C 30 | 2. Add home button shortcut, Alt+H 31 | 32 | 33 | ## V 1.0.1 34 | Add keyboard input options and select On in scrpy Settings -------------------------------------------------------------------------------- /Docs/Update.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | ## V 1.1 4 | 1. 增加自动化任务 5 | 6 | 7 | ## V 1.0.8.1 8 | 1. 增加窗口置顶和投屏置顶 9 | 10 | ## V 1.0.8 11 | 1. 增加自定义快捷键功能(入口为 右上角设置图标) 12 | 2. 增加无线授权方式(不用通过usb授权) 13 | 3. 增加无线授权的API 14 | 15 | ## V 1.0.7 16 | 1. 添加英语支持 17 | 18 | ## V 1.0.6 19 | 1. 增加投屏参数 可设置保持唤醒和投屏后息屏 20 | 21 | ## V 1.0.5 22 | 1. 增加更多的屏幕按钮,方便多控 23 | 2. IP连接后,保存上一次的连接IP 24 | 25 | 26 | ## V 1.0.4 27 | 1. 加入屏幕点击接口,快捷键Alt+C 28 | 2. 加入home键快捷键 Alt+H 29 | 30 | 31 | ## V 1.0.1 32 | 增加键盘输入选项,在scrpy设置里选择开启 -------------------------------------------------------------------------------- /Docs/autohotkey.ahk: -------------------------------------------------------------------------------- 1 | ;设备名称 如果是ip地址可以不用填写端口号 2 | device :="192.168.191.3" 3 | ;api地址 4 | api_url :="http://192.168.8.242:12345" 5 | 6 | ;锁屏 7 | !1:: 8 | { 9 | 10 | Run 'curl "' . api_url . '/api/Device/Lock?devicename=' . device . '"',,"hide" 11 | } 12 | ;音量- 13 | !Down:: 14 | { 15 | 16 | Run 'curl "' . api_url . '/api/Device/SendKey?devicename=' . device . '&Key=KEYCODE_VOLUME_DOWN"',,"hide" 17 | } 18 | ;音量+ 19 | !Up:: 20 | { 21 | 22 | Run 'curl "' . api_url . '/api/Device/SendKey?devicename=' . device . '&Key=KEYCODE_VOLUME_UP"',,"hide" 23 | } 24 | 25 | ;多媒体 下一个 26 | !Right:: 27 | { 28 | 29 | Run 'curl "' . api_url . '/api/Device/SendKey?devicename=' . device . '&Key=KEYCODE_MEDIA_NEXT"',,"hide" 30 | } 31 | 32 | ;多媒体 上一个 33 | !Left:: 34 | { 35 | 36 | Run 'curl "' . api_url . '/api/Device/SendKey?devicename=' . device . '&KEYCODE_MEDIA_PLAY_PAUSE"',,"hide" 37 | } 38 | ;多媒体 暂停播放 39 | !Numpad5:: 40 | { 41 | 42 | Run 'curl "' . api_url . '/api/Device/SendKey?devicename=' . device . '&Key=KEYCODE_MEDIA_PLAY"',,"hide" 43 | } 44 | ;上滑 45 | !q:: 46 | { 47 | 48 | Run 'curl "' . api_url . '/api/Device/SendSwipe?devicename=' . device . '&direct=up"',,"hide" 49 | } 50 | 51 | ;下滑 52 | !w:: 53 | { 54 | 55 | Run 'curl "' . api_url . '/api/Device/SendSwipe?devicename=' . device . '&direct=down"',,"hide" 56 | } 57 | 58 | ;桌面 59 | !2:: 60 | { 61 | 62 | Run 'curl "' . api_url . '/api/Device/SendKey?devicename=' . device . '&Key=3"',,"hide" 63 | } 64 | 65 | ;检查设备连接情况 66 | CheckConnection() { 67 | ; 使用Run而不是RunWait,这样我们可以看到curl的输出 68 | OutputVar :=ExecScript('curl --connect-timeout 5 "' . api_url . '/api/Device/List"') 69 | ;MsgBox "Result: " OutputVar 70 | 71 | If not InStr(OutputVar, device){ 72 | MsgBox "设备已经掉线" 73 | Pause 74 | } 75 | 76 | } 77 | 78 | 79 | ;测试 80 | !t:: 81 | { 82 | CheckConnection() 83 | 84 | } 85 | 86 | ExecScript(Script, Wait:=true) 87 | { 88 | shell := ComObject("WScript.Shell") 89 | exec := shell.Exec(Script) 90 | ; exec.StdIn.Write(Script) 91 | exec.StdIn.Close() 92 | if Wait 93 | return exec.StdOut.ReadAll() 94 | } 95 | ;隐藏控制台 96 | DllCall("AllocConsole") 97 | WinHide "ahk_id " DllCall("GetConsoleWindow", "ptr") 98 | ; 设置定时器,每5分钟调用一次RunBatchFile函数 99 | ; 注意:时间是以毫秒为单位的,所以5分钟是5 * 60 * 1000 = 300000毫秒 100 | SetTimer CheckConnection, 5000 ; 5分钟定时器 -------------------------------------------------------------------------------- /Docs/task.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "每5秒下滑", 4 | "TaskItems": [ 5 | { 6 | "Oprate": "shell input swipe", 7 | "Param": "540 1300 540 500 1000", 8 | "IsAdb": true 9 | } 10 | ], 11 | "id": 1, 12 | "RunTimes": 1, 13 | "DeviceName": "419218f7", 14 | "Corn": "0/5 * * * * ?", 15 | "IsRun": true 16 | }, 17 | { 18 | "Name": "打开钉钉进入工作台", 19 | "TaskItems": [ 20 | { 21 | "Oprate": "shell am start -n", 22 | "Param": "com.alibaba.android.rimet/com.alibaba.android.rimet.biz.LaunchHomeActivity", 23 | "IsAdb": true 24 | }, 25 | { 26 | "Oprate": "sleep", 27 | "Param": "5000", 28 | "IsAdb": false 29 | }, 30 | { 31 | "Oprate": "shell input tap", 32 | "Param": "523 2326", 33 | "IsAdb": true 34 | } 35 | ], 36 | "id": 2, 37 | "RunTimes": 1, 38 | "DeviceName": "192.168.191.2:5555", 39 | "Corn": "", 40 | "IsRun": false 41 | } 42 | ] -------------------------------------------------------------------------------- /HotKeyControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru 2 | { 3 | partial class HotKeyControl 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region 组件设计器生成的代码 24 | 25 | /// 26 | /// 设计器支持所需的方法 - 不要修改 27 | /// 使用代码编辑器修改此方法的内容。 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.EventName = new AntdUI.Label(); 32 | this.Key = new AntdUI.Button(); 33 | this.ModifyKey = new AntdUI.Dropdown(); 34 | this.SuspendLayout(); 35 | // 36 | // EventName 37 | // 38 | this.EventName.Location = new System.Drawing.Point(0, 9); 39 | this.EventName.Name = "EventName"; 40 | this.EventName.Size = new System.Drawing.Size(102, 23); 41 | this.EventName.TabIndex = 0; 42 | this.EventName.Text = "label1"; 43 | // 44 | // Key 45 | // 46 | this.Key.Location = new System.Drawing.Point(203, 4); 47 | this.Key.Name = "Key"; 48 | this.Key.Size = new System.Drawing.Size(79, 35); 49 | this.Key.TabIndex = 2; 50 | this.Key.Text = "button2"; 51 | this.Key.Click += new System.EventHandler(this.button2_Click); 52 | this.Key.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Key_KeyDown); 53 | // 54 | // ModifyKey 55 | // 56 | this.ModifyKey.Items.AddRange(new object[] { 57 | "Alt", 58 | "Ctrl", 59 | "Ctrl+Alt"}); 60 | this.ModifyKey.Location = new System.Drawing.Point(108, 3); 61 | this.ModifyKey.Name = "ModifyKey"; 62 | this.ModifyKey.Size = new System.Drawing.Size(89, 36); 63 | this.ModifyKey.TabIndex = 3; 64 | this.ModifyKey.Text = "dropdown1"; 65 | this.ModifyKey.SelectedValueChanged += new AntdUI.ObjectNEventHandler(this.ModifyKey_SelectedValueChanged); 66 | // 67 | // HotKeyControl 68 | // 69 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 70 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 71 | this.Controls.Add(this.ModifyKey); 72 | this.Controls.Add(this.Key); 73 | this.Controls.Add(this.EventName); 74 | this.Name = "HotKeyControl"; 75 | this.Size = new System.Drawing.Size(297, 44); 76 | this.Load += new System.EventHandler(this.HotKeyControl_Load); 77 | this.ResumeLayout(false); 78 | 79 | } 80 | 81 | #endregion 82 | 83 | private AntdUI.Label EventName; 84 | private AntdUI.Button Key; 85 | private AntdUI.Dropdown ModifyKey; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /HotKeyControl.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Model; 2 | using MobileControlGuru.Tools; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | 13 | namespace MobileControlGuru 14 | { 15 | public partial class HotKeyControl : UserControl 16 | { 17 | //public string EventName { get; set; } 18 | //public string ModifyKey { get; set; } 19 | //public string Key { get; set; } 20 | 21 | public KeyItem KeyItem { get; set; } 22 | public HotKeyControl(KeyItem keyItem) 23 | { 24 | InitializeComponent(); 25 | this.KeyItem = keyItem; 26 | } 27 | 28 | 29 | private void HotKeyControl_Load(object sender, EventArgs e) 30 | { 31 | //this.Tag = KeyItem; 32 | this.EventName.Text= KeyItem.EventName; 33 | if (ConfigHelp.GetConfig("lang") == "en") 34 | { 35 | this.EventName.Text = KeyItem.EventEngName; 36 | } 37 | this.ModifyKey.Text= KeyItem.ModifyKey; 38 | this.Key.Text= KeyItem.Key; 39 | 40 | 41 | } 42 | 43 | private void button2_Click(object sender, EventArgs e) 44 | { 45 | 46 | } 47 | 48 | private void ModifyKey_SelectedValueChanged(object sender, object value) 49 | { 50 | AntdUI.Dropdown s = (AntdUI.Dropdown)sender; 51 | s.Text = value.ToString(); 52 | } 53 | 54 | private void Key_KeyDown(object sender, KeyEventArgs e) 55 | { 56 | var input = (AntdUI.Button)sender; 57 | //KeysConverter keysConverter = new KeysConverter(); 58 | //input.Text = keysConverter.ConvertToString(e.KeyCode); 59 | string keyCode = e.KeyCode.ToString(); 60 | if(keyCode== "ProcessKey") 61 | { 62 | return; 63 | } 64 | input.Text = e.KeyCode.ToString(); 65 | } 66 | 67 | public KeyItem GetData() 68 | { 69 | this.KeyItem.ModifyKey = this.ModifyKey.Text; 70 | this.KeyItem.Key = this.Key.Text; 71 | return this.KeyItem; 72 | } 73 | 74 | 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /HotKeyControl.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /HotKeySetting.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru 2 | { 3 | partial class HotKeySetting 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(HotKeySetting)); 32 | this.button1 = new AntdUI.Button(); 33 | this.flowLayoutPanel1 = new AntdUI.In.FlowLayoutPanel(); 34 | this.label1 = new AntdUI.Label(); 35 | this.SuspendLayout(); 36 | // 37 | // button1 38 | // 39 | resources.ApplyResources(this.button1, "button1"); 40 | this.button1.AutoEllipsis = true; 41 | this.button1.Name = "button1"; 42 | this.button1.Click += new System.EventHandler(this.button1_Click); 43 | // 44 | // flowLayoutPanel1 45 | // 46 | resources.ApplyResources(this.flowLayoutPanel1, "flowLayoutPanel1"); 47 | this.flowLayoutPanel1.Name = "flowLayoutPanel1"; 48 | // 49 | // label1 50 | // 51 | resources.ApplyResources(this.label1, "label1"); 52 | this.label1.Name = "label1"; 53 | // 54 | // HotKeySetting 55 | // 56 | resources.ApplyResources(this, "$this"); 57 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 58 | this.Controls.Add(this.label1); 59 | this.Controls.Add(this.flowLayoutPanel1); 60 | this.Controls.Add(this.button1); 61 | this.Name = "HotKeySetting"; 62 | this.Load += new System.EventHandler(this.HotKeySetting_Load); 63 | this.ResumeLayout(false); 64 | 65 | } 66 | 67 | #endregion 68 | private AntdUI.Button button1; 69 | private AntdUI.In.FlowLayoutPanel flowLayoutPanel1; 70 | private AntdUI.Label label1; 71 | } 72 | } -------------------------------------------------------------------------------- /HotKeySetting.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using MobileControlGuru.Model; 3 | using MobileControlGuru.Src; 4 | using MobileControlGuru.Tools; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Data; 9 | using System.Drawing; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Windows.Forms; 14 | 15 | namespace MobileControlGuru 16 | { 17 | public partial class HotKeySetting : BaseForm 18 | { 19 | public Src.MyHotKey myHotKey { set; get; } 20 | public HotKeySetting(Src.MyHotKey myHotKey) 21 | { 22 | InitializeComponent(); 23 | resources = new ComponentResourceManager(typeof(HotKeySetting)); 24 | this.myHotKey = myHotKey; 25 | } 26 | 27 | private void HotKeySetting_Load(object sender, EventArgs e) 28 | { 29 | foreach (var item in MyHotKey.GetKeyItems()) 30 | { 31 | 32 | HotKeyControl htc = new HotKeyControl(item); 33 | this.flowLayoutPanel1.Controls.Add(htc); 34 | } 35 | 36 | } 37 | /// 38 | /// 初始化快捷键 39 | /// 40 | /// 41 | 42 | private void button1_Click(object sender, EventArgs e) 43 | { 44 | 45 | 46 | try 47 | { 48 | ConfigHelp.SetSetting("hotkeys", GetSetting()); 49 | myHotKey.UnRegister(); 50 | myHotKey.Register(); 51 | MessageBox.Show(resources.GetString("SaveInfo")); 52 | } 53 | catch(Exception ex) 54 | { 55 | MessageBox.Show(ex.Message); 56 | } 57 | 58 | } 59 | 60 | private string GetSetting() 61 | { 62 | var keyssetting= new List(); 63 | foreach(HotKeyControl control in this.flowLayoutPanel1.Controls) 64 | { 65 | keyssetting.Add(control.GetData()); 66 | } 67 | 68 | return Newtonsoft.Json.JsonConvert.SerializeObject(keyssetting); 69 | } 70 | 71 | private void button2_Click(object sender, EventArgs e) 72 | { 73 | 74 | } 75 | 76 | 77 | private void button2_KeyDown(object sender, KeyEventArgs e) 78 | { 79 | var input = (AntdUI.Button)sender; 80 | //KeysConverter keysConverter = new KeysConverter(); 81 | //input.Text = keysConverter.ConvertToString(e.KeyCode); 82 | input.Text = e.KeyCode.ToString(); 83 | } 84 | 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /IPConnect.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru 2 | { 3 | partial class IPConnect 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(IPConnect)); 32 | this.ip_input = new AntdUI.Input(); 33 | this.label1 = new AntdUI.Label(); 34 | this.panel1 = new AntdUI.Panel(); 35 | this.button1 = new AntdUI.Button(); 36 | this.connect_btn = new AntdUI.Button(); 37 | this.port_input = new AntdUI.InputNumber(); 38 | this.label2 = new AntdUI.Label(); 39 | this.panel1.SuspendLayout(); 40 | this.SuspendLayout(); 41 | // 42 | // ip_input 43 | // 44 | resources.ApplyResources(this.ip_input, "ip_input"); 45 | this.ip_input.Name = "ip_input"; 46 | // 47 | // label1 48 | // 49 | resources.ApplyResources(this.label1, "label1"); 50 | this.label1.Name = "label1"; 51 | // 52 | // panel1 53 | // 54 | resources.ApplyResources(this.panel1, "panel1"); 55 | this.panel1.Controls.Add(this.button1); 56 | this.panel1.Controls.Add(this.connect_btn); 57 | this.panel1.Controls.Add(this.port_input); 58 | this.panel1.Controls.Add(this.label2); 59 | this.panel1.Controls.Add(this.label1); 60 | this.panel1.Controls.Add(this.ip_input); 61 | this.panel1.Name = "panel1"; 62 | this.panel1.Click += new System.EventHandler(this.panel1_Click); 63 | // 64 | // button1 65 | // 66 | resources.ApplyResources(this.button1, "button1"); 67 | this.button1.Name = "button1"; 68 | this.button1.Type = AntdUI.TTypeMini.Success; 69 | this.button1.Click += new System.EventHandler(this.button1_Click); 70 | // 71 | // connect_btn 72 | // 73 | resources.ApplyResources(this.connect_btn, "connect_btn"); 74 | this.connect_btn.Name = "connect_btn"; 75 | this.connect_btn.Type = AntdUI.TTypeMini.Primary; 76 | this.connect_btn.Click += new System.EventHandler(this.connect_btn_Click); 77 | // 78 | // port_input 79 | // 80 | resources.ApplyResources(this.port_input, "port_input"); 81 | this.port_input.Maximum = new decimal(new int[] { 82 | 999999, 83 | 0, 84 | 0, 85 | 0}); 86 | this.port_input.Name = "port_input"; 87 | // 88 | // label2 89 | // 90 | resources.ApplyResources(this.label2, "label2"); 91 | this.label2.Name = "label2"; 92 | // 93 | // IPConnect 94 | // 95 | resources.ApplyResources(this, "$this"); 96 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 97 | this.Controls.Add(this.panel1); 98 | this.Name = "IPConnect"; 99 | this.Load += new System.EventHandler(this.IPConnect_Load); 100 | this.panel1.ResumeLayout(false); 101 | this.ResumeLayout(false); 102 | 103 | } 104 | 105 | #endregion 106 | 107 | private AntdUI.Input ip_input; 108 | private AntdUI.Label label1; 109 | private AntdUI.Panel panel1; 110 | private AntdUI.Label label2; 111 | private AntdUI.Button connect_btn; 112 | private AntdUI.InputNumber port_input; 113 | private AntdUI.Button button1; 114 | } 115 | } -------------------------------------------------------------------------------- /IPConnect.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using MobileControlGuru.Base; 3 | using MobileControlGuru.Src; 4 | using MobileControlGuru.Tools; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Data; 9 | using System.Drawing; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using System.Windows.Forms; 15 | 16 | namespace MobileControlGuru 17 | { 18 | public partial class IPConnect : BaseForm 19 | { 20 | 21 | public MobileControlGuru.MainForm main { set; get; } 22 | public IPConnect(MobileControlGuru.MainForm _main) 23 | { 24 | this.main = _main; 25 | InitializeComponent(); 26 | resources = new ComponentResourceManager(typeof(IPConnect)); 27 | } 28 | 29 | 30 | 31 | private void connect_btn_Click(object sender, EventArgs e) 32 | { 33 | string device_ip = this.ip_input.Text; 34 | string port = this.port_input.Text; 35 | var adbinfo = ADB.Connect($"{device_ip}:{port}"); 36 | DeviceManager.Instance.UpdateDevices(); 37 | Properties.Settings.Default.LastDeviceIP = device_ip; 38 | Properties.Settings.Default.Save(); 39 | this.Close(); 40 | } 41 | 42 | 43 | 44 | private void IPConnect_Load(object sender, EventArgs e) 45 | { 46 | this.ip_input.Text = Properties.Settings.Default.LastDeviceIP; 47 | 48 | 49 | } 50 | private void button1_Click(object sender, EventArgs e) 51 | { 52 | string device_ip = this.ip_input.Text; 53 | string port = this.port_input.Text; 54 | 55 | var p = Scrcpy.IPConnet(device_ip, port); 56 | if (p == null) 57 | { 58 | MessageBox.Show(resources.GetString("connetError")); 59 | return; 60 | } 61 | DeviceManager.Instance.BindDevicesProcees(device_ip + ":" + port, p); 62 | Properties.Settings.Default.LastDeviceIP = device_ip; 63 | Properties.Settings.Default.Save(); 64 | 65 | this.Close(); 66 | 67 | } 68 | 69 | private void panel1_Click(object sender, EventArgs e) 70 | { 71 | var lang = Tools.ConfigHelp.GetConfig("Lang"); 72 | if (!string.IsNullOrEmpty(lang)) 73 | { 74 | ChangeLang(lang); 75 | ApplyResource(); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /IPPair.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru 2 | { 3 | partial class IPPair 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(IPPair)); 32 | this.button1 = new AntdUI.Button(); 33 | this.port_input = new AntdUI.InputNumber(); 34 | this.label2 = new AntdUI.Label(); 35 | this.label1 = new AntdUI.Label(); 36 | this.ip_input = new AntdUI.Input(); 37 | this.code_input = new AntdUI.InputNumber(); 38 | this.label3 = new AntdUI.Label(); 39 | this.SuspendLayout(); 40 | // 41 | // button1 42 | // 43 | resources.ApplyResources(this.button1, "button1"); 44 | this.button1.Name = "button1"; 45 | this.button1.Type = AntdUI.TTypeMini.Success; 46 | this.button1.Click += new System.EventHandler(this.connect_btn_Click); 47 | // 48 | // port_input 49 | // 50 | resources.ApplyResources(this.port_input, "port_input"); 51 | this.port_input.Name = "port_input"; 52 | // 53 | // label2 54 | // 55 | resources.ApplyResources(this.label2, "label2"); 56 | this.label2.Name = "label2"; 57 | // 58 | // label1 59 | // 60 | resources.ApplyResources(this.label1, "label1"); 61 | this.label1.Name = "label1"; 62 | // 63 | // ip_input 64 | // 65 | resources.ApplyResources(this.ip_input, "ip_input"); 66 | this.ip_input.Name = "ip_input"; 67 | // 68 | // code_input 69 | // 70 | resources.ApplyResources(this.code_input, "code_input"); 71 | this.code_input.Name = "code_input"; 72 | // 73 | // label3 74 | // 75 | resources.ApplyResources(this.label3, "label3"); 76 | this.label3.Name = "label3"; 77 | // 78 | // IPPair 79 | // 80 | resources.ApplyResources(this, "$this"); 81 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 82 | this.Controls.Add(this.code_input); 83 | this.Controls.Add(this.label3); 84 | this.Controls.Add(this.button1); 85 | this.Controls.Add(this.port_input); 86 | this.Controls.Add(this.label2); 87 | this.Controls.Add(this.label1); 88 | this.Controls.Add(this.ip_input); 89 | this.Name = "IPPair"; 90 | this.ResumeLayout(false); 91 | 92 | } 93 | 94 | #endregion 95 | 96 | private AntdUI.Button button1; 97 | private AntdUI.InputNumber port_input; 98 | private AntdUI.Label label2; 99 | private AntdUI.Label label1; 100 | private AntdUI.Input ip_input; 101 | private AntdUI.InputNumber code_input; 102 | private AntdUI.Label label3; 103 | } 104 | } -------------------------------------------------------------------------------- /IPPair.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Base; 2 | using MobileControlGuru.Properties; 3 | using MobileControlGuru.Src; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace MobileControlGuru 15 | { 16 | public partial class IPPair : BaseForm 17 | { 18 | public MobileControlGuru.MainForm main { set; get; } 19 | public IPPair(MobileControlGuru.MainForm _main) 20 | { 21 | this.main = _main; 22 | InitializeComponent(); 23 | resources = new ComponentResourceManager(typeof(IPConnect)); 24 | } 25 | 26 | 27 | 28 | private void connect_btn_Click(object sender, EventArgs e) 29 | { 30 | string device_ip = this.ip_input.Text; 31 | int port = Convert.ToInt32(this.port_input.Text); 32 | int code =Convert.ToInt32( this.code_input.Text); 33 | var adbinfo = ADB.Pair(device_ip,port, code); 34 | DeviceManager.Instance.UpdateDevices(); 35 | Properties.Settings.Default.LastDeviceIP = device_ip; 36 | Properties.Settings.Default.Save(); 37 | this.Close(); 38 | } 39 | 40 | 41 | 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /MobileControlGuru.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34622.214 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MobileControlGuru", "MobileControlGuru.csproj", "{1608A03F-C735-44A7-9BD8-A4C654C03B70}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {1608A03F-C735-44A7-9BD8-A4C654C03B70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {1608A03F-C735-44A7-9BD8-A4C654C03B70}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {1608A03F-C735-44A7-9BD8-A4C654C03B70}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {1608A03F-C735-44A7-9BD8-A4C654C03B70}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {14A00F26-7AC4-4F6B-B4D0-41546EBE0CA0} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /MoblieControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MobileControlGuru 2 | { 3 | partial class MoblieControl 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region 组件设计器生成的代码 24 | 25 | /// 26 | /// 设计器支持所需的方法 - 不要修改 27 | /// 使用代码编辑器修改此方法的内容。 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MoblieControl)); 32 | this.input1 = new AntdUI.Input(); 33 | this.put = new AntdUI.Button(); 34 | this.wireless_connect = new AntdUI.Button(); 35 | this.flowLayoutPanel1 = new AntdUI.In.FlowLayoutPanel(); 36 | this.checkbox1 = new AntdUI.Checkbox(); 37 | this.disput = new AntdUI.Button(); 38 | this.disconnect = new AntdUI.Button(); 39 | this.show = new AntdUI.Button(); 40 | this.ontop = new AntdUI.Button(); 41 | this.flowLayoutPanel1.SuspendLayout(); 42 | this.SuspendLayout(); 43 | // 44 | // input1 45 | // 46 | resources.ApplyResources(this.input1, "input1"); 47 | this.input1.Name = "input1"; 48 | this.input1.ReadOnly = true; 49 | // 50 | // put 51 | // 52 | resources.ApplyResources(this.put, "put"); 53 | this.put.Name = "put"; 54 | this.put.Type = AntdUI.TTypeMini.Success; 55 | this.put.Click += new System.EventHandler(this.put_Click); 56 | // 57 | // wireless_connect 58 | // 59 | resources.ApplyResources(this.wireless_connect, "wireless_connect"); 60 | this.wireless_connect.Name = "wireless_connect"; 61 | this.wireless_connect.Click += new System.EventHandler(this.wireless_connect_Click); 62 | // 63 | // flowLayoutPanel1 64 | // 65 | this.flowLayoutPanel1.Controls.Add(this.checkbox1); 66 | this.flowLayoutPanel1.Controls.Add(this.input1); 67 | this.flowLayoutPanel1.Controls.Add(this.put); 68 | this.flowLayoutPanel1.Controls.Add(this.disput); 69 | this.flowLayoutPanel1.Controls.Add(this.wireless_connect); 70 | this.flowLayoutPanel1.Controls.Add(this.disconnect); 71 | this.flowLayoutPanel1.Controls.Add(this.show); 72 | this.flowLayoutPanel1.Controls.Add(this.ontop); 73 | resources.ApplyResources(this.flowLayoutPanel1, "flowLayoutPanel1"); 74 | this.flowLayoutPanel1.Name = "flowLayoutPanel1"; 75 | // 76 | // checkbox1 77 | // 78 | resources.ApplyResources(this.checkbox1, "checkbox1"); 79 | this.checkbox1.Name = "checkbox1"; 80 | this.checkbox1.CheckedChanged += new AntdUI.BoolEventHandler(this.checkbox1_CheckedChanged); 81 | // 82 | // disput 83 | // 84 | resources.ApplyResources(this.disput, "disput"); 85 | this.disput.Name = "disput"; 86 | this.disput.Type = AntdUI.TTypeMini.Warn; 87 | this.disput.Click += new System.EventHandler(this.disput_Click); 88 | // 89 | // disconnect 90 | // 91 | resources.ApplyResources(this.disconnect, "disconnect"); 92 | this.disconnect.Name = "disconnect"; 93 | this.disconnect.Type = AntdUI.TTypeMini.Error; 94 | this.disconnect.Click += new System.EventHandler(this.disconnect_Click); 95 | // 96 | // show 97 | // 98 | this.show.BackgroundImage = global::MobileControlGuru.Properties.Resources.see; 99 | this.show.Ghost = true; 100 | this.show.ImageSvg = ""; 101 | resources.ApplyResources(this.show, "show"); 102 | this.show.Name = "show"; 103 | this.show.Click += new System.EventHandler(this.show_Click); 104 | // 105 | // ontop 106 | // 107 | this.ontop.BackgroundImage = global::MobileControlGuru.Properties.Resources.to_top; 108 | this.ontop.Ghost = true; 109 | this.ontop.ImageSvg = ""; 110 | resources.ApplyResources(this.ontop, "ontop"); 111 | this.ontop.Name = "ontop"; 112 | this.ontop.Click += new System.EventHandler(this.ontop_Click); 113 | // 114 | // MoblieControl 115 | // 116 | resources.ApplyResources(this, "$this"); 117 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 118 | this.Controls.Add(this.flowLayoutPanel1); 119 | this.Name = "MoblieControl"; 120 | this.Load += new System.EventHandler(this.MoblieControl_Load); 121 | this.flowLayoutPanel1.ResumeLayout(false); 122 | this.ResumeLayout(false); 123 | 124 | } 125 | 126 | #endregion 127 | 128 | private AntdUI.Input input1; 129 | private AntdUI.Button put; 130 | private AntdUI.Button wireless_connect; 131 | private AntdUI.In.FlowLayoutPanel flowLayoutPanel1; 132 | private AntdUI.Button disconnect; 133 | private AntdUI.Button disput; 134 | private AntdUI.Checkbox checkbox1; 135 | private AntdUI.Button ontop; 136 | private AntdUI.Button show; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /MoblieControl.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using Microsoft.VisualBasic; 3 | using MobileControlGuru.Base; 4 | using MobileControlGuru.Model; 5 | using MobileControlGuru.Properties; 6 | using MobileControlGuru.Src; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel; 10 | using System.Data; 11 | using System.Diagnostics; 12 | using System.Drawing; 13 | using System.Linq; 14 | using System.Text; 15 | using System.Text.RegularExpressions; 16 | using System.Threading; 17 | using System.Threading.Tasks; 18 | using System.Windows.Forms; 19 | using static Microsoft.WindowsAPICodePack.Shell.PropertySystem.SystemProperties.System; 20 | 21 | namespace MobileControlGuru 22 | { 23 | public partial class MoblieControl : BaseControl 24 | { 25 | 26 | public Model.Device device { set; get; } 27 | public MoblieControl() 28 | { 29 | InitializeComponent(); 30 | } 31 | public MoblieControl(Model.Device device) 32 | { 33 | resources = new ComponentResourceManager(typeof(MoblieControl)); 34 | InitializeComponent(); 35 | 36 | this.device = device; 37 | 38 | } 39 | private void MoblieControl_Load(object sender, EventArgs e) 40 | { 41 | this.input1.Text = device.Name; 42 | 43 | this.checkbox1.Checked=device.Selected; 44 | this.wireless_connect.Visible = !this.device.IsTcpIP; 45 | this.disconnect.Visible = this.device.IsTcpIP; 46 | this.ontop.BackgroundImage = this.device.IsTop ? Resources.ontop : Resources.ontop_blur; 47 | this.disput.Visible= this.device.ScrcpyProcess!=null; 48 | this.put.Visible = this.device.ScrcpyProcess == null; 49 | 50 | if (device.form != null) 51 | { 52 | this.show.Visible = true; 53 | this.show.BackgroundImage = device.form.Visible? Resources.see: Resources.nosee; 54 | } 55 | else 56 | { 57 | this.show.Visible = false; 58 | } 59 | 60 | } 61 | 62 | private void put_Click(object sender, EventArgs e) 63 | { 64 | Process process = Scrcpy.Put(this.device.Name); 65 | //while (process.MainWindowHandle == IntPtr.Zero) 66 | //{ 67 | // Thread.Sleep(100); 68 | //} 69 | //device.form=new ScrcpyForm(device.Name,process); 70 | //device.ScrcpyProcess= process; 71 | //device.form.Show(); 72 | //device.form.FormClosed += ScrcpyClosed; 73 | DeviceManager.Instance.BindDevicesProcees(this.device.Name, process); 74 | DeviceManager.Instance.UpdateDevices(); 75 | } 76 | 77 | private void ScrcpyClosed(object sender, FormClosedEventArgs e) 78 | { 79 | //device.ScrcpyProcess.Kill(); 80 | device.ScrcpyProcess = null; 81 | DeviceManager.Instance.UpdateDevices(); 82 | } 83 | 84 | private void checkbox1_CheckedChanged(object sender, bool value) 85 | { 86 | device.Selected = value; 87 | 88 | } 89 | 90 | private void wireless_connect_Click(object sender, EventArgs e) 91 | { 92 | 93 | string str = Interaction.InputBox("提示信息", "请输入手机IP", "", -1, -1); 94 | if (string.IsNullOrEmpty(str)) 95 | { 96 | return; 97 | } 98 | ADB.Exec($"-s {this.device.Name} tcpip 5555"); 99 | Thread.Sleep(1000); 100 | ADB.Connect(str); 101 | } 102 | 103 | private void disconnect_Click(object sender, EventArgs e) 104 | { 105 | DialogResult AF = MessageBox.Show("确定断开连接吗?", "确认框", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); 106 | if (AF == DialogResult.OK) 107 | { 108 | ADB.Exec("disconnect " + this.device.Name); 109 | DeviceManager.Instance.UpdateDevices(); 110 | } 111 | } 112 | 113 | private void disput_Click(object sender, EventArgs e) 114 | { 115 | 116 | DialogResult AF = MessageBox.Show("确定关闭投屏", "确认框", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); 117 | if (AF == DialogResult.OK) 118 | { 119 | 120 | device.ScrcpyProcess.Kill(); 121 | device.ScrcpyProcess = null; 122 | device.form?.Dispose(); 123 | device.form = null; 124 | DeviceManager.Instance.UpdateDevices(); 125 | } 126 | } 127 | 128 | private void ontop_Click(object sender, EventArgs e) 129 | { 130 | this.device.IsTop = !this.device.IsTop; 131 | ((AntdUI.Button)sender).BackgroundImage = this.device.IsTop ? Resources.to_top : Resources.to_top_blur; 132 | DeviceManager.Instance.UpdateDevices(); 133 | } 134 | 135 | private void show_Click(object sender, EventArgs e) 136 | { 137 | if(this.device.form==null) { 138 | return; 139 | } 140 | var btn = (AntdUI.Button)sender; 141 | if (this.device.form.Visible) 142 | { 143 | this.device.form.Hide(); 144 | } 145 | else 146 | { 147 | this.device.form.Show(); 148 | } 149 | btn.BackgroundImage = device.form.Visible ? Resources.see : Resources.nosee; 150 | 151 | } 152 | 153 | private void button2_Click(object sender, EventArgs e) 154 | { 155 | 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Model/Device.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using MobileControlGuru.AutoTask; 3 | using MobileControlGuru.Src; 4 | using MobileControlGuru.Tools; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | using static AntdUI.Modal; 14 | using static System.Windows.Forms.VisualStyles.VisualStyleElement; 15 | 16 | namespace MobileControlGuru.Model 17 | { 18 | /// 19 | /// 设备模型类 20 | /// 21 | public class Device 22 | { 23 | public string Name { get; set; } 24 | 25 | public string ScrcpyParams { get; set; } 26 | public bool Selected { get; set; } 27 | public string Status { set; get; } 28 | 29 | //public IntPtr hook { set; get; } 30 | public ScrcpyMonitor monitor { set; get; } 31 | public bool IsTcpIP { get; set; } 32 | public bool IsTop { get; set; } 33 | 34 | public ScrcpyForm form { set; get; } 35 | public Process ScrcpyProcess { get; set; } 36 | public TaskRunWindow RunWindow { get; set; } 37 | 38 | } 39 | 40 | 41 | 42 | public enum PutType 43 | { 44 | None = 0, 45 | Puted = 1, 46 | 47 | } 48 | /// 49 | /// table 的模型类 50 | /// 51 | public class DeviceItem : Device 52 | { 53 | public bool IsSelected { get; set; } 54 | 55 | public bool IsTcpIP { get; set; } 56 | public PutType PutType { get; set; } 57 | public AntdUI.ICell[] btns { get; set; } 58 | public AntdUI.ICell status { get; set; } 59 | 60 | 61 | public DeviceItem(Device device) 62 | { 63 | this.Name = device.Name; 64 | this.PutType = PutType.None; 65 | this.IsTcpIP = Tools.Common.IsValidIP(device.Name); 66 | var btns = new List(); 67 | if(device.Status== "device") 68 | { 69 | 70 | if (!IsTcpIP) 71 | { 72 | btns.Add(new AntdUI.CellButton("usb_put", "有线投屏", AntdUI.TTypeMini.Success)); 73 | 74 | } 75 | else 76 | { 77 | btns.Add(new AntdUI.CellButton("ip_put", "无线投屏", AntdUI.TTypeMini.Success)); 78 | 79 | } 80 | if (device.ScrcpyProcess != null) 81 | { 82 | this.PutType = PutType.Puted; 83 | btns.Add(new AntdUI.CellButton("close_put", "停止投屏", AntdUI.TTypeMini.Error)); 84 | } 85 | //btns.Add(new AntdUI.CellButton("put_params", "投屏参数", AntdUI.TTypeMini.Primary)); 86 | status = new AntdUI.CellText("在线", Color.Green); 87 | 88 | 89 | } 90 | else 91 | { 92 | status=new AntdUI.CellText("离线", Color.Gray); 93 | } 94 | 95 | btns.Add(new AntdUI.CellButton("disconnected", "断开连接", AntdUI.TTypeMini.Error)); 96 | 97 | this.btns = btns.ToArray(); 98 | } 99 | 100 | 101 | } 102 | 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /Model/KeyItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MobileControlGuru.Model 8 | { 9 | public class KeyItem 10 | { 11 | public string EventName { set; get; } 12 | public string EventEngName { set; get; } 13 | public int EventID { set; get; } 14 | public string ModifyKey { set; get; } 15 | public string Key { set; get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Preview/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/api.png -------------------------------------------------------------------------------- /Preview/hotkeysetting.en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/hotkeysetting.en.png -------------------------------------------------------------------------------- /Preview/hotkeysetting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/hotkeysetting.png -------------------------------------------------------------------------------- /Preview/ipconnect.en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/ipconnect.en.png -------------------------------------------------------------------------------- /Preview/ipconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/ipconnect.png -------------------------------------------------------------------------------- /Preview/main.en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/main.en.png -------------------------------------------------------------------------------- /Preview/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/main.png -------------------------------------------------------------------------------- /Preview/taskedit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/taskedit.png -------------------------------------------------------------------------------- /Preview/taskmain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Preview/taskmain.png -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Principal; 5 | using System.Threading.Tasks; 6 | using System.Windows.Forms; 7 | 8 | namespace MobileControlGuru 9 | { 10 | internal static class Program 11 | { 12 | /// 13 | /// 应用程序的主入口点。 14 | /// 15 | [STAThread] 16 | static void Main() 17 | { 18 | Application.EnableVisualStyles(); 19 | Application.SetCompatibleTextRenderingDefault(false); 20 | Application.Run(new MainForm()); 21 | //Application.Run(new AutoTask.TaskList()); 22 | } 23 | //private static bool IsRunningAsAdministrator() 24 | //{ 25 | // WindowsIdentity identity = WindowsIdentity.GetCurrent(); 26 | // WindowsPrincipal principal = new WindowsPrincipal(identity); 27 | // return principal.IsInRole(WindowsBuiltInRole.Administrator); 28 | //} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("MobileControlGuru")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MobileControlGuru")] 13 | [assembly: AssemblyCopyright("Copyright © 2024")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("1608a03f-c735-44a7-9bd8-a4c654c03b70")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.1.8.0")] 36 | [assembly: AssemblyFileVersion("1.1.8.0")] 37 | -------------------------------------------------------------------------------- /Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MobileControlGuru.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.9.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("0, 0")] 29 | public global::System.Drawing.Point ClickPoint { 30 | get { 31 | return ((global::System.Drawing.Point)(this["ClickPoint"])); 32 | } 33 | set { 34 | this["ClickPoint"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("")] 41 | public string LastDeviceIP { 42 | get { 43 | return ((string)(this["LastDeviceIP"])); 44 | } 45 | set { 46 | this["LastDeviceIP"] = value; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 0, 0 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # MobileControlGuru (Support HotKey, WebAPI Control,Multi-device) 2 | 3 |

简体中文 | English

4 | 5 | Update Log 6 | 7 | ## Feature 8 | 9 | 1. Multi-device control 10 | 2. Bulk operations 11 | 3. Support HotKey 12 | 4. API Control 13 | 14 | ## Instructions for use 15 | 16 | 1. run MobileControlGuru.exe 17 | 2. Enable the debugging mode of the phone and authorize the debugging 18 | 3. Click to get the list of devices 19 | 4. Click to get the device list, the device information appears in the left list, and you can start to control the phone (no screen projection required). 20 | 21 | 22 | ## Matters needing attention 23 | 24 | Close directly will not exit this program, please click the icon in the lower right corner to exit this program 25 | 26 | ### HotKey 27 | 28 | By default, only the first device is operated 29 | 30 | | HotKey | Action| 31 | | --- | --- | 32 | | Alt+D| Lock| 33 | | Alt+H| Home| 34 | | Alt+C| Tap screen| 35 | | Alt+↑| Volume plus | 36 | | Alt+↓| Volume down | 37 | | Alt+←| MediaPrevious| 38 | | Alt+→| MediaNext| 39 | | Alt+NumPad55| MediaPlayPause| 40 | | Alt+NumPad2| Swipe up(TikTok videos next)| 41 | | Alt+NumPad8| Swipe down(TikTok videos up)| 42 | 43 | ### API Description 44 | 45 | This feature is used to control non-native devices and should be enabled with caution. 46 | 47 | After enabling this feature, local network devices can invoke control commands for the local machine through API requests. 48 | 49 | This feature match[Curl](https://curl.se/download.html)and [AutoHotkey](https://[AutoHotkey](https://www.autohotkey.com/)), When multiple devices can let their respective computers operate their own phones 50 | Example AutoHotkey script Look at autohotkey.ahk in the docs directory 51 | 52 | #### API List 53 | 54 | By default, the WebPort is enabled on port 12345. You need to enter the config file to modify Webport properties 55 | 56 | |Url | Description| 57 | | --- | --- | 58 | | /api/Device/Connect| Connecting devices by IP (Step 1) | 59 | | /api/Device/List | List of current devices | 60 | | /api/Device/Lock |Send the lock screen command | 61 | | /api/Device/SendKey| Send keyCode value Please search for the key (adb keycode) | 62 | | /api/Device/SendSwipe | Send Swipe | 63 | 64 | ## Preview 65 | 66 | Main 67 | 68 | ![image](Preview/main.en.png) 69 | 70 | IP Connect 71 | 72 | ![image](Preview/ipconnect.en.png) 73 | 74 | 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 电脑多控手机(MobileControlGuru) 支持快捷键,WebAPI控制,自动化任务 2 | 3 | 4 |

简体中文 | English

5 | 6 | 7 | [更新日志](Docs/Update.md) 8 | 9 | ## 功能 10 | 11 | 1. 手机多控 12 | 2. 批量操作 13 | 3. 快捷键操作 14 | 4. API发布 15 | 5. 自动化任务 16 | 17 | ## 使用说明 18 | 19 | 1. 打开MobileControlGuru.exe 20 | 2. 手机开启调试模式并授权调试 21 | 3. 点击获取设备列表 22 | 4. 左边列表出现设备信息,即可开始控制手机(无需投屏) 23 | 24 | 25 | 26 | 27 | ### 快捷键说明 28 | 29 | 快捷键默认只操作第一个设备 30 | 31 | | 快捷键 | 操作| 32 | | --- | --- | 33 | | Alt+D| 锁屏| 34 | | Alt+H| Home键| 35 | | Alt+C| 点击屏幕(主界面按钮点击后生效)| 36 | | Alt+↑| 音量加 | 37 | | Alt+↓| 音量减 | 38 | | Alt+←| 媒体键 上一个| 39 | | Alt+→| 媒体键 下一个| 40 | | Alt+5(小键盘)| 媒体键 播放暂停| 41 | | Alt+2(小键盘)| 上滑(对应短视频下一个)| 42 | | Alt+8(小键盘)| 下滑(对应短视频上一个)| 43 | 44 | ## 自动化 45 | 详情见[自动化文档](Docs/Task.md) 46 | 47 | 48 | 49 | 50 | ### API说明 51 | 52 | 此功能用于非本机控制设备,需要谨慎开启。 53 | 54 | 开始后局域网设备可以通过API形式调用本机的控制指令 55 | 56 | 此功能搭配[Curl](https://curl.se/download.html)与[AutoHotkey](https://[AutoHotkey](https://www.autohotkey.com/))配合使用,当多设备时可以让各自的电脑操作自己的手机 57 | AutoHotkey脚本示例查看docs目录下的autohotkey.ahk 58 | 59 | #### API 列表 60 | 61 | 默认在12345端口打开,需要请自行进入config文件修改WebPort属性 62 | 63 | |地址 | 说明| 64 | | --- | --- | 65 | | /api/Device/Connect| 通过IP连接设备(第一步) | 66 | | /api/Device/List | 当前的设备列表 | 67 | | /api/Device/Lock | 发送锁屏指令 | 68 | | /api/Device/SendKey| 发送按键 keyCode数值请自行搜索关键(adb keycode) | 69 | | /api/Device/SendSwipe | 发送滑动指令 | 70 | 71 | ## 界面预览 72 | 73 | 主界面 74 | 75 | ![image](Preview/main.png) 76 | 77 | IP连接 78 | 79 | ![image](Preview/ipconnect.png) 80 | 81 | API 82 | 83 | ![image](Preview/api.png) 84 | 85 | 86 | 87 | ## 注意事项 88 | 89 | 直接关闭不会退出本程序,请右击右下角图标退出本程序 -------------------------------------------------------------------------------- /Resources/fanhui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/fanhui.png -------------------------------------------------------------------------------- /Resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/favicon.ico -------------------------------------------------------------------------------- /Resources/jietu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/jietu.png -------------------------------------------------------------------------------- /Resources/nosee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/nosee.png -------------------------------------------------------------------------------- /Resources/ontop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/ontop.png -------------------------------------------------------------------------------- /Resources/ontop_blur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/ontop_blur.png -------------------------------------------------------------------------------- /Resources/scrcpy-win64-v2.4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/scrcpy-win64-v2.4.zip -------------------------------------------------------------------------------- /Resources/scrcpy_params.json: -------------------------------------------------------------------------------- 1 | { 2 | "Params": [ 3 | { 4 | //中文名 5 | "Name": "无音频", 6 | //英文名 7 | "EngName": "no-audio", 8 | //数据类型 0表示无值 1表示带值 9 | "Type": 0, 10 | "Val": "", 11 | //带值的限定范围 12 | "AllowValues": [ 13 | ], 14 | //默认值 15 | "DefaultValues": "", 16 | //分组 同一分组的不能一起设置 17 | "Group": "", 18 | //scrcpy 参数 19 | "Param": "--no-audio", 20 | //scrcpy 短参数 21 | "ShortVersion": "", 22 | //是否允许可以设置 23 | "AllowSetting": true 24 | }, 25 | 26 | { 27 | 28 | "Name": "键盘支持", 29 | "EngName": "keyboard", 30 | "Type": 0, 31 | "Val": "", 32 | "AllowValues": [ 33 | { 34 | "Name": "sdk ", 35 | "EngName": "sdk", 36 | "Val": "sdk " 37 | }, 38 | { 39 | "Name": "uhid", 40 | "EngName": "uhid", 41 | "Val": "uhid " 42 | }, 43 | { 44 | "Name": "aoa", 45 | "EngName": "aoa", 46 | "Val": "aoa " 47 | }, 48 | { 49 | "Name": "禁用", 50 | "EngName": "disabled", 51 | "Val": "disabled " 52 | } 53 | ], 54 | "DefaultValues": "sdk", 55 | "Group": "", 56 | "Param": "--keyboard", 57 | "ShortVersion": "", 58 | "AllowSetting": true 59 | }, 60 | { 61 | "Name": "初始X轴", 62 | "EngName": "window-x", 63 | "Type": 1, 64 | "Param": "--window-x", 65 | "ShortVersion": "", 66 | "Group": "", 67 | "Val": "", 68 | 69 | "AllowSetting": true, 70 | "AllowValues": [ 71 | 72 | ], 73 | "DefaultValues": "" 74 | 75 | }, 76 | { 77 | "Name": "初始Y轴", 78 | "EngName": "window-y", 79 | "Type": 1, 80 | "Param": "--window-y", 81 | "ShortVersion": "", 82 | "DefaultValues": "", 83 | "Group": "", 84 | "AllowSetting": true, 85 | "Val": "", 86 | "AllowValues": [ 87 | 88 | ] 89 | 90 | }, 91 | { 92 | "Name": "默认宽度", 93 | "EngName": "window-width", 94 | "Type": 1, 95 | "Param": "--window-width", 96 | "Group": "", 97 | "DefaultValues": "", 98 | "AllowSetting": true, 99 | "ShortVersion": "", 100 | "AllowValues": [ 101 | 102 | ], 103 | "Val": "" 104 | }, 105 | { 106 | "Name": "默认高度", 107 | "EngName": "window-height", 108 | "Type": 1, 109 | "Param": "--window-height", 110 | "Group": "", 111 | "DefaultValues": "", 112 | "AllowSetting": true, 113 | "ShortVersion": "", 114 | "AllowValues": [ 115 | 116 | ], 117 | "Val": "" 118 | } 119 | ], 120 | //运行时命令 121 | "RunningParams": [ 122 | 123 | 124 | ] 125 | } 126 | -------------------------------------------------------------------------------- /Resources/see.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/see.bmp -------------------------------------------------------------------------------- /Resources/see.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/see.png -------------------------------------------------------------------------------- /Resources/shouye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/shouye.png -------------------------------------------------------------------------------- /Resources/to_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/to_top.png -------------------------------------------------------------------------------- /Resources/to_top_blur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yclown/MobileControlGuru/e6d3ad77a6ef7ced07351f5bc3821152390ffc19/Resources/to_top_blur.png -------------------------------------------------------------------------------- /ScrcpyConrtrolPanel.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using MobileControlGuru.Base; 3 | using MobileControlGuru.Model; 4 | using MobileControlGuru.Src; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Data; 9 | using System.Diagnostics; 10 | using System.Drawing; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | using System.Windows.Forms; 15 | 16 | namespace MobileControlGuru 17 | { 18 | public partial class ScrcpyConrtrolPanel : Window 19 | { 20 | 21 | public Device Device { get; set; } 22 | public ScrcpyConrtrolPanel() 23 | { 24 | InitializeComponent(); 25 | //this.BackColor = Color.Transparent; 26 | // 设置窗体的背景色为 LimeGreen,并将其设为透明色 27 | //this.BackColor = Color.LimeGreen; 28 | //this.TransparencyKey = Color.LimeGreen; 29 | //this.FormBorderStyle = FormBorderStyle.None; 30 | } 31 | 32 | 33 | private void screencap_btn_Click(object sender, EventArgs e) 34 | { 35 | 36 | new DeviceADB(Device.Name).ScreenCap(); 37 | } 38 | 39 | private void home_btn_Click(object sender, EventArgs e) 40 | { 41 | new DeviceADB(Device.Name).SendHome(); 42 | } 43 | 44 | private void back_btn_Click(object sender, EventArgs e) 45 | { 46 | ADB.SendKeyEvent(Device.Name, ADBKey.Key.KEYCODE_BACK); 47 | 48 | } 49 | 50 | private void exit_btn_Click(object sender, EventArgs e) 51 | { 52 | 53 | 54 | DialogResult AF = MessageBox.Show("确定关闭投屏吗?", "确认框", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); 55 | if (AF == DialogResult.OK) 56 | { 57 | DeviceManager.Instance.Close(Device); 58 | DeviceManager.Instance.UpdateDevices(); 59 | this.Hide(); 60 | } 61 | else 62 | { 63 | //用户点击取消或者关闭对话框后执行的代码 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ScrcpyForm.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using MobileControlGuru.Base; 3 | using MobileControlGuru.Src; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Diagnostics; 9 | using System.Drawing; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Windows.Forms; 14 | 15 | namespace MobileControlGuru 16 | { 17 | public partial class ScrcpyForm : BaseForm 18 | { 19 | public Process ScrcpyProcess { get; set; } 20 | IntPtr childHwnd ; 21 | public string DeviceName { set; get; } 22 | public ScrcpyForm(string DeviceName, Process ScrcpyProcess) 23 | { 24 | 25 | this.ScrcpyProcess = ScrcpyProcess; 26 | this.DeviceName= DeviceName; 27 | InitializeComponent(); 28 | } 29 | 30 | private void ScrcpyForm_Load(object sender, EventArgs e) 31 | { 32 | childHwnd = ScrcpyProcess.MainWindowHandle; 33 | ProcessWindowController.SetParent(childHwnd, this.panel1.Handle); 34 | 35 | ProcessWindowController.RECT fx = new ProcessWindowController.RECT(); 36 | 37 | ProcessWindowController.GetWindowRect(childHwnd, ref fx); 38 | int width = fx.Right - fx.Left; //窗口的宽度 39 | int height = fx.Bottom - fx.Top; //窗口的高度 40 | int x = fx.Left; 41 | int y = fx.Top; 42 | 43 | ProcessWindowController.MoveWindow(childHwnd, 44 | -10, -30, 45 | this.panel1.Width + 20, this.panel1.Height + 30, true); 46 | 47 | tooltipComponent1.SetTip(home_btn, "主页"); 48 | tooltipComponent1.SetTip(back_btn, "返回"); 49 | //tooltipComponent1.SetTip(home_btn, "主页"); 50 | //tooltipComponent1.SetTip(home_btn, "主页"); 51 | 52 | } 53 | 54 | 55 | 56 | private void ScrcpyForm_FormClosing(object sender, FormClosingEventArgs e) 57 | { 58 | //ScrcpyProcess.Kill(); 59 | if (e.CloseReason == CloseReason.UserClosing) 60 | { 61 | e.Cancel = true; 62 | 63 | this.Hide(); 64 | //notifyIcon1.Visible = true; 65 | } 66 | } 67 | 68 | 69 | 70 | private void ScrcpyForm_Resize(object sender, EventArgs e) 71 | { 72 | 73 | int width = this.panel1.Width; 74 | int height = this.panel1.Height; 75 | ProcessWindowController.MoveWindow(childHwnd, 76 | -10, -30, 77 | width+20, height+30, true); 78 | } 79 | 80 | private void inputNumber1_ValueChanged(object sender, decimal value) 81 | { 82 | 83 | ScrcpyForm_Resize(null, null); 84 | } 85 | 86 | private void home_btn_Click(object sender, EventArgs e) 87 | { 88 | new DeviceADB(DeviceName).SendHome(); 89 | } 90 | 91 | private void back_btn_Click(object sender, EventArgs e) 92 | { 93 | new DeviceADB(DeviceName).SendKeyEvent(ADBKey.Key.KEYCODE_BACK); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ScrcpySetting.cs: -------------------------------------------------------------------------------- 1 | using AntdUI; 2 | using MobileControlGuru.Base; 3 | using MobileControlGuru.Properties; 4 | using MobileControlGuru.Tools; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Data; 9 | using System.Drawing; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Windows.Forms; 14 | using static System.Windows.Forms.VisualStyles.VisualStyleElement; 15 | 16 | namespace MobileControlGuru 17 | { 18 | public partial class ScrcpySetting : BaseForm 19 | { 20 | public ScrcpySetting() 21 | { 22 | InitializeComponent(); 23 | resources = new ComponentResourceManager(typeof(ScrcpySetting)); 24 | } 25 | 26 | public void GetParams() 27 | { 28 | ScrcpyParam scrcpyParam = new ScrcpyParam() { 29 | 30 | NoAudio= NoAudioSwitch.Checked, 31 | WindowHeight=WindowHeightInput.Text, 32 | WindowWidth=WindowWidthInput.Text, 33 | WindowX=WindowXInput.Text, 34 | WindowY=WindowYInput.Text, 35 | KeyboardUhid= KeyboardUhidSwitch.Checked, 36 | TurnScreenOff=turn_screen_off.Checked, 37 | StayAwake=stay_awake.Checked, 38 | AlwaysOnTop= AlwaysOnTop.Checked 39 | }; 40 | 41 | ConfigHelp.SetSetting("ScrcpyParamObj", JsonHelp.Obj2Str(scrcpyParam)); 42 | this.Close(); 43 | //return ""; 44 | } 45 | 46 | private void button1_Click(object sender, EventArgs e) 47 | { 48 | GetParams(); 49 | } 50 | 51 | private void ScrcpySetting_Load(object sender, EventArgs e) 52 | { 53 | var scrcpyParam= JsonHelp.Str2Obj (ConfigHelp.GetConfig("ScrcpyParamObj")); 54 | 55 | if(scrcpyParam != null) 56 | { 57 | NoAudioSwitch.Checked = scrcpyParam.NoAudio; 58 | WindowHeightInput.Text = scrcpyParam.WindowHeight; 59 | WindowWidthInput.Text= scrcpyParam.WindowWidth; 60 | WindowXInput.Text= scrcpyParam.WindowX; 61 | WindowYInput.Text= scrcpyParam.WindowY; 62 | KeyboardUhidSwitch.Checked = scrcpyParam.KeyboardUhid; 63 | turn_screen_off.Checked= scrcpyParam.TurnScreenOff; 64 | stay_awake.Checked= scrcpyParam.StayAwake ; 65 | AlwaysOnTop.Checked= scrcpyParam.AlwaysOnTop; 66 | } 67 | var lang = Tools.ConfigHelp.GetConfig("Lang"); 68 | if (!string.IsNullOrEmpty(lang)) 69 | { 70 | ChangeLang(lang); 71 | ApplyResource(); 72 | } 73 | } 74 | 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Src/DeviceADB.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Base; 2 | using MobileControlGuru.Tools; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace MobileControlGuru.Src 13 | { 14 | /// 15 | /// 设备ADB操作类 16 | /// 17 | public class DeviceADB 18 | { 19 | public string DeviceName { set; get; } 20 | 21 | public DeviceADB(string deviceName) 22 | { 23 | if (Common.IsValidIP(deviceName)) { 24 | DeviceName = GetDevice(deviceName); 25 | } 26 | else 27 | { 28 | DeviceName = deviceName; 29 | } 30 | 31 | } 32 | /// 33 | /// 检查设备在线状态 34 | /// 35 | /// 36 | public bool CheckOnline() 37 | { 38 | Model.Device d = null; 39 | if (Common.IsValidIP(DeviceName)) 40 | { 41 | DeviceName = DeviceName.Split(':')[0]+":"; 42 | } 43 | d = DeviceManager.Instance.devices.Where(n =>n.Status=="device"&& n.Name.StartsWith(DeviceName)).FirstOrDefault(); 44 | 45 | return d!=null; 46 | } 47 | /// 48 | /// 获取设备,如果是ip设备责只关心ip不关心端口号 49 | /// 50 | /// 51 | /// 52 | public string GetDevice(string deviceIP) 53 | { 54 | deviceIP= deviceIP.Split(':')[0]; 55 | var d= DeviceManager.Instance.devices.Where(n=>n.Name.StartsWith(deviceIP+":")).FirstOrDefault(); 56 | return d.Name; 57 | } 58 | /// 59 | /// 发送按键 60 | /// 61 | /// 62 | /// 63 | public AdbParse SendKeyEvent( ADBKey.Key key) 64 | { 65 | 66 | var res = ADB.Exec($"-s {DeviceName} shell input keyevent {key}"); 67 | 68 | return res; 69 | 70 | } 71 | /// 72 | /// 发送按键 73 | /// 74 | /// 75 | /// 76 | public AdbParse SendKeyEvent( string key) 77 | { 78 | 79 | var res = ADB.Exec($"-s {DeviceName} shell input keyevent {key}"); 80 | return res; 81 | } 82 | /// 83 | /// 发送滑动命令 84 | /// 85 | /// 86 | /// 87 | /// 88 | /// 89 | public AdbParse SendSwipe(Point form, Point to, int delta = 100) 90 | { 91 | var res = ADB.Exec($"-s {DeviceName} shell input swipe {form.X} {form.Y} {to.X} {to.Y} {delta}"); 92 | return res; 93 | } 94 | 95 | /// 96 | /// 下滑 97 | /// 98 | /// 99 | public AdbParse SendSwipeDown() 100 | { 101 | var res = SendSwipe( new Point(540, 500), new Point(540, 1300)); 102 | return res; 103 | } 104 | /// 105 | /// 上滑 106 | /// 107 | /// 108 | public AdbParse SendSwipeUp() 109 | { 110 | var res = SendSwipe( new Point(540, 1300), new Point(540, 500)); 111 | return res; 112 | } 113 | /// 114 | /// 发送锁屏 115 | /// 116 | /// 117 | public AdbParse SendLock() 118 | { 119 | var res =SendKeyEvent(ADBKey.Key.KEYCODE_POWER); 120 | return res; 121 | } 122 | public AdbParse SendHome() 123 | { 124 | var res = SendKeyEvent(ADBKey.Key.KEYCODE_HOME); 125 | return res; 126 | } 127 | public AdbParse TapScreen(Point point) 128 | { 129 | var res = ADB.Exec($"-s {DeviceName} shell input tap {point.X} {point.Y} "); 130 | return res; 131 | } 132 | 133 | 134 | public AdbParse ScreenCap() 135 | { 136 | var basepath = ConfigHelp.GetConfig("DeivesDir"); 137 | if(basepath == null) 138 | { 139 | basepath = Directory.GetCurrentDirectory(); 140 | ConfigHelp.SetSetting("DeivesDir", basepath); 141 | } 142 | 143 | 144 | var pathDir = Path.Combine(basepath, GetOkFileName(DeviceName), "ScreenCap"); 145 | if (!Directory.Exists(pathDir)) { 146 | Directory.CreateDirectory(pathDir); 147 | } 148 | 149 | var savePath = Path.Combine(pathDir, DateTime.Now.ToString("yyyyMMdd_HHmmssfff") + ".png"); 150 | var res= ADB.Screencap(DeviceName, savePath); 151 | return res; 152 | } 153 | 154 | private string GetOkFileName(string strFile) 155 | { 156 | //string strFileName = "文件名称"; 157 | StringBuilder rBuilder = new StringBuilder(strFile); 158 | foreach (char rInvalidChar in Path.GetInvalidFileNameChars()) 159 | rBuilder.Replace(rInvalidChar.ToString(), string.Empty); 160 | 161 | strFile = rBuilder.ToString().Trim(); 162 | strFile = strFile.Replace("+", ""); 163 | return strFile; 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Src/DeviceManager.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Base; 2 | using MobileControlGuru.Model; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace MobileControlGuru.Src 12 | { 13 | /// 14 | /// 全局设备管理类 15 | /// 16 | public class DeviceManager 17 | { 18 | public delegate void UpdateDelegate(); 19 | 20 | /// 21 | /// 更行设备信息委托 22 | /// 23 | public UpdateDelegate updateDelegates; 24 | public List devices { set; get; } 25 | 26 | private static DeviceManager instance = null; 27 | private static readonly object lockObj = new object(); 28 | 29 | private DeviceManager() { } 30 | /// 31 | /// 单例模式 32 | /// 33 | public static DeviceManager Instance 34 | { 35 | get 36 | { 37 | if (instance == null) 38 | { 39 | lock (lockObj) 40 | { 41 | if (instance == null) 42 | { 43 | instance = new DeviceManager(); 44 | instance.devices = new List(); 45 | } 46 | } 47 | } 48 | return instance; 49 | } 50 | } 51 | 52 | 53 | /// 54 | /// 更新设备列表 55 | /// 56 | public void UpdateDevices() 57 | { 58 | var data = ADB.GetDeiveces(); 59 | 60 | List newDevices = data.Select(n => n.Name).ToList(); 61 | List oldDevices = DeviceManager.Instance.devices.Select(n => n.Name).ToList(); 62 | 63 | //删除不在的 64 | var needRemove = DeviceManager.Instance.devices.Where(n => !newDevices.Contains(n.Name)).ToList(); 65 | foreach (var r in needRemove) 66 | { 67 | if (r.ScrcpyProcess != null) 68 | { 69 | r.ScrcpyProcess.Dispose(); 70 | } 71 | DeviceManager.Instance.devices.Remove(r); 72 | } 73 | //添加新的 74 | var addItems = data.Where(n => !oldDevices.Contains(n.Name)).ToList(); 75 | DeviceManager.Instance.devices = DeviceManager.Instance.devices.Concat(addItems).OrderByDescending(n=>n.IsTop).ToList(); 76 | 77 | //状态更新 78 | foreach(var r in DeviceManager.Instance.devices) 79 | { 80 | var d=data.Where(n=>n.Name == r.Name).FirstOrDefault(); 81 | if(d != null) 82 | { 83 | r.Status=d.Status; 84 | } 85 | } 86 | 87 | 88 | updateDelegates(); 89 | } 90 | /// 91 | /// 绑定scrcpy 到设备 92 | /// 93 | /// 94 | /// 95 | public void BindDevicesProcees(string devicename, Process process) 96 | { 97 | 98 | var d = GetDevice(devicename); 99 | d.ScrcpyProcess = process; 100 | d.ScrcpyProcess.Exited += new EventHandler(exitHandle); 101 | updateDelegates(); 102 | } 103 | /// 104 | /// scrcpy 进程退出事件 105 | /// 106 | /// 107 | /// 108 | private void exitHandle(object sender, EventArgs e) 109 | { 110 | var p = (Process)sender; 111 | var d = DeviceManager.Instance.devices. 112 | Where(n => n.ScrcpyProcess != null && n.ScrcpyProcess.Id == p.Id).FirstOrDefault(); 113 | if (d != null) 114 | { 115 | d.ScrcpyProcess = null; 116 | if (d.monitor != null) 117 | { 118 | d.monitor.Stop(); 119 | } 120 | 121 | } 122 | DeviceManager.Instance.UpdateDevices(); 123 | } 124 | 125 | public Device GetDevice(string name) 126 | { 127 | var d = DeviceManager.Instance.devices.Where(n => n.Name == name).FirstOrDefault(); 128 | return d; 129 | 130 | } 131 | 132 | 133 | public void CloseAll() 134 | { 135 | foreach(var d in devices) 136 | { 137 | Close(d); 138 | 139 | } 140 | } 141 | public void Close(Device d) 142 | { 143 | 144 | if (d.monitor != null) 145 | { 146 | d.monitor.Stop(); 147 | } 148 | if (d.ScrcpyProcess != null) 149 | { 150 | if (!d.ScrcpyProcess.HasExited) 151 | { 152 | d.ScrcpyProcess.Kill(); // 强制结束进程 153 | } 154 | 155 | } 156 | 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Src/ScrcpyMonitor.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Model; 2 | using MobileControlGuru.Tools; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using static MobileControlGuru.Base.ProcessWindowController; 10 | namespace MobileControlGuru.Src 11 | { 12 | public class ScrcpyMonitor 13 | { 14 | public Device Device { get { 15 | return DeviceManager.Instance.GetDevice(DeviceName); 16 | } 17 | } 18 | public string DeviceName { get; set; } 19 | public MainForm main { set; get; } 20 | IntPtr hook { get; set; } 21 | GCHandle handle { get; set; } 22 | public ScrcpyMonitor(string DeviceName) 23 | { 24 | this.DeviceName = DeviceName; 25 | } 26 | 27 | public void Start() 28 | { 29 | if (Device.ScrcpyProcess == null) 30 | { 31 | return; 32 | } 33 | WinEventDelegate myDelegate = WinEventProc; 34 | handle = GCHandle.Alloc(myDelegate); 35 | hook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_OBJECT_LOCATIONCHANGE, 36 | IntPtr.Zero,(WinEventDelegate)handle.Target, (uint)Device.ScrcpyProcess.Id, 0, WINEVENT_OUTOFCONTEXT); 37 | 38 | if (hook == IntPtr.Zero) 39 | { 40 | LogHelper.Error($"Failed to set WinEventHook. Error: {Marshal.GetLastWin32Error()}"); 41 | return; 42 | } 43 | } 44 | 45 | 46 | public void Stop() 47 | { 48 | if (hook != IntPtr.Zero) 49 | { 50 | UnhookWinEvent(hook); 51 | 52 | } 53 | if (handle.IsAllocated) 54 | handle.Free(); 55 | 56 | } 57 | 58 | public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) 59 | { 60 | uint processId; 61 | var a= Device.Name; 62 | GetWindowThreadProcessId(hwnd, out processId); 63 | if (processId == 0) 64 | return; // Not the target process's window 65 | RECT fx = new RECT(); 66 | switch (eventType) 67 | { 68 | case EVENT_SYSTEM_FOREGROUND: 69 | fx = new RECT(); 70 | GetWindowRect(hwnd, ref fx);//h为窗口句柄 71 | main.SetControl(fx); 72 | main.ShowControl(Device); 73 | Console.WriteLine(a + $"Target window was activated.type: {eventType} time:" + DateTime.Now); 74 | break; 75 | case EVENT_OBJECT_LOCATIONCHANGE: 76 | fx = new RECT(); 77 | GetWindowRect(hwnd, ref fx);//h为窗口句柄 78 | main.SetControl(fx); 79 | Console.WriteLine(a + $"Target window was moved.type: {eventType} time:" + DateTime.Now); 80 | break; 81 | default: 82 | Console.WriteLine($"{a} Unknown event type: {eventType} time:" + DateTime.Now); 83 | break; 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Src/TaskManager.cs: -------------------------------------------------------------------------------- 1 | using Quartz; 2 | using Quartz.Impl; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace MobileControlGuru.Src 10 | { 11 | /// 12 | /// 任务管理 13 | /// 14 | public class TaskManager 15 | { 16 | private static TaskManager instance = null; 17 | private static readonly object lockObj = new object(); 18 | private TaskManager() { 19 | 20 | 21 | factory = new StdSchedulerFactory(); 22 | 23 | } 24 | /// 25 | /// 单例模式 26 | /// 27 | public static TaskManager Instance 28 | { 29 | get 30 | { 31 | if (instance == null) 32 | { 33 | lock (lockObj) 34 | { 35 | if (instance == null) 36 | { 37 | instance = new TaskManager(); 38 | } 39 | } 40 | } 41 | return instance; 42 | } 43 | } 44 | 45 | StdSchedulerFactory factory ; 46 | IScheduler scheduler; 47 | 48 | 49 | public async void Start() 50 | { 51 | 52 | scheduler= await factory.GetScheduler(); 53 | await scheduler.Start(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tools/Common.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace MobileControlGuru.Tools 9 | { 10 | public class Common 11 | { 12 | public static bool IsValidIP(string input) 13 | { 14 | // 匹配IP地址,后面可选地跟一个冒号和1-65535之间的端口号 15 | string pattern = @"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:[1-9][0-9]{1,4})?$"; 16 | 17 | return Regex.IsMatch(input, pattern); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tools/ConfigHelp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MobileControlGuru.Tools 9 | { 10 | public class ConfigHelp 11 | { 12 | public static string GetConfig(string key) 13 | { 14 | Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); 15 | var c = config.AppSettings.Settings[key]; 16 | if (c == null) 17 | { 18 | return ""; 19 | } 20 | else 21 | { 22 | return c.Value; 23 | } 24 | 25 | 26 | } 27 | public static void SetSetting(string key, string value) 28 | { 29 | if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) 30 | { 31 | Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); 32 | 33 | // 如果键已经存在,则更新值 34 | if (config.AppSettings.Settings[key] != null) 35 | { 36 | config.AppSettings.Settings[key].Value = value; 37 | } 38 | // 否则,添加新的键值对 39 | else 40 | { 41 | config.AppSettings.Settings.Add(key, value); 42 | } 43 | 44 | config.Save(ConfigurationSaveMode.Modified); // 保存更改 45 | ConfigurationManager.RefreshSection("appSettings"); // 刷新配置,使得更改立即生效 46 | } 47 | else 48 | { 49 | throw new ArgumentException("Both Key and Value must have a non-null, non-empty value."); 50 | } 51 | } 52 | 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Tools/JsonHelp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web.Script.Serialization; 7 | namespace MobileControlGuru.Tools 8 | { 9 | public class JsonHelp 10 | { 11 | 12 | public static T Str2Obj(string input) 13 | { 14 | return new JavaScriptSerializer().Deserialize(input); 15 | } 16 | 17 | public static string Obj2Str(object obj) 18 | { 19 | return new JavaScriptSerializer().Serialize(obj); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tools/LanguageHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Forms; 8 | 9 | namespace MobileControlGuru.Tools 10 | { 11 | public class LanguageHelper 12 | { 13 | 14 | #region SetAllLang 15 | /// 16 | /// Set language 17 | /// 18 | /// language:zh-CN, en-US 19 | private static void SetAllLang(string lang) 20 | { 21 | System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang); 22 | Form frm = null; 23 | 24 | string name = "MainForm"; 25 | 26 | frm = (Form)Assembly.Load("CameraTest").CreateInstance(name); 27 | 28 | if (frm != null) 29 | { 30 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(); 31 | resources.ApplyResources(frm, "$this"); 32 | AppLang(frm, resources); 33 | } 34 | } 35 | #endregion 36 | 37 | #region SetLang 38 | /// 39 | /// 40 | /// 41 | /// language:zh-CN, en-US 42 | /// the form you need to set 43 | /// the type of the form 44 | public static void SetLang(string lang, Form form, Type formType) 45 | { 46 | System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang); 47 | if (form != null) 48 | { 49 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(formType); 50 | resources.ApplyResources(form, "$this"); 51 | AppLang(form, resources); 52 | } 53 | } 54 | #endregion 55 | 56 | #region AppLang for control 57 | /// 58 | /// loop set the propery of the control 59 | /// 60 | /// 61 | /// 62 | private static void AppLang(Control control, System.ComponentModel.ComponentResourceManager resources) 63 | { 64 | if (control is MenuStrip) 65 | { 66 | resources.ApplyResources(control, control.Name); 67 | MenuStrip ms = (MenuStrip)control; 68 | if (ms.Items.Count > 0) 69 | { 70 | foreach (ToolStripMenuItem c in ms.Items) 71 | { 72 | AppLang(c, resources); 73 | } 74 | } 75 | } 76 | 77 | foreach (Control c in control.Controls) 78 | { 79 | resources.ApplyResources(c, c.Name); 80 | AppLang(c, resources); 81 | } 82 | } 83 | #endregion 84 | 85 | #region AppLang for menuitem 86 | /// 87 | /// set the toolscript 88 | /// 89 | /// 90 | /// 91 | private static void AppLang(ToolStripMenuItem item, System.ComponentModel.ComponentResourceManager resources) 92 | { 93 | if (item is ToolStripMenuItem) 94 | { 95 | resources.ApplyResources(item, item.Name); 96 | ToolStripMenuItem tsmi = (ToolStripMenuItem)item; 97 | if (tsmi.DropDownItems.Count > 0) 98 | { 99 | foreach (ToolStripMenuItem c in tsmi.DropDownItems) 100 | { 101 | AppLang(c, resources); 102 | } 103 | } 104 | } 105 | } 106 | #endregion 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Tools/LogHelper.cs: -------------------------------------------------------------------------------- 1 | using log4net; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | [assembly: log4net.Config.XmlConfigurator()] 9 | namespace MobileControlGuru.Tools 10 | { 11 | 12 | public class LogHelper 13 | { 14 | 15 | static readonly ILog _logger = LogManager.GetLogger("LogTrace"); 16 | public static void Info(string message) 17 | { 18 | _logger.Info(message); //打印事件 19 | } 20 | 21 | public static void Debug(string message) 22 | { 23 | _logger.Debug(message); //调试 24 | } 25 | 26 | public static void Warn(string message) 27 | { 28 | _logger.Warn(message); //警告 29 | } 30 | 31 | public static void Error(string message) 32 | { 33 | _logger.Error(message); //错误 34 | } 35 | public static void Error(Exception ex, string memo = "") 36 | { 37 | _logger.Error(FormatExceptionDetails(ex, memo)); 38 | } 39 | /// 40 | /// 格式化异常信息 41 | /// 42 | /// 43 | /// 44 | /// 45 | public static string FormatExceptionDetails(Exception ex, string memo = "") 46 | { 47 | StringBuilder sb = new StringBuilder(); 48 | 49 | // 异常基本信息 50 | sb.AppendLine(memo + "Exception Details:"); 51 | sb.AppendLine($"Exception Type: {ex.GetType().FullName}"); 52 | sb.AppendLine($"Message: {ex.Message}"); 53 | if (!string.IsNullOrEmpty(ex.Source)) 54 | sb.AppendLine($"Source: {ex.Source}"); 55 | 56 | // 堆栈跟踪 57 | sb.AppendLine("Stack Trace:"); 58 | sb.AppendLine(ex.StackTrace); 59 | 60 | // 内部异常 61 | Exception innerEx = ex.InnerException; 62 | while (innerEx != null) 63 | { 64 | sb.AppendLine(); 65 | sb.AppendLine("Inner Exception:"); 66 | sb.AppendLine($"Exception Type: {innerEx.GetType().FullName}"); 67 | sb.AppendLine($"Message: {innerEx.Message}"); 68 | if (!string.IsNullOrEmpty(innerEx.Source)) 69 | sb.AppendLine($"Source: {innerEx.Source}"); 70 | sb.AppendLine(innerEx.StackTrace); 71 | innerEx = innerEx.InnerException; 72 | } 73 | 74 | // (可选)添加自定义上下文信息,如当前时间、线程ID等 75 | sb.AppendLine(); 76 | sb.AppendLine($"Time: {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss UTC")}"); 77 | sb.AppendLine($"Thread ID: {Thread.CurrentThread.ManagedThreadId}"); 78 | 79 | return sb.ToString(); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Tools/ZipHelper.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Properties; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO.Compression; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MobileControlGuru.Tools 11 | { 12 | public class ZipHelper 13 | { 14 | public static void ExtractZipToDirectory(string outputDirectory) 15 | { 16 | 17 | byte[] zipBytes = Resources.scrcpy; // 获取 ZIP 文件的字节数组 18 | 19 | // 确保输出目录存在 20 | Directory.CreateDirectory(outputDirectory); 21 | 22 | // 创建一个临时 ZIP 文件 23 | string tempZipPath = Path.GetTempFileName(); 24 | try 25 | { 26 | // 将字节数组写入临时 ZIP 文件 27 | File.WriteAllBytes(tempZipPath, zipBytes); 28 | 29 | // 使用 ZipFile 解压临时 ZIP 文件到指定的输出目录 30 | ZipFile.ExtractToDirectory(tempZipPath, outputDirectory); 31 | } 32 | catch(Exception e) 33 | { 34 | 35 | } 36 | finally 37 | { 38 | // 删除临时 ZIP 文件 39 | File.Delete(tempZipPath); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /WebAPI/Controllers/DeviceController.cs: -------------------------------------------------------------------------------- 1 | using MobileControlGuru.Base; 2 | using MobileControlGuru.Src; 3 | using MobileControlGuru.Tools; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Web.Http; 11 | 12 | namespace MobileControlGuru.WebAPI.Controllers 13 | { 14 | public class DeviceController : ApiController 15 | { 16 | /// 17 | /// 设备列表 18 | /// 19 | /// 20 | [HttpGet] 21 | public string List() 22 | { 23 | return string.Join(",", DeviceManager.Instance.devices.Select(n => n.Name)); 24 | 25 | } 26 | 27 | /// 28 | /// 检查设备在线情况 29 | /// 30 | /// 31 | /// 32 | [HttpGet] 33 | public string CheckOnline(string devicename) 34 | { 35 | if(new DeviceADB(devicename).CheckOnline()) 36 | { 37 | return "online"; 38 | } 39 | else 40 | { 41 | return "offline"; 42 | } 43 | } 44 | /// 45 | /// 连接设备 46 | /// 47 | /// 设备名/IP 48 | /// 49 | [HttpGet] 50 | public string Connect(string devicename) 51 | { 52 | 53 | var res= ADB.Connect(devicename); 54 | 55 | return res.Message; 56 | } 57 | 58 | /// 59 | /// 无线授权调试 60 | /// 61 | /// 设备IP 62 | /// 端口号 63 | /// 6位配对码 64 | /// 65 | [HttpGet] 66 | public string Pair(string devicename,int port,int code) 67 | { 68 | 69 | var res = ADB.Pair(devicename,port,code); 70 | 71 | return res.Message; 72 | } 73 | 74 | /// 75 | /// 发送锁屏键 76 | /// 77 | /// 设备名/IP 78 | /// 79 | [HttpGet] 80 | public string Lock(string devicename) 81 | { 82 | var res = new DeviceADB(devicename).SendLock(); 83 | return res.Message; 84 | } 85 | 86 | 87 | /// 88 | /// 发送自定义按键 89 | /// 90 | /// 设备名/IP 91 | /// 按键范围应当在正常的范围内 92 | /// 93 | [HttpGet] 94 | public string SendKey(string devicename,string Key) 95 | { 96 | 97 | var res = new DeviceADB(devicename).SendKeyEvent( Key); 98 | return res.Message; 99 | } 100 | /// 101 | /// 屏幕滑动 102 | /// 103 | /// 设备名/IP 104 | /// up表示上滑,在短视频里就表示下一个 105 | /// 106 | [HttpGet] 107 | public string SendSwipe(string devicename, string direct) 108 | { 109 | var res = new AdbParse("",""); 110 | if (direct == "up") 111 | { 112 | res= new DeviceADB(devicename).SendSwipeUp(); 113 | } 114 | else 115 | { 116 | res= new DeviceADB(devicename).SendSwipeDown(); 117 | } 118 | 119 | return res.Message; 120 | } 121 | 122 | /// 123 | /// 屏幕点击 124 | /// 125 | /// 126 | /// 127 | /// 128 | /// 129 | [HttpGet] 130 | public string SendTap(string devicename, int x,int y) 131 | { 132 | var res = new DeviceADB(devicename).TapScreen(new Point(x, y)); 133 | 134 | 135 | return res.Message; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /WebAPI/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web.Http; 7 | 8 | namespace MobileControlGuru.WebAPI.Controllers 9 | { 10 | 11 | 12 | public class HomeController : ApiController 13 | { 14 | 15 | 16 | public string Index() { 17 | 18 | return "opened"; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WebAPI/Startup.cs: -------------------------------------------------------------------------------- 1 | using Owin; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Http.Formatting; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Web.Http; 9 | using Swashbuckle.Application; 10 | using static System.Windows.Forms.Design.AxImporter; 11 | using System.IO; 12 | using System.Reflection; 13 | 14 | namespace MobileControlGuru.WebAPI 15 | { 16 | public class Startup 17 | { 18 | public void Configuration(IAppBuilder appBuilder) 19 | { 20 | HttpConfiguration config = new HttpConfiguration(); 21 | config.MapHttpAttributeRoutes(); 22 | config.Routes.MapHttpRoute(name: "DefaultApi", 23 | routeTemplate: "api/{controller}/{action}", 24 | defaults: new { 25 | controller = "Home", 26 | action = "Index", 27 | id = RouteParameter.Optional 28 | } 29 | ); 30 | config 31 | .EnableSwagger(c => { 32 | c.SingleApiVersion("v1", "mobile control guru api"); 33 | c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); 34 | var baseDirectory = AppDomain.CurrentDomain.BaseDirectory; 35 | 36 | var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".XML"; 37 | var commentsFile = Path.Combine(baseDirectory, commentsFileName); 38 | c.IncludeXmlComments(commentsFile); 39 | }) 40 | .EnableSwaggerUi(); 41 | 42 | //清除xml格式,使用json格式 43 | config.Formatters.XmlFormatter.SupportedMediaTypes.Clear(); 44 | config.Formatters.Add(new JsonMediaTypeFormatter()); 45 | 46 | 47 | 48 | appBuilder.UseWebApi(config); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /WebAPI/WebHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin.Hosting; 2 | using MobileControlGuru.Tools; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Sockets; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Web; 11 | 12 | namespace MobileControlGuru.WebAPI 13 | { 14 | public class WebHelper 15 | { 16 | public string Port { set; get; } 17 | public bool Runnig { set; get; } 18 | IDisposable myOwinServer; 19 | 20 | public string Url { set; get; } 21 | 22 | public WebHelper(string port) 23 | { 24 | if (string.IsNullOrEmpty(port)) 25 | { 26 | Port = port; 27 | }else 28 | if (string.IsNullOrEmpty(ConfigHelp.GetConfig("WebPort"))) 29 | { 30 | Port = ConfigHelp.GetConfig("WebPort"); 31 | } 32 | else 33 | { 34 | Port = "12345"; 35 | } 36 | } 37 | 38 | public WebHelper() 39 | { 40 | if (string.IsNullOrEmpty(ConfigHelp.GetConfig("WebPort"))) 41 | { 42 | Port = ConfigHelp.GetConfig("WebPort"); 43 | } 44 | else 45 | { 46 | Port = "12345"; 47 | } 48 | } 49 | 50 | public void Run() 51 | { 52 | int port = Convert.ToInt32(Port); 53 | StartOptions options = new StartOptions(); 54 | Url = "http://localhost:" + port; 55 | 56 | // options.Urls.Add(Url); 57 | options.Urls.Add("http://+:" + port); 58 | 59 | myOwinServer = WebApp.Start(options); 60 | 61 | 62 | } 63 | public void Dispose() 64 | { 65 | 66 | myOwinServer?.Dispose(); 67 | Runnig = false; 68 | } 69 | 70 | 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /adb_cmds.txt: -------------------------------------------------------------------------------- 1 |  #获取设备 ip 2 | #192.168.191.0/24 dev wlan0 proto kernel scope link src 192.168.191.3 3 | shell ip route 4 | 5 | # -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | --------------------------------------------------------------------------------