visual - threadstart c#
¿Cómo invocar una función en el hilo principal en.NET? (6)
Aquí está mi situación exacta: tengo una biblioteca de clases .NET expuesta como un objeto COM (usando regasm.exe). El objeto COM contiene un método que ejecuta una aplicación externa (utilizando Process.Start). El objeto COM se utiliza en Internet Explorer. Entonces mi página web ejecuta la aplicación externa y necesito encontrar una forma de pasar el Código de Salida a la página web.
Al principio no inicié la aplicación externa en un nuevo hilo y simplemente esperé a que el usuario cerrara la aplicación y luego mi función devolvió el código de salida a la persona que llamaba. Pero mientras la aplicación se estaba ejecutando, IE no respondía. Así que decidí iniciar la aplicación en un nuevo hilo, pero ahora ya no puedo devolver el código de salida.
El objeto COM se crea con: new ActiveXObject
instrucción new ActiveXObject
y, lamentablemente, javascript no admite el hundimiento de eventos, por lo que no puedo escribir un evento en C # que se desencadena cuando la aplicación finaliza.
Tengo una biblioteca de clases .NET que contiene una clase con un método que realiza una operación larga. Cuando un cliente llama a este método, debe realizar una operación larga en un nuevo hilo para evitar bloquear al que llama. Pero una vez que el método termina, debe ejecutar algún código en el hilo principal. En una aplicación WinForms podría haber usado el método System.Windows.Forms.Control.Invoke, pero este no es mi caso. Entonces, ¿cómo puedo lograr esto en C #?
Si un hilo tiene que poder ejecutar un poco de código (generalmente en forma de delegado) publicado por otro hilo, básicamente tendrá que esperar esas instrucciones. ¿Qué más está haciendo tu hilo principal? No es tan difícil construir un equivalente del ciclo de eventos (básicamente, tendrías una cola de delegados de productores / consumidores), pero debes ser consciente de que no puedes simplemente interrumpir el hilo principal y decir "hazlo ahora".
¿Por qué esto tiene que ejecutarse en el hilo principal?
Un hilo no puede simplemente ejecutar cosas en otro hilo. Lo más cercano que puede obtener es poner un delegado en una cola para que se ejecute el otro subproceso, pero eso supone que el otro subproceso está cooperando al respecto.
En una aplicación de WinForms, el ciclo principal busca dichos mensajes en cola en cada iteración de bucle.
Si solo necesita comunicar el acabado del hilo de trabajo, puede usar, por ejemplo, un indicador de variable. Si el hilo principal debería poder esperar la terminación del trabajo, use un semáforo o una variable de condición (monitor).
Encontré una solución simple al problema:
Mi objeto COM se declara así:
public class Runner
{
public void Run(string executable, object processExitHandler)
{
ThreadPool.QueueUserWorkItem(state =>
{
var p = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = executable
}
};
p.Start();
while (!p.HasExited)
{
Thread.Sleep(100);
}
state
.GetType()
.InvokeMember(
"call",
BindingFlags.InvokeMethod,
null,
state,
new object[] { null, p.ExitCode }
);
}, processExitHandler);
}
}
Y en mi página HTML lo uso así:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head><title>ActiveXRunner</title>
<script type="text/javascript">
function runNotepad() {
var ax = new ActiveXObject(''ActiveXRunner.Runner'');
ax.Run(''c://windows//notepad.exe'', h);
}
function h(exitCode) {
alert(''exitCode = '' + exitCode);
}
</script>
</head>
<body>
<a href="#" onclick="runNotepad();">Run notepad and show exit code when finished</a>
</body>
</html>
No hay forma de hacer que el código se ejecute explícitamente en un subproceso específico (excepto por el subproceso que se usó para crear Controles de UI, esta es una excepción) pero si simplemente desea llamar al código que no sea el código cuando se completa un subproceso, usted usa delegados.
Primero declare a un delegado con la firma del método que desea ejecutar en el nuevo hilo ...
public delegate bool CheckPrimeDelegate(long n);
Luego, en su código, cree una instancia del delegado, llámelo utilizando BeginInvoke (pasando los parámetros que necesite) y PASE un delegado de la función de devolución de llamada (OnChkPrimeDone)
class MyClassApp
{
static void Main()
{
CheckPrimeDelegate ckPrimDel = new CheckPrimeDelegate(Prime.Check);
// Initiate the operation
ckPrimDel.BeginInvoke(4501232117, new AsyncCallback(OnChkPrimeDone), null);
// go do something else . . . .
}
static void OnChkPrimeDone( IAsyncResult iAr)
{
AsyncResult ar = iAr as AsynchResult;
CheckPrimeDelegate ckPrimDel = ar.AsyncDelegate as CheckPrimeDelegate;
bool isPrime = ckPrimDel.EndInvoke(ar);
Console.WriteLine(" Number is " + (isPrime? "prime ": "not prime");
}
}
Cuando termine, llamará a la función de devolución de llamada (OnChkPrimeDone)
Si necesita ejecutar explícitamente esta función de devolución de llamada en el hilo que se utilizó para crear el objeto COM Active-X, entonces verifique la variable contenedora de .Net Managed Code que contiene una referencia a este objeto ... Si tiene un método llamado InvokeRequirido (), entonces, en su función de devolución de llamada, pruebe el valor de retorno booleano de este método.
Si tiene el método InvokeRequired () y devuelve verdadero, entonces el objeto X activo también expondrá un método "BeginInvoke ()". Luego, crea OTRO delegado, rellenado con la misma función, y llama a BeginInvoke en el objeto Active-X, pasándolo a este nuevo delegado ... Luego se ejecutará en el mismo subproceso que se usó para crear el objeto Active-X
If (MyActiveXObject.InvokeRequired())
MyActiveXObject.BeginInvoke(...);
Puede invocar una función en un subproceso específico utilizando un objeto System.Windows.Threading.Dispatcher
(del ensamblado WindowsBase).
Por ejemplo:
public class ClassCreatedBySomeThread
{
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
public void SafelyCallMeFromAnyThread(Action a)
{
dispatcher.Invoke(a);
}
}