Thread Innerhalb einer Methode starten



  • Hallo zusammen.

    Ich bin recht neu in der C++ Welt und muss (bzw. sollte) für mein Studium als Hausarbeit ein Multithreading Client/Server System erstellen.

    Ich habe mich für einen ChatServer entschieden. Meine Server Software funktioniert auch ohne weiteres, jedoch tue ich mich sehr schwer mit dem Client. Wie gesagt bin ich ganz neu in der C++ Welt und wollte für den Client ein GUI erstellen.

    Ich benutze VS Community 2015, und habe meine GUI mithilfe des integrierten Designers und mithilfe eines Tutorials erstellt. Innerhalb der Refferenzklasse "MyForm" die ich erstellt habe (welche die GUI beherrbergt), habe ich einige kleine Methoden hinzugeschrieben die meine Chat Funktionen realisieren sollen.

    Villeicht fragt ihr euch wieso ich das so gemacht habe, naja ich bin auch alles andere als Glücklich damit. Ich wollte eigentlich für meine Chat Funktionen eine Kleine Klasse erstellen welche ich als Header File realisieren würde und einfach einbinden würde. Ich bin jedoch daran gescheiert, dass ich nicht wirklich wusste wie ich von "aussen" mit der "MyForm" Klasse kommunizieren kann um z.b. Nachrichten auszulesen oder in die GUI reinzuschreibebn.. 😞

    Jetzt ist das Problem folgendes: Das Versenden von Nachrichten etc funktioniert einwandfrei. Aber, ich kriege einfach keinen Thread für die Receiving Methode erstellt. Diesen muss ich in einer anderen Methode in der ich das Socket initialisiere und die verbindung aufbaue aufrufen.

    ich kann <thread> nicht benutzen da es sich um CLI Code handelt und wenn ich mittels "using namespace System::Threading;" einen Thread starten möchte bekomme ich fehler.

    Hier ist mein Code:

    int MyForm::ConnectToServer(int Port, char IP[14]){
    		WSADATA wsaData;
    		long result = WSAStartup(DLLVERSION, &wsaData);											//WSA DLL initialisieren
    		if (result == 0) {																		//Wenn kein Fehler aufgetreten ist
    			this->listBox1->Items->Add("WSA DLL Initialized successfully!");					//Erfolgreiche initialisieurng in ListBox ausgeben
    		} else {ErrorClean();}																	//Wenn ein Fehler aufgetreten ist
    
    		sockaddr_in Server;																		//Server Struktur
    		Server.sin_addr.s_addr = inet_addr(IP);													//IP des Servers
    		Server.sin_family = AF_INET;															//Protokoll Familie
    		Server.sin_port = htons(Port);															//Port des Servers
    		int ServerLen = sizeof(Server);															//Länge der Serverstruktur
    
    		Sock = socket(AF_INET, SOCK_STREAM, 0);													//Client Socket initialisieren
    		if (Sock != INVALID_SOCKET) {															//Wenn kein Fehler unterlaufen ist
    			this->listBox1->Items->Add("Socket initialized successfully!");						//Initialisierung ausgeben
    		} else { ErrorClean(); }																//Wenn ein Fehler aufgetreten ist ErrorClean aufrufen
    
    		result = connect(Sock, (SOCKADDR*)&Server, ServerLen);									//Verbindung zum Server herstellen!
    		if (result == 0) {																		//Wenn kein Fehler aufgetreten ist
    			this->listBox1->Items->Add("Connection to Server established successfully!");		//Meldung ausgeben
    		} else { ErrorClean(); }																//Bei Fehler Errorclean aufrufen
    
    		connected = true;																		//connected Flag setzen
    		this->listBox1->Items->Add("Client prepared successfully!");
    		this->listBox1->Items->Add("====================================================================");
    
    		//Launch Thread Here
    
    		//int pointer = this->ReceiveMessage();
    
    		Thread^ NewThread = gcnew Thread(gcnew ThreadStart(this, &this->MyForm::ReceiveMessage));
    
    		return 0;
    	}
    

    Ich wäre euch für jede Idee unheimlich Dankbar (da dieses Projekt mir echt den letzten Nerv raubt...) Mit freundlichen Grüßen 🙂



  • schausi schrieb:

    wenn ich mittels "using namespace System::Threading;" einen Thread starten möchte bekomme ich fehler.

    Bitte zeige hier die Fehlermeldung, so kann dir besser geholfen werden. Auch der Code MyForm::ReceiveMessage(..) ist interessant zu sehen.



  • Hallo Theta. Vielen Dank für deine schnelle Antwort. Hier ist mein gesamter Code:

    Inhalt der MyForm.h

    //******************************************************************************
    //used Libraries
    #include <winsock2.h>
    #include <iostream>
    #include <string>
    #include <windows.h>
    
    //***********************************************************************************************
    //Definitions
    #pragma once
    //using namespace std;
    #pragma comment(lib, "ws2_32.lib")
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    //***********************************************************************************************
    //UI Klassendefinition (enthält ChatClient Methoden)
    namespace ChatClient {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    	using namespace System::Runtime::InteropServices;
    	using namespace System::Threading;
    
    	public ref class MyForm : public System::Windows::Forms::Form{					//UI Klasse
    	public:																			//öffentliche Attribute und Methoden
    		MyForm(void);																//Konstruktor der Klasse
    	protected:																		//Geschützte Attribute und Methoden
    		~MyForm();
    	private: System::Windows::Forms::Button^  button1;
    	private: System::Windows::Forms::Label^  label1;
    	private: System::Windows::Forms::TextBox^  textBox1;
    	private: System::Windows::Forms::TextBox^  textBox2;
    	private: System::Windows::Forms::Label^  label2;
    	private: System::Windows::Forms::Label^  label3;
    	private: System::Windows::Forms::Button^  button2;
    	private: System::Windows::Forms::Button^  button3;
    	private: System::Windows::Forms::ListBox^  listBox1;
    	private: System::Windows::Forms::RichTextBox^  richTextBox1;
    	private:																		//Private Attribute und Methoden
    		//Attribute
    		WORD DLLVERSION = MAKEWORD(2, 1);											//Version der WSA DLL
    		SOCKET Sock;																//Socket für Verbindung
    		bool connected = false;														//Flag das Verbindungsstatus anzeigt
    
    		//Methoden
    		int ConnectToServer(int Port, char IP[14]);									//Methode um Verbindung zum Server aufzubauen
    		int SendMSG(char MSGToSend[4096]);											//Methode zum Versenden von Nachrichten
    		int ReceiveMessage();														//MEthode zum Empfangen von Nachrichten
    		void ErrorClean();															//ErrorClean Methode um Fehler zu behandeln
    		void Disconnect();															//Methode um Verbindung zum Server zu lösen
    		System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
    		void InitializeComponent(void)
    		{
    			this->button1 = (gcnew System::Windows::Forms::Button());
    			this->label1 = (gcnew System::Windows::Forms::Label());
    			this->textBox1 = (gcnew System::Windows::Forms::TextBox());
    			this->textBox2 = (gcnew System::Windows::Forms::TextBox());
    			this->label2 = (gcnew System::Windows::Forms::Label());
    			this->label3 = (gcnew System::Windows::Forms::Label());
    			this->button2 = (gcnew System::Windows::Forms::Button());
    			this->button3 = (gcnew System::Windows::Forms::Button());
    			this->listBox1 = (gcnew System::Windows::Forms::ListBox());
    			this->richTextBox1 = (gcnew System::Windows::Forms::RichTextBox());
    			this->SuspendLayout();
    			// 
    			// button1
    			// 
    			this->button1->Location = System::Drawing::Point(317, 44);
    			this->button1->Name = L"button1";
    			this->button1->Size = System::Drawing::Size(75, 23);
    			this->button1->TabIndex = 0;
    			this->button1->Text = L"Connect";
    			this->button1->UseVisualStyleBackColor = true;
    			this->button1->Click += gcnew System::EventHandler(this, &MyForm::button1_Click);
    			// 
    			// label1
    			// 
    			this->label1->AutoSize = true;
    			this->label1->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 14, static_cast<System::Drawing::FontStyle>((System::Drawing::FontStyle::Bold | System::Drawing::FontStyle::Underline)),
    				System::Drawing::GraphicsUnit::Point, static_cast<System::Byte>(0)));
    			this->label1->Location = System::Drawing::Point(111, 9);
    			this->label1->Name = L"label1";
    			this->label1->Size = System::Drawing::Size(170, 24);
    			this->label1->TabIndex = 1;
    			this->label1->Text = L"Chat Client V1.0a";
    			// 
    			// textBox1
    			// 
    			this->textBox1->ForeColor = System::Drawing::SystemColors::WindowText;
    			this->textBox1->Location = System::Drawing::Point(84, 46);
    			this->textBox1->MaxLength = 15;
    			this->textBox1->Name = L"textBox1";
    			this->textBox1->Size = System::Drawing::Size(227, 20);
    			this->textBox1->TabIndex = 2;
    			// 
    			// textBox2
    			// 
    			this->textBox2->Location = System::Drawing::Point(84, 72);
    			this->textBox2->MaxLength = 4;
    			this->textBox2->Name = L"textBox2";
    			this->textBox2->Size = System::Drawing::Size(226, 20);
    			this->textBox2->TabIndex = 3;
    			this->textBox2->TextChanged += gcnew System::EventHandler(this, &MyForm::textBox2_TextChanged);
    			// 
    			// label2
    			// 
    			this->label2->AutoSize = true;
    			this->label2->Location = System::Drawing::Point(13, 49);
    			this->label2->Name = L"label2";
    			this->label2->Size = System::Drawing::Size(59, 13);
    			this->label2->TabIndex = 4;
    			this->label2->Text = L"Servers IP:";
    			// 
    			// label3
    			// 
    			this->label3->AutoSize = true;
    			this->label3->Location = System::Drawing::Point(13, 75);
    			this->label3->Name = L"label3";
    			this->label3->Size = System::Drawing::Size(68, 13);
    			this->label3->TabIndex = 5;
    			this->label3->Text = L"Servers Port:";
    			// 
    			// button2
    			// 
    			this->button2->Location = System::Drawing::Point(317, 70);
    			this->button2->Name = L"button2";
    			this->button2->Size = System::Drawing::Size(75, 23);
    			this->button2->TabIndex = 6;
    			this->button2->Text = L"Exit";
    			this->button2->UseVisualStyleBackColor = true;
    			this->button2->Click += gcnew System::EventHandler(this, &MyForm::button2_Click);
    			// 
    			// button3
    			// 
    			this->button3->Location = System::Drawing::Point(316, 450);
    			this->button3->Name = L"button3";
    			this->button3->Size = System::Drawing::Size(75, 53);
    			this->button3->TabIndex = 7;
    			this->button3->Text = L"Send";
    			this->button3->UseVisualStyleBackColor = true;
    			this->button3->Click += gcnew System::EventHandler(this, &MyForm::button3_Click);
    			// 
    			// listBox1
    			// 
    			this->listBox1->FormattingEnabled = true;
    			this->listBox1->Location = System::Drawing::Point(15, 102);
    			this->listBox1->Name = L"listBox1";
    			this->listBox1->Size = System::Drawing::Size(376, 342);
    			this->listBox1->TabIndex = 8;
    			// 
    			// richTextBox1
    			// 
    			this->richTextBox1->ForeColor = System::Drawing::SystemColors::WindowText;
    			this->richTextBox1->Location = System::Drawing::Point(16, 450);
    			this->richTextBox1->MaxLength = 4096;
    			this->richTextBox1->Name = L"richTextBox1";
    			this->richTextBox1->Size = System::Drawing::Size(295, 53);
    			this->richTextBox1->TabIndex = 9;
    			this->richTextBox1->Text = L"";
    			// 
    			// MyForm
    			// 
    			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
    			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    			this->ClientSize = System::Drawing::Size(411, 515);
    			this->Controls->Add(this->richTextBox1);
    			this->Controls->Add(this->listBox1);
    			this->Controls->Add(this->button3);
    			this->Controls->Add(this->button2);
    			this->Controls->Add(this->label3);
    			this->Controls->Add(this->label2);
    			this->Controls->Add(this->textBox2);
    			this->Controls->Add(this->textBox1);
    			this->Controls->Add(this->label1);
    			this->Controls->Add(this->button1);
    			this->Name = L"MyForm";
    			this->Text = L"Chat Client V0.1a";
    			this->Load += gcnew System::EventHandler(this, &MyForm::MyForm_Load);
    			this->ResumeLayout(false);
    			this->PerformLayout();
    
    		}
    #pragma endregion
    	private: System::Void MyForm_Load(System::Object^  sender, System::EventArgs^  e) {}
    	private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
    
    		//Variabeln in denen der Inhalt der TextFelder beherrbergt wird:
    		System::String^ IP = this->textBox1->Text;										//String IP Addresse auslesen
    		System::String^ Port = this->textBox2->Text;									//String Port Nummer auslesen
    
    		if (IP == "") {																	//Sicherstellen, dass IP Feld nicht leer ist
    			MessageBox::Show("Please Enter a Valide IP Address");						//Sonst Meldung machen
    			return;
    		}
    		if (Port == "") {																//Sicherstellen, dass Port Feld nicht leer ist
    			MessageBox::Show("Please Enter a Valide Port Number");						//Sonst Meldung machen
    			return;
    		}
    		__int32 intPort = System::Int32::Parse(Port);									//Portnummer in Integer Variable transferieren
    		IntPtr ptrToNativeString = Marshal::StringToHGlobalAnsi(IP);					//Vorbereitung zum überführen des String zu Char Array
    		char* nativeString = static_cast<char*>(ptrToNativeString.ToPointer());			//Char Array 
    
    		ConnectToServer(intPort, nativeString);											//Initialisieren der Verbindung zum Server
    
    		if (connected == true) {														//Wenn Verbindung etabliert wurde
    			this->button1->Enabled = false;												//Connection Knopf deaktivieren
    			this->textBox1->Enabled = false;											//IP Feld deaktivieren
    			this->textBox2->Enabled = false;											//Port Feld deaktivieren
    			this->button2->Text = "Disconnect";											//Exit Knopf umbenennen
    		}
    	}
    private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {	//Exit Knopf 
    
    	if (this->button2->Text == "Exit") {												//Wenn der Titel des Knopfes Exit lautet
    		closesocket(Sock);																//Socket wieder schliessen
    		WSACleanup();																	//WSACleanup durchführen
    		exit(0);																		//Applikation beenden
    	}
    
    	if (this->button2->Text == "Disconnect") {											//Wenn der Titel des Knopfes Disconnect lautet
    		Disconnect();																	//Verbindung Trennen
    	}
    }
    private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) {	//Sende Knopf
    
    	System::String^ Message = this->richTextBox1->Text;									//Text des SendeFeldes 
    
    	if (Message == "") {																//Wenn Textbox Leer ist
    		MessageBox::Show("Please Enter a Message to Send!");							//Ausgabe das nichts gesendet werden kann
    		return;																			//Beenden der abarbeitung
    	}
    
    	IntPtr ptrToNativeString = Marshal::StringToHGlobalAnsi(Message);					//Vorbereitung zum überführen des String zu Char Array
    	char* nativeString = static_cast<char*>(ptrToNativeString.ToPointer());				//Char Array 
    
    	SendMSG(nativeString);																//Nachricht an SendMSG Methode übergeben
    	this->richTextBox1->Text = "";														//Textfeld leeren.
    
    }
    private: System::Void textBox2_TextChanged(System::Object^  sender, System::EventArgs^  e) {}
    };
    
    	//***********************************************************************************************
    	//Konstruktor der Klasse
    	MyForm::MyForm(void) {
    		InitializeComponent();
    	}
    
    	//***********************************************************************************************
    	//Destruktor der Klasse
    	MyForm::~MyForm() {
    		if (components){
    			delete components;
    		}
    	}
    
    	//***********************************************************************************************
    	//Methode zum verbinden des Clients mit dem Server
    	int MyForm::ConnectToServer(int Port, char IP[14]){
    		WSADATA wsaData;
    		long result = WSAStartup(DLLVERSION, &wsaData);											//WSA DLL initialisieren
    		if (result == 0) {																		//Wenn kein Fehler aufgetreten ist
    			this->listBox1->Items->Add("WSA DLL Initialized successfully!");					//Erfolgreiche initialisieurng in ListBox ausgeben
    		} else {ErrorClean();}																	//Wenn ein Fehler aufgetreten ist
    
    		sockaddr_in Server;																		//Server Struktur
    		Server.sin_addr.s_addr = inet_addr(IP);													//IP des Servers
    		Server.sin_family = AF_INET;															//Protokoll Familie
    		Server.sin_port = htons(Port);															//Port des Servers
    		int ServerLen = sizeof(Server);															//Länge der Serverstruktur
    
    		Sock = socket(AF_INET, SOCK_STREAM, 0);													//Client Socket initialisieren
    		if (Sock != INVALID_SOCKET) {															//Wenn kein Fehler unterlaufen ist
    			this->listBox1->Items->Add("Socket initialized successfully!");						//Initialisierung ausgeben
    		} else { ErrorClean(); }																//Wenn ein Fehler aufgetreten ist ErrorClean aufrufen
    
    		result = connect(Sock, (SOCKADDR*)&Server, ServerLen);									//Verbindung zum Server herstellen!
    		if (result == 0) {																		//Wenn kein Fehler aufgetreten ist
    			this->listBox1->Items->Add("Connection to Server established successfully!");		//Meldung ausgeben
    		} else { ErrorClean(); }																//Bei Fehler Errorclean aufrufen
    
    		connected = true;																		//connected Flag setzen
    		this->listBox1->Items->Add("Client prepared successfully!");
    		this->listBox1->Items->Add("====================================================================");
    
    		//Launch Thread Here
    
    		//Thread^ NewThread = gcnew Thread(gcnew ThreadStart(this, &this->MyForm::ReceiveMessage));
    
    		return 0;
    	}
    
    	//***********************************************************************************************
    	//ErrorClean Methode
    	void MyForm::ErrorClean() {																	//ErrorClean Methode
    		WSACleanup();																			//WSACleanup durchführen
    		closesocket(Sock);																		//Socket schliessen
    		MessageBox::Show("Error during Operation! Application will halt now!");					//Fehlermeldung ausgeben
    		exit(1);																				//Applikation mit Fehler 1 beenden
    	}
    
    	//***********************************************************************************************
    	//Send Message Methode
    	int MyForm::SendMSG(char MSGToSend[4096]) {													//SendMessage Methode
    		long Status = 0;																		//Status Variable 
    		Status = send(Sock, MSGToSend, 4096, 0);												//Versenden einer Nachricht
    
    		if (Status == -1) {																		//Wenn ein Fehler aufgetreten ist
    			this->listBox1->Items->Add("--> ERROR DURING SEND! PLEASE REPEAT");					//Ausgeben, das die Nachricht nicht versendet wurde
    			return 1;																			//Mit Fehler beenden
    		}
    		return 0;																				//Wenn kein Fehler aufgetreten ist, ohne Fehler beenden
    	}
    	//***********************************************************************************************
    	//Receive Message Methode
    	int MyForm::ReceiveMessage() {
    		long Status = 0;																		//Return Variable
    		char ReceivedData[4096];																//Nachrichten Char Array
    		System::String^ ReceivedDataString;														//String für Char Array
    
    		while (true) {																			//Endlosschleife zum Empfang (für Thread vorgesehen)
    			recv(Sock, ReceivedData, 4096, 0);													//Empfangen einer Nachricht (Blocking)
    			String^ ReceivedDataString = gcnew String(ReceivedData);							//Char Array in String ablegen
    			this->listBox1->Items->Add(ReceivedDataString);										//Empfangene Nachricht ausgeben
    			memset(ReceivedData, 0, 4096);														//Char Array leeren
    
    			if (Status == -1) {																	//Wenn Verbindung abgebrochen wurde
    				Disconnect();																	//Verbindung zum Server beenden
    				return 1;																		//Methode mit Fehler verlassen.
    			}
    		}
    		return 0;
    	}
    
    	//***********************************************************************************************
    	//Disconnect Methode
    	void MyForm::Disconnect() {																	//Disconnect Methode
    		closesocket(Sock);																		//Socket Schliessen
    		WSACleanup();																			//WSACleanup durchführen
    		this->listBox1->Items->Add("Verbindung zum Server wurde getrennt!");					//Getrennte Verbindung ausgeben
    		this->listBox1->Items->Add("====================================================================");
    		this->button2->Text = "Exit";															//Exit Knopf wieder umbenennen
    		this->button1->Enabled = true;															//Connect Knopf wieder aktivieren
    		this->textBox1->Text = "";																//IP Feld Leeren
    		this->textBox2->Text = "";																//Port Feld Leeren
    		this->textBox1->Enabled = true;															//IP Feld wieder aktivieren
    		this->textBox2->Enabled = true;															//Port Feld wieder aktivieren
    	}
    }
    

    Inhalt meiner MyForm.cpp (enthällt die Main Funktion

    #include "MyForm.h"
    
    using namespace System;
    using namespace System::Windows::Forms;
    
    [STAThread]
    void Main(array<String^>^ args)
    {
    	Application::EnableVisualStyles();
    	Application::SetCompatibleTextRenderingDefault(false);
    
    	ChatClient::MyForm form;
    	Application::Run(%form);
    
    }
    


  • Und die Fehlermeldung? 🙄 😉



  • Achja stimmt 🙂 also die Fehlermeldung ist die folgende:

    Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand
    Fehler C2276 "&": Ungültige Operation auf Ausdruck einer gebundenen Memberfunktion ChatClient MyForm.h 295

    Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand
    Fehler C3364 "System::Threading::ThreadStart": Ungültiges Argument für den Delegatkonstruktor. Delegatziel muss ein Zeiger auf eine Memberfunktion sein. ChatClient MyForm.h 295

    Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand
    Fehler C2512 "System::Threading::Thread::Thread": Kein geeigneter Standardkonstruktor verfügbar ChatClient MyForm.h 295

    Die Meldungen bezieht sich auf die Zeile:

    Thread^ NewThread = gcnew Thread(gcnew ThreadStart(this, &this->MyForm::ReceiveMessage));
    




  • Hi Roflo. Danke für deine Antwort. Ich versuche ParameterizedThreadStart zu verwenden aber ich scheine doch was falsch zu machen. Ich habe folgendes versucht:

    void MyForm::StartThread() {
    		MyForm^ someWork = gcnew MyForm;
    		Thread^ newThread = gcnew Thread(gcnew ParameterizedThreadStart(someWork, &MyForm::ReceiveMessage));
    		newThread->Start();
    	}
    

    Er unterstreicht mir immer noch das "&" Zeichen bei %MyForm::ReceiveMessage und sagt "ungültiger delegationsinitialisierer Funktion stimmt nicht mit Datentyp überein...



  • Hi,

    bin mir nicht sicher ob dir das Hilft aber laut DrDoobs Webseite, nimmt der

    ThreadStart
    

    Konstruktor nur Funktion auf die keinen Argumente brauchen und kein Rückgabewerte liefern.

    void MyForm::ReceiveMessage()
    {
    }
    Thread^ newThread = gcnew Thread(this, &MyForm::ReceiveMessage);
    


  • Hi Leute.

    Vielen Dank nochmal für eure schnellen Antworten. Ich habe mein Problem gelöst 🙂

    Hier ist der Code, der Funktioniert (für den Fall das noch mal jemand ein ähnliches Problem hat)

    ThreadStart^ myThreadDelegate = gcnew ThreadStart(this, &MyForm::ReceiveMessage);
    		Thread^ thread1 = gcnew Thread(myThreadDelegate);
    		thread1->Start();
    

Anmelden zum Antworten