如何在C#中获取ADB.exe push命令的进程输出百分比?
问题描述
我正在开发一款向Android设备复制文件的应用。通过CMD或PowerShell执行adb push命令时,会显示文件复制的进度百分比,但使用以下C#代码时,无法实时重定向输出,仅在进程结束后才显示内容。我尝试创建CMD进程、ADB进程、PowerShell进程,均无效果。由于adb push耗时较长时用户会误以为程序卡顿,因此我希望实现实时显示进度(如进度条)的功能,以下是我使用的代码:
folderToLoad = txtOBBFolder.Text; process = new Process(); process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; process.StartInfo.FileName = AppDomain.CurrentDomain.BaseDirectory + @"\adb.exe"; process.StartInfo.Arguments = @"push " + folderToLoad + " /sdcard/Android/obb/" + Path.GetFileName(folderToLoad); process.StartInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardInput = false; process.Start(); q = ""; while (!process.HasExited) { q += process.StandardOutput.ReadToEnd(); q += process.StandardError.ReadToEnd(); txtOutput.AppendText(q); }
解决方案
当前代码的核心问题是ReadToEnd()方法会阻塞线程,必须等到进程输出流完全结束才返回内容,因此无法实时获取进度。正确的实现方式是利用进程的异步事件监听输出,每次有新内容输出时立即触发回调,同时处理跨线程更新UI的问题。
以下是修改后的完整代码示例:
private void StartAdbPush() { string folderToLoad = txtOBBFolder.Text; Process process = new Process(); process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; process.StartInfo.FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "adb.exe"); // 用字符串插值和引号包裹路径,避免含空格的路径引发错误 process.StartInfo.Arguments = $"push \"{folderToLoad}\" /sdcard/Android/obb/{Path.GetFileName(folderToLoad)}"; process.StartInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardInput = false; // 绑定标准输出监听事件 process.OutputDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { // 切换到UI线程更新控件,避免跨线程异常 txtOutput.Invoke(new Action(() => { txtOutput.AppendText(e.Data + Environment.NewLine); // 解析输出中的进度百分比,更新进度条 if (e.Data.Contains("%")) { // 提取进度数字(适配adb push的输出格式,比如"10% 1234KB/s") string[] parts = e.Data.Split(new[] {'%', ' '}, StringSplitOptions.RemoveEmptyEntries); if (int.TryParse(parts[0], out int progress)) { progressBar.Value = progress; } } })); } }; // 绑定错误输出监听事件 process.ErrorDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { txtOutput.Invoke(new Action(() => { txtOutput.AppendText($"[错误] {e.Data}" + Environment.NewLine); })); } }; process.Start(); // 启动异步读取输出流 process.BeginOutputReadLine(); process.BeginErrorReadLine(); // 可选:等待进程结束后释放资源,也可在外部通过Exited事件处理 process.WaitForExit(); process.Dispose(); }
关键优化点
- 替换阻塞式的
ReadToEnd()为BeginOutputReadLine()/BeginErrorReadLine(),实现异步实时读取 - 通过
OutputDataReceived/ErrorDataReceived事件捕获每一行输出,即时更新UI - 使用
Control.Invoke()切换到UI线程操作控件,解决跨线程访问异常 - 优化路径拼接和参数格式,用
Path.Combine()和引号包裹路径,避免特殊字符导致的命令错误 - 增加进度解析逻辑,从adb输出中提取百分比,直接更新进度条控件
内容的提问来源于stack exchange,提问作者Paulo
相关产品推荐
相关产品推荐

