23.4 WinInet和FTP
(1)WinInet接口(含HTTP、FTP)及FTP函数层次关系
(2)Ftp函数介绍
①InternetOpen——初始化,它告诉 Internet DLL 初始化内部数据结构并准备接收应用程序之后的其他调用。
参数 |
含义 |
LPCTSTR lpszAgent |
调用WinInet函数的应用程序名字,在HTTP协议中作为用户代理项 |
DWORD dwAccessType |
访问要求类型: INTERNET_OPEN_TYPE_DIRECT:解析所有本地主机,使用直接连接网络。 INTERNET_OPEN_TYPE_PRECONFIG:获取代理或直接从注册表中的配置,使用代理连接网络。 INTERNET_OPEN_TYEP_PRECONFIG_WITH_NO_AUTOPROXY:返回注册表中代理或直接配置,并防止Microsoft Jscript或INS文件的使用 INTERNET_OPEN_TYPE_PROXY:为代理传递请求 |
LPCTSTR lpszProxyName |
当dwAccessType指定为INTERNET_OPEN_TYPE_PROXY时,为代理服务器名字。 |
LPCTSTR lpszProxyBypass |
指定一个字符串,它提定一个可选的主机名列表或IP地址 |
DWORD dwFlags |
INTERNET_FLAG_ASYNC:仅作用于在该函数返回句柄的子句柄上的异步请求。 INTERNET_FLAG_FROM_CACHE:不做网络请求,所以的实体由缓存给出,如果请求的条目不在缓存中,会返回ERROR_FILE_NOT_FOUND错误。 INTERNET_FLAG_OFFLINE:与INTERNET_FLAG_FROM_CACHE一样。 |
举例:hIntSession = InternetOpen(szAppName,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,INTERNET_FLAG_ASYNC);
②InternetConnect——建立 Internet 的连接,返回连接句柄
参数 |
含义 |
HINTERNET hInternet |
由InternetOpen返回的句柄 |
LPCTSTR lpszServerName |
连接的IP或者主机名 |
INTERNET_PORT nServerPort |
连接的端口。如0,则为默认端口 |
LPCTSTR lpszUsername |
用户名,如无置NULL |
LPCTSTR lpszPassword |
密码,如无置NULL |
DWORD dwService |
使用的服务类型,可以使用以下 INTERNET_SERVICE_FTP(1):连接到一个FTP服务器 INTERNET_SERVICE_GOPHER(2):Gopher服务器 INTERNET_SERVICE_HTTP(3):连接到一个 HTTP 服务器上 |
DWORD dwFlags |
文档传输形式及缓存标记。一般置0。 |
DWORD_PTR dwContext |
当使用回叫信号时, 用来识别应用程序的前后关系 |
返回值:非0——成功;0——失败,要用InternetCloseHandle来关闭这个句柄 |
③FtpFindFirstFile——是用来设置ftp当前目录的
参数 |
含义 |
HINTERNET hConnect |
由InternetConnect返回的FTP会话句柄 |
LPCTSTR lpszSearchFile |
在FTP服务器上特定的目录里,要查询的指定名称的目录或文件名的文件,还可以使用通配符查找(如*、?) |
LPWIN32_FIND_DATA lpFindFileData |
就是用来保存查找后,所返回的文件的信息 |
DWORD dwFlags |
文档传输形式及缓存标记。一般置0。 |
DWORD_PTR dwContext |
当使用回叫信号时, 用来识别应用程序的前后关系。 一般为0 |
返回值:有效句柄表示成功。NULL表示失败,可用GetLastError返回错误代码。 |
④InternetFindNextFile——查找下一个文件或目录
参数 |
含义 |
HINTERNET hConnect |
hFind 是 FtpFindFirstFile 调用所返回的句柄 |
LPWIN32_FIND_DATA lpFindFileData |
用来保存查找后,所返回的文件的信息 |
返回值:如果调用成功, 返回 True,否则为False,可进一步调用GetLastError查询错误代码,如果为 ERROR_NO_MORE_FILES表明再没有文件存在了。边查找,边可以从lpFindFileData提取文件名/目录名。 |
⑤FtpGetFile——下载文件到本地磁盘
参数 |
含义 |
HINTERNET hConnect |
由InternetConnect返回的FTP会话句柄 |
LPCTSTR lpszRemoteFile |
FTP服务器上的文件名 |
LPCTSTR lpszNewFile |
本地机上创建的文件 |
BOOL fFailIfExists |
0——替换本地文件 1——如果本地文件已经存在则取消 |
DWORD dwFlagsAndAttributes |
用来指定本地文件的文件属性(详细参考CreateFile函数) FILE_ATTRIBUTE_NORMAL、FILE_ATTRIBUTE_HIDDEN、 FILE_ATTRIBUTE_READONLY、FILE_ATTRIBUTE_ARCHIVE等 |
DWORD dwFlags |
FTP_TRANSFER_TYPE_ASCII(1):用ASCII传输文件 FTP_TRANSFER_TYPE_BINARY(2):是用二进制传输文件 |
DWORD dwContext |
当使用回叫信号时, 用来识别应用程序的前后关系。 一般为0 |
返回值:有效句柄表示成功。NULL表示失败,可用GetLastError返回错误代码。 |
【UpdDemo程序】——读取FTP服务器上的文件
效果图
服务器上的目录结构 正在从FTP下载文件
软件运行时截图
/*------------------------------------------------------------ UPDDEMO.C -- Demonstrates Anonymous FTP Access (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #include <process.h> #include <wininet.h> #include "resource.h" #pragma comment(lib,"WININET.LIB") //用户自定义消息 #define WM_USER_CHECKFILE (WM_USER + 1) #define WM_USER_GETFILES (WM_USER + 2) //FTP下载的信息 #define FTPSERVER TEXT("ftp.5iedu.net") #define USERNAME TEXT("rollingstone") #define PASSWORD TEXT("approach304304") #define DIRECTORY TEXT("/rollingstone/web") #define TEMPLATE TEXT("UD??????.TXT") //指定文件名格式的模版(其中的?为通配符,eg.UD201507.TXT) //用来存储文件名和内容的结构体 typedef struct { TCHAR* szFileName; char* szContents; }FILEINFO,*PFILEINFO; typedef struct { int iNum; FILEINFO info[1]; }FILELIST,*PFILELIST; typedef struct //供子线程使用的结构体 { BOOL bContinue; HWND hwnd; }PARAMS,*PPARAMS; LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM); VOID FtpThread(PVOID); //程序所用到的其他函数的声明 FILELIST* GetFileList(VOID); int Compare(const FILEINFO *, const FILEINFO *); VOID ButtonSwitch(HWND, HWND, TCHAR*); TCHAR szAppName[] = TEXT("UpdDemo"); HINSTANCE hInst; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hwnd ; MSG msg ; WNDCLASS wndclass ; hInst = hInstance; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("Update Demo with Anonymous FTP"), // window caption WS_OVERLAPPEDWINDOW | WS_VSCROLL, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; //主窗口显示后,检查是否存在最新的文件 SendMessage(hwnd, WM_USER_CHECKFILE, 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 int cxClient, cyClient, cxChar, cyChar; static FILELIST* pList; SCROLLINFO si; HDC hdc; PAINTSTRUCT ps; SYSTEMTIME st; TCHAR szFileName[MAX_PATH]; int i; 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(SCROLLBARINFO); 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.nPos -= 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_CHECKFILE: //获得系统日期、文件名(年和月格式) GetSystemTime(&st); wsprintf(szFileName, TEXT("UD%04i%2i.TXT"), st.wYear, st.wMonth); //检查一下文件是否存在,如果存在,则读取所有文件 if (GetFileAttributes(szFileName) !=(DWORD)-1) { SendMessage(hwnd, WM_USER_GETFILES, 0, 0); return 0; } //如果文件不存在,则从Internet中获取 //但首先要检测磁盘的类型,当参数为NULL时,表示当前目录的驱动器类型 //如果程序当前的目录是在光驱上的,则退出(因为后面要拷贝文件到程序 //所在目录下。 if (DRIVE_CDROM == GetDriveType(NULL)) { MessageBox(hwnd, TEXT("Cannot run this program from CD-ROM!"), szAppName, MB_OK | MB_ICONEXCLAMATION); return 0; } //询问用户是否要连接到网络 if (IDYES == MessageBox(hwnd, TEXT("Update information from Internet?"), szAppName, MB_YESNO | MB_ICONQUESTION)) DialogBox(hInst, szAppName, hwnd, DlgProc); //更新显示 SendMessage(hwnd, WM_USER_GETFILES, 0, 0); return 0; case WM_USER_GETFILES: SetCursor(LoadCursor(NULL, IDC_WAIT)); ShowCursor(TRUE); //从磁盘文件中读取所有的文件列表 pList = GetFileList(); ShowCursor(FALSE); SetCursor(LoadCursor(NULL, IDC_ARROW)); //模拟发送一个WM_SIZE消息来改变滚动条和重绘(因为滚动条的大小与文件数量有关) SendMessage(hwnd, WM_SIZE, 0, MAKELONG(cxClient, cyClient)); InvalidateRect(hwnd, NULL, TRUE); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); //TA_UPDATECP:每次文字输出调用后当前基准点改变。当前位置作为基准点 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(":"), lstrlen(TEXT(":"))); TextOutA(hdc, 0, 0, pList->info[i].szContents, strlen(pList->info[i].szContents)); } } EndPaint(hwnd, &ps); return 0; case WM_DESTROY: if (pList) { //for (i = 0; i < pList->iNum; i++) //{ // if (pList->info[i].szFileName) // free(pList->info[i].szFileName); // if (pList->info[i].szContents) // free(pList->info[i].szContents); //} //free(pList->info); } 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, ¶ms); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: params.bContinue = FALSE; return TRUE; case IDOK: EndDialog(hwnd, 0); return TRUE; } return FALSE; case WM_CLOSE: EndDialog(hwnd, FALSE); return TRUE; } return FALSE; } //FtpThread:从FTP服务器读取文本,并拷贝到本地磁盘中 VOID FtpThread(PVOID parg) { BOOL bSuccess; PPARAMS pparams; HWND hwndStatus, hwndButton; HINTERNET hIntSession, hFtpSession, hFind; TCHAR szBuffer[64]; WIN32_FIND_DATA finddata; pparams = (PARAMS*)parg; hwndStatus = GetDlgItem(pparams->hwnd, IDC_STATUS); hwndButton = GetDlgItem(pparams->hwnd, IDCANCEL); //打开Internet会话 hIntSession = InternetOpen(szAppName, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_PASSIVE); //INTERNET_FLAG_ASYNC,本例用异步会不成功 if (NULL == hIntSession) { wsprintf(szBuffer, TEXT("InternetOpen error %i"), GetLastError()); ButtonSwitch(hwndStatus, hwndButton, szBuffer); _endthread(); } SetWindowText(hwndStatus, TEXT("Internet session opened...")); //检查用户是否按了取消按钮 if (!pparams->bContinue) { InternetCloseHandle(hIntSession); ButtonSwitch(hwndStatus, hwndButton, NULL); _endthread(); } //打开FTP会话 hFtpSession = InternetConnect(hIntSession, FTPSERVER, INTERNET_DEFAULT_FTP_PORT, USERNAME, PASSWORD, INTERNET_SERVICE_FTP, 0, 0); if (NULL == hFtpSession) { wsprintf(szBuffer, TEXT("InternetConnect error %i"), GetLastError()); ButtonSwitch(hwndStatus, hwndButton, szBuffer); InternetCloseHandle(hIntSession); _endthread(); } SetWindowText(hwndStatus, TEXT("Ftp session opened...")); //检查用户是否按了取消按钮 if (!pparams->bContinue) { InternetCloseHandle(hFtpSession); InternetCloseHandle(hIntSession); ButtonSwitch(hwndStatus, hwndButton, NULL); _endthread(); } //设置FTP当前目录 bSuccess = FtpSetCurrentDirectory(hFtpSession, DIRECTORY); if (!bSuccess) { wsprintf(szBuffer, TEXT("Cannot set directory to %s"),DIRECTORY); InternetCloseHandle(hFtpSession); InternetCloseHandle(hIntSession); ButtonSwitch(hwndStatus, hwndButton, szBuffer); _endthread(); } SetWindowText(hwndStatus, TEXT("Directory found...")); //检查用户是否按了取消按钮 if (!pparams->bContinue) { InternetCloseHandle(hFtpSession); InternetCloseHandle(hIntSession); ButtonSwitch(hwndStatus, hwndButton, NULL); _endthread(); } //获取第1个匹配特征文件名的文件 hFind = FtpFindFirstFile(hFtpSession, TEMPLATE, &finddata, 0, 0); if (NULL == hFind) { InternetCloseHandle(hFtpSession); InternetCloseHandle(hIntSession); ButtonSwitch(hwndStatus, hwndButton, TEXT("Cannot find files")); _endthread(); } //将所有文件名符合“UD??????.TXT”的文件下载到本地磁盘的当前目录下 do { //检查用户是否按了取消按钮 if (!pparams->bContinue) { InternetCloseHandle(hFtpSession); InternetCloseHandle(hIntSession); ButtonSwitch(hwndStatus, hwndButton, NULL); _endthread(); } wsprintf(szBuffer, TEXT("Reading file %s..."), finddata.cFileName); SetWindowText(hwndStatus, szBuffer); //将文件从Ftp服务器下载到本地磁盘,如果本地磁盘己经存在,则失败 FtpGetFile(hFtpSession, finddata.cFileName, 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("Internet Download Complete")); } //ButtonSwitch:用来显示最后的状态并将“取消”按钮改变为“OK”按钮 VOID ButtonSwitch(HWND hwndStatus, HWND hwndButton, TCHAR* szText) { if (szText) SetWindowText(hwndStatus, szText); else SetWindowText(hwndStatus, TEXT("Internet Session Cancelled")); SetWindowText(hwndButton, TEXT("OK")); SetWindowLong(hwndButton, GWL_ID, IDOK); } //从磁盘中读取文件并把各文件名和内容保存在指定的缓冲区中 FILELIST* GetFileList(VOID) { DWORD dwRead; FILELIST* pList; int iNum,iSize; HANDLE hFile, hFind; WIN32_FIND_DATA finddata; hFind = FindFirstFile(TEMPLATE, &finddata); if (hFind == INVALID_HANDLE_VALUE) return NULL; pList = NULL; iNum = 0; do { //打开文件并获得文件大小 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结构体,以增加一个新的项目 pList = realloc(pList, sizeof(FILELIST)+iNum*sizeof(FILEINFO)); //分配空间以存储文件名 pList->info[iNum].szFileName = malloc(lstrlen(finddata.cFileName) + sizeof(TCHAR)); lstrcpy(pList->info[iNum].szFileName, finddata.cFileName); //分配空间以存储文件的内容 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(hFind,&finddata)); //按文件名排序(降序) qsort(pList->info, iNum, sizeof (FILEINFO), Compare); pList->iNum = iNum; return pList; } int Compare(const FILEINFO * pInfo1, const FILEINFO * pInfo2) { return lstrcmp(pInfo2->szFileName, pInfo1->szFileName); }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 UpdDemo.rc 使用 // #define IDC_STATUS 1001 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1002 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//UdpDemo.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h " END 2 TEXTINCLUDE BEGIN "#include ""winres.h"" " "