Nightfall的文章

  • 基于WinInet的FTP文件下载实现

    背景对于在网络之间的文件传输,我们通常使用FTP传输协议。因为,FTP就是专门为了文件传输而生的,传输效率高,稳定性好。所以,FTP文件传输协议,是我们网络传输中常用的协议。
    为了学习这方面的开发知识,自己专门写了个使用Windows提供的WinInet库实现了FTP的文件传输的基本功能。现在,我就把基于WinInet库实现的FTP文件下载和FTP文件上传分成两个文档分别进行解析。本文介绍的是基于WinInet的FTP文件下载的实现。
    函数介绍介绍FTP下载文件使用到的主要的WinInet库中的API函数。
    1. InternetOpen 介绍
    初始化一个应用程序,以使用 WinINet 函数。
    函数声明
    HINTERNET InternetOpen(In LPCTSTR lpszAgent,In DWORD dwAccessType,In LPCTSTR lpszProxyName,In LPCTSTR lpszProxyBypass,In DWORD dwFlags);
    参数lpszAgent指向一个空结束的字符串,该字符串指定调用WinInet函数的应用程序或实体的名称。使用此名称作为用户代理的HTTP协议。dwAccessType指定访问类型,参数可以是下列值之一:



    Value
    Meaning




    INTERNET_OPEN_TYPE_DIRECT
    使用直接连接网络


    INTERNET_OPEN_TYPE_PRECONFIG
    获取代理或直接从注册表中的配置,使用代理连接网络


    INTERNETOPEN_TYPE_PRECONFIG WITH_NO_AUTOPROXY
    获取代理或直接从注册表中的配置,并防止启动Microsoft JScript或Internet设置(INS)文件的使用


    INTERNET_OPEN_TYPE_PROXY
    通过代理的请求,除非代理旁路列表中提供的名称解析绕过代理,在这种情况下,该功能的使用



    lpszProxyName指针指向一个空结束的字符串,该字符串指定的代理服务器的名称,不要使用空字符串;如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY,则此参数应该设置为NULL。
    lpszProxyBypass指向一个空结束的字符串,该字符串指定的可选列表的主机名或IP地址。如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY的 ,参数省略则为NULL。
    dwFlags参数可以是下列值的组合:



    VALUE
    MEANING




    INTERNET_FLAG_ASYNC
    使异步请求处理的后裔从这个函数返回的句柄


    INTERNET_FLAG_FROM_CACHE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND


    INTERNET_FLAG_OFFLINE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND



    返回值成功:返回一个有效的句柄,该句柄将由应用程序传递给接下来的WinInet函数。失败:返回NULL。

    2. InternetConnect 介绍
    建立 Internet 的连接。
    函数声明
    HINTERNET WINAPI InternetConnect( HINTERNET hInternet, LPCTSTR lpszServerName, INTERNET_PORT nServerPort, LPCTSTR lpszUserName, LPCTSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD dwContext);
    参数说明hInternet:由InternetOpen返回的句柄。lpszServerName:连接的ip或者主机名nServerPort:连接的端口。lpszUserName:用户名,如无置NULL。lpszPassword:密码,如无置NULL。dwService:使用的服务类型,可以使用以下

    INTERNET_SERVICE_FTP = 1:连接到一个 FTP 服务器上INTERNET_SERVICE_GOPHER = 2INTERNET_SERVICE_HTTP = 3:连接到一个 HTTP 服务器上
    dwFlags:文档传输形式及缓存标记。一般置0。dwContext:当使用回叫信号时, 用来识别应用程序的前后关系。返回值成功返回非0。如果返回0。要InternetCloseHandle释放这个句柄。

    3. FtpOpenFile介绍
    启动访问FTP服务器上的远程文件以进行读取或写入。
    函数声明
    HINTERNET FtpOpenFile( _In_ HINTERNET hConnect, _In_ LPCTSTR lpszFileName, _In_ DWORD dwAccess, _In_ DWORD dwFlags, _In_ DWORD_PTR dwContext);
    参数

    hConnect [in]处理FTP会话。
    lpszFileName [in]指向包含要访问的文件名称的以NULL结尾的字符串。
    dwAccess [in]文件访问。 该参数可以是GENERIC_READ或GENERIC_WRITE,但不能同时使用。
    dwFlags [in]转移发生的条件。 应用程序应选择一种传输类型,以及指示文件缓存如何被控制的任何标志。传输类型可以是以下值之一。




    VALUE
    MEANING




    FTP_TRANSFER_TYPE_ASCII
    使用FTP的ASCII(类型A)传输方法传输文件。 控制和格式化信息被转换为本地等价物。


    FTP_TRANSFER_TYPE_BINARY
    使用FTP的图像(类型I)传输方法传输文件。 文件完全按照存在的方式进行传输,没有任何变化。 这是默认的传输方式。


    FTP_TRANSFER_TYPE_UNKNOWN
    默认为FTP_TRANSFER_TYPE_BINARY。


    INTERNET_FLAG_TRANSFER_ASCII
    以ASCII格式传输文件。


    INTERNET_FLAG_TRANSFER_BINARY
    将文件作为二进制文件传输。



    以下值用于控制文件的缓存。 应用程序可以使用这些值中的一个或多个。



    VALUE
    MEANING




    INTERNET_FLAG_HYPERLINK
    在确定是否从网络重新加载项目时,如果没有到期时间并且没有LastModified时间从服务器返回,则强制重新加载。


    INTERNET_FLAG_NEED_FILE
    如果无法缓存文件,则导致创建临时文件。


    INTERNET_FLAG_RELOAD
    强制从源服务器下载所请求的文件,对象或目录列表,而不是从缓存中下载。


    INTERNET_FLAG_RESYNCHRONIZE
    如果资源自上次下载以来已被修改,请重新加载HTTP资源。 所有FTP资源都被重新加载。




    dwContext [in]指向包含将此搜索与任何应用程序数据相关联的应用程序定义值的变量。 这仅在应用程序已经调用InternetSetStatusCallback来设置状态回调函数时才会使用。
    返回值

    如果成功则返回一个句柄,否则返回NULL。 要检索特定的错误消息,请调用GetLastError。

    4. InternetReadFile 介绍
    函数声明
    BOOL InternetReadFile( __in HINTERNET hFile,__out LPVOID lpBuffer,__in DWORD dwNumberOfBytesToRead,__out LPDWORD lpdwNumberOfBytesRead);
    参数

    hFile[in]
    由InternetOpenUrl,FtpOpenFile, 或HttpOpenRequest函数返回的句柄.
    lpBuffer[out]
    缓冲器指针
    dwNumberOfBytesToRead[in]
    欲读数据的字节量。
    lpdwNumberOfBytesRead[out]
    接收读取字节量的变量。该函数在做任何工作或错误检查之前都设置该值为零

    返回值成功:返回TRUE,失败,返回FALSE

    实现原理首先,我们先介绍下FTP的URL格式:
    FTP://账号:密码@主机/子目录或文件例如:ftp://admin:123456@192.168.0.1/mycode/520.zip
    其中,“FTP”就表示使用FTP传输数据;“账号”即登录FTP服务器的用户名;“密码”即登录FTP服务器用户名对应的密码;“主机”表示服务器的IP地址;“子目录或文件”即要进行操作的文件或目录的路径。
    在,WinInet库中,提供了InternetCrackUrl这个函数专门用于URL的分解,在我写的《URL分解之InternetCrackUrl》则篇文章中有详细介绍和使用方法。
    那么,基于WinInet库的FTP文件下载的原理如下所示:

    首先,我们先调用对URL进行分解,从URL中获取后续操作需要的信息。
    然后,使用InternetOpen初始化WinInet库,建立网络会话。
    接着,使用InternetConnect与服务器建立连接,并设置FTP数据传输方式。
    之后,我们就可以调用FtpOpenFile函数,根据文件路径,以GENERIC_READ的方式,打开文件并获取服务器上文件的句柄。
    接着,根据文件句柄,调用FtpGetFileSize获取文件的大小,并根据文件大小在程序申请一块动态内存,以便存储下载的数据。
    然后,就可以调用InternetReadFile从服务器上下载文件数据,并将下载的数据存放在上述申请的动态内存中。
    最后,关闭上述打开的句柄,进行清理工作。

    这样,就可以成功实现FTP文件下载的功能了。与服务器建立FTP连接后,我们使用WinInet库中FTP函数对服务器上文件的操作就如果使用Win32 API函数对本地文件操作一样方便。
    编码实现导入WinInet库文件#include <WinInet.h>#pragma comment(lib, "WinInet.lib")
    FTP文件下载// 数据下载// 输入:下载数据的URL路径// 输出:下载数据内容、下载数据内容长度BOOL FTPDownload(char *pszDownloadUrl, BYTE **ppDownloadData, DWORD *pdwDownloadDataSize){ // INTERNET_SCHEME_HTTPS、INTERNET_SCHEME_HTTP、INTERNET_SCHEME_FTP等 char szScheme[MAX_PATH] = { 0 }; char szHostName[MAX_PATH] = { 0 }; char szUserName[MAX_PATH] = { 0 }; char szPassword[MAX_PATH] = { 0 }; char szUrlPath[MAX_PATH] = { 0 }; char szExtraInfo[MAX_PATH] = { 0 }; ::RtlZeroMemory(szScheme, MAX_PATH); ::RtlZeroMemory(szHostName, MAX_PATH); ::RtlZeroMemory(szUserName, MAX_PATH); ::RtlZeroMemory(szPassword, MAX_PATH); ::RtlZeroMemory(szUrlPath, MAX_PATH); ::RtlZeroMemory(szExtraInfo, MAX_PATH); // 分解URL if (FALSE == Ftp_UrlCrack(pszDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH)) { return FALSE; } if (0 < ::lstrlen(szExtraInfo)) { // 注意此处的连接 ::lstrcat(szUrlPath, szExtraInfo); } HINTERNET hInternet = NULL; HINTERNET hConnect = NULL; HINTERNET hFTPFile = NULL; BYTE *pDownloadData = NULL; DWORD dwDownloadDataSize = 0; DWORD dwBufferSize = 4096; BYTE *pBuf = NULL; DWORD dwBytesReturn = 0; DWORD dwOffset = 0; BOOL bRet = FALSE; do { // 建立会话 hInternet = ::InternetOpen("WinInet Ftp Download V1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == hInternet) { Ftp_ShowError("InternetOpen"); break; } // 建立连接 hConnect = ::InternetConnect(hInternet, szHostName, INTERNET_INVALID_PORT_NUMBER, szUserName, szPassword, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); if (NULL == hConnect) { Ftp_ShowError("InternetConnect"); break; } // 打开FTP文件, 文件操作和本地操作相似 hFTPFile = ::FtpOpenFile(hConnect, szUrlPath, GENERIC_READ, FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_RELOAD, NULL); if (NULL == hFTPFile) { Ftp_ShowError("FtpOpenFile"); break;; } // 获取文件大小 dwDownloadDataSize = ::FtpGetFileSize(hFTPFile, NULL); // 申请动态内存 pDownloadData = new BYTE[dwDownloadDataSize]; if (NULL == pDownloadData) { break; } ::RtlZeroMemory(pDownloadData, dwDownloadDataSize); pBuf = new BYTE[dwBufferSize]; if (NULL == pBuf) { break; } ::RtlZeroMemory(pBuf, dwBufferSize); // 接收数据 do { bRet = ::InternetReadFile(hFTPFile, pBuf, dwBufferSize, &dwBytesReturn); if (FALSE == bRet) { Ftp_ShowError("InternetReadFile"); break; } ::RtlCopyMemory((pDownloadData + dwOffset), pBuf, dwBytesReturn); dwOffset = dwOffset + dwBytesReturn; } while (dwDownloadDataSize > dwOffset); } while (FALSE); // 返回数据 if (FALSE == bRet) { delete[]pDownloadData; pDownloadData = NULL; dwDownloadDataSize = 0; } *ppDownloadData = pDownloadData; *pdwDownloadDataSize = dwDownloadDataSize; // 释放内存并关闭句柄 if (NULL != pBuf) { delete []pBuf; pBuf = NULL; } if (NULL != hFTPFile) { ::InternetCloseHandle(hFTPFile); } if (NULL != hConnect) { ::InternetCloseHandle(hConnect); } if (NULL != hInternet) { ::InternetCloseHandle(hInternet); } return bRet;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ BYTE *pDownloadData = NULL; DWORD dwDownloadDataSize = 0; // 下载 if (FALSE == FTPDownload("ftp://admin:123456789@192.168.0.1/Flower520.zip", &pDownloadData, &dwDownloadDataSize)) { printf("FTP Download Error!\n"); } // 将数据保存为文件 Ftp_SaveToFile("myftpdownloadtest.zip", pDownloadData, dwDownloadDataSize); // 释放内存 delete []pDownloadData; pDownloadData = NULL; printf("FTP Download OK.\n"); system("pause"); return 0;}
    测试结果
    运行程序,程序提示下载成功。然后,打开目下查看下载文件,成功下载文件。

    总结在打开Internet会话并和服务器建立连接后,接下来使用WinInet库中的FTP函数对服务器上的文件操作,就如同在自己的计算机上使用Win32 API函数操作一样。都是打开或者创建文件,获取文件句柄,然后根据文件句柄,调用函数对文件进行读写操作,最后,关闭文件句柄。
    所以,大家注意和本地的文件操作进行类比下,就很容易理解了。
    参考参考自《Windows黑客编程技术详解》一书
    2  留言 2018-12-20 12:07:10
  • 基于WinInet的FTP文件上传实现

    背景对于在网络之间的文件传输,我们通常使用FTP传输协议。因为,FTP就是专门为了文件传输而生的,传输效率高,稳定性好。所以,FTP文件传输协议,是我们网络传输中常用的协议。
    为了学习这方面的开发知识,自己专门写了个使用Windows提供的WinInet库实现了FTP的文件传输的基本功能。现在,我就把基于WinInet库实现的FTP文件下载和FTP文件上传分成两个文档分别进行解析。本文介绍的是基于WinInet的FTP文件上传的实现。
    函数介绍介绍FTP上传文件使用到的主要的WinInet库中的API函数。
    1. InternetOpen 介绍
    初始化一个应用程序,以使用 WinINet 函数。
    函数声明
    HINTERNET InternetOpen(In LPCTSTR lpszAgent,In DWORD dwAccessType,In LPCTSTR lpszProxyName,In LPCTSTR lpszProxyBypass,In DWORD dwFlags);
    参数lpszAgent指向一个空结束的字符串,该字符串指定调用WinInet函数的应用程序或实体的名称。使用此名称作为用户代理的HTTP协议。dwAccessType指定访问类型,参数可以是下列值之一:



    Value
    Meaning




    INTERNET_OPEN_TYPE_DIRECT
    使用直接连接网络


    INTERNET_OPEN_TYPE_PRECONFIG
    获取代理或直接从注册表中的配置,使用代理连接网络


    INTERNETOPEN_TYPE_PRECONFIG WITH_NO_AUTOPROXY
    获取代理或直接从注册表中的配置,并防止启动Microsoft JScript或Internet设置(INS)文件的使用


    INTERNET_OPEN_TYPE_PROXY
    通过代理的请求,除非代理旁路列表中提供的名称解析绕过代理,在这种情况下,该功能的使用



    lpszProxyName指针指向一个空结束的字符串,该字符串指定的代理服务器的名称,不要使用空字符串;如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY,则此参数应该设置为NULL。
    lpszProxyBypass指向一个空结束的字符串,该字符串指定的可选列表的主机名或IP地址。如果dwAccessType未设置为INTERNET_OPEN_TYPE_PROXY的 ,参数省略则为NULL。
    dwFlags参数可以是下列值的组合:



    VALUE
    MEANING




    INTERNET_FLAG_ASYNC
    使异步请求处理的后裔从这个函数返回的句柄


    INTERNET_FLAG_FROM_CACHE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND


    INTERNET_FLAG_OFFLINE
    不进行网络请求,从缓存返回的所有实体,如果请求的项目不在缓存中,则返回一个合适的错误,如ERROR_FILE_NOT_FOUND



    返回值成功:返回一个有效的句柄,该句柄将由应用程序传递给接下来的WinInet函数。失败:返回NULL。

    2. InternetConnect 介绍
    建立 Internet 的连接。
    函数声明
    HINTERNET WINAPI InternetConnect( HINTERNET hInternet, LPCTSTR lpszServerName, INTERNET_PORT nServerPort, LPCTSTR lpszUserName, LPCTSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD dwContext);
    参数说明hInternet:由InternetOpen返回的句柄。lpszServerName:连接的ip或者主机名nServerPort:连接的端口。lpszUserName:用户名,如无置NULL。lpszPassword:密码,如无置NULL。dwService:使用的服务类型,可以使用以下

    INTERNET_SERVICE_FTP = 1:连接到一个 FTP 服务器上INTERNET_SERVICE_GOPHER = 2INTERNET_SERVICE_HTTP = 3:连接到一个 HTTP 服务器上
    dwFlags:文档传输形式及缓存标记。一般置0。dwContext:当使用回叫信号时, 用来识别应用程序的前后关系。返回值成功返回非0。如果返回0。要InternetCloseHandle释放这个句柄。

    3. FtpOpenFile 介绍
    启动访问FTP服务器上的远程文件以进行读取或写入。
    函数声明
    HINTERNET FtpOpenFile( _In_ HINTERNET hConnect, _In_ LPCTSTR lpszFileName, _In_ DWORD dwAccess, _In_ DWORD dwFlags, _In_ DWORD_PTR dwContext);
    参数

    hConnect [in]处理FTP会话。
    lpszFileName [in]指向包含要访问的文件名称的以NULL结尾的字符串。
    dwAccess [in]文件访问。 该参数可以是GENERIC_READ或GENERIC_WRITE,但不能同时使用。
    dwFlags [in]转移发生的条件。 应用程序应选择一种传输类型,以及指示文件缓存如何被控制的任何标志。传输类型可以是以下值之一。




    VALUE
    MEANING




    FTP_TRANSFER_TYPE_ASCII
    使用FTP的ASCII(类型A)传输方法传输文件。 控制和格式化信息被转换为本地等价物。


    FTP_TRANSFER_TYPE_BINARY
    使用FTP的图像(类型I)传输方法传输文件。 文件完全按照存在的方式进行传输,没有任何变化。 这是默认的传输方式。


    FTP_TRANSFER_TYPE_UNKNOWN
    默认为FTP_TRANSFER_TYPE_BINARY。


    INTERNET_FLAG_TRANSFER_ASCII
    以ASCII格式传输文件。


    INTERNET_FLAG_TRANSFER_BINARY
    将文件作为二进制文件传输。



    以下值用于控制文件的缓存。 应用程序可以使用这些值中的一个或多个。



    VALUE
    MEANING




    INTERNET_FLAG_HYPERLINK
    在确定是否从网络重新加载项目时,如果没有到期时间并且没有LastModified时间从服务器返回,则强制重新加载。


    INTERNET_FLAG_NEED_FILE
    如果无法缓存文件,则导致创建临时文件。


    INTERNET_FLAG_RELOAD
    强制从源服务器下载所请求的文件,对象或目录列表,而不是从缓存中下载。


    INTERNET_FLAG_RESYNCHRONIZE
    如果资源自上次下载以来已被修改,请重新加载HTTP资源。 所有FTP资源都被重新加载。




    dwContext [in]指向包含将此搜索与任何应用程序数据相关联的应用程序定义值的变量。 这仅在应用程序已经调用InternetSetStatusCallback来设置状态回调函数时才会使用。
    返回值

    如果成功则返回一个句柄,否则返回NULL。 要检索特定的错误消息,请调用GetLastError。

    4. InternetWriteFile 介绍
    函数声明
    BOOL InternetWriteFile( __in HINTERNET hFile,__out LPVOID lpBuffer,__in DWORD dwNumberOfBytesToRead,__out LPDWORD lpdwNumberOfBytesRead);
    参数

    hFile[in]
    由InternetOpenUrl,FtpOpenFile, 或HttpOpenRequest函数返回的句柄.
    lpBuffer[out]
    缓冲器指针
    dwNumberOfBytesToRead[in]
    欲写入数据的字节量。
    lpdwNumberOfBytesRead[out]
    接收写入字节量的变量。该函数在做任何工作或错误检查之前都设置该值为零

    返回值成功:返回TRUE,失败,返回FALSE

    实现原理首先,我们先介绍下FTP的URL格式:
    FTP://账号:密码@主机/子目录或文件例如:ftp://admin:123456@192.168.0.1/mycode/520.zip
    其中,“FTP”就表示使用FTP传输数据;“账号”即登录FTP服务器的用户名;“密码”即登录FTP服务器用户名对应的密码;“主机”表示服务器的IP地址;“子目录或文件”即要进行操作的文件或目录的路径。
    在,WinInet库中,提供了InternetCrackUrl这个函数专门用于URL的分解,在我写的《URL分解之InternetCrackUrl》则篇文章中有详细介绍和使用方法。
    那么,基于WinInet库的FTP文件上传的原理如下所示:

    首先,我们先调用对URL进行分解,从URL中获取后续操作需要的信息。
    然后,使用InternetOpen初始化WinInet库,建立网络会话。
    接着,使用InternetConnect与服务器建立连接,并设置FTP数据传输方式。
    之后,我们就可以调用FtpOpenFile函数,根据文件路径,以GENERIC_WRITE的方式创建文件,并获取服务器上文件的句柄。
    接着,就可以调用InternetWriteFile把本地文件数据上传到服务器上,写入上述创建的文件中。
    最后,关闭上述打开的句柄,进行清理工作。

    这样,就可以成功实现FTP文件上传的功能了。与服务器建立FTP连接后,我们使用WinInet库中FTP函数对服务器上文件的操作就如果使用Win32 API函数对本地文件操作一样方便。
    编码实现导入WinInet库文件#include <WinInet.h>#pragma comment(lib, "WinInet.lib")
    FTP文件上传// 数据上传// 输入:上传数据的URL路径、上传数据内容、上传数据内容长度BOOL FTPUpload(char *pszUploadUrl, BYTE *pUploadData, DWORD dwUploadDataSize){ // INTERNET_SCHEME_HTTPS、INTERNET_SCHEME_HTTP、INTERNET_SCHEME_FTP等 char szScheme[MAX_PATH] = { 0 }; char szHostName[MAX_PATH] = { 0 }; char szUserName[MAX_PATH] = { 0 }; char szPassword[MAX_PATH] = { 0 }; char szUrlPath[MAX_PATH] = { 0 }; char szExtraInfo[MAX_PATH] = { 0 }; ::RtlZeroMemory(szScheme, MAX_PATH); ::RtlZeroMemory(szHostName, MAX_PATH); ::RtlZeroMemory(szUserName, MAX_PATH); ::RtlZeroMemory(szPassword, MAX_PATH); ::RtlZeroMemory(szUrlPath, MAX_PATH); ::RtlZeroMemory(szExtraInfo, MAX_PATH); // 分解URL if (FALSE == Ftp_UrlCrack(pszUploadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH)) { return FALSE; } if (0 < ::lstrlen(szExtraInfo)) { // 注意此处的连接 ::lstrcat(szUrlPath, szExtraInfo); } HINTERNET hInternet = NULL; HINTERNET hConnect = NULL; HINTERNET hFTPFile = NULL; DWORD dwBytesReturn = 0; BOOL bRet = FALSE; do { // 建立会话 hInternet = ::InternetOpen("WinInet Ftp Upload V1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == hInternet) { Ftp_ShowError("InternetOpen"); break; } // 建立连接 hConnect = ::InternetConnect(hInternet, szHostName, INTERNET_INVALID_PORT_NUMBER, szUserName, szPassword, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); if (NULL == hConnect) { Ftp_ShowError("InternetConnect"); break; } // 打开FTP文件, 文件操作和本地操作相似 hFTPFile = ::FtpOpenFile(hConnect, szUrlPath, GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_RELOAD, NULL); if (NULL == hFTPFile) { Ftp_ShowError("FtpOpenFile"); break;; } // 上传数据 bRet = ::InternetWriteFile(hFTPFile, pUploadData, dwUploadDataSize, &dwBytesReturn); if (FALSE == bRet) { break; } } while (FALSE); // 释放内存并关闭句柄 if (NULL != hFTPFile) { ::InternetCloseHandle(hFTPFile); } if (NULL != hConnect) { ::InternetCloseHandle(hConnect); } if (NULL != hInternet) { ::InternetCloseHandle(hInternet); } return bRet;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ if (FALSE == FTPUpload("ftp://admin:123456789@192.168.0.1/myftpuploadtest.txt", (BYTE *)"Hello Wolrd", 12)) { printf("FTP Upload Error.\n"); } printf("FTP Upload OK.\n"); system("pause"); return 0;}
    测试结果
    运行程序,程序提示上传成功。然后,使用FTP管理工具,查看服务器目录,发现文件上传成功。

    然后,打开文件,查看文件数据内容,数据正确,所以程序测试成功。

    总结在打开Internet会话并和服务器建立连接后,接下来使用WinInet库中的FTP函数对服务器上的文件操作,就如同在自己的计算机上使用Win32 API函数操作一样。都是打开或者创建文件,获取文件句柄,然后根据文件句柄,调用函数对文件进行读写操作,最后,关闭文件句柄。
    所以,大家注意和本地的文件操作进行类比下,就很容易理解了。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2018-12-20 12:07:17
  • 快速启动目录实现开机自启动

    背景想必实现程序开机自启动,是很常见的功能了。无论是恶意程序,还是正常的应用软件,都会提供这个功能,方便用户的使用。程序开机自启动,顾名思义,就是计算机开机后,不用人为地去运行程序,程序就可以自己运行起来。对于这个功能的,一直都是杀软重点监测的地方。因为,对于病毒来说,重要的不是如何被破坏,而是如何启动。
    在过去写的大大小小的程序中,我也实现过程序自启动的功能。现在,我把这些自启动功能的实现方式进行下总结。常见的方式有:修改开机自启动注册表、开机自启动目录、创建开机自启计划任务、创建开机自启系统服务等方式。现在对这些技术一一进行分析,并形成文档分享给大家。本文介绍的是快速启动目录实现开机自启动的方式,其它的实现方式可以搜索我写的相关系列的文档。
    函数介绍SHGetSpecialFolderPath 函数
    获取指定的系统路径。
    函数声明
    BOOL SHGetSpecialFolderPath( HWND hwndOwner, LPTSTR lpszPath, int nFolder, BOOL fCreate);
    参数

    hwndOwner
    窗口所有者的句柄。
    lpszPath
    返回路径的缓冲区,该缓冲区的大小至少为MAX_PATH。
    nFolder
    系统路径的CSIDL标识。




    VALUE
    MEANING




    CSIDL_ADMINTOOLS
    (用户)\开始菜单\程序\系统管理工具


    CSIDL_ALTSTARTUP
    未本地化的启动


    CSIDL_APPDATA
    (用户)\应用程序的数据


    CSIDL_BITBUCKET
    (桌面)\回收站


    CSIDL_CONTROLS
    我的电脑\控制面板


    CSIDL_COOKIES



    CSIDL_DESKTOP
    桌面


    CSIDL_DESKTOPDIRECTORY
    (用户)\桌面


    CSIDL_FAVORITES
    (用户)\个性化设置


    CSIDL_FONTS
    Windows\字体


    CSIDL_HISTORY



    CSIDL_INTERNET
    IE(桌面上的图标


    CSIDL_INTERNET_CACHE
    Internet缓存文件夹


    CSIDL_LOCAL_APPDATA
    (用户)\本地设置\应用程序数据


    CSIDL_DRIVES
    我的电脑


    CSIDL_MYPICTURES
    C:\Program Files\My Pictures


    CSIDL_NETHOOD
    (用户)\网上邻居中的元素


    CSIDL_NETWORK
    网上邻居


    CSIDL_PRINTERS
    我的电脑\打印机


    CSIDL_PRINTHOOD
    (用户)\打印机连接


    CSIDL_PERSONAL
    我的文档


    CSIDL_PROGRAM_FILES
    C:\Program Files


    CSIDL_PROGRAM_FILESX86
    x86 apps (Alpha)的程序文件目录


    CSIDL_PROGRAMS
    开始菜单\程序


    CSIDL_PROGRAM_FILES_COMMON
    Program Files\Common


    CSIDL_PROGRAM_FILES_COMMONX86
    RISC上的x86 \Program Files\Common


    CSIDL_RECENT
    (用户)\最近记录目录


    CSIDL_SENDTO
    (用户)\发送到目录


    CSIDL_STARTMENU
    (用户)\开始菜单


    CSIDL_STARTUP
    开始菜单\程序\启动


    CSIDL_SYSTEM
    System文件夹


    CSIDL_SYSTEMX86
    x86 apps (Alpha)的system文件夹


    CSIDL_TEMPLATES



    CSIDL_PROFILE
    用户概貌文件夹


    CSIDL_WINDOWS
    Windows目录


    CSIDL_COMMON_ADMINTOOLS
    (所有用户)\开始菜单\程序\系统管理工具


    CSIDL_COMMON_ALTSTARTUP
    未本地化的通用启动


    CSIDL_COMMON_APPDATA
    (所有用户)\应用程序数据


    CSIDL_COMMON_DESKTOPDIRECTORY
    (所有用户)\桌面


    CSIDL_COMMON_DOCUMENTS
    (所有用户)\文档


    CSIDL_COMMON_FAVORITES
    (所有用户)\设置


    CSIDL_COMMON_PROGRAMS
    (所有用户)\程序


    CSIDL_COMMON_STARTMENU
    (所有用户)\开始菜单


    CSIDL_COMMON_STARTUP
    (所有用户)\启动


    CSIDL_COMMON_TEMPLATES
    (所有用户)\临时




    fCreate
    指示文件夹不存在时是否创建。为FALSE则不创建,否则创建。

    返回值

    返回TRUE,表示执行成功;否则,执行失败。

    CopyFile 函数
    复制文件。
    函数声明
    BOOL CopyFile( LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName, BOOL bFailIfExists );
    参数

    pExistingFileName
    源文件名
    lpNewFileName
    目标文件名
    bFailIfExists
    如果设为TRUE(非零),那么一旦目标文件已经存在,则函数调用会失败。否则目标文件被改写。

    返回值

    返回非零TRUE表示成功,返回零FALSE表示失败。

    实现原理在 Windows 系统中有自带的快速启动文件夹,它是最常见的自启动方式。只要把程序就放入到这个文件夹中,系统启动就会自动地加载相应程序,实现开机自启动功能。
    我们可以使用 SHGetSpecialFolderPath 获取Windows系统上的快速启动目录路径,然后,使用 CopyFile 函数,将想要自启动程序拷贝到快速启动目录下即可。当然,为程序创建快捷方式,把快捷方式放入快速启动目录中,也同样达到开机自启动的效果。
    编码实现导入库文件因为 SHGetSpecialFolderPath 是在 “shell32.lib”静态库中,所以要导入库文件:
    #include <Shlobj.h>#pragma comment(lib, "shell32.lib")
    复制文件到快速启动目录下BOOL AutoRun_Startup(char *lpszSrcFilePath, char *lpszDestFileName){ BOOL bRet = FALSE; char szStartupPath[MAX_PATH] = {0}; char szDestFilePath[MAX_PATH] = {0}; // 获取 快速启动目录 路径 bRet = ::SHGetSpecialFolderPath(NULL, szStartupPath, CSIDL_STARTUP, TRUE); printf("szStartupPath=%s\n", szStartupPath); if (FALSE == bRet) { return FALSE; } // 构造拷贝的 目的文件路径 ::wsprintf(szDestFilePath, "%s\\%s", szStartupPath, lpszDestFileName); // 拷贝文件到快速启动目录下 bRet = ::CopyFile(lpszSrcFilePath, szDestFilePath, FALSE); if (FALSE == bRet) { return FALSE; } return TRUE;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。
    main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ if (FALSE == AutoRun_Startup("C:\\Users\\DemonGan\\Desktop\\520.exe", "520.exe")) { printf("Startup Error!\n"); } printf("Startup OK!\n"); system("pause"); return 0;}
    测试结果:
    运行程序,提示执行成功。

    我们根据显示的快速启动目录路径,去查看拷贝有没有成功。

    文件成功拷贝到了快速启动目录下,然后关机重启,程序成功自启动。
    总结这个程序不是很复杂,逻辑也比较清晰。主要是快速启动路径不是固定的,所以,要通过 SHGetSpecialFolderPath 函数去获取。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2018-12-20 12:18:20

发送私信

上辈子一千次的卖萌,终于换来你今生一次的回眸

18
文章数
23
评论数
eject