# Schleife stürzt ab! Windows Forms



## CKS07 (14. Januar 2009)

Also ich habe ein problem mit einer Schleife, da diese einen großen Bereich durchläuft, stürzt die windows anwendung immer ab bzw. wenn man lange wartet und die schleife fertig ist, geht es wieder...gibt es da irgend eine lösung es besser hinzubekommen?

ich dachte so was wie "Application->ProcessMessages();" ...doch ich das geht bei mir nicht. Ich verwende MS Visual Studios!

Danke schon mal für die Hilfe!


----------



## vfl_freak (14. Januar 2009)

Moin,



> Also ich habe ein problem mit einer Schleife, da diese einen großen Bereich durchläuft, stürzt die windows anwendung immer ab bzw. wenn man lange wartet und die schleife fertig ist, geht es wieder...gibt es da irgend eine lösung es besser hinzubekommen?



Aha - und was soll dazu jetzt jemand sagen ? ? ? ? ? 

Nach dem einen Satz von Dir doch wohl nur bestenfalls "_dann mach' halt die Schleife kleiner_" ....... :suspekt:

Poste erstmal Deinen Code und ggf. die Fehlermeldung beim Absturz und dann Deine genau Frage dazu, sonst wird das wohl nix  

Gruß
Klaus


----------



## CKS07 (14. Januar 2009)

Okay also kleiner machen geht leider net...und es kommt keine richtige fehlermeldung...und zwar steht dann nur "Programm reagiert nicht mehr"....man kann während die schleife läuft nicht mal mehr auf ein button klicken!


----------



## jsendrow (14. Januar 2009)

Erstmal Begriffsdefinitionen:

Absturz = Programm stürzt ab, wird also ungeplant mit einem Fehler beendet. NAchdem ein Programm abgestürzt ist kann es de fakto nicht mehr "weiterlaufen"

Programmhänger = Das Programm hängt sich auf ohne abzustürzen und muss dann meisten von Aussen manuell "abgeschossen" werden.



So, das Problem ist simpel. Windows.Forms ist Singlethreaded (siehe MSDN). Wenn Du also in einer Funktion der Form eine langlaufende Schleife hast blokiert die natürlich solange den Thread der Form. Solange der Thread blockiert ist kann die form natürlich nichts anderes mehr machen.  

Solche Sachen darf man halt nicht im ausführenden Thread der Form machen sondenr dafür gibts das Konzept von BackgroundWorker.


----------



## CKS07 (14. Januar 2009)

Okay, jetzt müsste ich nur noch wissen wie ich das mache. Kenne mich Threads fast gar nicht aus...


----------



## MCoder (14. Januar 2009)

Hallo CKS07,

schaue dir einfach die Beschreibung zum BackgroundWorker in der MSDN an. Mit dabei ist ein recht ausführliches Beispiel, das du an deine Bedürfnisse anpassen kannst.

Gruß
MCoder


----------



## CKS07 (14. Januar 2009)

Okay danke erstmal, sie schon ganz schön kompliziert aus, aber werde mich da mal versuchen durch zu arbeiten


----------



## MCoder (14. Januar 2009)

Alles halb so wild 

Du erzeugst ein BackgroundWorker-Objekt und abonnierst die Events "DoWork" und "RunWorkerCompleted".
In die DoWork-Methode kommt die lange Schleife und die RunWorkerCompleted-Methode wird aufgerufen, wenn "DoWork" fertig ist.
Gestartet wird der Backgroundworker mit der Methode "RunWorkerAsync" des BackgroundWorker-Objekts.

Gruß
MCoder


----------



## CKS07 (15. Januar 2009)

okay das hat mich nen bissel verwirrt jetzt....habe gestern schon ein wenig im internet gesucht nach tutorials...aber bis jetzt habe ich noch net ganz hinbekommen...vielleicht kann mal jemand ganz kurz einen kompletten bsp. code reinstellen...das wäre nett...

Gruß


----------



## MCoder (15. Januar 2009)

Damit sollte das Prinzip klar sein:

```
MyForm()
{
    //...

    // Der BackgroundWorker wird im Kontruktor der Form initialisiert
    System::ComponentModel::BackgroundWorker^ Worker = new System.ComponentModel.BackgroundWorker();
    Worker->DoWork             += gcnew DoWorkEventHandler(this, &BackgroundWorker_DoWork );
    Worker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler(this, &BackgroundWorker_RunWorkerCompleted);
    Worker->->RunWorkerAsync();

    // ...
}

void BackgroundWorker_DoWork( Object^ sender, DoWorkEventArgs^ e )
{
    // lange Schleife
}

void BackgroundWorker_RunWorkerCompleted( Object^ sender, RunWorkerCompletedEventArgs^ e )
{
    // Backgroundworker ist fertig -> Ergebnisse der Schleife können verarbeitet werden
}
```
Gruß
MCoder


----------



## CKS07 (15. Januar 2009)

danke erstmal

bei mir kommen schon alleine wenn ich den BackgroundWorker Kontruktor der Form einfüge mehrere fehlermeldungen... 


```
Fehler	9	error C2061: Syntaxfehler: Bezeichner 'System'	c:\projekte\windows1\windows1\Form1.h	61
Fehler	10	error C2065: 'BackgroundWorker_DoWork': nichtdeklarierter Bezeichner	c:\projekte\windows1\windows1\Form1.h	62
Fehler	12	error C2065: 'BackgroundWorker_RunWorkerCompleted': nichtdeklarierter Bezeichner	c:\projekte\windows1\windows1\Form1.h	63
Fehler	11	error C3350: "System::ComponentModel::DoWorkEventHandler": Ein Delegatkonstruktor erwartet 2 Argument(e).	c:\projekte\windows1\windows1\Form1.h	62
Fehler	13	error C3350: "System::ComponentModel::RunWorkerCompletedEventHandler": Ein Delegatkonstruktor erwartet 2 Argument(e).	c:\projekte\windows1\windows1\Form1.h	63
```


----------



## MCoder (15. Januar 2009)

Sorry, ich's habs von C# übertragen, da gab's wohl ein paar Reibungsverluste 
Wenn du den Part im Konstruktor änderst, sollte es passen:

```
System::ComponentModel::BackgroundWorker^ Worker = gcnew System::ComponentModel::BackgroundWorker();
Worker->DoWork             += gcnew DoWorkEventHandler(this, &Form1::BackgroundWorker_DoWork );
Worker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler(this, &Form1::BackgroundWorker_RunWorkerCompleted);
Worker->RunWorkerAsync();
```
"Form1" musst du durch den Namen deiner Form ersetzen.

Gruß
MCoder


----------



## CKS07 (15. Januar 2009)

Danke erstmal, hab es gerade probiert...jetzt habe ich dann andere Fehlermeldungen


```
Fehler	9	error C2039: 'BackgroundWorker_DoWork': Ist kein Element von 'windows1::Form1'	c:\projekte\windows1\windows1\Form1.h	62
Fehler	12	error C2039: 'BackgroundWorker_RunWorkerCompleted': Ist kein Element von 'windows1::Form1'	c:\projekte\windows1\windows1\Form1.h	63
Fehler	10	error C2065: 'BackgroundWorker_DoWork': nichtdeklarierter Bezeichner	c:\projekte\windows1\windows1\Form1.h	62
Fehler	13	error C2065: 'BackgroundWorker_RunWorkerCompleted': nichtdeklarierter Bezeichner	c:\projekte\windows1\windows1\Form1.h	63
Fehler	11	error C3350: "System::ComponentModel::DoWorkEventHandler": Ein Delegatkonstruktor erwartet 2 Argument(e).	c:\projekte\windows1\windows1\Form1.h	62
Fehler	14	error C3350: "System::ComponentModel::RunWorkerCompletedEventHandler": Ein Delegatkonstruktor erwartet 2 Argument(e).	c:\projekte\windows1\windows1\Form1.h	63
```


----------



## MCoder (15. Januar 2009)

```
Ist kein Element von 'windows1::Form1'
```
Wie heißt deine Form? Hier ist entweder "windows1" oder "Form1" überflüssig.

Gruß
MCoder


----------



## CKS07 (15. Januar 2009)

Meine Firm heißt "Form1" und mein projekt heißt "windows2"..so


----------



## MCoder (15. Januar 2009)

Dann sollte der letzte Code von mir eigentlich ohne Änderung passen. Zeige doch mal deinen Konstruktor.

Gruß
MCoder


----------



## CKS07 (15. Januar 2009)

so hier


```
Form1(void)
		{
			InitializeComponent();
			System::ComponentModel::BackgroundWorker^ Worker = gcnew System::ComponentModel::BackgroundWorker();
			Worker->DoWork += gcnew DoWorkEventHandler(this, &Form1::BackgroundWorker_DoWork );
			Worker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler(this, &Form1::BackgroundWorker_RunWorkerCompleted);
			Worker->RunWorkerAsync();
		}
```


----------



## MCoder (15. Januar 2009)

Der Code schaut ok aus. Bei mir läuft diese kleine Beispiel fehlerfrei. Vergleiche mal mit deinem Code. Evt. fehlt nur das "using namespace System::ComponentModel;".

```
#pragma once

namespace WinFormsApp
{
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Windows::Forms;

    public ref class Form1 : public System::Windows::Forms::Form
    {
        public:
            Form1(void)
            {
                InitializeComponent();
                System::ComponentModel::BackgroundWorker^ Worker = gcnew System::ComponentModel::BackgroundWorker();
                Worker->DoWork             += gcnew DoWorkEventHandler(this, &Form1::BackgroundWorker_DoWork );
                Worker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler(this, &Form1::BackgroundWorker_RunWorkerCompleted);
                Worker->RunWorkerAsync();
            }

        protected:
            ~Form1()
            {
                if (components)
                {
                    delete components;
                }
            }

        private:
            System::ComponentModel::Container ^components;

        #pragma region Windows Form Designer generated code
            void InitializeComponent(void)
            {
                this->SuspendLayout();
                this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
                this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
                this->ClientSize = System::Drawing::Size(277, 193);
                this->Name = L"Form1";
                this->Text = L"Form1";
                this->ResumeLayout(false);
            }
        #pragma endregion

        private:
            void BackgroundWorker_DoWork( Object^ sender, DoWorkEventArgs^ e )
            {
                // lange Schleife
            }
 
            void BackgroundWorker_RunWorkerCompleted( Object^ sender, RunWorkerCompletedEventArgs^ e )
            {
                // Backgroundworker ist fertig -> Ergebnisse der Schleife können verarbeitet werden
            }
    };
}
```
Gruß
MCoder


----------



## CKS07 (16. Januar 2009)

Okay danke, also funktioniert erstmal...hatte was vergessen zu kopieren...aber jetzt geht alles...(erstmal)...werde jetzt mal versuchen meine schleife dort unterzubringen...ist es irgendwie möglich dann auch einen stop button für die schleife einzubauen?

Gruß


----------



## CKS07 (16. Januar 2009)

Achso und wie bekomme ich es hin das BackgroundWorker_DoWork erst bei einem Button event aufgerufen wird?

Achso und die Fehlermeldung hier taucht auf:

Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textBox1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.

Gruß


----------



## MCoder (16. Januar 2009)

CKS07 hat gesagt.:


> Achso und wie bekomme ich es hin das BackgroundWorker_DoWork erst bei einem Button event aufgerufen wird?


Verschiebe den Code vom Konstruktur in die Methode vom Button Event.

Gruß
MCoder


----------



## CKS07 (16. Januar 2009)

Okay das hab ich schon hinbekommen, aber jetzt noch zu meiner fehlermeldung und zwar:


```
Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textBox1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
```

Gruß


----------



## MCoder (16. Januar 2009)

Hallo,

ja, innerhalb der der Schleife (in BackgroundWorker_DoWork) kannst du nicht ohne weiteres auf Controls zugreifen. Wann willst du denn die Textbox aktualisieren? 
Wenn sie nur am Ende aktualisiert werden soll, schreibe das Ergebnis in eine Member-Variable und erledige in "BackgroundWorker_RunWorkerCompleted" die Zuweisung an die Textbox.
Falls es zwischendrin passieren soll, musst du zusätzlich noch den ProgressChanged-Event verwenden.

Gruß
MCoder


----------



## CKS07 (16. Januar 2009)

Also es muss zwischen durch passieren...is so ne art log-funktion...ähm könntest du mir kurz beispiel geben?

Danke!


----------



## MCoder (16. Januar 2009)

Ich habe meinen zuletzt geposteten Code etwas aufgebohrt:

```
#pragma once

namespace WinFormsApp
{
	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Windows::Forms;

	public ref class Form1 : public System::Windows::Forms::Form
	{
	public:
		Form1(void)
		{
	    	InitializeComponent();
	    	
            Worker = gcnew System::ComponentModel::BackgroundWorker();
            Worker->DoWork             += gcnew DoWorkEventHandler(this, &Form1::BackgroundWorker_DoWork );
            Worker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler(this, &Form1::BackgroundWorker_RunWorkerCompleted);
            Worker->ProgressChanged    += gcnew ProgressChangedEventHandler( this, &Form1::BackgroundWorker_ProgressChanged );
            Worker->WorkerReportsProgress = true;
		}

	protected:
		~Form1()
		{
            if (components)
			{
				delete components;
			}
		}
    private: System::Windows::Forms::Button^  button1;
    private: System::Windows::Forms::TextBox^  textBox1;
    protected: 

	private:
		System::ComponentModel::Container ^components;
		System::ComponentModel::BackgroundWorker ^Worker; 
		System::String ^ergebnis;

    #pragma region Windows Form Designer generated code
		void InitializeComponent(void)
		{
            this->button1 = (gcnew System::Windows::Forms::Button());
            this->textBox1 = (gcnew System::Windows::Forms::TextBox());
            this->SuspendLayout();
            // 
            // button1
            // 
            this->button1->Location = System::Drawing::Point(21, 31);
            this->button1->Name = L"button1";
            this->button1->Size = System::Drawing::Size(75, 23);
            this->button1->TabIndex = 0;
            this->button1->Text = L"button1";
            this->button1->UseVisualStyleBackColor = true;
            this->button1->Click += gcnew System::EventHandler(this, &Form1::OnClickButton);
            // 
            // textBox1
            // 
            this->textBox1->Location = System::Drawing::Point(114, 33);
            this->textBox1->Name = L"textBox1";
            this->textBox1->Size = System::Drawing::Size(151, 20);
            this->textBox1->TabIndex = 1;
            // 
            // Form1
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(277, 83);
            this->Controls->Add(this->textBox1);
            this->Controls->Add(this->button1);
            this->Name = L"Form1";
            this->Text = L"Form1";
            this->ResumeLayout(false);
            this->PerformLayout();
        }
    #pragma endregion

    private:
        void BackgroundWorker_DoWork( Object^ sender, DoWorkEventArgs^ e )
        {
            for( int i = 0; i < 100; i++ )
            {
                System::Threading::Thread::Sleep(100);
            
                ergebnis = String::Format("Fortschritt: {0}", i);
                Worker->ReportProgress(0);
            }
        }
 
        void BackgroundWorker_RunWorkerCompleted( Object^ sender, RunWorkerCompletedEventArgs^ e )
        {
            textBox1->Text = "Fertig";
        }
        
        void BackgroundWorker_ProgressChanged( Object^ sender, ProgressChangedEventArgs^ e )
        {
            textBox1->Text = ergebnis;
        }
        
        private: System::Void OnClickButton(System::Object^  sender, System::EventArgs^  e)
        {
            Worker->RunWorkerAsync();
        }
    };
}
```
Gruß
MCoder


----------



## CKS07 (16. Januar 2009)

Also denke ich habe alle änderungen eingefügt, doch leider schreibt er nix in die textbox...also normaler weise hatte ich einen Stringen der jedes mal neu in die textbox mit:


```
textBox2->AppendText(link);
```

so und jetzt habe ich diese zeile einfach weggenommen und dafür hingeschrieben:


```
ergebnis=link;
```
und habe dann unten bei ProgressChanged das eingefügt:


```
textBox2->AppendText(ergebnis);
```

so aber es t net


----------



## MCoder (16. Januar 2009)

Versuche mal zu debuggen und schaue, was bei den einzelnen Variablen (link, ergebnis) zugewiesen wird.

//EDIT: Springt das Programm überhaupt in "BackgroundWorker_ProgressChanged" hinein? Wichtig ist die Zeile "Worker->ReportProgress(0); ". 

Gruß
MCoder


----------



## CKS07 (26. Januar 2009)

Morgen, sry das ich net mehr geantwortet hatte, aber hatte letzte woche viel zu tun...

so habe immer noch das problem...und ich glaube es liegt daran das der backupworker überhaupt gar nicht gestartet wird...hmmm

Gruß


----------



## CKS07 (26. Januar 2009)

Okay hab mich geirrt...also der backupworker läuft auf jeden fall...nur mit dem log stimmt irgendwas net...nichtmal wenn ich die variable direkt bei ProgressChanged definiere...also ergebnis="Hallo"; t es net...obwohl er ja jetzt min. das hallo ausgeben müsste...

Gruß


----------



## CKS07 (26. Januar 2009)

So das Problem hat sich auch erledigt...also es t...jetzt habe ich nur ein problem...und zwar beim log soll er ja jeden einzelnen Wert anzeigen: Bsp: for-schleife von 0-10

er soll anzeigen in der textbox während die schleife läuft:


```
0
```


```
0
1
```


```
0
1
2
```

aber erzeigt nach dem schleife durch is...NUR den letzen Wert an:


```
10
```


----------



## CSANecromancer (26. Januar 2009)

Dann würde ich nochmal prüfen, ob sich in deiner inneren Schleife nicht evtl.ein } eingeschlichen hat, das dort nicht reingehört.
Ansonsten nochmal prüfen, daß die Ausgabe auch wirklich innerhalb der Schleife liegt.

Zugegeben, klingt beides trivial, aber durch solche Sachen hat es auch schon bei mir Loggings versaut.


----------



## CKS07 (26. Januar 2009)

Also ich habe mal die log funktion in den BackgroundWorker_DoWork reingehauen und da kommt leider folgende Fehlermeldung! Aber wenn ich es bei ProgressChanged reinmache, kommt die fehlermeldung nicht!



```
Zusätzliche Informationen: Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textBox2 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
```


----------



## deepthroat (26. Januar 2009)

Hi.

Wie sieht denn dein Code im  BackgroundWorker_RunWorkerCompleted(), BackgroundWorker_ProgressChanged() und in der BackgroundWorker_DoWork() Methode aus?

Gruß


----------



## CKS07 (26. Januar 2009)

beim BackgroundWorker_DoWork is eine schleife drin die immer ihren aktuellen stand in eine textbos schreiben soll (LOG):

und so sieht er aus...





MCoder hat gesagt.:


> Ich habe meinen zuletzt geposteten Code etwas aufgebohrt:
> 
> ```
> #pragma once
> ...


----------



## CKS07 (26. Januar 2009)

ah hab es...ich trottel hatte irgendwann mal das Worker->ReportProgress(0); auskommentiert und das is natürlich dann blöd


----------



## CKS07 (28. Januar 2009)

gibt es irgendwie eine möglichkeit den backgroundworker mit einer progressbar zu verknüpfen?

Gruß


----------



## MCoder (28. Januar 2009)

CKS07 hat gesagt.:


> gibt es irgendwie eine möglichkeit den backgroundworker mit einer progressbar zu verknüpfen?


Funktioniert analog zur zur bisherigen Event-Methode "BackgroundWorker_ProgressChanged": Statt der Ausgabe in ein Textfeld steuerst du mit dem Ergebnis-Wert die Progressbar.

Gruß
MCoder


----------

