CreateThread: Zugriffsverletzung beim Lesen an Position 0x00000000 ???



  • Hallo Zusammen,
    habe mich seit einigen Tagen mit Socketprogrammierung und Threads beschäftigt, jedoch komme ich an einer Stelle nicht weiter, da zur Laufzeit ein Fehler erzeugt wird.

    Unbehandelte Ausnahme bei 0x00000000 in PortScan1IP.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00000000.
    

    Ich vermute es liegt an CreateThread.
    Wenn ich die Funktion CreateThread weg lasse und nur die Funktion PortScan() ausführen lasse, kommt keine Fehlermeldung und das Programm terminiert.
    Wenn ich einen Haltepunkt zum Debuggen (nach CreateThread) setze kommt der Fehler noch nicht. Auch werden die Zeilen CloseHandle(hThread); delete [] TempPort; ausgeführt. Erst ab WSACleanup() kommt der Fehler. Wenn ich diese Zeile auskommentiere, kommt der Fehler dennoch bei cout << endl << "Fertig!" << endl;.
    Nun die Frage:
    Liegt es wirklich am CreateThread oder mache ich vorher schon was falsch?
    Ich lösche ja eigentlich alles bevor ich das Programm terminieren lasse.

    Achja, ich weiß das die Globalen nicht verwendet werden sollen. Habe den Quelltext aus einem Forum kopiert, doch da waren so viele Fehler, die ich erstmal ausgebessert habe und der Autor ist nicht zu erreichen.
    Was das Programm machen soll?
    Eine IP nach offenen Ports prüfen.

    Hier der Code:

    #include <WinSock2.h>
    #include <iostream>
    using namespace std;
    
    #pragma comment(lib, "Ws2_32.lib")
    
    HANDLE hThread;
    
    char IP[20];
    unsigned int startPort,endPort,tempPort;
    
    typedef struct PortDetails {
    	char * IP;
    	unsigned short Start;
    	unsigned short End;
    }PortInfo;
    
    DWORD PortScan(PortInfo * Ports);
    
    int main(void){
    	WSADATA wsadata;
    	WSAStartup(MAKEWORD(2,2), &wsadata);
    
    	cout << "IP-Adresse: ";
    	cin >> IP;
    	cout << "Start Port";
    	cin >> startPort;
    	cout << "End Port";
    	cin >> endPort;
    
    	cin.ignore();
    
    	cout << endl << endl << "Scan gestartet..." << endl << endl;
    
    	//temp für Multithreading, aber wegen Fehler komplett rausgenommen	
    	unsigned int temp = (endPort / 10);
    	unsigned int s = startPort;
    	DWORD dummy = 1;
    
    		PortInfo * TempPort = new PortInfo;
    		//vorher:
    		//PortInfo * TempPort = (PortInfo*) calloc(sizeof(PortInfo),sizeof(PortInfo));
    
    		TempPort->Start = startPort;
    		TempPort->End = endPort;
    		TempPort->IP = IP;
    
    		//Thread erzeugen
    		hThread = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)PortScan(TempPort),&TempPort,0,&dummy);
    		if(hThread==NULL) 
    			return 0; 
    
    	CloseHandle(hThread);
    	delete [] TempPort; //Objekt löschen
    	WSACleanup();	
    	cout << endl << "Fertig!" << endl;
    	return 0;
    }
    
    DWORD PortScan(PortInfo * Ports){
    	int d = Ports->Start;
    	SOCKET			sock;
    	SOCKADDR_IN		Info;
    	LPHOSTENT host = gethostbyname(IP);
    	while(Ports->Start < Ports->End){
    		sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    		Info.sin_family = AF_INET;
    		Info.sin_port = htons(Ports->Start);
    
    		Info.sin_addr = *((LPIN_ADDR)*host->h_addr_list);
    		unsigned int nMillis = 3000;
    		if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &nMillis, sizeof(nMillis)) != SOCKET_ERROR){
    			if(connect(sock,(sockaddr*)(&Info),sizeof(Info))  != SOCKET_ERROR)
    				cout << "Port: " << Ports->Start << " - OFFEN!" << endl;
    		}
    		Ports->Start++;
    		closesocket(sock);
    	}
    	return 0;
    }
    


  • Dein Problem ist (vermutlich) ganz einfach.

    Nachdem du deinen Thread startest, ziehst du dir die Daten auf denen
    du arbeiten möchtest unterm Hintern weg.

    Alles ab Zeile 54 wird ausgeführt, während dein neuer Thread noch läuft.

    Außerdem sollte in Zeile 55 ein

    delete TempPort; // ohne []
    

    Gruß,
    XSpille



  • Warum benutzt du delete[] statt delete? Warum löschst du die Struktur überhaupt, wenn du sie im Thread noch verwenden willst? Typedef struct ist in C++ überflüssig.



  • Achso.
    Okay dann versteh ich das jetzt auch mit der Fehlermeldung 🙂

    Wie kann ich denn das Programm merken lassen wann der Thread fertig ist?



  • skater schrieb:

    Wie kann ich denn das Programm merken lassen wann der Thread fertig ist?

    Ich bin kein Windows-User, aber so scheint es zu gehen:
    http://www.codeproject.com/KB/threads/Threads_1.aspx

    Irgendwo steht:
    // Wait until all threads have terminated.



  • Wenn du den Cast auf LPTHREAD_START_ROUTINE weglässt, sagt dir der Compiler, wo das Problem liegt.



  • Habe mir den o.g. Link durchgelesen und sehe im Bezug auf LPTHREAD_START_ROUTINE keinen Fehler. wenn ich aus meiner DWORD wie im Link eine DWORD WINAPI mache, konvertiert er das trotzdem beim erstellen des Threads nicht?

    Jedoch sagt mir diese Compiler Meldung nichts:

    Fehler 1 error C2664: 'CreateThread': Konvertierung des Parameters 3 von 'DWORD' in 'LPTHREAD_START_ROUTINE' nicht möglich
    Fehler 2 IntelliSense: Das Argument vom Typ ""DWORD"" ist mit dem Parameter vom Typ ""LPTHREAD_START_ROUTINE"" inkompatibel.
    

    In anderen Quelltexten die ich gelesen hab wird auch gecastet? Ist das falsch, bzw sollte man das nicht machen?

    Edit:
    Habe nun die Funktion PortScan ohne Parameter übergeben lassen und PortScan(LPVOID lpParam ) verwendet. siehe da nun funktionierte es. Also zumindest gab es keine Fehlermeldungen.

    wie kann ich denn dennoch das erstellte Objekt Ports verwenden? es ist ja sogesehen nicht verfügbar?



  • DWORD CALLBACK PortScan(LPVOID pv)
    {
      PortInfo * Ports = reinterpretcast<PortScan*>(pv);
      //..
    }
    

    Zu beachten ist ebenfalls, dass innerhalb der ThreadProc keine Funktionen der CRT genutzt werden dürfen.

    Falls dies dennoch notwendig sein sollte, sollte _beginthread(ex)/_endthread(ex) verwendet werden.

    Beachte auch das Wörtchen CALLBACK, diese Funktion hat also die stdcall-Aufrufkonvention, der Stack wird von ihr selber wieder bereinigt (anders als bei cdecl).



  • skater schrieb:

    In anderen Quelltexten die ich gelesen hab wird auch gecastet? Ist das falsch, bzw sollte man das nicht machen?

    Es hat in diesem Fall den Fehler verdeckt, dass du statt eines Funktionszeigers einen Funktionsaufruf übergeben hast.

    skater schrieb:

    wie kann ich denn dennoch das erstellte Objekt Ports verwenden? es ist ja sogesehen nicht verfügbar?

    Du kannst deinen Parameter immer noch als void-Zeiger übergeben. Du musst nur in der Threadfunktion auf den passenden Typ casten.



  • Vielen Dank für die Hilfen!
    Jedoch musste ich den cast etwas ändern da ich sonst falsche Inhalte in den Variablen der Objekte hätte.

    DWORD CALLBACK PortScan(LPVOID lpParam ){ 
    ...
    	PortInfo * Ports = *(PortInfo**)(lpParam);
    ...
    }
    

    Und um auf die Threads zu warten habe ich (da vorerst nur 1 Thread folgendes benutzt:

    WaitForSingleObject(hThread,INFINITE);
    

    Später werde ich dann auf WaitForMultilpleObjects und Arrays arbeiten.

    Wie sieht es eigentlich mit dem kritischen Bereich bei Threads aus? Gibt es eine gute Referenz dazu?


Anmelden zum Antworten