我的项目有问题。我想启动一个进程,7z.exe(控制台版)。 我尝试过三种不同的东西:
没有任何作用。它始终“等待”过程结束以显示我想要的内容。 我没有任何代码可以放,只是如果你想要我的代码与其中一个列出的东西。感谢。
编辑: 我的代码:
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
this.sr = process.StandardOutput;
while (!sr.EndOfStream)
{
String s = sr.ReadLine();
if (s != "")
{
System.Console.WriteLine(DateTime.Now + " - " + s);
}
}
或者
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += new DataReceivedEventHandler(recieve);
process.StartInfo.CreateNoWindow = true;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
public void recieve(object e, DataReceivedEventArgs outLine)
{
System.Console.WriteLine(DateTime.Now + " - " + outLine.Data);
}
或者
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
string output = p.StandardOutput.ReadToEnd();
process.WaitForExit();
“过程”是我的预制过程
好的我知道为什么它不能正常工作:7z.exe是错误:它在控制台中显示百分比加载,并且仅在当前文件完成时才发送信息。例如,在提取中,它工作得很好:)。我将搜索另一种方法来使用没有7z.exe的7z函数(可能使用7za.exe或一些DLL)。谢谢大家。 要回答这个问题,OuputDataRecieved事件工作正常!
答案 0 :(得分:20)
看一下这个页面,看起来这是适合您的解决方案:http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx和http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
[编辑] 这是一个有效的例子:
Process p = new Process();
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = @"C:\Program Files (x86)\gnuwin32\bin\ls.exe";
p.StartInfo.Arguments = "-R C:\\";
p.OutputDataReceived += new DataReceivedEventHandler(
(s, e) =>
{
Console.WriteLine(e.Data);
}
);
p.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { Console.WriteLine(e.Data); });
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
Btw,ls -R C:\递归地列出来自C的根目录的所有文件。这些是很多文件,我确信当第一个结果显示在屏幕上时它没有完成。 在显示之前,7zip有可能保持输出。我不确定你给这个过程提供了什么样的参数。
答案 1 :(得分:5)
我不知道是否还有人在寻找解决方案,但我已多次提出这个问题,因为我在Unity编写了一个支持某些游戏的工具,并且由于某些系统与单声道的有限互操作性(例如PIA用于从Word读取文本),我经常需要编写特定于操作系统(有时是Windows,有时是MacOS)的可执行文件,并从Process.Start()启动它们。
问题是,当您启动这样的可执行文件时,它会在另一个阻止您的主应用程序的线程中启动,从而导致挂起。如果您希望在此时间段内为您的用户提供有用的反馈,而不是您各自的操作系统所形成的旋转图标,那么您就会被搞砸了。使用流不会起作用,因为线程仍然被阻塞,直到执行完成。
我遇到的解决方案,对于某些人来说可能看起来很极端,但我发现对我来说效果很好,就是使用套接字和多线程在两个应用之间建立可靠的同步通信。当然,这只适用于您同时创作这两个应用的情况。如果没有,我想你运气不好。 ...我想看看它是否适用于使用传统流方法的多线程,所以如果有人想尝试并在此处发布结果会很棒。
无论如何,这是目前为我工作的解决方案:
在主应用程序或调用应用程序中,我执行以下操作:
/// <summary>
/// Handles the OK button click.
/// </summary>
private void HandleOKButtonClick() {
string executableFolder = "";
#if UNITY_EDITOR
executableFolder = Path.Combine(Application.dataPath, "../../../../build/Include/Executables");
#else
executableFolder = Path.Combine(Application.dataPath, "Include/Executables");
#endif
EstablishSocketServer();
var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = Path.Combine(executableFolder, "WordConverter.exe"),
Arguments = locationField.value + " " + _ipAddress.ToString() + " " + SOCKET_PORT.ToString(),
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
我在这里建立套接字服务器:
/// <summary>
/// Establishes a socket server for communication with each chapter build script so we can get progress updates.
/// </summary>
private void EstablishSocketServer() {
//_dialog.SetMessage("Establishing socket connection for updates. \n");
TearDownSocketServer();
Thread currentThread;
_ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0];
_listener = new TcpListener(_ipAddress, SOCKET_PORT);
_listener.Start();
UnityEngine.Debug.Log("Server mounted, listening to port " + SOCKET_PORT);
_builderCommThreads = new List<Thread>();
for (int i = 0; i < 1; i++) {
currentThread = new Thread(new ThreadStart(HandleIncomingSocketMessage));
_builderCommThreads.Add(currentThread);
currentThread.Start();
}
}
/// <summary>
/// Tears down socket server.
/// </summary>
private void TearDownSocketServer() {
_builderCommThreads = null;
_ipAddress = null;
_listener = null;
}
这是我的线程的套接字处理程序...请注意,在某些情况下你必须创建多个线程;这就是为什么我在那里有_builderCommThreads列表的原因(我从其他地方的代码移植它,我做了类似的事情,但连续调用了多个实例):
/// <summary>
/// Handles the incoming socket message.
/// </summary>
private void HandleIncomingSocketMessage() {
if (_listener == null) return;
while (true) {
Socket soc = _listener.AcceptSocket();
//soc.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000);
NetworkStream s = null;
StreamReader sr = null;
StreamWriter sw = null;
bool reading = true;
if (soc == null) break;
UnityEngine.Debug.Log("Connected: " + soc.RemoteEndPoint);
try {
s = new NetworkStream(soc);
sr = new StreamReader(s, Encoding.Unicode);
sw = new StreamWriter(s, Encoding.Unicode);
sw.AutoFlush = true; // enable automatic flushing
while (reading == true) {
string line = sr.ReadLine();
if (line != null) {
//UnityEngine.Debug.Log("SOCKET MESSAGE: " + line);
UnityEngine.Debug.Log(line);
lock (_threadLock) {
// Do stuff with your messages here
}
}
}
//
} catch (Exception e) {
if (s != null) s.Close();
if (soc != null) soc.Close();
UnityEngine.Debug.Log(e.Message);
//return;
} finally {
//
if (s != null) s.Close();
if (soc != null) soc.Close();
UnityEngine.Debug.Log("Disconnected: " + soc.RemoteEndPoint);
}
}
return;
}
当然,您需要在顶部声明一些内容:
private TcpListener _listener = null;
private IPAddress _ipAddress = null;
private List<Thread> _builderCommThreads = null;
private System.Object _threadLock = new System.Object();
...然后在调用的可执行文件中,设置另一端(我在这种情况下使用静态,你可以使用你想要的):
private static TcpClient _client = null;
private static Stream _s = null;
private static StreamReader _sr = null;
private static StreamWriter _sw = null;
private static string _ipAddress = "";
private static int _port = 0;
private static System.Object _threadLock = new System.Object();
/// <summary>
/// Main method.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args) {
try {
if (args.Length == 3) {
_ipAddress = args[1];
_port = Convert.ToInt32(args[2]);
EstablishSocketClient();
}
// Do stuff here
if (args.Length == 3) Cleanup();
} catch (Exception exception) {
// Handle stuff here
if (args.Length == 3) Cleanup();
}
}
/// <summary>
/// Establishes the socket client.
/// </summary>
private static void EstablishSocketClient() {
_client = new TcpClient(_ipAddress, _port);
try {
_s = _client.GetStream();
_sr = new StreamReader(_s, Encoding.Unicode);
_sw = new StreamWriter(_s, Encoding.Unicode);
_sw.AutoFlush = true;
} catch (Exception e) {
Cleanup();
}
}
/// <summary>
/// Clean up this instance.
/// </summary>
private static void Cleanup() {
_s.Close();
_client.Close();
_client = null;
_s = null;
_sr = null;
_sw = null;
}
/// <summary>
/// Logs a message for output.
/// </summary>
/// <param name="message"></param>
private static void Log(string message) {
if (_sw != null) {
_sw.WriteLine(message);
} else {
Console.Out.WriteLine(message);
}
}
...我正在使用它在Windows上启动命令行工具,该工具使用PIA的东西从Word文档中提取文本。我在Unity中尝试过PIA的.dll,但遇到了单声道的互操作问题。我还在MacOS上使用它来调用shell脚本,这些脚本在批处理模式下启动其他Unity实例,并在那些通过此套接字连接与工具对话的实例中运行编辑器脚本。这很棒,因为我现在可以向用户发送反馈,调试,监控并响应流程中的特定步骤,等等。
HTH
答案 2 :(得分:5)
要正确处理输出和/或错误重定向,您还必须重定向输入。 它似乎是你正在启动的外部应用程序的运行时的功能/错误,从我到目前为止看到的,在其他任何地方都没有提到。
使用示例:
Public Class Form1
Private Const WM_APP As Integer = &H8000
Private Const WM_FAXMSG As Integer = WM_APP + 9999 ' set by you, send this in the `AppMsgNumber` parameter when you register for feedback
Protected Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
Case WM_FAXMSG
Dim Status As Integer = m.WParam.ToInt32
Dim FaxID As Integer = m.LParam.ToInt32
Debug.Print("Status: " & Status)
Debug.Print("FaxID: " & FaxID)
End Select
MyBase.WndProc(m)
End Sub
End Class
...
Process p = new Process(...);
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true; // Is a MUST!
p.EnableRaisingEvents = true;
p.OutputDataReceived += OutputDataReceived;
p.ErrorDataReceived += ErrorDataReceived;
Process.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
p.OutputDataReceived -= OutputDataReceived;
p.ErrorDataReceived -= ErrorDataReceived;
答案 3 :(得分:1)
我在几个项目中使用了here描述的CmdProcessor类,取得了很大的成功。它起初看起来有点令人生畏,但很容易使用。
答案 4 :(得分:0)
试试这个。
Process notePad = new Process();
notePad.StartInfo.FileName = "7z.exe";
notePad.StartInfo.RedirectStandardOutput = true;
notePad.StartInfo.UseShellExecute = false;
notePad.Start();
StreamReader s = notePad.StandardOutput;
String output= s.ReadToEnd();
notePad.WaitForExit();
请将上述内容放在thread
。
现在,要将输出更新为UI,您可以使用timer
两行
Console.Clear();
Console.WriteLine(output);
这可能对您有所帮助
答案 5 :(得分:0)
问题是由调用Process.WaitForExit method引起的。根据文档,这是做什么的:
设置等待关联进程退出的时间, 并阻止当前线程执行,直到时间结束 或进程已退出。 为避免阻塞当前线程,请使用 退出事件。
因此,为防止线程在进程退出之前阻塞,请如下所示连接Process对象的Process.Exited event处理程序。仅当EnableRaisingEvents属性的值为true时,才可以发生Exited事件。
process.EnableRaisingEvents = true;
process.Exited += Proc_Exited;
private void Proc_Exited(object sender, EventArgs e)
{
// Code to handle process exit
}
通过这种方式,您可以像现在一样通过Process.OutputDataReceived event获取仍在运行的进程的输出。 (PS-该事件页面上的代码示例也犯了使用Process.WaitForExit的错误。)
另一个注意事项是,您需要确保在退出Exited方法之前不会清除Process对象。如果您的Process是在using语句中初始化的,则可能是一个问题。
答案 6 :(得分:0)
Windows 以不同的方式对待管道和控制台。管道是缓冲的,控制台不是。 RedirectStandardOutput
连接管道。只有两种解决方案。
请注意,RTConsole 不处理遇到相同问题的 STDERR。
感谢 https://stackoverflow.com/users/4139809/jeremy-lakeman 与我分享有关另一个问题的信息。