2011-01-13 28 views
25

Sto creando un'applicazione di test automatica in esecuzione. In questa parte dell'applicazione, sto lavorando su un server di polling. Funziona eseguendo costantemente il polling del server Web per determinare quando deve essere eseguito un nuovo test automatico (per le sessioni automatizzate notturne della nostra applicazione GUI).Come posso impostare un thread di background worker impostato su Single Thread Apartment?

Quando il server di polling rileva una richiesta, scarica tutte le informazioni necessarie e quindi esegue l'esecuzione di test in un operatore in background. Il problema è che parte dell'esecuzione di test ha OLE, COM e altre chiamate (ad esempio, Clipboard.Clear()) che si verificano nel thread di lavoro in background. Quando si verifica una di queste chiamate, si verifica la seguente eccezione:

Il thread corrente deve essere impostato sulla modalità apartment a thread singolo (STA) prima che sia possibile effettuare le chiamate OLE. Assicurati che la tua funzione principale abbia STAThreadAttribute segnato su di essa.

Come posso contrassegnare un thread di background worker come apartment a thread singolo? La chiamata principale nel mio Program.cs ovviamente ha già quell'attributo.

+0

' Clipboard.Clear() 'non è COM E 'nativo API di Windows – Aliostad

+7

Clipboard utilizza COM per negoziare.. dati e formati degli appunti –

risposta

35

Questo non è possibile, BGW utilizza un thread del thread. I thread TP sono sempre MTA, non possono essere modificati. Dovrai utilizzare una discussione regolare, chiamare SetApartmentState() prima di avviarla. Anche questo thread dovrebbe pompare un ciclo di messaggi, chiamare Application.Run().

Forse dovresti considerare di chiamare questo codice dal thread dell'interfaccia utente. Perché con ogni probabilità, il server COM esegue comunque i suoi metodi sul thread dell'interfaccia utente. Il marshalling delle chiamate da un thread di lavoro al thread STA che ha creato il server COM è automatico, COM se ne prende cura.

Oppure prendere il toro per le corna e maresciallo. È possibile creare il proprio thread STA per dare al server una casa felice. Troverai il codice in this post, assicurati di creare l'oggetto COM nella sovrascrittura Initialize().

+1

+1 - Usa il thread UI –

+0

Suppongo che abbia senso, dal momento che non riesco a vedere un uso per interagire con l'applicazione mentre test è in esecuzione – KallDrexx

1

Normalmente si imposta definendo attributo [STAThread()] sul punto di ingresso (ad esempio Static Main).

+1

Questo funziona solo per il metodo Main, non per le attività in background –

8

BackgroundWorker utilizza per impostazione predefinita un thread ThreadPool, ma è possibile sovrascrivere questo comportamento. In primo luogo è necessario definire una consuetudine 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); 
    } 
} 

e sovrascrivere lo SynchronizationContext predefinito, in questo modo, prima di utilizzare il BackgroundWorker:

AsyncOperationManager.SynchronizationContext = new MySynchronizationContext(); 

NOTA: questo può avere effetti prestazioni sul resto della tua applicazione, quindi potresti voler limitare la nuova implementazione Post (ad esempio utilizzando lo stato o d parametri).

+0

Non funziona per me Il metodo DoWork viene ancora chiamato da un thread MTA. – Maxence

+0

grazie! Questo ha funzionato per me.> Thread t = new Thread (new T hreadStart (() => {Clipboard.Clear(); })); T.SetApartmentState (ApartmentState.STA); t.Start(); – jaysonragasa

+0

Questo ha funzionato anche per me +1 – Ragnar

3

Non l'ho provato, ma se invochi il modulo WinForms, dovresti tornare al thread dell'interfaccia utente e la maggior parte delle cose dovrebbe funzionare di nuovo.

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

Questo funziona per me! Grazie! Ma cosa significa "invocare la winform" in realtà? C'è qualche problema legato alle prestazioni nell'usare questo? –

-1

Ho usato l'idea di Conrad de Wet e ha funzionato benissimo!

C'è un piccolo problema con quel codice, però, devi chiudere il "this.Invoke ....."Come con un});

Ecco il codice di Conrad de Wet con questa correzione:

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

Questo downvote senza lasciare un commento è semplicemente stupido. Quindi immagino che cosa intendesse dire che la tua risposta è cattiva perché corregge solo alcuni errori di sintassi. Quello che avresti dovuto fare invece era commentare la risposta di Conrad de Wet o modificare la sua risposta. –