我不知道是否还有人在寻找这个问题的解决方案,但出现了几次,因为我正在使用Unity编写一些游戏工具,并且由于某些系统与mono的互操作性受到限制(例如PIA用于从Word中读取文本),我经常不得不编写特定于操作系统的(有时为Windows,有时为MacOS)可执行文件,并从Process.Start()启动它们。
问题是,当您像这样启动可执行文件时,它将在另一个线程中启动,该线程会阻止您的主应用程序,导致挂起。如果您想在此期间向用户提供有用的反馈,超出了各自操作系统生成的旋转图标,那么您就有点进退两难了。使用流行也无效,因为线程仍会被阻塞,直到执行完成。
我找到的解决方案对某些人来说可能看起来很极端,但我发现对我而言非常有效,就是使用套接字和多线程设置可靠的同步通信。当然,这只适用于您编写两个应用程序的情况。如果不是这样,我认为您就没有办法了。...我想测试是否可以使用传统流方法仅使用多线程实现,如果有人愿意尝试并在此处发布结果,那就太好了。
无论如何,这是目前适用于我的解决方案:
在主应用程序或调用应用程序中,我会执行类似于以下操作:
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();
这里是我建立套接字服务器的地方:
private void EstablishSocketServer() {
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();
}
}
private void TearDownSocketServer() {
_builderCommThreads = null;
_ipAddress = null;
_listener = null;
}
以下是线程的socket处理程序...请注意,您有时需要创建多个线程;这就是为什么我在那里有_builderCommThreads列表的原因(我从其他代码中移植过来,其中我正在做类似的事情,但是连续调用多个实例):
private void HandleIncomingSocketMessage() {
if (_listener == null) return;
while (true) {
Socket soc = _listener.AcceptSocket();
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;
while (reading == true) {
string line = sr.ReadLine();
if (line != null) {
UnityEngine.Debug.Log(line);
lock (_threadLock) {
}
}
}
} catch (Exception e) {
if (s != null) s.Close();
if (soc != null) soc.Close();
UnityEngine.Debug.Log(e.Message);
} 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();
static void Main(string[] args) {
try {
if (args.Length == 3) {
_ipAddress = args[1];
_port = Convert.ToInt32(args[2]);
EstablishSocketClient();
}
if (args.Length == 3) Cleanup();
} catch (Exception exception) {
if (args.Length == 3) Cleanup();
}
}
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();
}
}
private static void Cleanup() {
_s.Close();
_client.Close();
_client = null;
_s = null;
_sr = null;
_sw = null;
}
private static void Log(string message) {
if (_sw != null) {
_sw.WriteLine(message);
} else {
Console.Out.WriteLine(message);
}
}
我正在使用这个方法在Windows上启动一个命令行工具,该工具使用PIA来从Word文档中提取文本。我尝试在Unity中使用PIA的.dll文件,但遇到了与mono的互操作问题。我还在MacOS上使用它来调用启动批处理模式下的其他Unity实例和在这些实例中运行编辑器脚本的shell脚本,并通过此套接字连接向该工具反馈。这很棒,因为现在我可以发送反馈给用户,进行调试,监视并响应过程中的特定步骤等等。
希望对你有所帮助。