C#调用python打包的exe,并实时(逐行)读取其输出信息

首先看python项目代码:

import time
import argparse


# cxfreeze test.py --target-dir dist
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("total_count", type=int)
    parser.add_argument("time_span", type=int)

    args = parser.parse_args()

    count = 0
    for i in range(0, args.total_count):
        time.sleep(args.time_span)
        count = count + 1
        print(f'count now :{count}', end='\n', flush=True)


if __name__ == "__main__":
    main()

这段代码就是根据总计数和时间间隔来循环输出测试信息。

由于要实时的读取信息,所以必须采用异步编程的方式

如何异步读取exe的输出请参考这篇文章:【.net 深呼吸】启动一个进程并实时获取状态信息 - 东邪独孤 - 博客园 (cnblogs.com)

后台调取CMD的类:

        //设置CMD命令及其参数
        public CMD(string path)
        {
            process = new Process();
            startInfo = new ProcessStartInfo();

            startInfo.FileName = "cmd.exe";
            //设置工作的目录
            startInfo.WorkingDirectory = path;
            // 不使用操作系统PowerShell启动进程
            startInfo.UseShellExecute = false;
            // 重定向标准输入流
            startInfo.RedirectStandardInput = true;
            // 重定向标准输出流
            startInfo.RedirectStandardOutput = true;
            // 重定向错误输出流
            startInfo.RedirectStandardError = true;
            // 不创建新窗口
            startInfo.CreateNoWindow = true;
            //隐藏窗口
            //startInfo.WindowStyle = ProcessWindowStyle.Hidden;

        }

同步执行命令:

        // 执行命令
        public string CMD_Run_Sync(string command)
        {
            //定义返回信息
            string cmd_putout = null;
            //获取参数对象
            process.StartInfo = startInfo;
            //启动
            process.Start();
            // 写入CMD命令到标准输入流
            process.StandardInput.WriteLine(command);
            process.StandardInput.WriteLine("exit");
            // 读取CMD的输出
            string output = process.StandardOutput.ReadToEnd();
            cmd_putout = output;
            string error = process.StandardError.ReadToEnd();

            // 输出错误信息
            if (!string.IsNullOrEmpty(error))
            {
                cmd_putout = error;
            }

            return cmd_putout;
            // 等待进程退出
            //process.WaitForExit();
        }

异步执行命令:

        public void CMD_Run_Async(string command)
        {
            process.EnableRaisingEvents = true;

            process.OutputDataReceived += (sender, e) =>
            {
                if (!string.IsNullOrEmpty(e.Data))
                {
                    if(output_information != e.Data)
                    {
                        output_information = e.Data;
                        OnDataSend(output_information);
                    }
                    Console.WriteLine(e.Data);
                }
            };

            //获取参数对象
            process.StartInfo = startInfo;
            // 启动进程并开始异步读取输出  
            process.Start();

            process.StandardInput.WriteLine(command);

            process.StandardInput.WriteLine("exit");

            process.BeginOutputReadLine();

        }

在WPF前台调用的代码:

        private async void Button4_Click(object sender, RoutedEventArgs e)
        {
            CMD cmdTransform;

            DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory);
            string cmd_start_path = di.FullName;

            //开始异步执行任务
            await Task.Run(() =>
            {
                cmd_start_path = cmd_start_path + "\\tool\\count";
                string command = "test.exe 5 1";
                cmdTransform = new CMD(cmd_start_path);
                cmdTransform.DataSend += ReceiveData;

                cmdTransform.CMD_Run_Async(command);
            });
        }

上述都是修改后的代码,应该没问题

修改前,当我通过C#调用python生成的exe时,发现并不是逐行读取,而是一次性读取,此时我已经改为异步执行CMD命令的代码了。我想到之前的参考文章中说明,一般都是先写入缓冲区,然后当缓冲区满了或者进程结束才会输出到标准流中,由于参考文献中使用的是C#编写的测试输出有

StreamWriter writer
writer.Flush()

可以强制刷新,那么python中的print是不是也是这样呢?根据文心一言回答,print的强制刷新需要添加print(f'count now :{count}', end='\n', flush=True)。这样每次调用print的时候会以换行符结束,然后强制刷新到标准流。

这时候将修改过的python打包,再去C#调用时,发现还是一次性读取。解决方法还是在那篇参考文章中,需要加上process.EnableRaisingEvents = true;否则 process.OutputDataReceived事件根本不会触发

加上之后,再去调用,发现在Debug模式下,可以正常一行行输出,但是Release模式下却会创建一个新的窗口,然后在新窗口中逐行输出

我以为是使用cx_Freeze打包的问题,于是我在setup.py脚本文件中添加了参数base="Win32GUI"

from cx_Freeze import setup, Executable

# 打包配置
build_exe_options = {
    "packages": [],
    "excludes": [],
    "include_files": [],
    "include_msvcr": True
}

executables = [Executable("test.py", base="Win32GUI", icon="app.ico")]

setup(
    name="AppName",
    version="1.0",
    description="描述你的应用",
    options={"build_exe": build_exe_options},
    executables=executables
)

调用还是会创建新窗口,改为base="None",一样会。

突然想到在调用的时候,系统弹出消息框,是否执行此exe,反应过来原来是权限问题,所以要以管理员身份启动VS,这时候再去调用就没问题了。

在此基础上,添加委托和事件,让exe的输出从控制台传到主线程UI中,效果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值