2011-01-13 11 views
25

Ich erstelle eine automatisierte Testlaufanwendung. In diesem Teil der Anwendung arbeite ich an einem Abfrageserver. Es funktioniert, indem der Web-Server ständig abgefragt wird, um zu bestimmen, wann ein neuer automatisierter Test ausgeführt werden sollte (für nächtliche automatisierte Läufe unserer GUI-Anwendung).Wie kann ich einen Hintergrund-Worker-Thread auf Single Thread Apartment setzen?

Wenn der Abrufserver eine Anforderung sieht, lädt er alle erforderlichen Informationen herunter und führt dann den Testlauf in einem Hintergrund-Worker aus. Das Problem besteht darin, dass ein Teil des Testlaufs OLE-, COM- und andere Aufrufe (z. B. Clipboard.Clear()) aufweist, die im Hintergrund-Worker-Thread auftreten. Wenn einer dieser Anrufe auftritt, tritt die folgende Ausnahme:

aktuellen Thread muss einzelnen Thread Apartment (STA) Modus eingestellt werden, bevor OLE Anrufe getätigt werden können. Stellen Sie sicher, dass die Main-Funktion STAThreadAttribute enthält.

Wie kann ich einen Hintergrundarbeiter Thread als Single-Thread-Wohnung markieren? Der Hauptaufruf in meinem Program.cs hat offensichtlich dieses Attribut bereits.

+0

' Clipboard.Clear() 'ist nicht COM Es ist native Windows-API – Aliostad

+7

Zwischenablage verwendet COM zu verhandeln.. Zwischenablage Daten und Formate –

Antwort

35

Dies ist nicht möglich, BGW verwendet einen Threadpool-Thread. TP-Threads sind immer MTA, sie können nicht geändert werden. Sie müssen einen regulären Thread verwenden und SetApartmentState() aufrufen, bevor Sie ihn starten. Dieser Thread sollte auch eine Nachrichtenschleife pumpen, Application.Run() aufrufen.

Vielleicht sollten Sie in Betracht ziehen, diesen Code aus dem UI-Thread aufzurufen. Da der COM-Server aller Wahrscheinlichkeit nach seine Methoden auf dem UI-Thread ausführt. Marshalling-Aufrufe von einem Worker-Thread an den STA-Thread, der den COM-Server erstellt hat, sind automatisch, COM kümmert sich darum.

Oder nehmen Sie den Stier bei den Hörnern und Marshal. Sie können Ihren eigenen STA-Thread erstellen, um dem Server ein glückliches Zuhause zu geben. Sie werden Code in this post finden, stellen Sie sicher, das COM-Objekt in Ihrer Initialize() überschreiben zu erstellen.

+1

+1 - Verwenden Sie das UI-Thread –

+0

Ich denke, das macht Sinn, da ich nicht wirklich eine Verwendung für die Interaktion mit der Anwendung während a sehen kann Test läuft – KallDrexx

1

Normalerweise setzen Sie es, indem Sie das Attribut [STAThread()] auf dem Einstiegspunkt definieren (z. B. Statisch Main).

+1

Dies funktioniert nur für die Main-Methode, nicht für Hintergrundaufgaben –

8

BackgroundWorker verwendet standardmäßig einen ThreadPool-Thread, aber Sie können dieses Verhalten überschreiben. Zuerst müssen Sie eine benutzerdefinierte SynchronizationContext definieren:

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); 
    } 
} 

Und die Standard SynchronizationContext außer Kraft setzen, wie diese, bevor Sie Ihre Background verwenden:

AsyncOperationManager.SynchronizationContext = new MySynchronizationContext(); 

HINWEIS: Dies kann von dem, Leistung Auswirkungen auf den Rest Ihrer B. die -Status oder d-Parameter verwenden möchten.

+0

Funktioniert nicht für mich Die DoWork Methode wird immer noch von einem MTA Thread aufgerufen – Maxence

+0

Danke, das funktionierte für mich> Thread t = new Thread (new T hreadStart (() => {Zwischenablage.Clear(); })); t.SetApartmentState (ApartmentState.STA); t.Start(); – jaysonragasa

+0

Das funktionierte auch für mich +1 – Ragnar

3

Ich habe es nicht getestet, aber wenn Sie das WinForms-Formular aufrufen, sollten Sie zurück zum UI-Thread sein und die meisten Sachen sollten wieder funktionieren.

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

Das funktioniert für mich! Vielen Dank! Aber was bedeutet "Winform nennen" eigentlich? Gibt es ein leistungsbezogenes Problem bei der Verwendung? –

-1

Ich habe + Conrad de Wet Idee verwendet und es hat super funktioniert!

Es gibt ein kleines Problem mit diesem Code, aber Sie müssen die "this.Invoke ....."Wie mit a});

Hier ist Conrad de Wet den Code mit diesem Update:

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

Dieser Downvote ohne einen Kommentar zu hinterlassen ist einfach dumm. Ich vermute, was er damit meinte, war, dir zu sagen, dass deine Antwort schlecht ist, weil sie nur einige Syntaxfehler korrigiert. Was Sie stattdessen hätten tun sollen, war entweder Conrad de Wet's Antwort zu kommentieren oder seine Antwort zu editieren. –

Verwandte Themen