吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 600|回复: 10
收起左侧

[学习记录] 问一个c#读取命令行程序不显示的问题

[复制链接]
onlyclxy 发表于 2024-9-24 03:22
本帖最后由 onlyclxy 于 2024-9-25 18:32 编辑

这个问题已经解决.写作学习记录.
是python有个缓冲机制,缓冲是一种将输出暂时存储在内存中,然后批量写入到文件或屏幕的技术。当Python程序的输出被重定向到文件或管道时,通常会启用缓冲以提高性能,但这可能会导致输出的延迟或顺序混乱。
在Python中,-u选项用于在输出中禁用缓冲。使用-u选项可以禁用这种缓冲机制,即实时地将输出写入到文件或屏幕,从而确保输出立即显示。
经测试 python -u xxx.py 这个可以在c#实时读取
pyinstaller说明里也有个-u  说是可以生成无缓冲文件.  pyinstaller --onefile -u your_script.py
同时还有个写法
[Python] 纯文本查看 复制代码
import sys
import time

print("Starting...", flush=True)  # 添加 flush=True
while True:
    print("Running...", flush=True)
    time.sleep(1)



还有可以设置全局变量 set PYTHONUNBUFFERED=1 这个经测试对于python运行的有效 对于已经生成的exe无效


但是虽然说可以生成无缓冲的文件.但是对于别人的exe,还是做不到实时读取

然后又去问了群里的资深球佬.
球佬给我讲了个故事. 说以前有个群友需要用ssh做远程连接. 但是ssh那个需要输入一个密码. 那个密码不能说通过传参数的形式去传过去.只能是在那个框框去输入.

说你看包括vscode 或者 ide 里面都有那种框框. 那种你无论拖入什么程序,.他都可以去打印里面的应该打印的信息. 他那个东西叫伪终端.

他说他自己在unix上就搞了一套伪终端, 用这个伪终端解决了很多问题.

然后我很激动嘛 就去查这个伪终端. 查到一个WinPty的终端模拟器

就去网上搜, 搜到的项目貌似还需要我自己编译.. 我就去问gpt这个不是exe吗 ? gpt说你去下个git git里有.我寻思我有啊 一搜本地还真有

没想到这个winpry不光git有. vscode有,vsstudio有.anaconda里有.. 原来这个这么通用啊.

然后就尝试找c#去怎使用这个winpty.

最后结果发现我自己还是不太会弄这个winpty. 然后发现c#的库里有个现成的库winpty.Net

这个库我看解决了上面那个缓冲问题. 弄上后. 成功可以在c#输出python的实时消息了

我观察任务管理器里,他这个和那些一样. 会挂一个命令提示符,和打开的python文件


不过现在仍然有些小问题 就是framework下, 打印的字会有乱码. 我猜测可能是\r\n这种东西变成了乱码.  但这个乱码同样的代码复制到.net6就不存在了.暂时不清楚原理.

为了能在framework用..暂时只能用替换的方法.把那些乱码替换掉了. 但是不确定是否还会有其他乱码. 如果有了解这块的大佬还请指教.

下面贴一下现在能跑的c#代码.

[C#] 纯文本查看 复制代码
using System;
using System.IO;
using System.IO.Pipes;
using static winpty.WinPty;

class WinPtyExample
{
    static int Main(string[] args)
    {
        IntPtr handle = IntPtr.Zero;
        IntPtr err = IntPtr.Zero;
        IntPtr cfg = IntPtr.Zero;
        IntPtr spawnCfg = IntPtr.Zero;
        Stream stdin = null;
        Stream stdout = null;

        try
        {
            // 创建 WinPTY 配置
            cfg = winpty_config_new(WINPTY_FLAG_COLOR_ESCAPES, out err);
            winpty_config_set_initial_size(cfg, 80, 32);

            handle = winpty_open(cfg, out err);
            if (err != IntPtr.Zero)
            {
                Console.WriteLine($"Error opening WinPTY: {winpty_error_code(err)}");
                return 1;
            }

            // 指定要运行的 exe(cmd.exe)和参数(为空字符串)
            string exe = @"C:\Windows\System32\cmd.exe";
            string exeArgs = ""; // 无参数
            string cwd = @"C:\"; // 工作目录

            // 创建进程配置
            spawnCfg = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, exe, exeArgs, cwd, null, out err);
            if (err != IntPtr.Zero)
            {
                Console.WriteLine($"Error creating spawn config: {winpty_error_code(err)}");
                return 1;
            }

            // 创建输入和输出管道
            stdin = CreatePipe(winpty_conin_name(handle), PipeDirection.Out);
            stdout = CreatePipe(winpty_conout_name(handle), PipeDirection.In);

            // 启动进程
            if (!winpty_spawn(handle, spawnCfg, out IntPtr process, out IntPtr thread, out int procError, out err))
            {
                Console.WriteLine($"Spawn failed with error code: {procError}"); // 输出错误代码
                Console.WriteLine($"Error during spawn: {winpty_error_code(err)}");
                return 1;
            }

            // 读取输出
            using (var reader = new StreamReader(stdout))
            {
                string outputLine;
                while ((outputLine = reader.ReadLine()) != null)
                {
                    Console.WriteLine(outputLine);
                }
            }

            return 0;
        }
        finally
        {
            stdin?.Dispose();
            stdout?.Dispose();
            winpty_config_free(cfg);
            winpty_spawn_config_free(spawnCfg);
            winpty_error_free(err);
            winpty_free(handle);
        }
    }

    private static Stream CreatePipe(string pipeName, PipeDirection direction)
    {
        string serverName = ".";
        if (pipeName.StartsWith("\\"))
        {
            int slash3 = pipeName.IndexOf('\\', 2);
            if (slash3 != -1)
            {
                serverName = pipeName.Substring(2, slash3 - 2);
            }
            int slash4 = pipeName.IndexOf('\\', slash3 + 1);
            if (slash4 != -1)
            {
                pipeName = pipeName.Substring(slash4 + 1);
            }
        }

        var pipe = new NamedPipeClientStream(serverName, pipeName, direction);
        pipe.Connect();
        return pipe;
    }
}

下面是暂时的成品界面版的.

[C#] 纯文本查看 复制代码
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using static winpty.WinPty;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private TextBox outputTextBox;
        private Button runButton;

        public Form1()
        {
            InitializeComponent();
            InitializeControls();
        }

        private void InitializeControls()
        {
            outputTextBox = new TextBox
            {
                Multiline = true,
                Dock = DockStyle.Fill,
                ScrollBars = ScrollBars.Both,
                Font = new System.Drawing.Font("Consolas", 10)
            };
            this.Controls.Add(outputTextBox);

            runButton = new Button
            {
                Text = "运行命令",
                Dock = DockStyle.Top
            };
            runButton.Click += runButton_Click;
            this.Controls.Add(runButton);

            this.Text = "WinPty 示例";
            this.Size = new System.Drawing.Size(800, 600);
        }

        private async void runButton_Click(object sender, EventArgs e)
        {
            outputTextBox.Clear();
            await Task.Run(() => RunExecutable());
        }

        private void RunExecutable()
        {
            IntPtr handle = IntPtr.Zero;
            IntPtr err = IntPtr.Zero;
            IntPtr cfg = IntPtr.Zero;
            IntPtr spawnCfg = IntPtr.Zero;
            Stream stdin = null;
            Stream stdout = null;

            try
            {
                cfg = winpty_config_new(WINPTY_FLAG_COLOR_ESCAPES, out err);
                winpty_config_set_initial_size(cfg, 80, 32);

                handle = winpty_open(cfg, out err);
                if (err != IntPtr.Zero)
                {
                    UpdateOutput($"Error opening WinPTY: {winpty_error_code(err)}");
                    return;
                }
                
                string exe = @"C:\Windows\System32\cmd.exe";
                //exe = @"C:\死循环测试.exe"; 
                string exeArgs = "";
                string cwd = @"C:\";

                spawnCfg = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, exe, exeArgs, cwd, null, out err);
                if (err != IntPtr.Zero)
                {
                    UpdateOutput($"Error creating spawn config: {winpty_error_code(err)}");
                    return;
                }

                stdin = CreatePipe(winpty_conin_name(handle), PipeDirection.Out);
                stdout = CreatePipe(winpty_conout_name(handle), PipeDirection.In);

                if (!winpty_spawn(handle, spawnCfg, out IntPtr process, out IntPtr thread, out int procError, out err))
                {
                    UpdateOutput($"Spawn failed with error code: {procError}");
                    UpdateOutput($"Error during spawn: {winpty_error_code(err)}");
                    return;
                }

                using (var reader = new StreamReader(stdout, Encoding.UTF8))
                {
                    string outputLine;
                    while ((outputLine = reader.ReadLine()) != null)
                    {
                        //string cleanOutput = RemoveSpecialCharacters(outputLine);
                        //cleanOutput = RemoveSpecificCharacters(cleanOutput);
                        outputLine = CleanUp(outputLine);
                        UpdateOutput(outputLine);
                    }
                }
            }
            finally
            {
                stdin?.Dispose();
                stdout?.Dispose();
                winpty_config_free(cfg);
                winpty_spawn_config_free(spawnCfg);
                winpty_error_free(err);
                winpty_free(handle);
            }
        }

        private string RemoveSpecialCharacters(string input)
        {
            // 过滤掉 ANSI 转义序列和其他特定控制字符
            string pattern = @"(\x1B\[[0-?9;]*[mG]|(\x1B\]0;.*?\x07)|[\x07])";
            string cleanedInput = Regex.Replace(input, pattern, string.Empty);

            // 过滤掉特殊字符、ANSI 转义序列和控制字符
             pattern = @"(\x1B\[[0-?9;]*[mK]|[\x07])"; // 匹配 ANSI 转义序列
             cleanedInput = Regex.Replace(cleanedInput, pattern, string.Empty);


            // 去掉行首和行尾的空白字符
            return cleanedInput.Trim();
        }

        public static string CleanUp(string input)
        {
            // 移除第一组字符
            input = input.Replace("\x1B[0m", ""); // 删除字符:  (ASCII: 27) + [ + 0 + m
            input = input.Replace("\x1B[0K", ""); // 删除字符:  (ASCII: 27) + [ + 0 + K

            // 移除第二组字符
            input = input.Replace("\x1B[?25l", ""); // 删除字符:  (ASCII: 27) + [ + ? + 2 + 5 + l

            // 移除第三组字符
            input = input.Replace("\x1B[?25h", ""); // 删除字符:  (ASCII: 27) + [ + ? + 2 + 5 + h

            // 移除第四组字符
            input = input.Replace("\x1B]0;", ""); // 删除字符:  (ASCII: 27) + ] + 0 + ;

            // 移除第五组字符
            input = input.Replace("\a", ""); // 删除字符:  (ASCII: 7)
            input = input.Replace("\x1B[0m", ""); // 删除字符:  (ASCII: 27) + [ + 0 + m


            input = input.Replace("\r\n", "\n") // 统一换行符
              .Replace("\n\n", "\n"); // 删除连续空行
            while (input.Contains("\n\n")) // 循环直到没有连续空行
            {
                input = input.Replace("\n\n", "\n");
            }
            if (input.StartsWith("\n"))
            {
                input = input.Substring(1); // 删除开头的换行符
            }


            return input;
        }




        private void UpdateOutput(string message)
        {
            if (outputTextBox.InvokeRequired)
            {
                outputTextBox.Invoke(new Action<string>(UpdateOutput), message);
            }
            else
            {
                outputTextBox.AppendText(message + Environment.NewLine);
            }
        }

        private Stream CreatePipe(string pipeName, PipeDirection direction)
        {
            string serverName = ".";
            if (pipeName.StartsWith("\\"))
            {
                int slash3 = pipeName.IndexOf('\\', 2);
                if (slash3 != -1)
                {
                    serverName = pipeName.Substring(2, slash3 - 2);
                }
                int slash4 = pipeName.IndexOf('\\', slash3 + 1);
                if (slash4 != -1)
                {
                    pipeName = pipeName.Substring(slash4 + 1);
                }
            }

            var pipe = new NamedPipeClientStream(serverName, pipeName, direction);
            pipe.Connect();
            return pipe;
        }
    }
}






求助各位大佬 有点想不通
本来那种命令行的exe.可以通过cmd打开.
我想着用c#写一个, 也是获取这种命令行的exe 然后输出到编辑框里
结果写到一半发现这个c#无法获取我用python写的一个东西,打印都打印不出来
python是写了一个死循环,生成了个exe
[Python] 纯文本查看 复制代码
import time

def main():
    start_time = time.time()  # 获取脚本开始运行的时间
    while True:
        elapsed_time = time.time() - start_time  # 计算已过时间
        print(f"这是一个测试循环,已运行 {int(elapsed_time)} 秒")
        time.sleep(1)  # 暂停一秒

if __name__ == "__main__":
    main()

然后c#这边写了个异步读取.现在连打印都不打印这个,就卡在第一行里
[Java] 纯文本查看 复制代码
using System;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // 输入 exe 文件的路径
        Console.WriteLine("请输入 exe 文件的路径:");
        string exePath = Console.ReadLine();

        // 检查路径是否为空或无效
        if (string.IsNullOrWhiteSpace(exePath))
        {
            Console.WriteLine("请输入有效的路径。");
            return;
        }

        try
        {
            // 异步捕获并输出
            await RunProcessAsync(exePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"运行过程中发生错误: {ex.Message}");
        }
    }

    static async Task RunProcessAsync(string exePath)
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = exePath,         // EXE 路径
                RedirectStandardOutput = true, // 重定向标准输出
                RedirectStandardError = true,  // 重定向标准错误
                UseShellExecute = false,       // 必须禁用 Shell 执行以重定向流
                CreateNoWindow = true          // 禁止创建窗口
            },
            EnableRaisingEvents = true // 启用事件以便异步通知
        };

        // 当输出时触发事件
        process.OutputDataReceived += (sender, e) =>
        {
            if (!string.IsNullOrEmpty(e.Data))
            {
                Console.WriteLine(e.Data); // 打印标准输出的每一行
            }
        };

        // 当错误输出时触发事件
        process.ErrorDataReceived += (sender, e) =>
        {
            if (!string.IsNullOrEmpty(e.Data))
            {
                Console.Error.WriteLine(e.Data); // 打印标准错误的每一行
            }
        };

        // 异步启动进程
        process.Start();

        // 异步读取输出和错误流
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        // 等待进程完成
        await process.WaitForExitAsync();

        Console.WriteLine($"进程结束,退出代码: {process.ExitCode}");
    }
}

界面的:
[C#] 纯文本查看 复制代码
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace 命令行界面
{
    public partial class Form1 : Form
    {
        private string programPath = "C:\\死循环测试.exe";  // 这里假设程序路径已固定,您可以根据实际情况修改

        public Form1()
        {
            InitializeComponent();
        }

        private async Task button1_Click(object sender, EventArgs e)
        {
            await ExecuteCommandAsync(programPath);

        }

        private async Task ExecuteCommandAsync(string command)
        {
            // 设置为启动cmd.exe,并通过参数传递要执行的命令
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                FileName = "cmd.exe",  // 使用cmd.exe作为启动程序
                Arguments = $"/c {command}",  // /c 表示执行字符串指定的命令然后结束
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true, // 同时捕获错误输出
                CreateNoWindow = true
            };

            using (Process process = new Process { StartInfo = startInfo })
            {
                process.Start();

                // 异步读取标准输出
                while (!process.StandardOutput.EndOfStream)
                {
                    string line = await process.StandardOutput.ReadLineAsync();
                    UpdateTextBox(line);
                }

                // 异步读取标准错误输出
                while (!process.StandardError.EndOfStream)
                {
                    string errorLine = await process.StandardError.ReadLineAsync();
                    UpdateTextBox("Error: " + errorLine);  // 可以区分错误信息
                }
            }
        }

        private void UpdateTextBox(string text)
        {
            if (textBoxOutput.InvokeRequired)
            {
                textBoxOutput.Invoke(new MethodInvoker(delegate
                {
                    textBoxOutput.AppendText(text + Environment.NewLine);
                }));
            }
            else
            {
                textBoxOutput.AppendText(text + Environment.NewLine);
            }
        }
    }
}


我感觉不应该啊 这种东西实现不了吗  gpt出了一个晚上也没搞定.
求助各位大佬

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

only998 发表于 2024-9-24 05:44
本帖最后由 only998 于 2024-9-24 07:07 编辑

写的太绕了,试试直接在c#的读取exe上开一个线程来运行python的exe,不需要通过  cmd   xxxx.exe来执行。

我试了一下,写死循环无法获得任何输出,估计是python的exe一直在线程中无法处理其他事件。

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
onlyclxy + 1 + 1 先点赞了 感谢!

查看全部评分

vscos 发表于 2024-9-24 06:28
不知道改成啥 发表于 2024-9-24 08:48
flyer_2001 发表于 2024-9-24 10:19
[Python] 纯文本查看 复制代码
import asyncio

async def my_coroutine():
    print(“协程开始执行”)
    await asyncio.sleep(5)
    print(“协程继续执行”)

asyncio.run(my_coroutine())
sleep是堵塞的,使用异步试试

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
onlyclxy + 1 + 1 先感谢大佬

查看全部评分

 楼主| onlyclxy 发表于 2024-9-24 17:52

休眠哪个啊?
 楼主| onlyclxy 发表于 2024-9-24 17:59
vscos 发表于 2024-9-24 06:28
你那个EXE最好不要有死循环,

python肯定要死循环的. 服务类的东西都是不手动关,就不自动停止
我倒是怀疑是python的exe有毛病,,,
这边做的主要是c#读取exe的输出信息. 不限定什么exe. 不能对exe有要求..
 楼主| onlyclxy 发表于 2024-9-24 18:09
flyer_2001 发表于 2024-9-24 10:19
[mw_shl_code=python,true]
import asyncio

感谢大佬!不过我本质不是要对python有要求。是看看c#这边能否实现这种效果
这个背景是当时python不方便生成界面。 前一阵突然想到是否可以直接用c#写一个界面? 然后直接获取python的exe输出,打印到c#的编辑框里。 因为c#这边很容易做界面。那些python原来写的exe,过于复杂。 所以先拿个简单的python做测试。 所以不能说把python以前的代码改了。
 楼主| onlyclxy 发表于 2024-9-24 18:31
flyer_2001 发表于 2024-9-24 10:19
[mw_shl_code=python,true]
import asyncio

试了试这个异步的..python这几个都不好使..
不知道改成啥 发表于 2024-9-24 21:23

python里面啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-13 18:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表