No sé si alguien todavía está buscando una solución a esto, pero me ha surgido varias veces porque estoy escribiendo una herramienta en Unity para algunos juegos y debido a la limitada interoperabilidad de ciertos sistemas con mono (como PIA para leer texto de Word, por ejemplo), a menudo tengo que escribir ejecutables específicos del sistema operativo (a veces Windows, a veces MacOS) y ejecutarlos desde Process.Start().
El problema es que cuando se ejecuta un ejecutable como este, se activará en otro subproceso que bloquea la aplicación principal, causando un bloqueo. Si desea proporcionar comentarios útiles a sus usuarios durante este tiempo más allá de los íconos giratorios evocados por su sistema operativo respectivo, entonces está medio enredado. El uso de una transmisión no funcionará porque el hilo aún está bloqueado hasta que finalice la ejecución.
La solución que he encontrado, que puede parecer extrema para algunas personas pero me parece que funciona bastante bien, es usar sockets y multiprocesamiento para configurar comunicaciones síncronas confiables entre las dos aplicaciones. Por supuesto, esto solo funciona si estás creando ambas aplicaciones. Si no, creo que no tienes suerte. ... Me gustaría ver si funciona solo con multihebra usando un enfoque de transmisión tradicional, por lo que si alguien quisiera probar eso y publicar los resultados aquí, sería genial.
De todos modos, aquí está la solución que actualmente trabaja para mí:
En la aplicación principal, o llamando, hago algo como esto:
/// <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();
es donde yo establezca el servidor de socket aquí:
/// <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;
}
Aquí está mi controlador de socket para el hilo ... tenga en cuenta que tendrá que crear varios hilos en algunos casos; es por eso que tengo que _builderCommThreads Lista allí (I portado desde el código en otro lugar donde yo estaba haciendo algo similar, pero llamando varias instancias en una 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;
}
Por supuesto, tendrá que declarar algunas cosas hasta en la parte superior:
private TcpListener _listener = null;
private IPAddress _ipAddress = null;
private List<Thread> _builderCommThreads = null;
private System.Object _threadLock = new System.Object();
... entonces en el ejecutable invocado, configurar el otro extremo (utilicé la estática en este caso, se puede usar cualquier cosa que quiera):
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);
}
}
. ..YO' Estoy usando esto para lanzar una herramienta de línea de comando en Windows que usa el material de PIA para sacar texto de un documento de Word. Probé PIA los .dlls en Unity, pero encontré problemas de interoperabilidad con mono. También lo estoy usando en MacOS para invocar scripts de shell que inician instancias adicionales de Unity en modo batch y ejecutar scripts de editor en aquellas instancias que responden a la herramienta a través de esta conexión de socket. Es genial, porque ahora puedo enviar comentarios al usuario, depurar, monitorear y responder a pasos específicos en el proceso, etcétera, etcétera.
HTH
razón por la que no utiliza el DLL/SDK downloadabe de 7zip que permite un control mucho mayor que cualquier técnica basada en la consola? – Yahia
Ayudaría a ver el código que ha probado con Process por ejemplo, donde está creando el Proceso – MethodMan
Porque 7z.exe cubre todas las funciones que quería. – Extaze