Comment puis-je faire un fond worker thread réglé à un seul Thread Appartement?

je crée une application de test automatique. Dans cette partie de l'application, je travaille sur un serveur de sondage. Il fonctionne en sondant constamment le serveur web pour déterminer quand un nouveau test automatisé devrait être lancé (pour les passages automatisés de nuit de notre application GUI).

lorsque le serveur de sondage voit une requête, il télécharge toutes les informations nécessaires puis exécute le test dans un worker background. Le problème est qu'une partie de l'essai a OLE, COM, et les autres appels (par exemple, Clipboard.Clear()) qui se produisent dans le fil de travail de fond. Lorsque l'un de ces appels se produit, l'exception suivante se produit:

le thread courant doit être configuré en mode "single thread apartment" (STA) avant que les appels OLE puissent être faits. Assurez-vous que votre fonction principale a STAThreadAttribute marqué dessus.

Comment puis-je marquer un arrière-plan thread thread unique appartement? Le Principal appel dans mon Programme.cs évidemment le possède déjà attribut.

25
demandé sur abatishchev 2011-01-14 00:04:10

5 réponses

ce n'est pas possible, BGW utilise un ThreadPool thread. TP threads sont toujours MTA, il ne peut pas être changé. Vous devrez utiliser un Thread régulier, appeler SetApartmentState() avant de le démarrer. Ce thread devrait également pomper une boucle de message, application d'appel.Exécuter.)(

peut-être devriez-vous envisager d'appeler ce code à partir du thread de L'interface utilisateur. Parce que selon toute probabilité, le serveur COM exécute ses méthodes sur le thread UI de toute façon. Groupage des appels d'un fil ouvrier vers le fil STA qui a créé le serveur COM est automatique, COM s'en occupe.

ce post, assurez-vous de créer L'objet COM dans votre commande Initialize ().

35
répondu Hans Passant 2017-05-23 10:29:27

BackgroundWorker utilise par défaut un ThreadPool thread, mais vous pouvez outrepasser ce comportement. Tout d'abord, vous devez définir une coutume 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);
    }
}

Et remplacer la valeur par défaut SynchronizationContext, comme cela, avant de vous utiliser votre BackgroundWorker:

   AsyncOperationManager.SynchronizationContext = new MySynchronizationContext();

NOTE: cela peut avoir des effets de performance sur le reste de votre application, donc vous pourriez vouloir restreindre la nouvelle implémentation Post (par exemple en utilisant le état ou d paramètres).

8
répondu Simon Mourier 2013-02-14 09:43:00

Je ne l'ai pas testé, mais si vous invoquez le formulaire WinForms, vous devriez revenir au fil UI et la plupart des trucs devraient fonctionner à nouveau.

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
    });
}
3
répondu Conrad de Wet 2014-11-11 04:15:19

vous le définissez normalement en définissant attributre [STAThread()] sur le point d'entrée (par exemple, la ligne principale statique).

1
répondu Aliostad 2011-01-13 21:09:50

j'ai utilisé L'idée de + Conrad de Wet et ça a bien fonctionné!

Il y a un petit problème avec ce code, vous devez fermer le "cette.Invoquer....."comme avec un });

voici le code de Conrad de Wet avec cette correction:

    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
        });
    }
-1
répondu moont10 2014-07-08 17:30:29