You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#调用外部进程获取Android设备信息时的异常与重复输出问题

解决Android设备信息获取程序的重复点击异常与输出重复问题

我来帮你梳理并解决这个问题,你的代码里有几个关键设计问题导致了这些异常和输出重复的情况:

问题根源分析

  1. 复用Process实例引发异步读取冲突
    你的apktool类里的cmd是类级别的成员,第一次点击按钮后,这个Process实例已经启动过异步读取操作。第二次点击时直接复用该实例,就会触发An async read operation has already been started on the stream异常——因为同一个流上不能同时进行多个异步读取操作。

  2. StringBuilder未清空导致输出累积重复
    output同样是类级别的成员,每次调用RunExternalPing时没有清空之前的内容,新输出会追加到旧内容后面,自然会出现重复显示的问题。

  3. 错误创建Form1实例
    在cmd_OutputDataReceived方法里,你每次都new Form1(),这会创建一个全新的窗体实例,而非使用已经存在的Form1.instance,这会导致后续输出处理逻辑完全失效。

  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:24:19