web-dev-qa-db-fra.com

Comment puis-je faire un thread de travailleur de fond réglé sur un appartement à fil unique?

Je crée une application de test de test automatisée. Dans cette partie de l'application, je travaille sur un serveur de sondage. Il fonctionne en interrogant en permanence le serveur Web pour déterminer lorsqu'un nouveau test automatisé doit être exécuté (pour les courses automatisées nocturales de notre application GUI).

Lorsque le serveur de sondage voit une demande, il télécharge toutes les informations nécessaires, puis exécute l'exécution du test dans un ouvrier d'arrière-plan. Le problème est que la partie de l'exécution du test a OLE, COM et d'autres appels (par exemple, Clipboard.Clear()) qui se produisent dans le fil de travail de l'arrière-plan. Lorsque l'un de ces appels se produit, l'exception suivante se produit:

Le fil de courant doit être réglé sur le mode d'appartement à filetage unique (STA) avant OLE appels peut être effectué. Assurez-vous que votre fonction principale a la starradeadattribute marquée dessus.

Comment puis-je marquer un fil de travail d'arrière-plan comme un appartement à fil unique? L'appel principal de mon programme.cs a évidemment déjà cet attribut.

26
KallDrexx

Ce n'est pas possible, BGW utilise un fil threadpool. Les threads TP sont toujours MTA, il ne peut pas être changé. Vous devrez utiliser un thread régulier, un sessatation d'appel () avant de commencer. Ce fil doit également pomper une boucle de message, appelez Application.Run ().

Peut-être que vous devriez envisager d'appeler ce code à partir du fil de l'interface utilisateur. Parce que dans toutes les vraisemblances, le serveur COM exécute ses méthodes sur le fil d'interface utilisateur. Le maréchalation appelle à partir d'un fil de travail au fil de STA qui a créé le serveur COM est automatique, com prend en charge.

Ou prenez le taureau par les cornes et le maréchal. Vous pouvez créer votre propre thread STA pour donner au serveur une maison heureuse. Vous trouverez du code dans ce message , assurez-vous de créer l'objet COM dans votre substitution d'initialisation ().

36
Hans Passant

Backgroundworker utilise par défaut un thread threadpool, mais vous pouvez remplacer ce comportement. Vous devez d'abord définir une personnalisation synchronisationContext :

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 synchronisation par défautContext, comme ceci, avant d'utiliser votre travailleur d'arrière-plan:

   AsyncOperationManager.SynchronizationContext = new MySynchronizationContext();

Remarque: cela peut avoir des effets de performance sur le reste de votre application, vous pouvez donc vouloir restreindre la nouvelle mise en œuvre de la post (par exemple à l'aide de l'état ou D Paramètres).

8
Simon Mourier

Je ne l'ai pas testé, mais si vous invoquez le formulaire WinForms, vous devez être de retour au fil de l'interface utilisateur et la plupart des choses 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
    });
}
4
Conrad de Wet

Vous l'avez normalement défini en définissant l'attribut d'[STAThread()] sur le point d'entrée (E.G. Main statique).

1
Aliostad

J'ai utilisé l'idée de + Conrad de Wet et ça a fonctionné super!

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

Voici le code de Conrad de Wet avec ce correctif:

    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
moont10