我正在尝试编写一个程序来创建大量 .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
在这个例子中,我正在做一个简单的回声。如果我将其替换为 7z.exe 代码,行为仍然相同,但此示例更容易排除故障。
如果您有任何其他方式获取 7z.exe 进度而不在 cmd 窗口中显示它,我会很乐意接受这个答案。另外,我不能让我的程序创建 .cmd 脚本并执行它。虽然理论上可行,但我的程序将在处理每个文件之间执行一些步骤,包括通知用户,并且我不希望这部分发生在 cmd 窗口中。用户应该能够最小化 cmd 窗口,并在必要时返回以查看进度。 cmd 窗口更多的是一种跟踪进程尚未停止的方式。我的程序会以更方便的方式显示状态。
下面展示了如何使用 System.Diagnostics.Process 和 7-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()
资源: