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 295Schweregrad 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 295Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand
Fehler C2512 "System::Threading::Thread::Thread": Kein geeigneter Standardkonstruktor verfügbar ChatClient MyForm.h 295Die Meldungen bezieht sich auf die Zeile:
Thread^ NewThread = gcnew Thread(gcnew ThreadStart(this, &this->MyForm::ReceiveMessage));
-
Verwende statt Threadstart ParameterizedThreadStart. Siehe auch: https://msdn.microsoft.com/en-us/library/6x4c42hc(v=vs.110).aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-2
-
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();