WinHttpGateway 库
具有异步调用机制和 HTTPS 协议支持的 WinHttp API 包装器库。

引言
WinHttpGateway 是一个动态库,用于简化 WinHttp API 的工作。它是该 API 的封装代码。WinHttp API 是 WinINet API 的替代品(有关 Microsoft 推荐使用 WinHttp API 的更多信息,请参阅 关于 WinHTTP)。该库的特点是使用异步数据传输模式。该库被用作动态连接模块,并提供了一个简单的接口,使得可以从任何线程独立于其他线程调用 HTTP 协议。
库使用快速概述。
动态模块导出函数
由于该库是作为动态连接的,实际上它只导出一个函数(即 GetInterface)。
extern "C" LPVOID __declspec(dllexport) GetInterface() { return static_cast(&g_winhttplauncher); } //WinHttpGateway\HttpGateway.cpp
该函数返回一个接口指针,该接口提供了该库的功能。为了方便在 C++ 中使用,该库的代码封装了 IWinHttpLauncher 接口(CWinHttpLauncherStub 类,它提供了调用动态 WinHttpGateway.dll 模块的机制)。要使用该库,只需包含 WinHttpGateway.h 文件。然后连接到库模块,您应该创建一个 CWinHttpLauncherStub 类。
使用示例可以在 WinHttpGatewayTest 项目中找到。
WinHttpGateway.dll 模块的基本用法。
int _tmain(int _argc, TCHAR* _argv[], TCHAR* _envp[]) { // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; return 1; } CWinHttpLauncherStub winhttp; CString sPath = _T("WinHttpGateway.dll"); if(!winhttp.load(sPath) || !winhttp) { CString mess; mess.Format(_T("Can`t load %s"),(LPCTSTR)sPath); ::AfxMessageBox(mess,MB_OK|MB_ICONERROR); return 1; } CString sProxyURL = _T(""); CString sBypass = _T(""); CString sLogin = _T(""); CString sPassword = _T(""); DWORD dwErrCode = 0; LPTSTR szError = NULL; if(!winhttp.initilize(IWinHttpLauncher::PAT_NO_PROXY,sProxyURL,sBypass,&dwErrCode,&szError)) { CString mess; mess.Format(_T("Initialization error code = %d \"%s\""),dwErrCode,szError); ::AfxMessageBox(mess,MB_OK|MB_ICONERROR); winhttp.free_string(szError); return 1; } winhttp.set_options( IWinHttpLauncher::Option_ProxyLogin ,IWinHttpLauncher::OptionScope_Root ,(LPVOID)(LPCTSTR)sLogin ); winhttp.set_options( IWinHttpLauncher::Option_ProxyPassword ,IWinHttpLauncher::OptionScope_Root ,(LPVOID)(LPCTSTR)sPassword ); CString sUrl = _T("https://codeproject.org.cn/info/about.aspx"); CString sMethod = _T("GET"); CString sHeaders = _T(""); CString sData2Send = _T(""); LPTSTR szHeaders = NULL; LPTSTR szContentType = NULL; LPTSTR szReadedData = NULL; if(!winhttp.request( sUrl,sMethod,sData2Send,sHeaders ,&szHeaders,&szContentType,&szReadedData ,&dwErrCode,&szError ) ) { CString mess; mess.Format(_T("Request error [url=%s] code = %d \"%s\""),(LPCTSTR)sUrl,dwErrCode,szError); ::AfxMessageBox(mess,MB_OK|MB_ICONERROR); winhttp.free_string(szError); return 1; } long i = 0; CString sReadedData = szReadedData; cout << _T("Data was readed") << endl; cout << _T("ContentType = ") << szContentType << endl; cout << _T("Data length ") << sReadedData.GetLength() << endl; cout << _T("Data:") << endl; for(i=0;i<sReadedData.GetLength();i+=256) cout << (LPCTSTR)sReadedData.Mid(i,256); winhttp.free_string(szHeaders); winhttp.free_string(szContentType); winhttp.free_string(szReadedData); return 0; }
源代码可以在“contest”文件夹中找到。如果使用代理服务器,则需要在初始化时使用其他参数,并且有必要设置代理服务器的 URL,以及用户的登录名和密码(如果需要)。
更复杂的库用法示例可以在测试示例(WinHttpGatewayTest 文件夹)中找到。
我将对 IWinHttpLauncher
接口进行简要说明。
IWinHttpLauncher 接口已在代码中记录(WinHttpGateway.h 文件)。
枚举。
enum ProxyAccessTypeEn
– 描述 WinHttp API 的操作模式。
enum OptionsEn
– 描述用于自定义 WinHttp API 的选项,这些选项用于 WinHttpGateway 动态模块的自定义。
enum OptionScopeEn
– 设置已安装选项的范围(请参阅“设置选项(通过 WinHttpGateway 配置 WinHttp API)”部分)。
函数。
void enable_tracing (BOOL _en) const
– 启用或禁用 WinHttp API 的跟踪(有关详细信息,请参阅 WinHttpTraceCfg.exe,跟踪配置工具)
BOOL check_platform () const
– 如果当前操作系统支持 WinHttp API,则返回 TRUE。
BOOL initilize (
– WinHttpGateway 模块初始化函数。可以选择使用或不使用代理服务器,安装代理服务器的 URL,以及要绕过此代理服务器的 URL 列表。之后,所有调用都将使用设置的值进行。
IN ProxyAccessTypeEn _proxyaccess
,IN LPCTSTR _proxyname
,IN LPCTSTR _proxybypass
,OUT DWORD* _dwErrCode
,OUT LPTSTR* _szError
);
BOOL set_options(
- 设置调用选项。
IN OptionsEn _option
,IN OptionScopeEn _scope
,IN LPVOID _lpdata
);
BOOL request(
– 使用
IN LPCTSTR _szUrl
,IN LPCTSTR _szMethod
,IN LPCTSTR _szData2Send
,IN LPCTSTR _szHeaders2Send
,OUT LPTSTR* _pszReadedHeaders
,OUT LPTSTR* _pszContentType
,OUT LPTSTR* _pszReadedData
,OUT DWORD* _dwErrCode
, OUT LPTSTR* _pszError
);_szUrl
参数指定的 URL,通过 _szMethod
方法,使用 _szData2Send
发送数据,以及 _szHeaders2Send
发送的头部。但重要的是,从并行线程调用时,独立的调用(请求将同时独立地进行)。
BOOL request(
– 与前一个函数类似,但是如果服务器响应的类型为“text/xml”,则将以两个字符串列表的形式返回(第一个列表将返回标签名称,第二个列表将返回这些标签的值)。返回的数据还可以通过 xslt 转换进行处理(参数
IN LPCTSTR _szUrl
,IN LPCTSTR _szMethod
,IN LPCTSTR _szData2Send
,IN LPCTSTR _szHeaders2Send
,IN LPCTSTR _szXSLTFName
,OUT LPTSTR* _pszReadedHeaders
,OUT LPTSTR* _pszContentType
,OUT LPTSTR* _pszReadedData
,OUT LPTSTR** _ppszTagNames
,OUT LPTSTR** _ppszTagValues
,OUT DWORD* _dwErrCode
,OUT LPTSTR* _pszError
);_szXSLTFName
,指定 xslt 转换的文件名(xstl 文件))。
void enum_certificates(
– 返回系统中已安装证书的列表。用于通过
OUT LPTSTR** _ppszStorage
,OUT LPTSTR** _ppszName
);set_options()
函数安装用于 HTTPS 调用(通过 set_options()
函数)的证书名称。
上面描述的函数,如果调用成功则返回 TRUE,否则返回 FALSE。发生错误时,错误消息文本会返回到参数 _pszError
,错误代码会返回到参数 _dwErrCode
。WinHttpGateway 模块定义了一些错误代码(请参阅 WinHttpGateway\messages.mc 文件),其他错误代码属于操作系统 API,包括 WinHttp API。另外,如果函数返回指向字符串或字符串数组的指针,则需要在客户端释放它们。最好使用 free_list()
和 free_string()
函数来释放这些分配的数据。
CWinHttpLauncherStub 类。
它旨在以类的形式向程序员呈现 WinHttpGateway 动态模块。
函数。
bool load(LPCTSTR _szDllName)
– 将 WinHttpGateway 模块加载到内存中,获取用于操作此 dll 模块的接口。
void unload()
– 卸载 WinHttpGateway(dll 模块)。
operator bool()
- 提供 CWinHttpLauncherStub 类的有效性信息。当模块成功加载并且可以调用该模块的 IWinHttpLauncher
接口提供的函数时,该类才有效。该类继承自 IWinHttpLauncher
接口,并在模块加载时将调用重定向到已加载的动态模块(或者仅输出断言而不进行任何调用)。
static void free_list(LPTSTR* _ppsz)
– 该函数释放为通过接口调用(WinHttpGateway 模块)接收到的字符串值列表分配的内存。
static long get_listlength(LPTSTR* _ppsz)
– 该函数返回通过接口 IWinHttpLauncher
函数(WinHttpGateway 模块)返回的字符串值列表的长度。
static void free_string(LPTSTR _psz)
– 该函数释放 WinHttpGateway 模块为字符串值分配的内存,并将该字符串值返回给使用该模块的代码。
有关该库的更多信息。
设置选项(通过 WinHttpGateway 配置 WinHttp API)。
由于 WinHttpGateway 模块的常规创建/使用方式是在一个(通常是主)线程中创建 CWinHttpLauncherStub
类,然后在不同线程(工作流)中使用,因此提供了已安装选项的范围设置器。因此,区分根选项(可以作为所有线程的默认选项使用的选项)和当前线程选项(仅在此线程中使用)。也就是说,如果在任何线程中使用某个选项作为默认选项而不是当前线程选项,那么该选项将从根选项集中获取(默认情况下,它们由默认值初始化(请参阅 WinHttpGateway\WinHttpGateway.h 文件,OptionsEn
枚举的说明以指定这些值))。
库的内部视图。
类的简要说明(按字母顺序)。
class CAutoLock
- 实现使用临界区对某些代码片段进行自动锁定的类。
template class CAutoRefPtr
- 用于实现数据智能指针的模板类。在代码中,此模板类用于支持按需接收的数据的各个部分。
struct CBuffer
– 数据缓冲区。它(目前)用于内部数据存储。它实现数据转换,以便在不同表示中使用这些数据。
class CCertificate
– 用于表示证书的类。存储证书存储名称和证书名称,并提供 PCCERT_CONTEXT
以在系统中表示证书。
class CCertificates
– 证书列表。该列表由为给定用户或计算机安装的所有证书组成(有关可能集合的数据从注册表中提取)。
struct CHttpVersionInfo
– 结构是系统结构 HTTP_VERSION_INFO
的包装器。
class CmpStrNoCase
– 一个扩展 CString 类并支持不区分大小写匹配的类。
class CPassword
– 用于表示密码的类。
template class CPropertyBase
– 用于实现基本数据类型选项类的模板类。
class CPropMassAction
– 执行对所有选项的操作。
class CPropMassActionIni
– 扩展 CPropMassAction
并实现选项在注册表中的保存/加载。
struct CRefCounter
– 引用计数基类。
struct CUrlComponents
– 系统结构 URL_COMPONENTSW
的包装器。
class CWinHttp
– 异步操作 WinHttp API 的类。
struct CWinHttp::CRequestInfo
- 请求信息集。
struct CWinHttpAsyncResult
– 系统结构 WINHTTP_ASYNC_RESULT
的包装器。
struct CWinHttpAutoproxyOptions
– 系统结构 WINHTTP_AUTOPROXY_OPTIONS
的包装器。
struct CWinHttpCertificateInfo
– 系统结构 WINHTTP_CERTIFICATE_INFO
的包装器。
struct CWinHttpCurrentUserIEProxyConfig
– 系统结构 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
的包装器。
class CWinHttpError
– WinHttpGateway 模块中使用的异常类。
class CWinHttpGatewayApp
- 模块实现类。
class CWinHttpLauncher
- 用于在以下模式下读取数据的实现:一个线程一个请求。IWinHttpLauncher
接口的内部实现。
class CWinHttpLauncherStub
– (面向程序员 - WinHttpGateway 模块的用户)IWinHttpLauncher
接口的包装器。该类旨在简化客户端代码与 WinHttpGateway 模块的操作。
class CWinHttpLog
– WinHttpGateway 模块的日志文件实现。
struct CWinHttpProxyInfo
– 系统结构 WINHTTP_PROXY_INFO
的包装器。
struct CWinHttpRequest
– 一个请求的实现。
struct CXSLTFile
– 用于支持 xslt 文件池以优化转换的类。
class CXSLTProcessor
– 数据处理器。实现 xslt 转换。
template class IniAccessorBase
– 用于实现用作选项的数据特性的模板类。
template class IniAccessorMassAction
– 用于将选项数据保存到 ini 文件中的机制(策略)的模板实现。
interface IWinHttpLauncher
– 与 WinHttpGateway 模块进行调用的接口
interface IWinHttpRequest
– 操作单个请求的接口。
template class NullAccessorAction
– 用于数据(不保存/加载任何内容)的 null 保存机制(策略)的模板实现。
库功能审查
接口类 IWinHttpLauncher
。
class CWinHttpLauncher
– 是 IWinHttpLauncher
接口的内部实现,通过它提供了 WinHttpGateway 库的功能。该类存储了一组选项,用于支持 WinHttp API 的配置。对 CWinHttp 类的引用,该类直接实现 WinHttp API 的操作(实现了异步调用机制)。还存储了证书列表,即 CCertificates
类的变量,该变量用于存储和表示系统中用于支持 WinHttpGateway 模块中证书操作的证书数据。
在初始化阶段,创建了一个 CWinHttp 类的对象,该对象随后用于组织请求。
该类主要工作发生在 request() 函数中。它包含一个 CWinHttpRequest 类的对象,该对象被初始化,传递给 CWinHttp 类,并简单地等待该请求的结束(行:request.waitEOR();
)。
选项设置通过 set_options() 函数进行,该函数仅设置存储选项列表的内部变量的值。(错误通过 SetLastError()
返回,可以通过调用 GetLastError()
函数获得,如果 set_options()
返回 FALSE)。
WinHttp API 操作的核心类
class CWinHttp
- 是该库功能的基础。它将组织异步请求处理:回调函数的安装、回调支持、数据传输、等待数据传输的可能性、保存/累积通过调用接收到的数据。此外,还支持处理一些错误,这些错误可以由该模块自动处理,并且只有在无法自动处理这些错误时才通知 WinHttpGateway 模块的用户。
由于操作是异步进行的,所以该类几乎所有的工作都包含在回调函数 CWinHttp::WinHttpStatusCallback()
的操作中,该函数调用启动了 WinHttpGateway 模块在处理 HTTP/HTTPS 请求方面的所有操作。我们将考虑来自 WinHttp API 的消息以及处理这些消息的函数。
WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER
WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER
WINHTTP_CALLBACK_STATUS_NAME_RESOLVED
WINHTTP_CALLBACK_STATUS_REDIRECT
WINHTTP_CALLBACK_STATUS_RESOLVING_NAME
由 CWinHttp::RequestStatus()
函数处理,该函数简单地将状态信息传递给请求类 CWinHttpRequest
。
WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED
WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE
WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED
WINHTTP_CALLBACK_STATUS_SENDING_REQUEST
WINHTTP_CALLBACK_STATUS_HANDLE_CREATED
– 未处理。
WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
由 CWinHttp::RequestDataAvailable()
函数处理。该消息在有数据可接收或通知不再有数据可用时出现。如果所有数据都已传输(大小 == 0),则请求将被关闭并结束处理。如果大小大于零,则会创建一个缓冲区,并调用 WinHttp API 中的函数来查询请求数据。
WINHTTP_CALLBACK_STATUS_HEADERS_AVAILAB
由 CWinHttp::RequestHeadersAvalable()
函数处理。该函数支持身份验证的可能性:来自网站和来自代理服务器,如果代理服务器或网站请求此类操作。
如果不需要身份验证,则直接请求传入数据的头部。然后请求读取此请求所需的数据大小。
WinHttpGateway 模块可以处理的“错误”(即请求证书和需要重复发送请求 ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED
、ERROR_WINHTTP_RESEND_REQUEST
)也得到额外处理。
WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE
由 CWinHttp::RequestIntermediateResponse()
函数处理,该函数不做其他事情。
WINHTTP_CALLBACK_STATUS_READ_COMPLETE
由 CWinHttp::RequestReadComplete()
函数处理。该函数将缓冲区从“读取数据”状态转到“数据已读取”状态,并查询新的数据块。
WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
由 CWinHttp::RequestError()
处理。该函数处理 WinHttpGateway 模块可以处理的错误,或形成错误消息并将其传输到表示请求数据的 CWinHttpRequest
类对象。
WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
由 CWinHttp::RequestSecureFailure()
函数处理。该函数安装忽略某些证书操作错误。
WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
由 CWinHttp::RequestSendCompleted()
函数处理。当发送的查询包含多个部分时,此消息出现,并且该函数强制传输连续的数据块。如果所有数据都已发送,则开始数据接收。
WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE
由 CWinHttp::RequestWriteCompleted()
函数处理,该函数几乎是 CWinHttp::RequestSendCompleted()
函数的精确副本,具有相同的功能。
其他函数。
CWinHttp::create()
– 根据 CWinHttpRequest
类对象的指针创建请求,该对象作为该函数的参数传递。在此函数中,对请求的 URL 进行分析,打开请求并发送它。
CWinHttp::Send()
– 发送请求。如何分派请求(根据错误信息和/或来自 WinHttp API 的消息),该函数安装指定的身份验证然后分派请求(也包括重复分派)。
CWinHttp::set_Certificate()
– 组织为某些请求安装证书的操作。
CWinHttp::add_request()
、CWinHttp::find_request()
、CWinHttp::remove_request()
– 支持函数:将请求添加到内部请求列表,根据其描述符搜索所需请求,以及删除请求(终止与请求的操作)。
void CWinHttp::free()
– 停止 CWinHttp 类对象运行的函数。
注意:可以使用/必须在 CWinHttp::RequestSecureFailure()
中自定义与证书操作相关的安全级别。
请求类
struct CWinHttpRequest
– 提供请求的类。该类实现了 IWinHttpRequest
接口,该接口在 CWinHttp
类中用于操作请求。该类用于在将请求传输到 CWinHttp
类之前进行请求配置,以及进一步按需获取数据。
注意:目前未实现此类的任何同步机制。
其他类。
template class CPool
– 用于组织池的模板类。池对象的类必须继承自 template struct Construct
类,该类对池数据项施加了特定约定。