2011-01-13 40 views
25

Estoy creando una aplicación de prueba automatizada. En esta parte de la aplicación, estoy trabajando en un servidor de sondeo. Funciona al sondear constantemente el servidor web para determinar cuándo debe ejecutarse una nueva prueba automatizada (para ejecuciones automáticas nocturnas de nuestra aplicación GUI).¿Cómo puedo configurar un subproceso de trabajador de fondo en Single Thread Apartment?

Cuando el servidor de sondeo ve una solicitud, descarga toda la información necesaria y luego ejecuta la ejecución de prueba en un trabajador de segundo plano. El problema es que parte de la ejecución de prueba tiene OLE, COM y otras llamadas (por ejemplo, Clipboard.Clear()) que se producen en el subproceso de trabajo en segundo plano. Cuando se produce una de estas llamadas, se produce la siguiente excepción:

El hilo actual debe establecerse en el modo de apartamento de una sola rosca (STA) antes de poder realizar llamadas OLE. Asegúrese de que su función principal tenga marcado STAThreadAttribute.

¿Cómo puedo marcar un hilo de trabajador de fondo como un único apartamento de hilo? La llamada principal en mi Program.cs obviamente ya tiene ese atributo.

+0

' Clipboard.Clear() 'no es COM Es API nativa de Windows – Aliostad

+7

portapapeles utiliza COM para negociar.. datos y formatos del portapapeles –

Respuesta

35

Esto no es posible, BGW utiliza un subproceso de subprocesos. Los hilos TP siempre son MTA, no se pueden cambiar. Deberá utilizar un hilo común, llamar a SetApartmentState() antes de iniciarlo. Este hilo también debería bombear un bucle de mensaje, llame a Application.Run().

Tal vez deba considerar llamar a este código desde el hilo de la interfaz de usuario. Porque con toda probabilidad, el servidor COM está ejecutando sus métodos en el subproceso de IU de todos modos. Las llamadas de Marshaling desde un subproceso de trabajo al subproceso STA que creó el servidor COM son automáticas, COM se encarga de ello.

O tome el toro por los cuernos y mariscal usted mismo. Puede crear su propio hilo STA para darle al servidor un hogar feliz. Encontrará el código en this post, asegúrese de crear el objeto COM en su inicialización().

+1

+1 - Use el subproceso UI –

+0

Supongo que tiene sentido, ya que realmente no puedo ver un uso para interactuar con la aplicación mientras la prueba se está ejecutando – KallDrexx

1

Lo configura normalmente definiendo attributre [STAThread()] en el punto de entrada (por ejemplo, Static Main).

+1

Esto funciona solo para el método principal, no para tareas en segundo plano. –

8

BackgroundWorker utiliza por defecto un hilo ThreadPool, pero puede anular este comportamiento. En primer lugar es necesario definir una costumbre SynchronizationContext:

public class MySynchronizationContext : SynchronizationContext 
{ 
    public override void Post(SendOrPostCallback d, object state) 
    { 
     Thread t = new Thread(d.Invoke); 
     t.SetApartmentState(ApartmentState.STA); 
     t.Start(state); 
    } 
} 

y anular la SynchronizationContext defecto, de esta manera, antes de utilizar el BackgroundWorker:

AsyncOperationManager.SynchronizationContext = new MySynchronizationContext(); 

NOTA: esto puede tener efectos en el rendimiento en el resto de su aplicación, por lo que es posible que desee restringir la implementación del nuevo Post (por ejemplo, usando los estados o d).

+0

No funciona para mí. El método DoWork todavía se invoca a partir de un hilo MTA. – Maxence

+0

¡Gracias! Esto funcionó para mí.> Subproceso t = subproceso nuevo (nuevo T) hreadStart (() => {Clipboard.Clear(); })); t.SetApartmentState (ApartmentState.STA); t.Start(); – jaysonragasa

+0

Esto funcionó para mí también +1 – Ragnar

3

No lo he probado, pero si invoca el formulario de WinForms, debe volver al hilo de la interfaz de usuario y la mayoría de las cosas deberían funcionar de nuevo.

BackgroundWorker bgw = new BackgroundWorker(); 
bgw.DoWork += new DoWorkEventHandler(this.bgw_DoWork); 
bgw.RunWorkerAsync(); 

private void bgw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    // Invoke the UI thread 
    // "this" is referring to the Form1, or what ever your form is 
    this.Invoke((MethodInvoker)delegate 
    { 
     Clipboard.GetText(); 
     // etc etc 
    }); 
} 
+0

¡Esto funciona para mí! ¡Gracias! Pero, ¿qué significa "invocar el winform" en realidad? ¿Hay algún problema relacionado con el rendimiento en el uso de esto? –

-1

Utilicé la idea + Conrad de Wet y funcionó de maravilla!

Aunque hay un pequeño problema con ese código, debe cerrar el "this.Invoke ....."Al igual que con un});

Aquí es el código de Conrad de Wet con esta revisión:

BackgroundWorker bgw = new BackgroundWorker(); 
    bgw.DoWork += new DoWorkEventHandler(this.bgw_DoWork); 
    bgw.RunWorkerAsync();> 

    private void bgw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     // Invoke the UI thread 
     // "this" is referring to the Form1, or what ever your form is 
     this.Invoke((MethodInvoker)delegate 
     { 
      Clipboard.GetText(); 
      // etc etc 
     }); 
    } 
+6

Este voto negativo sin dejar un comentario es simplemente estúpido. Así que supongo que lo que quería decir es que te diga que tu respuesta es mala porque solo corrige algunos errores de sintaxis. Lo que debería haber hecho en su lugar fue comentar la respuesta de Conrad de Wet o editar su respuesta. –

Cuestiones relacionadas