使用VB.Net,启动cmd.exe,然后执行多个命令并在该窗口中本地输出

问题描述 投票:0回答:1

简介

我正在尝试编写一个程序来创建大量 .7z 文件。超过 1000 个。如果我为每个实例创建一个 cmd 窗口,它要么同时创建 1000 个窗口,要么连续几个小时创建一个窗口。这两种情况都不好。

我不能说:隐藏窗口,因为我实际上需要向用户显示7z的进度,因为有些文件可能很大,并且没有窗口,进程似乎已经死了,但这只是需要有点,我不知道如何使用 7z.dll 并能够获取状态。

我的问题解决方案

如果我可以生成一个用户可以与之交互的cmd窗口(不一定是),然后将按键发送到该窗口,那么一切都会得到整合。

我的问题

我似乎无法让它发挥作用。到目前为止,我所拥有的最好的事情是它确实生成了一个窗口,但是每次尝试在 cmd 窗口上打印一些内容,最终都会出现在我的调试输出窗口中。由于 7z 归档过程具有动态文本更新,因此捕获输出并将其放入文本字段不起作用,因为它不会捕获正在创建的文件的归档百分比。虽然它显示了有关存档过程的一些内容,但缺少重要部分。

我的代码:

1 表格:表格1
1 个按钮:bInteractWithCMD
使用 Visual Studio 2022 以 .net 4.8.1 为目标(如果修复将使用不同版本的 .net,请告诉我哪个版本。)

Public Class Form1

    Private Process As New Process
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Process.StartInfo.FileName = "cmd.exe"
        Me.Process.StartInfo.Arguments = "/k echo Hello World"
        Me.Process.StartInfo.UseShellExecute = False
        Me.Process.StartInfo.RedirectStandardOutput = False
        Me.Process.StartInfo.RedirectStandardInput = True
        Me.Process.Start()

    End Sub

    Private Sub bInteractWithCMD_Click(sender As Object, e As EventArgs) Handles bInteractWithCMD.Click
        Me.Process.StandardInput.WriteLine("echo This is a test.")
    End Sub
End Class

发生的事情是这样的: enter image description here

脚注

在这个例子中,我正在做一个简单的回声。如果我将其替换为 7z.exe 代码,行为仍然相同,但此示例更容易排除故障。

如果您有任何其他方式获取 7z.exe 进度而不在 cmd 窗口中显示它,我会很乐意接受这个答案。另外,我不能让我的程序创建 .cmd 脚本并执行它。虽然理论上可行,但我的程序将在处理每个文件之间执行一些步骤,包括通知用户,并且我不希望这部分发生在 cmd 窗口中。用户应该能够最小化 cmd 窗口,并在必要时返回以查看进度。 cmd 窗口更多的是一种跟踪进程尚未停止的方式。我的程序会以更方便的方式显示状态。

vb.net cmd
1个回答
0
投票

下面展示了如何使用 System.Diagnostics.Process7-Zip 来压缩目录中的文件。如果在命令行参数中指定

-bsp1
,则会显示进度。

创建一个类(名称:HelperProcess.vb)

Imports System.IO
Imports Microsoft.Win32

Public Class HelperProcess

    Private Shared Function Get7ZipLocation(Optional regView As RegistryView = RegistryView.Registry64) As String
        Dim hive As RegistryHive = RegistryHive.LocalMachine
        Dim subkey As String = "Software\7-Zip"
        Dim valueName As String = "Path"

        Using rKey As RegistryKey = RegistryKey.OpenBaseKey(hive, regView)
            If rKey IsNot Nothing Then

                'open subkey
                Using sKey As RegistryKey = rKey.OpenSubKey(subkey, False)
                    If sKey IsNot Nothing Then
                        'read from registry
                        'Debug.WriteLine($"'{valueName}' Data Type: {sKey.GetValueKind(valueName)}")
                        Return sKey.GetValue(valueName)?.ToString()
                    Else
                        Throw New Exception($"Error (GetRegistryValue) - Could not open '{subkey}'")
                    End If
                End Using
            Else
                Throw New Exception($"Error (GetRegistryValue) - Could Not open '{hive.ToString()}' ")
            End If
        End Using
    End Function


    Public Shared Sub RunProcess7Zip(arguments As String, Optional filename As String = Nothing)

        If String.IsNullOrEmpty(filename) Then
            'if 7-Zip fully-qualified filename wasn't supplied, get path from registry
            filename = $"""{Path.Combine(Get7ZipLocation(), "7z.exe")}"""
        End If

        'create new instance
        Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}

        If Not String.IsNullOrEmpty(arguments) Then
            startInfo.Arguments = arguments
        End If

        Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}

            AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                                If Not String.IsNullOrWhiteSpace(e.Data) Then
                                                    'ToDo: add desired code
                                                    Debug.WriteLine("error: " & e.Data)
                                                End If
                                            End Sub

            AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                                 If Not String.IsNullOrWhiteSpace(e.Data) Then
                                                     'ToDo: add desired code
                                                     Debug.WriteLine("output: " & e.Data)
                                                 End If
                                             End Sub

            p.Start()

            p.BeginErrorReadLine()
            p.BeginOutputReadLine()

            'wait for exit
            p.WaitForExit()

        End Using
    End Sub
End Class

用法

Dim targetFilename As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test.7z")

'-bsp1: output progress to StandardOutput
'-mmt4: set number of CPU threads to 4
'-mx=5: set compression level to 5 (default)
Dim t As Thread = New Thread(Sub() HelperProcess.RunProcess7Zip($"a -bsp1 -mx=5 -mmt4 -ssp -t7z {targetFilename} {sourceFolder}\*.*"))
t.Start()

资源

© www.soinside.com 2019 - 2024. All rights reserved.