将 WinINet 应用程序移植到 WinHTTP
Microsoft Windows HTTP Services (WinHTTP) 面向需要访问 HTTP 客户端堆栈的中间层和后端服务器应用程序。 Microsoft Windows Internet (WinINet) 为客户端应用程序提供 HTTP 客户端堆栈,以及访问文件传输协议 (FTP) 、SOCKSv4 和 Gopher 协议。 此概述有助于确定将 WinINet 应用程序移植到 WinHTTP 是否有益。 它还描述了特定的转换要求。
移植 WinINet 应用程序之前要考虑的事项
如果应用程序将受益于以下优势,请考虑将 WinINet 应用程序移植到 WinHTTP:
- 服务器安全的 HTTP 客户端堆栈。
- 最小化堆栈使用量。
- 服务器应用程序的可伸缩性。
- 减少对平台相关 API 的依赖关系。
- 支持线程模拟。
- 服务友好的 HTTP 堆栈。
- 访问可编写脚本的 WinHttpRequest 对象。
如果 WinINet 应用程序必须支持以下一项或多项,请不要考虑将 WinHTTP 移植到 WinHTTP:
- HTTP 堆栈中的 FTP 或 Gopher 协议。
- 支持 SOCKSv4 协议,用于与 SOCKS 代理通信。
- 自动拨号服务。
如果决定将应用程序移植到 WinHTTP,以下部分将指导你完成转换过程。
对于 WinINet 和 WinHTTP 的示例应用程序,请将 WinINet 的 AsyncDemo 示例与 WinHTTP 的 AsyncDemo 示例进行比较。
WinHTTP 等效于 WinINet 函数
下表列出了与 HTTP 客户端堆栈相关的 WinINet 函数以及 WinHTTP 等效项。
如果应用程序需要未列出的 WinINet 函数,请不要将应用程序移植到 WinHTTP。
异步请求的不同处理
请注意,在 WinINet 和 WinHTTP 中,某些函数可以同步或异步完成异步请求。 应用程序必须处理任一情况。 WinINet 和 WinHTTP 处理这些潜在异步函数的方式存在显著差异。
WinINet
同步完成:如果潜在异步 WinINet 函数调用同步完成,则函数的 OUT 参数将返回操作结果。 发生错误时,通过在 WinINet 函数调用后调用 GetLastError 来检索错误代码。
异步完成:如果可能的异步函数调用异步完成,则操作的结果和任何错误都可以在回调函数中访问。 回调函数在工作线程上执行,而不是在进行初始函数调用的线程上执行。
换句话说,应用程序必须复制逻辑,以在两个位置处理此类操作的结果:立即在函数调用后和回调函数中。
WinHTTP 简化了此模型,使你能够仅在回调函数中实现操作逻辑,该回调函数接收完成通知,而不管操作是同步完成还是异步完成。 启用异步操作后,WinHTTP 函数的 OUT 参数不会返回有意义的数据,必须设置为 NULL。
从应用程序的角度来看,WinHTTP 中异步完成和同步完成之间的唯一显著区别在于执行回调函数的位置。
WinHTTP
同步完成:当操作同步完成时,结果将在与原始函数调用相同的线程中执行的回调函数中返回。
异步完成:当操作异步完成时,将在工作线程中执行的回调函数中返回结果。
尽管大多数错误也可以在回调函数中完全处理,但由于调用 GetLastError 检索到的ERROR_INVALID_PARAMETER或其他类似错误,WinHTTP 应用程序仍必须准备好函数返回 FALSE。
与 WinINet 不同,WinINet 可以同时执行多个异步操作,WinHTTP 强制执行每个请求句柄一个挂起的异步操作的策略。 如果一个操作处于挂起状态,另一个 WinHTTP 函数被调用,则第二个函数将失败, GetLastError 将返回ERROR_INVALID_OPERATION。
WinHTTP 简化了此模型,使你能够仅在回调函数中实现操作逻辑,该回调函数接收完成通知,而不管操作是同步完成还是异步完成。 启用异步操作后,WinHTTP 函数的 OUT 参数不会返回有意义的数据,必须设置为 NULL。
WinHTTP 回调通知的差异
状态回调函数通过通知标志接收有关操作状态的更新。 在 WinHTTP 中,使用 WinHttpSetStatusCallback 函数的 dwNotificationFlags 参数选择通知。 使用 WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS 标志可收到所有状态更新的通知。
指示特定操作已完成的通知称为完成通知,或只是完成通知。 在 WinINet 中,每次回调函数收到完成时, lpvStatusInformation 参数都包含 INTERNET_ASYNC_RESULT 结构。 在 WinHTTP 中,此结构并非适用于所有完成。 请务必查看 WINHTTP_STATUS_CALLBACK的参考页,其中包含有关通知的信息以及每个通知可以预期的数据类型。
在 WinHTTP 中, 单个完成WINHTTP_CALLBACK_STATUS_REQUEST_ERROR表示操作失败。 所有其他完成都表示操作成功。
WinINet 和 WinHTTP 都使用用户定义的上下文值将信息从main线程传递到可在工作线程上执行的状态回调函数。 在 WinINet 中,状态回调函数使用的上下文值是通过调用多个函数之一设置的。 在 WinHTTP 中,上下文值仅使用 WinHttpSendRequest 或 WinHttpSetOption 设置。 因此,在 WinHTTP 中,可以在设置上下文值之前发出通知。 如果回调函数在设置上下文值之前收到通知,则必须准备好应用程序在回调函数的 dwContext 参数中接收 NULL。
身份验证差异
在 WinINet 中,用户凭据是通过调用 InternetSetOption 函数设置的,使用类似于以下代码示例中提供的代码。
// Use the WinINet InternetSetOption function to set the
// user credentials to the user name contained in strUsername
// and the password to the contents of strPassword.
InternetSetOption( hRequest, INTERNET_OPTION_PROXY_USERNAME,
strUsername, strlen(strUsername) + 1 );
InternetSetOption( hRequest, INTERNET_OPTION_PROXY_PASSWORD,
strPassword, strlen(strPassword) + 1 );
为了保持兼容性,同样可以使用 WinHttpSetOption 函数在 WinHTTP 中设置用户凭据,但不建议这样做,因为它可能会造成安全漏洞。
相反,当应用程序在 WinHTTP 中收到 401 状态代码时,建议设置凭据的方法是首先使用 WinHttpQueryAuthSchemes 标识身份验证方案,然后使用 WinHttpSetCredentials 设置凭据。 下面的代码示例演示如何执行此操作。
DWORD dwSupportedSchemes;
DWORD dwPrefered;
DWORD dwTarget;
// Obtain the supported and first schemes.
WinHttpQueryAuthSchemes( hRequest, &dwSupportedSchemes, &dwPrefered, &dwTarget );
// Set the credentials before resending the request.
WinHttpSetCredentials( hRequest, dwTarget, dwPrefered, strUsername, strPassword, NULL );
由于 WinHTTP 中没有与 InternetErrorDlg 等效的,因此通过用户界面获取凭据的应用程序必须提供自己的接口。
与 WinINet 不同,WinHTTP 不缓存密码。 必须为每个请求提供有效的用户凭据。
WinHTTP 不支持 WinINet 支持的分布式密码身份验证 (DPA) 方案。 但是,WinHTTP 支持 Microsoft Passport 1.4。 有关在 WinHTTP 中使用 Passport 身份验证的详细信息,请参阅 WinHTTP 中的 Passport 身份验证。
WinHTTP 不依赖于 Internet Explorer 设置来确定自动登录策略。 而是使用 WinHttpSetOption 设置自动登录策略。 有关 WinHTTP 中的身份验证的详细信息(包括自动登录策略),请参阅 WinHTTP 中的身份验证。
安全 HTTP 事务的差异
在 WinINet 中,使用 HttpOpenRequest 或 InternetConnect 启动安全会话,但在 WinHTTP 中,必须使用 WINHTTP_FLAG_SECURE 标志调用 WinHttpOpenRequest。
在安全 HTTP 事务中,可以使用服务器证书向客户端验证服务器。 在 WinINet 中,如果服务器证书包含错误, HttpSendRequest 会失败,并提供有关证书错误的详细信息。
在 WinHttp 中,服务器证书错误根据版本进行处理,如下所示:
- 从 WinHttp 5.1 开始,如果服务器证书失败或包含错误,则对 WinHttpSendRequest 的调用将报告回调函数中的 WINHTTP_CALLBACK_STATUS_SECURE_FAILURE 。 如果忽略 WinHttpSendRequest 生成的错误,则对 WinHttpReceiveResponse 的 后续调用将失败并出现ERROR_WINHTTP_OPERATION_CANCELLED错误。
- 在 WinHTTP 5.0 中,默认情况下,服务器证书中的错误不会导致请求失败。 相反,错误在回调函数中报告,并 带有WINHTTP_CALLBACK_STATUS_SECURE_FAILURE 通知。
在一些早期平台上,WinINet 支持专用通信技术 (PCT) 和/或 Fortezza 协议,尽管在 Windows XP 上不支持。
WinHTTP 不支持任何平台上的 PCT 和 Fortezza 协议,而是依赖于安全套接字层 (SSL) 2.0、SSL 3.0 或传输层安全性 (TLS) 1.0。