FTP-Client



  • Hallo,

    ich habe mir das Buch Windows-Programmierung von C. Petzold gekauft und ein paar Probleme mit einem Beispielprogramm.

    Da der "originale" Code genau die gleichen Fehler auswirft, bin ich langsam mit meinem Latein am Ende.

    Hier der Code:

    #include <windows.h>
    #include <wininet.h>
    #include <process.h>
    #include "resource.h"
    
    //Selbstdefinierte Nachrichten für WndProc
    #define WM_USER_CHECKFILES		(WM_USER + 1)
    #define WM_USER_GETFILES		(WM_USER + 2)
    
    //Informationen für den FTP-Download
    #define FTPSERVER TEXT			("ftp.petzold.com")
    #define DIRECTORY TEXT			("cpetzold.com/ProgWin/UpdDemo")
    #define TEMPLATE TEXT			("UD??????.TXT")
    
    //Strukturen für Dateinamen und Inhalte
    typedef struct
    {
    	TCHAR * szFilename;
    	char * szContents;
    }
    FILEINFO;
    
    typedef struct
    {
    	int iNum;
    	FILEINFO info[1];
    }
    FILELIST;
    
    //Kommunikationsstruktur für zweiten Thread
    typedef struct
    {
    	BOOL bContinue;
    	HWND hwnd;
    }
    PARAMS;
    
    //forward-Deklaration
    LRESULT		CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    BOOL		CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
    VOID		FtpThread(PVOID);
    VOID		ButtonSwitch(HWND, HWND, TCHAR *);
    FILELIST *	GetFileList(VOID);
    int			Compare(const FILEINFO*, const FILEINFO*);
    
    //Globale Variablen
    HINSTANCE	hInst;
    TCHAR		szAppName[] = TEXT("SmashFTP");
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    {
    	HWND		hwnd;
    	MSG			msg;
    	WNDCLASS	wndclass;
    
    	hInst = hInstance;
    
    	wndclass.style				= 0;
    	wndclass.lpfnWndProc		= WndProc;
    	wndclass.cbClsExtra			= 0;
    	wndclass.cbWndExtra			= 0;
    	wndclass.hInstance			= hInstance;
    	wndclass.hIcon				= LoadIcon(NULL, IDI_APPLICATION);
    	wndclass.hCursor			= NULL;
    	wndclass.hbrBackground		= (HBRUSH)GetStockObject(WHITE_BRUSH);
    	wndclass.lpszMenuName		= NULL;
    	wndclass.lpszClassName		= szAppName;
    
    	if(!RegisterClass(&wndclass))
    	{
    		//UNICODE-Kompilierung ist die einzige realistische Fehlermöglichkeit
    		MessageBox(NULL, TEXT("Programm arbeitet mit Unicode und setzt Windows NT voraus!"), szAppName, MB_ICONERROR);
    		return 0;
    	}
    
    	hwnd = CreateWindow(szAppName, TEXT("SmashFTP blabla"), WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT,
    						CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    	ShowWindow(hwnd, iCmdShow);
    	UpdateWindow(hwnd);
    
    	//Verbindungsaufnahme und Prüfung, ob der FTP-Server aktualisierte Dateiversion zu bieten hat
    	SendMessage(hwnd, WM_USER_CHECKFILES, 0, 0);
    
    	while(GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static FILELIST		*plist;
    	static int			cxClient, cyClient, cxChar, cyChar;
    	HDC					hdc;
    	int					i;
    	PAINTSTRUCT			ps;
    	SCROLLINFO			si;
    	SYSTEMTIME			st;
    	TCHAR				szFilename[MAX_PATH];
    
    	switch(message)
    	{
    	case WM_CREATE:
    		cxChar = LOWORD(GetDialogBaseUnits());
    		cyChar = HIWORD(GetDialogBaseUnits());
    		return 0;
    
    	case WM_SIZE:
    		cxClient = LOWORD(lParam);
    		cyClient = HIWORD(lParam);
    
    		si.cbSize	= sizeof(SCROLLINFO);
    		si.fMask	= SIF_RANGE | SIF_PAGE;
    		si.nMin		= 0;
    		si.nMax		= plist ? plist->iNum-1:0;
    		si.nPage	= cyClient/cyChar;
    
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    		return 0;
    
    	case WM_VSCROLL:
    		si.cbSize	= sizeof(SCROLLINFO);
    		si.fMask	= SIF_POS | SIF_RANGE | SIF_PAGE;
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		switch(LOWORD(wParam))
    		{
    			case SB_LINEDOWN:		si.nPos += 1;				break;
    			case SB_LINEUP:			si.nPage -= 1;				break;
    			case SB_PAGEDOWN:		si.nPos += si.nPage;		break;
    			case SB_PAGEUP:			si.nPos -= si.nPage;		break;
    			case SB_THUMBPOSITION:	si.nPos = HIWORD(wParam);	break;
    			default:				return 0;
    		}
    		si.fMask = SIF_POS;
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    		InvalidateRect(hwnd, NULL, TRUE);
    		return 0;
    
    	case WM_USER_CHECKFILES:
    		//Systemzeit ermitteln, aus Monat und Jahr einen Dateinamen bilden
    		GetSystemTime(&st);
    		wsprintf(szFilename, TEXT("UD%04%02i.TXT"), st.wYear, st.wMonth);
    
    		//Gibt es diese Datei lokal? Wenn ja, gleich weiter mit einem Download
    		if(GetFileAttributes(szFilename) != (DWORD)-1)
    		{
    			SendMessage(hwnd, WM_USER_GETFILES, 0, 0);
    			return 0;
    		}
    		//Ansonsten neueste Datei vom FTP-Server holen. Zuerst aber prüfen, ob der Zieldatenträger keine CD-ROM ist!
    		if(GetDriveType(NULL) == DRIVE_CDROM)
    		{
    			MessageBox(hwnd, TEXT("Dieses Programm schreibt in sein eignes Verzeichnis und läßt sich nicht von CD aus ausführen!"),
    						szAppName, MB_OK | MB_ICONEXCLAMATION);
    			return 0;
    		}
    		//Rückfrage an den Benutzer, ob Internetzugriff erwünscht ist
    		if(IDYES == MessageBox(hwnd, TEXT("Information aus dem Internet aktualisieren?"), szAppName, 
    								MB_YESNO | MB_ICONQUESTION))
    		//Dialogfeld anzeigen
    		DialogBox(hInst, szAppName, hwnd, DlgProc);
    
    		//Anzeige aktualisieren
    		SendMessage(hwnd, WM_USER_GETFILES, 0, 0);
    
    	case WM_USER_GETFILES:
    		SetCursor(LoadCursor(NULL, IDC_WAIT));
    		ShowCursor(TRUE);
    
    		//Dateiliste vom lokalen Datenträger lesen
    		plist = GetFileList();
    
    		ShowCursor(FALSE);
    		SetCursor(LoadCursor(NULL, IDC_ARROW));
    
    		//WM_SIZE-Nachricht zur Anpassung der Bildlaufleiste erzeugen
    		SendMessage(hwnd, WM_SIZE, 0, MAKELONG(cxClient, cyClient));
    		InvalidateRect(hwnd, NULL, TRUE);
    		return 0;
    
    	case WM_PAINT:
    		hdc = BeginPaint(hwnd, &ps);
    		SetTextAlign(hdc, TA_UPDATECP);
    
    		si.cbSize = sizeof(SCROLLINFO);
    		si.fMask = SIF_POS;
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		if(plist)
    		{
    			for(i=0; i<plist->iNum; i++)
    			{
    				MoveToEx(hdc, cxChar, (i-si.nPos)*cyChar, NULL);
    				TextOut(hdc, 0, 0, plist->info[i].szFilename, lstrlen(plist->info[i].szFilename));
    				TextOut(hdc, 0, 0, TEXT(": "), 2);
    				TextOutA(hdc, 0, 0, plist->info[i].szContents, strlen(plist->info[i].szContents));
    			}
    		}
    		EndPaint(hwnd, &ps);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	}
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    
    BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static PARAMS params;
    
    	switch(message)
    	{
    	case WM_INITDIALOG:
    		params.bContinue = TRUE;
    		params.hwnd = hwnd;
    
    		_beginthread(FtpThread, 0, &params);
    		return true;
    
    	case WM_COMMAND:
    		switch(LOWORD (wParam))
    		{
    		case IDCANCEL:			//Benutzer bricht Download ab
    			params.bContinue = FALSE;
    			return TRUE;
    		case IDOK:
    			EndDialog(hwnd, 0);
    			return TRUE;
    		}
    	}
    	return FALSE;
    }
    
    //FtpThread: Liest Dateien von FTP-Server und kopiert sie auf lokalen Datenträger
    
    void FtpThread(PVOID parg)
    {
    	BOOL			bSuccess;
    	HINTERNET		hIntSession, hFtpSession, hFind;
    	HWND			hwndStatus, hwndButton;
    	PARAMS			*pparams;
    	TCHAR			szBuffer[64];
    	WIN32_FIND_DATA	finddata;
    
    	pparams = parg;
    	hwndStatus = GetDlgItem(pparams->hwnd, IDC_STATUS);
    	hwndButton = GetDlgItem(pparams->hwnd, IDCANCEL);
    
    	//Internet-Sitzung anlegen
    	hIntSession = InternetOpen(szAppName, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0) ;
    
    	if(hIntSession == NULL)
    	{
    		wsprintf(szBuffer, TEXT("InternetOpen: Fehler %i"), GetLastError());
    		ButtonSwitch(hwndStatus, hwndButton, szBuffer);
    		_endthread();
    	}
    
    	SetWindowText(hwndStatus, TEXT("Internet-Sitzung eröffnet..."));
    
    	//Hat der Besitzer "Abbrechen" geklickt?
    	if(!pparams->bContinue)
    	{
    		InternetCloseHandle(hIntSession);
    		ButtonSwitch(hwndStatus, hwndButton, NULL);
    		_endthread();
    	}
    
    	//FTP-Sitzung anlegen
    	hFtpSession = InternetConnect(hIntSession, FTPSERVER, INTERNET_DEFAULT_FTP_PORT, NULL, NULL, INTERNET_SERVICE_FTP,
    									0, 0);
    	if(hFtpSession == NULL)
    	{
    		InternetCloseHandle(hIntSession);
    		wsprintf(szBuffer, TEXT("InternetConnect: Fehler %i", GetLastError()));
    		ButtonSwitch(hwndStatus, hwndButton, NULL);
    		_endthread();
    	}
    
    	SetWindowText(hwndStatus, TEXT("FTP-Sitzung eröffnet..."));
    
    	//Hat der Besitzer "Abbrechen" geklickt?
    	if(!pparams->bContinue)
    	{
    		InternetCloseHandle(hFtpSession);
    		InternetCloseHandle(hIntSession);
    		ButtonSwitch(hwndStatus, hwndButton, NULL);
    		_endthread();
    	}
    
    	//FTP-Verzeichnis wählen
    	bSuccess = FtpSetCurrentDirectory(hFtpSession, DIRECTORY);
    
    	if(!bSuccess)
    	{
    		InternetCloseHandle(hFtpSession);
    		InternetCloseHandle(hIntSession);
    		wsprintf(szBuffer, TEXT("Fehler beim Wechsel ins FTP-Verzeicnis %s"), DIRECTORY);
    		ButtonSwitch(hwndStatus, hwndButton, NULL);
    		_endthread();
    	}
    
    	SetWindowText(hwndStatus, TEXT("Verzeichnis gefunden..."));
    
    	//Hat Benutzer "Abbrechen" geklickt?
    	if(!pparams->bContinue)
    	{
    		InternetCloseHandle(hFtpSession);
    		InternetCloseHandle(hIntSession);
    		ButtonSwitch(hwndStatus, hwndButton, NULL);
    		_endthread();
    	}
    
    	//Erste Datei lesen, die ins Suchmuster passt
    	hFind = FtpFindFirstFile(hFtpSession, TEMPLATE, &finddata, 0, 0);
    
    	if(hFind == NULL)
    	{
    		InternetCloseHandle(hFtpSession);
    		InternetCloseHandle(hIntSession);
    		ButtonSwitch(hwndStatus, hwndButton, TEXT("Datei(en) nicht gefunden"));
    		_endthread();
    	}
    
    	do
    	{
    		if(!pparams->bContinue)		//"Abbrechen"
    		{
    			InternetCloseHandle(hFind);
    			InternetCloseHandle(hFtpSession);
    			InternetCloseHandle(hIntSession);
    			ButtonSwitch(hwndStatus, hwndButton, NULL);
    			_endthread();
    		}
    
    		//Datei vom FTP-Server auf den lokalen Datenträger kopieren - aber nur, wenn sie dort noch nicht existiert
    		wsprintf(szBuffer, TEXT("Lese Date $s..."), finddata.cFileName, TRUE, FILE_ATTRIBUTE_NORMAL, 
    					FTP_TRANSFER_TYPE_BINARY, 0);
    	}
    	while(InternetFindNextFile(hFind, &finddata));
    
    	InternetCloseHandle(hFind);
    	InternetCloseHandle(hFtpSession);
    	InternetCloseHandle(hIntSession);
    
    	ButtonSwitch(hwndStatus, hwndButton, TEXT("Download aus dem Internet beendet"));
    }
    
    // ButtonSwitch: Ausgabe der letzten Statusmeldung und Umbennenung der Schaltfläche "Abbrechen" in OK
    
    VOID ButtonSwitch(HWND hwndStatus, HWND hwndButton, TCHAR *szText)
    {
    	if(szText)
    		SetWindowText(hwndStatus, szText);
    	else
    		SetWindowText(hwndStatus, TEXT("Internet-Sitzung abgebrochen"));
    
    	SetWindowText(hwndButton, TEXT("OK"));
    	SetWindowLong(hwndButton, GWL_ID, IDOK);
    }
    
    //GetFileList: Daten von lokalem Datenträger lesen, Namen und Inhalte speichern
    FILELIST *GetFileList(void)
    {
    	DWORD				dwRead;
    	FILELIST			*plist;
    	HANDLE				hFile, hFind;
    	int					iSize, iNum;
    	WIN32_FIND_DATA		finddata;
    
    	hFind = FindFirstFile(TEMPLATE, &finddata);
    
    	if(hFind == INVALID_HANDLE_VALUE)
    		return NULL;
    
    	plist	= NULL;
    	iNum	= 0;
    
    	do
    	{
    		//Datei öffnen und Größe ermitteln
    		hFile = CreateFile(finddata.cFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    
    		if(hFile == INVALID_HANDLE_VALUE)
    			continue;
    
    		iSize = GetFileSize(hFile, NULL);
    
    		if(iSize == (DWORD)-1)
    		{
    			CloseHandle(hFile);
    			continue;
    		}
    
    		//FILELIST-Struktur für den neuen Eintrag umkopieren und vergrößern
    		plist = realloc(plist, sizeof(FILELIST)+iNum*sizeof(FILEINFO));
    
    		//Speicherplatz für den Dateinamen anfordern, Dateinamen speichern
    		plist->info[iNum].szFilename = malloc(lstrlen(finddata.cFileName)+sizeof(TCHAR));
    		lstrcpy(plist->info[iNum].szFilename, finddata.cFileName);
    
    		//Speicherplatz für den Dateiinhalt anfordern, Dateiinhalt speichern
    		plist->info[iNum].szContents = malloc(iSize+1);
    		ReadFile(hFile, plist->info[iNum].szContents, iSize, &dwRead, NULL);
    		plist->info[iNum].szContents[iSize] = 0;
    
    		CloseHandle(hFile);
    		iNum++;
    	}
    	while(FindNextFile (hFile, &finddata));
    
    	FindClose(hFile);
    
    	//Dateien nach Namen sortieren
    	qsort(plist->info, iNum, sizeof(FILEINFO), Compare);
    
    	plist->iNum = iNum;
    
    	return plist;
    }
    
    //Vergleich für qsort
    int Compare(const FILEINFO *pinfo1, const FILEINFO *pinfo2)
    {
    	return lstrcmp(pinfo2->szFilename, pinfo1->szFilename);
    }
    

    Ich habe mittlerweile schon 6 Fehler behoben, 5 sind leider noch übrig:

    main.cpp(409): error C2440: '=' : 'void *' kann nicht in 'char *' konvertiert werden
    main.cpp(250): error C2440: '=' : 'PVOID' kann nicht in 'PARAMS *' konvertiert werden
    main.cpp(402): error C2440: '=' : 'void *' kann nicht in 'FILELIST *' konvertiert werden
    main.cpp(405): error C2440: '=' : 'void *' kann nicht in 'TCHAR *' konvertiert werden
    main.cpp(421): error C2664: 'qsort' : Konvertierung des Parameters 4 von 'int (const FILEINFO *,const FILEINFO *)' in 'int (__cdecl *)(const void *,const void *)' nicht möglich
    main.cpp(281): warning C4002: Zu viele übergebene Parameter für das Makro 'TEXT'
    main.cpp(89): warning C4244: 'return' : Konvertierung von 'WPARAM' in 'int', möglicher Datenverlust
    main.cpp(199): warning C4267: 'Argument' : Konvertierung von 'size_t' nach 'int', Datenverlust möglich
    

    Ich hoffe mir kann jemand etwas unter die Arme greifen dabei.

    MfG,
    Zoran



  • schonmal mit dateiendung .c versucht?



  • Frag im C++ Forum oder im Compilerforum...

    Beim Borland würdest du andere Fehler angezeigt bekommen...
    Und dein Problem ist wie an den Fehlermeldungen erkennbar ein C-Problem.

    MfG.



  • Dank der Änderung auf .c und ein paar Änderungen im Code klappt es. Vielen Dank!


Anmelden zum Antworten