Estoy creando un componente no visual en .Net 2.0. Este componente utiliza un socket asíncrono (BeginReceive, EndReceive, etc.). Las devoluciones de llamada asincrónicas se llaman en el contexto de un subproceso de trabajo creado por el tiempo de ejecución. El usuario componente no debería tener que preocuparse por el multihilo (este es el objetivo principal, lo que yo quiero)Evento de incendio del componente Async en el subproceso UI
El usuario componente puede crear mi componente no visual en cualquier hilo (el hilo de la interfaz de usuario es un hilo común para simple aplicaciones. Las aplicaciones más serias podrían crear el componente dentro de un hilo de trabajo arbitrario). El componente desencadena eventos como "SessionConnected" o "DataAvailable".
El problema: debido a las rellamadas asíncronas y los eventos que se generan, el controlador de eventos se ejecuta en el contexto del hilo de trabajo. Quiero utilizar una capa intermedia que fuerza el controlador de eventos para ejecutar en el contexto de la secuencia que creó el componente en primer lugar.
Código de ejemplo (despojado de gestión de excepciones, etc ...)
/// <summary>
/// Occurs when the connection is ended
/// </summary>
/// <param name="ar">The IAsyncResult to read the information from</param>
private void EndConnect(IAsyncResult ar)
{
// pass connection status with event
this.Socket.EndConnect(ar);
this.Stream = new NetworkStream(this.Socket);
// -- FIRE CONNECTED EVENT HERE --
// Setup Receive Callback
this.Receive();
}
/// <summary>
/// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect
/// </summary>
/// <param name="ar">The IAsyncResult that was used by BeginRead</param>
private void EndReceive(IAsyncResult ar)
{
int nBytes;
nBytes = this.Stream.EndRead(ar);
if (nBytes > 0)
{
// -- FIRE RECEIVED DATA EVENT HERE --
// Setup next Receive Callback
if (this.Connected)
this.Receive();
}
else
{
this.Disconnect();
}
}
Debido a la naturaleza de la asíncrono sockets todas las aplicaciones que usan mi componente están llenas de "Si (this.InvokeRequired) {... "y todo lo que quiero es que el usuario pueda usar mi componente sin preocupaciones como una especie de complemento.
Entonces, ¿cómo haré para plantear los eventos sin requerir que el usuario revise InvokeRequired (o, dicho de otro modo, cómo fuerzo los eventos planteados en el mismo hilo que el hilo que inició el evento en primer lugar)?
He leído cosas sobre AsyncOperation, BackgroundWorkers, SynchronizingObjects, AsyncCallbacks y toneladas de otras cosas, pero todo hace girar la cabeza.
Yo he venido para arriba con esto, sin duda torpe, "solución" pero parece fallar en algunas situaciones (cuando mi componente se llama desde un proyecto de Windows Forms a través de una clase estática, por ejemplo)
/// <summary>
/// Raises an event, ensuring BeginInvoke is called for controls that require invoke
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
/// <remarks>http://www.eggheadcafe.com/articles/20060727.asp</remarks>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
try
{
Control ed = eventDelegate.Target as Control;
if ((ed != null) && (ed.InvokeRequired))
ed.Invoke(eventDelegate, args);
else
eventDelegate.DynamicInvoke(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
Console.WriteLine(ex.Message);
//Swallow
}
}
}
Cualquier ayuda sería apreciada ¡Gracias por adelantado!
EDIT: De acuerdo con this thread mi mejor opción sería usar SyncrhonizationContext.Post pero no veo cómo aplicarlo a mi situación.
No realmente; Esto es complejo para las personas al usar mi componente. Todo lo que hace es hacer referencia al componente y usar código como: // Inicializar MyComponent con IP, puerto, etc. y luego: MyComponent.SendString ("This is cool"); MyComponent plantea eventos; ya sea que se manejen mediante un proyecto GUI o no GUI no debería importar y no quiero que el usuario sea responsable de verificar InvokeRequired. – ComponentBuilder