Entwickler-Ecke

WinForms - OpenFileDialog in einem neuen Thread öffnen


xToast - Fr 08.10.10 13:43
Titel: OpenFileDialog in einem neuen Thread öffnen
Ich möchste einen openFileDialog in einem neuen Thread öffnen, allerdings bekomm ich dabei immer eine Fehlermeldung:
"Für den aktuellen Thread muss der STA-Modus (Single Thread Apartment) festgelegt werden, bevor OLE-Aufrufe ausgeführt werden können. Stellen Sie sicher, dass die Hauptfunktion mit STAThreadAttribute gekennzeichnet ist. Diese Ausnahme wird nur ausgelöst, wenn ein Debugger mit dem Prozess verbunden ist."


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
        //Thread starten
        private void mapToolStripMenuItem1_Click(object sender, EventArgs e) //beim Klick soll der Thread gestartet werden
        {
            Thread loadMapThread = new Thread(new ThreadStart(loadMap));
            loadMapThread.Start();

            //Thread schließen
            loadMapThread.Interrupt();
        }

        //Map öffnen
        private void loadMap()
        {
            //openFileDialog
            OpenFileDialog openMapDialog = new OpenFileDialog();

            //openFileDialog Filter setzen
            openMapDialog.AddExtension = true;
            openMapDialog.CheckFileExists = false;
            openMapDialog.CheckPathExists = false;
            openMapDialog.Filter = "Maps (*.png)|*.png; *.PNG";
            openMapDialog.Multiselect = false;
            openMapDialog.ValidateNames = false;
            openMapDialog.Title = "Map öffnen";

            if (openMapDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    //...

                    //Thread unterbrechen
                    Thread.Sleep(1000);
                }
                catch
                {
                    //...

                    //Thread unterbrechen
                    Thread.Sleep(1000);

                    return;
                }
            }
            else
            {
                //...

                //Thread unterbrechen
                Thread.Sleep(1000);

                return;
            }
        }


Das [STAThread] hab ich schon in meiner main Methode. Wo und was muss ich einfügen damit der OFD ohne Fehlermeldung geöffnet wird?


Th69 - Fr 08.10.10 13:57

Alle WinForms-Elemente (auch Dialoge) müssen im GUI-Mainthread ausgeführt werden. Du könntest aber mittels "Control.Invoke" (bzw. Form.Invoke) den Dialog vom Thread aus anzeigen lassen.

Bei deinem Beispielcode reicht es aber doch, wenn du erst den OpenFileDialog aufrufst und danach dann den Thread (mit dem ausgewählten Dateinamen als Parameter) startest!
Aber was das Thread.Sleep() dort jeweils soll verstehe ich nicht...


xToast - Fr 08.10.10 14:30

Zitat:
Bei deinem Beispielcode reicht es aber doch, wenn du erst den OpenFileDialog aufrufst und danach dann den Thread (mit dem ausgewählten Dateinamen als Parameter) startest!

Nein, das reicht in meinem fall nicht. Ich hab nämlich ein Control, was sich nach dem öffnen des Dialogs nicht wieder weiterrendert (XNA), dann dacht ich mir, wieso den Dialog nich in nem neuen Thread starten? Naja, danke für deinen Tipp, ich schaus mir mal an.

Zitat:
Aber was das Thread.Sleep() dort jeweils soll verstehe ich nicht...

Ich muss den Thread ja erstamal in einen bestimmten Zustand (hab grad den Namen vergessen) versetzen, damit ich ihn dann mit Interrupt() "löschen" kann.


Kha - Fr 08.10.10 15:12

user profile iconxToast hat folgendes geschrieben Zum zitierten Posting springen:
Nein, das reicht in meinem fall nicht. Ich hab nämlich ein Control, was sich nach dem öffnen des Dialogs nicht wieder weiterrendert (XNA), dann dacht ich mir, wieso den Dialog nich in nem neuen Thread starten?
Ich weiß nicht, ob irgendwo noch versteckte Fallgruben lauern, aber in diesem Fall könnte ein neuer Thread wirklich sinnvoll sein. Schau mal, ob du in der Thread-Klasse etwas zu STA findest ;) .

user profile iconxToast hat folgendes geschrieben Zum zitierten Posting springen:
Zitat:
Aber was das Thread.Sleep() dort jeweils soll verstehe ich nicht...

Ich muss den Thread ja erstamal in einen bestimmten Zustand (hab grad den Namen vergessen) versetzen, damit ich ihn dann mit Interrupt() "löschen" kann.
Huch? Threads werden am Ende der Methode automatisch entfernt, was genau hast du mit dem Interrupt vor?


xToast - Fr 08.10.10 17:31

Zitat:
Huch? Threads werden am Ende der Methode automatisch entfernt, was genau hast du mit dem Interrupt vor?

Achso, das wusste ich nicht, werd ich dann macl schnell ausbessern :D

Zitat:
Ich weiß nicht, ob irgendwo noch versteckte Fallgruben lauern, aber in diesem Fall könnte ein neuer Thread wirklich sinnvoll sein. Schau mal, ob du in der Thread-Klasse etwas zu STA findest ;) .

In der MSDN hab ich leider nichts weiterhelfendes gefunden, deshalbt frag ich hier nach^^


Kha - Fr 08.10.10 17:46

Dann schau ich eben nach... :P

C#-Quelltext
1:
Thread.SetApartmentState                    


xToast - Fr 08.10.10 19:16

Danke, das klappt jetzt soweit :D
Aber kann es sein, das man aus einem Thread nicht auf die Variablen in einem anderen Tread zugreifen kann?


Kha - Fr 08.10.10 19:34

Generell schon, aber damit wären wir beim großen Thema Thread-Synchronisierung, das den Rahmen dieses Threads sprengen dürfte ;) . Am einfachsten machst du es dir, wenn du dem Thread am Anfang schon alle Informationen übergeben kannst, auf die er keinen Schreibzugriff braucht.


xToast - Fr 08.10.10 20:37

Huh, naja, ich habe genau 2 Variablen die von diesem Thread geändert werden müssen.
1 bool-Varible
und 1 String.

Kennst du vielleicht einen Link, wo das synchronisieren vonn 2 Variablen erläutert wird?
Ich höhre den Begriff im Rahmen der Programmierung zum ersten mal und weiß nicht, wonach ich genau google muss^^


Yogu - Fr 08.10.10 22:52

Am einfachsten ist es, eine Klasse zu erstellen, die genau diese Objekte beinhaltet. Auf Instanzen dieser Klasse kann aus allen Threads lesend und schreibend zugegriffen werden.

Edit: Macht natürlich keinen Unterschied, ob auf Felder in der Formularklasse oder einer anderen Instanz zugegriffen wird. Ich hab zu schlampig gelesen :oops:


Kha - Fr 08.10.10 23:40

user profile iconYogu hat folgendes geschrieben Zum zitierten Posting springen:
Auf Instanzen dieser Klasse kann aus allen Threads lesend und schreibend zugegriffen werden.
Solltest du auf Race Conditions aus sein, die schaffst du auch mit Klassenfeldern der Form-Klasse ;P .

Für euch beide vielleicht mal das komplette Programm :) : http://www.yoda.arachsys.com/csharp/threads/


Yogu - Sa 09.10.10 00:44

Ok, je nach Implementierung kann das zu Problem führen. Die Monitor [http://msdn.microsoft.com/de-de/library/system.threading.monitor.aspx]-Klasse kannte ich vorher noch nicht; sie sieht ganz nett aus. Danke! :)

user profile iconxToast hat folgendes geschrieben Zum zitierten Posting springen:
Huh, naja, ich habe genau 2 Variablen die von diesem Thread geändert werden müssen.
1 bool-Varible
und 1 String.

Also der Wert, der angibt, ob das Dialogfenster mit OK bestätigt wurde, und der Dateiname?

Dann würde ich nicht in Felder schreiben, sondern Methoden per Invoke aufrufen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
if (openMapDialog.ShowDialog == DialogResult.OK) {
  loadMap(openMapDialog.FileName);
  Invoke(new Action(showMap));
}

void loadMap(string fileName) {
  /* ... */
}

void showMap() {
  /* ... */
}

Invoke stellt sicher, dass nur aus dem Hauptthread auf Formularelemente zugegriffen wird.