Entwickler-Ecke

Basistechnologien - Clipboard.GetText() ist schon was mysteriöses?


BlackMatrix - Do 22.07.10 23:00
Titel: Clipboard.GetText() ist schon was mysteriöses?
Hi.


C#-Quelltext
1:
2:
        [STAThread]
        static void Main()


Ich rufe in der Main eine Methode einer Klasse auf und diese Methode ruft wiederum im Laufe der Abarbeitung wie eine private Methode auf, die folgendes ausführt:

C#-Quelltext
1:
            string str = System.Windows.Forms.Clipboard.GetText();                    


Funktioniert wunderbar. Der Inhalt des Zwischenablage wird in den string geschrieben.

Rufe ich nun jedoch die Methode der Klasse in der Main nicht direkt auf, sondern lasse einen Threadpool das tun, dann wird der Inhalt der Zwischanablage nicht in den string kopiert.

C#-Quelltext
1:
                ThreadPool.QueueUserWorkItem(klasse.aufruf, streamreader.ReadLine());                    


Wo liegt das Problem?


Ralf Jansen - Do 22.07.10 23:22

Laut Doku [http://msdn.microsoft.com/de-de/library/system.windows.forms.clipboard.aspxx] funktioniert die Clipboard Klasse nur im single Thread Apartment. Die Threads des Threadpools laufen aber aber im multithreaded Apartment. Siehe dazu auch den Punkt 'Gründe, die gegen die Verwendung von Threadpoolthreads sprechen [http://msdn.microsoft.com/de-de/library/0ka9477y.aspx]' in der MSDN. Musst du wohl ganz klassisch selbst einen Thread abspalten und das Apartment passend setzen. Threadpool is nich.


BlackMatrix - Do 22.07.10 23:38

Hmm, kann es sein, dass dies auch nicht das Problem ist?

Ich habe nun den Threadpool entfernt und folgendes dafür geschrieben:

C#-Quelltext
1:
2:
                Thread thread = new Thread(new ParameterizedThreadStart(klasse.aufruf));
Thread.Start(sr.ReadLine());


Trotzdem übernimmt er es nicht in den string, obwohl der Inhalt in der Zwischenablage ist.

Vielleicht kann man auch mein Problem grundauf abändern/verbessern, sodass dann das Problem gar nicht mehr entsteht? Besserer Code ist mir immer lieber.

Ich möchte gerne, dass die Abarbeitung parallel abläuft, die einzelnen Abarbeitungen immer das selbe tun, nur mit unterschiedlichen Parametern.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
Klasse klasse = new Klasse();
            StreamReader sr = new StreamReader(pfad);
            while (sr.EndOfStream == false)
            {
                Thread thread = new Thread(new ParameterizedThreadStart(klasse.aufruf));
                thread.Start(sr.ReadLine());
            }


Wie ist da der Code zu bewerten?


Ralf Jansen - Fr 23.07.10 00:00

Du hast vermutlich den Punkt mit dem richtigen setzen des Apartments [http://msdn.microsoft.com/en-us/library/system.threading.thread.setapartmentstate.aspx] überlesen. Auch einen normaler Thread ist ohne eingreifen erst mal ein MTA Thread. Du musst das Apartment schon auf STA setzen.


BlackMatrix - Fr 23.07.10 02:46

Unglaublich, das geht sogar :) Vielen Dank.


BlackMatrix - Do 29.07.10 16:29

Ich muss nochmal nachhaken.

Ich starte einen Timer. Wie erreiche ich, dass der Timer auch im STA Mode läuft?


C#-Quelltext
1:
2:
3:
            Timer timer = new Timer(wartezeit);
            timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            timer.Enabled = true;


Ralf Jansen - Do 29.07.10 16:50

Du benutzt den System.Timers.Timer? Dann versuch mal vom Timer die SynchronizingObject Property zu setzen. Wenn du den Timer auf einer Form des Hauptthreads verwendest sollt ein

C#-Quelltext
1:
timer.SynchronizingObject = this;                    

reichen. Alternativ auf den Timer im Winforms Namespace umsteigen der kommt ohne Thread aus.


BlackMatrix - Do 29.07.10 17:31

Funktioniert der Windows Forms Timer auch bei einem Windows Dienst? Denn irgendwie startet er nicht.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
             System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
            timer.Interval = 1000;
            timer.Tick += new EventHandler(OnTimedEvent);
            timer.Enabled = true;
            timer.Start();
        }

        void OnTimedEvent(object source, EventArgs e)
        {
[...]
}


und wenn ich den System.Timers.Timer verwende mit timer.SynchronizingObject = this; kommt folgendes mit dem ich nichts anfangen kann:

Zitat:
Klasse kann nicht in System.ComponentModel.ISynchronizeInvoke konvertiert werden kann.


Ralf Jansen - Do 29.07.10 17:54

Ah. Ein Windows Dienst. Wichtiger Hinweis. Gut das du das gleich erwähnt hast :roll: Da werden beide Ratschläge nicht helfen. ISynchronizeInvoke muss schon von den entsprechenden Klassen implementiert sein, wie etwa bei Winforms Controls damit das mit dem SynchronizingObject funktioniert und der andere Timer braucht eh eine MessagePump die ein Windows Dienst üblicherweise nicht hat.
Vielleicht hilft die das vorgehen aus diesem Thread [http://stackoverflow.com/questions/2001667/net-windows-service-needs-to-use-stathread].

Rückfrage - Ich habe Vorstellungsprobleme warum man vom einem Dienst aus auf das Clipboard zuzugreifen muß. Das Clipboard gehört doch zur Usersession und auf den Desktop eines Users zuzugreifen ist eher ungewöhnlich für einen Dienst. Maximal kommt man eh nur ans Clipboard des interaktiven Users.


BlackMatrix - Do 29.07.10 18:05

Tut mir leid, dass ich das nicht hinzugeschrieben habe.

Mein Dienst soll auf Clipboard zugreifen könnnen, weil mir ein externes Programm (FineReader) die Ausgabe nur über sehr, sehr viele Umwege in eine Textdatei bringt. Die (fast) einzigste Möglichkeit ist es den String aus der Zwischenablage zu lesen und das funktioniert auch soweit sehr gut, nur eben nicht in Verbindung mit einem Timer.

Also kann ich sozusagen gleich wieder zu meiner anfänglichen Lösung zurückkehren. Einfach für jedes Objekt einen Thread starten und den STA setzen?


Ralf Jansen - Do 29.07.10 20:10

Vermutlich ja.

Du könntest mal die Clipboard Implementierung aus WPF(System.Windows.Clipboard) anstatt die aus Winforms ausprobieren. Zumindest die Hilfe sagt nicht aus über das Apartment Verhalten. Ist aber wahrscheinlich das die das gleiche Problem haben wird da die Ursache eigentlich unterhalb der Winforms bzw. WPF Schicht liegt.

Im VisualBasic.MyService Namespace gibts auch noch ein ClipboardProxy Implementierung. Wird aber vermutlich ebenfalls von dem Problem betroffen sein.


BlackMatrix - Do 29.07.10 21:52

Habe das WPF Cliboard probiert, jedoch funktioniert das auch nicht.

Irgendwo habe ich gerade gelesen, dass bei einem Windows Dienst der [STAThread] z.B. vor der Main keine Auswirkung hätte, jedoch stimmt das nicht, in der Main funktioniert das Clipboard wunderbar.
Nur dann beim Timer eben nicht mehr.

Ich starte jetzt bei den Timern jeweils in der ElapsedTimeEventMethode einen neuen STAThread. Finde ich ehrlich gesagt sehr unschön, aber wie es so schein, tut es erstmal funktionieren. Ich danke dir.