C#调用外部进程获取Android设备信息时的异常与重复输出问题
解决Android设备信息获取程序的重复点击异常与输出重复问题
我来帮你梳理并解决这个问题,你的代码里有几个关键设计问题导致了这些异常和输出重复的情况:
问题根源分析
复用Process实例引发异步读取冲突
你的apktool类里的cmd是类级别的成员,第一次点击按钮后,这个Process实例已经启动过异步读取操作。第二次点击时直接复用该实例,就会触发An async read operation has already been started on the stream异常——因为同一个流上不能同时进行多个异步读取操作。StringBuilder未清空导致输出累积重复
output同样是类级别的成员,每次调用RunExternalPing时没有清空之前的内容,新输出会追加到旧内容后面,自然会出现重复显示的问题。错误创建Form1实例
在cmd_OutputDataReceived方法里,你每次都new Form1(),这会创建一个全新的窗体实例,而非使用已经存在的Form1.instance,这会导致后续输出处理逻辑完全失效。Process资源未及时清理
即便在cmd_Exited里调用了Dispose,但因为复用实例,资源没有在每次调用后及时释放,导致状态残留。
修正后的代码实现
1. 重构apktool类,每次调用创建全新Process实例
namespace dream_tool { class apktool { // 移除类级别的Process和StringBuilder,改为每次调用时创建局部实例 public string RunExternalPing(string commandArg) { StringBuilder output = new StringBuilder(); // 使用using语句自动释放Process资源 using (Process cmd = new Process()) { cmd.StartInfo.FileName = "cmd.exe"; cmd.StartInfo.UseShellExecute = false; cmd.StartInfo.CreateNoWindow = true; cmd.StartInfo.RedirectStandardOutput = true; cmd.StartInfo.RedirectStandardInput = true; cmd.StartInfo.Arguments = commandArg; cmd.EnableRaisingEvents = true; // 用Lambda表达式绑定输出接收事件,直接操作当前局部的output cmd.OutputDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { output.AppendLine(e.Data); } }; // 绑定退出事件,直接调用窗体的输出更新方法 cmd.Exited += (sender, e) => { Form1.instance.UpdateOutput(output.ToString()); }; cmd.Start(); cmd.BeginOutputReadLine(); cmd.WaitForExit(); } return commandArg; } } }
2. 调整Form1代码,新增独立的输出更新方法
public partial class Form1 : Form { public static Form1 instance; apktool a2; Process_adb a1 = new Process_adb(); List<String> fullFileName; public Form1() { InitializeComponent(); instance = this; a2 = new apktool(); } // 抽离跨线程更新UI的逻辑,避免重复代码 public void UpdateOutput(string outputContent) { if (RCH_OUT.InvokeRequired) { RCH_OUT.Invoke(new Action(() => { RCH_OUT.Text = outputContent; MessageBox.Show(outputContent); })); } else { RCH_OUT.Text = outputContent; MessageBox.Show(outputContent); } } private void button2_Click(object sender, EventArgs e) { // 每次点击都调用全新的Process实例,无状态残留 a2.RunExternalPing("/c adb shell getprop ro.product.cpu.abi"); } }
关键改进点说明
- 每次调用创建新Process:用
using语句自动管理Process资源,彻底避免复用实例带来的异步读取冲突问题。 - 局部StringBuilder:每次调用都创建新的StringBuilder,确保输出内容不会累积重复。
- Lambda简化事件绑定:避免单独写方法时的实例错误,直接在事件中处理输出和窗体更新逻辑。
- 独立的UI更新方法:把跨线程更新UI的逻辑抽离出来,代码更清晰,也避免了错误创建窗体实例的问题。
这样修改后,无论你点击多少次按钮,都不会再出现异常,输出内容也不会重复了。
内容的提问来源于stack exchange,提问作者hamed abdulhadi
相关产品推荐
相关产品推荐

