C# Thread-Problem?

Fou-Lu

Lt. Junior Grade
Registriert
Aug. 2006
Beiträge
290
Hallo Leute,
ich steck derzeit ganz schön in Schwierigkeiten bei meinem Code und komme wirklich nicht mehr weiter...

Ich wollte mir eigentlich eine Bibliothek schreiben um Dateien über das Netzwerk zu versenden. Das Hauptaugenmerk beruht darauf, dass es wirklich eine "Bibliothek" also DLL sein soll, damit man es in anderen Anwendungen jederzeit einbauen kann.

Nun denn... jedenfalls funktioniert der Datei-Versand und der Verbindungsaufbau bereits beinahe perfekt, aber ich wollte eigentlich beim Verbinden oder Dateiempfang jeweils Rückmeldungen darüber haben, dass z.B. "Verbindung hergestellt" oder "Datei empfangen" ist/wurde.

Erst habe ich dabei an Tooltips gedacht, doch wie ich feststellen musste funktioniert das nur, wenn man ein TrayIcon einblendet (da es jedoch eine Bibliothek ist, kommt das mehr als nur schlecht).

Dann war meine Idee ich erstelle mir einfach eine passende Form und lass es im Stil von den Outlook-Meldungen einblenden... hat auch Anfangs wunderbar geklappt, jedoch bin ich jetzt auf ein Problem gestoßen und komm nich weiter.


Hier erstmal der Code wo das Problem auftaucht:

PHP:
public void Connect() {
	//prüft ob bereits eine Verbindung zu einem Host besteht
	if (_status == Status.Stopped) {
		try {
			//Schaut ob der Listener bereits gestartet ist
			if (listening == false) {
				listener = new TcpListener(IPAddress.Any, port);
				listener.Start();
			}

			listening = true;

			//wartet auf eingehende Verbindungen
			client = listener.AcceptTcpClient();
					
			_status = Status.Connected;
				
			//stopt den Listener, nachdem sich ein Client verbunden hat
			listener.Stop();
			listening = false;

			nStream = client.GetStream();

			//startet den Thread der auf eingehende Dateien wartet
			//und diese abspeichert
			receiverThread = new Thread(new ThreadStart(ReceiveFile));
			receiverThread.Start();

			//zeigt eine Meldung, über das erfolgreiche Herstellen einer Verbindung, an.
			ShowBalloon("Verbindung erfolgreich hergestellt.\r\n" +
				"Sie können nun Dateien senden und empfangen!");
		} catch (Exception ex) {
			_status = Status.Stopped;
					
			ShowBalloon("Connect()\r\n" + ex.Message);
		}
	} else {
		ShowBalloon("Es besteht bereits eine Verbindung mit einem Remotehost!");
	}
}


BalloonTip ist lediglich eine normale Form (ohne FormBorder) und mit einem Label.
Im Label wird einfach nur der Text angezeigt. An sonsten hat es noch 2 Timer.
Einer davon ist für das ein- und ausblenden der Form zuständig (so hab ich die
Animation realisiert - opacity der Form von 0 auf 1 setzen bei jedem Tick im 0.1 Schritt).
Der zweite Timer prüft nur ob die Form bereits X Sekunden eingeblendet ist, wenn
ja, dann wird diese geschlossen.

PHP:
private void ShowBalloon(string msg) {
	BalloonTip balloon = new BalloonTip(msg);
	balloon.Show();
	balloon.TopMost = true;
	balloon.BringToFront();
}


Ok... also das ganze ein- und ausblenden der Form funktioniert auch soweit, aber meine Connect-Methode wird in einem Extra-Thread ausgeführt (damit die Anwendung nicht einfriert während sie auf eingehende Verbindungen wartet) und da ist das Problem. In diesem Fall wird nämlich die Form mit der Meldung nicht eingeblendet wie sie soll. In der Taskleiste sieht man kurz eine Meldung aufblinken als ob ein Programm (bzw. eine Form) geöffnet wurde und dann wird sie sofort wieder geschlossen, aber ich verstehe wirklich nicht wieso.

In jedem anderen Szenario funktioniert sie ja auch nur eben nicht in dieser Connect-Methode. Kann es daran liegen, dass diese in einem eigenen Thread ausgeführt wird?
 
Fou-Lu schrieb:
Dann war meine Idee ich erstelle mir einfach eine passende Form und lass es im Stil von den Outlook-Meldungen einblenden...
Auch für diese Idee gilt folgendes:
Fou-Lu schrieb:
da es jedoch eine Bibliothek ist, kommt das mehr als nur schlecht

Vergiss das mit den Balloon Tips sofort wieder.
Lass deine Bibliothek einfach Events feuern, die man dann abonnieren kann, wenn man die Biblothek einbindet.
Derjenige der die Biblothek benutzt kann somit selbst entscheiden, ob Statusmeldungen angezeigt werden und wenn ja in welcher Form.

Du kannst dir mal das Tutorial durchlesen, da ist sehr gut erkälrt wie man sowas macht:
Create an Asynchronous Method [C#]
(Vor allem das 2. Kapitel dürfte für dich interessant sein)
 
wieso bin ich nich gleich auf die idee mit den events gekommen :D :D :D

danke für den ratschlag, werd mir das tutorial gleich mal durchlesen.
jedoch würde mich trotzdem nur zu gern interessieren wieso das in der methode so rumspinnt die form?? wenn jemand nen tipp hat, dann wär ich sehr froh wenn ihr es mit mir teilen würdet :) ;-)
Ergänzung ()

Also mit den Events hab ich jetzt immernoch das selbe Problem wie mit der Form.
Das Event wird allem anschein nach einfach nicht ausgelöst bei dieser Funktion. Ich habe echt keine Ahnung woran das liegt...
Ergänzung ()

Ok muss mich korrigieren... das Event wird ausgelöst, nur gibts allem Anschein nach Probleme mit der Form... wenn ich stattdessen nämlich eine MessageBox einblenden lasse in meinem abonnierten Event im Programm, dann funktioniert es, aber die BalloonTips tauchen nicht auf! (allerdings nur in dieser einen Funktion nicht, an sonsten in jeder anderen schon)! :-/

Hier mal der Code von der Form für die Balloons

PHP:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Network.Files {
	internal class BalloonTip : Form {

		#region ############# MEMBER #############

		/// <summary>
		/// Text which will be shown on the Tooltip.
		/// </summary>
		private string text;

		/// <summary>
		/// Counts the seconds since the Form was opened
		/// </summary>
		private int zaehler;

		/// <summary>
		/// Opacity for the Mainform which shows the Text-Message
		/// </summary>
		private double opacity;

		/// <summary>
		/// Says if this Form have to be Closed
		/// </summary>
		private bool end;

		private Label lblText;
		
		private Timer tTimeout;
		
		private Timer tEinblenden;

		private PictureBox pictureBox1;

		private IContainer components;

		#endregion


		internal BalloonTip(string msg) {
			InitializeComponent();

			this.text = msg;
			this.zaehler = 0;
			this.opacity = 0;
			this.end = false;
		}

		private void BalloonTip_Load(object sender, EventArgs e) {
			lblText.Text = text;

			this.Height = lblText.Size.Height + 20;
			
			JustifyBalloonTip();

			tEinblenden.Enabled = true;
		}

		private void tTimeout_Tick(object sender, EventArgs e) {
			zaehler++;

			if (zaehler == 7) {
				end = true;
				tEinblenden.Enabled = true;
				tTimeout.Enabled = false;
			}
		}

		private void tEinblenden_Tick(object sender, EventArgs e) {
			this.Opacity = opacity;

			if (end == false) {
				opacity += 0.1;
			} else {
				opacity -= 0.1;
			}

			if (opacity >= 1.0) {
				opacity = 1.0;
				tTimeout.Enabled = true;
				tEinblenden.Enabled = false;
			} else if (opacity <= 0.0) {
				this.Close();
			}
		}

		private void JustifyBalloonTip() {
			int screenWidth = Screen.PrimaryScreen.Bounds.Width;
			int screenHeight = Screen.PrimaryScreen.Bounds.Height;

			int balloonWidth = this.Width;
			int balloonHeight = this.Height;

			int xPosition = screenWidth - balloonWidth;
			int yPosition = screenHeight - balloonHeight - 30; //30 für die Höhe der Taskleiste

			this.Location = new Point(xPosition, yPosition);
		}


		private void InitializeComponent() {
			this.components = new System.ComponentModel.Container();
			this.lblText = new System.Windows.Forms.Label();
			this.tTimeout = new System.Windows.Forms.Timer(this.components);
			this.tEinblenden = new System.Windows.Forms.Timer(this.components);
			this.pictureBox1 = new System.Windows.Forms.PictureBox();
			((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
			this.SuspendLayout();
			// 
			// lblText
			// 
			this.lblText.AutoSize = true;
			this.lblText.Location = new System.Drawing.Point(12, 19);
			this.lblText.MinimumSize = new System.Drawing.Size(225, 45);
			this.lblText.Name = "lblText";
			this.lblText.Size = new System.Drawing.Size(225, 45);
			this.lblText.TabIndex = 0;
			this.lblText.Text = "label1";
			// 
			// tTimeout
			// 
			this.tTimeout.Interval = 1000;
			this.tTimeout.Tick += new System.EventHandler(this.tTimeout_Tick);
			// 
			// tEinblenden
			// 
			this.tEinblenden.Tick += new System.EventHandler(this.tEinblenden_Tick);
			// 
			// pictureBox1
			// 
			this.pictureBox1.BackColor = System.Drawing.Color.DarkRed;
			this.pictureBox1.Location = new System.Drawing.Point(0, 0);
			this.pictureBox1.Name = "pictureBox1";
			this.pictureBox1.Size = new System.Drawing.Size(250, 10);
			this.pictureBox1.TabIndex = 1;
			this.pictureBox1.TabStop = false;
			// 
			// BalloonTip
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(250, 75);
			this.Controls.Add(this.pictureBox1);
			this.Controls.Add(this.lblText);
			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
			this.MaximizeBox = false;
			this.MinimizeBox = false;
			this.MinimumSize = new System.Drawing.Size(250, 75);
			this.Name = "BalloonTip";
			this.Text = "BalloonTip";
			this.Load += new System.EventHandler(this.BalloonTip_Load);
			((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
			this.ResumeLayout(false);
			this.PerformLayout();

		}
	}
}


Vielleicht sieht ja jemand etwas was ich nicht sehe :freak: :freak:
 
Zuletzt bearbeitet:
Ich schätze mal dein Event wird aus einem anderen Thread gefeuert, genauso wie du auch vorher schon deine ShowBalloon-Methode in dem anderen Thread aufgerufen hast.
Du kannst aber nicht einfach in nem anderen Thread auf Objekte zugreifen die dem GUI Thread "gehören".

Wenn du dir beim Tutorial das ich schon gepostet habe das 2. Kapitel nochmal genau anschaust, siehst du, dass dort das Event nicht "normal" gefeurt wird, sondern über AsyncOperation.Post.
Dadurch wird das Event im Kontext des GUI Thread gefeuert und nicht im Kontext des Worker Threads.
Das ist imo auch ne sehr schöne Lösung, gibt aber auch noch andere (evtl. leichtere) Möglichkeiten.

Auf codeproject gibts nen guten Artikel dazu, der ziemlich genau dein Problem behandelt und mehrere Lösungsmöglichkeiten aufzeigt: Avoiding InvokeRequired
Der Weg über AsyncOperation ist da nicht dabei, den solltest du dir aber auf jeden Fall auch nochmal extra anschauen, da du damit dem Benutzer der Bibliothek Arbeit ersparen kannst.
 
Zuletzt bearbeitet:
Das Problem ist viel simpler.
Der Invoke ist hier nicht erforderlich, da er ein neues UI Element erzeugt und es anzeigt, aber kein bestehendes bzw. keines, das vom UI-Thread verwaltet wird, verändert. Du hast aber vollkommen recht mit dem eigenen Thread für das Event.
Und genau hier liegt das Problem.

Die Connect-Methode läuft laut TE auf einem eigenen Thread. Da die Form nicht modal angezeigt wird, endet der Thread und damit auch die Lebensdauer der Form. Deswegen wird sie nur für einen Bruchteil einer Sekunde angezeigt.
 
Ah ich dachte mir schon, dass ich was übersehen habe. :/
Würde es am CrossThread Call liegen würde VS ja auch ne CrossThreadException werfen.

Nichtsdestotrotz wäre es aber imo trotzdem die bessere Lösung, das Event gleich im richtigen Thread Context zu feuern und dann im GUI Thread den Balloon Tip (=ein GUI Element) anzuzeigen.
 
Grantig schrieb:
Nichtsdestotrotz wäre es aber imo trotzdem die bessere Lösung, das Event gleich im richtigen Thread Context zu feuern und dann im GUI Thread den Balloon Tip (=ein GUI Element) anzuzeigen.

Jap.
 
Vielen Dank für die Hilfe Leute,
werd mir die Tutorials gleich nochmals etwas besser und mit mehr Aufmerksamkeit durchlesen :D
 
Zurück
Oben