Non so se qualcuno è ancora alla ricerca di una soluzione a questo, ma è venuto fuori più volte per me perché sto scrivendo uno strumento in Unity a supporto di alcuni giochi e grazie alla limitata interoperabilità di alcuni sistemi con mono (ad esempio PIA per leggere il testo da Word, ad esempio), spesso devo scrivere eseguibili specifici del sistema operativo (a volte Windows, a volte MacOS) e lanciarli da Process.Start().
Il problema è che quando si avvia un eseguibile come questo, verrà attivato in un altro thread che blocca l'app principale, causando un blocco. Se desideri fornire un feedback utile ai tuoi utenti durante questo periodo al di là delle icone rotanti evocate dal tuo rispettivo sistema operativo, allora sei un po 'fregato. L'utilizzo di uno stream non funzionerà perché il thread è ancora bloccato fino al termine dell'esecuzione.
La soluzione su cui mi sono imbattuto, che potrebbe sembrare estrema per alcune persone ma trovo che funziona abbastanza bene per me, è utilizzare socket e multithreading per configurare comunicazioni sincrone affidabili tra le due app. Naturalmente, questo funziona solo se si stanno creando entrambe le app. Altrimenti, penso che tu non abbia fortuna. ... Mi piacerebbe vedere se funziona con il multithreading usando un approccio stream tradizionale, quindi se qualcuno vorrebbe provarlo e pubblicare i risultati qui sarebbe fantastico.
Ad ogni modo, ecco la soluzione attualmente lavora per me:
Nel l'applicazione principale, o chiamando, faccio qualcosa di simile:
/// <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();
Ecco dove stabilisco il server socket:
/// <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;
}
Ecco il mio gestore di socket per il thread ... si noti che in alcuni casi sarà necessario creare più thread; è per questo che ho che List _builderCommThreads in là (ho portato dal codice altrove dove stavo facendo qualcosa di simile, ma chiamando più istanze di fila):
/// <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;
}
Naturalmente, avrete bisogno di dichiarare un certo roba in alto:
private TcpListener _listener = null;
private IPAddress _ipAddress = null;
private List<Thread> _builderCommThreads = null;
private System.Object _threadLock = new System.Object();
... poi nel file eseguibile invocato impostare l'altra estremità (io ho usato statica in questo caso, è possibile utilizzare quello che volete):
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);
}
}
. ..IO' m usando questo per lanciare uno strumento da riga di comando su Windows che usa le cose PIA per estrarre il testo da un documento di Word. Ho provato PIA i file .dll in Unity, ma ho incontrato problemi di interoperabilità con mono. Lo sto utilizzando anche su MacOS per invocare script di shell che lanciano istanze di Unity aggiuntive in modalità batch ed eseguono script di editor in quelle istanze che rispondono allo strumento tramite questa connessione socket. È fantastico, perché ora posso inviare feedback all'utente, eseguire il debug, monitorare e rispondere a specifici passaggi del processo, eccetera, eccetera.
HTH
qualsiasi motivo non si utilizza la DLL/SDK downloadabe da 7zip che permette un controllo molto maggiore di qualsiasi tecnica basata su console? – Yahia
Sarebbe utile vedere il codice che hai provato con Process ad esempio dove stai creando il processo – MethodMan
Perché 7z.exe copre tutte le funzioni che volevo. – Extaze