WinHttp Delphi 包装器

Posted

技术标签:

【中文标题】WinHttp Delphi 包装器【英文标题】:WinHttp Delphi wrapper 【发布时间】:2011-10-07 05:01:23 【问题描述】:

请告知 Delphi XE 中是否有 WinHTTP 包装器

按优先顺序:

    开箱即用的 Delphi 单元 带有移植入口例程的第三方开源 pas 文件 xxx_TLB.pas 包装器

解决方案:

由于 cmets 不允许格式化代码,我将解决方案粘贴到问题中:

const
  winhttpdll = 'winhttp.dll';

  WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0;
  WINHTTP_FLAG_REFRESH              = $00000100;
  WINHTTP_FLAG_SECURE               = $00800000;
  WINHTTP_ADDREQ_FLAG_COALESCE      = $40000000;
  WINHTTP_QUERY_FLAG_NUMBER         = $20000000;

function WinHttpOpen(pwszUserAgent: PWideChar; dwAccessType: DWORD;
  pwszProxyName, pwszProxyBypass: PWideChar; dwFlags: DWORD): HINTERNET; stdcall; external winhttpdll;
function WinHttpConnect(hSession: HINTERNET; pswzServerName: PWideChar;
  nServerPort: INTERNET_PORT; dwReserved: DWORD): HINTERNET; stdcall; external winhttpdll;
function WinHttpOpenRequest(hConnect: HINTERNET; pwszVerb: PWideChar;
  pwszObjectName: PWideChar; pwszVersion: PWideChar; pwszReferer: PWideChar;
  ppwszAcceptTypes: PLPWSTR; dwFlags: DWORD): HINTERNET; stdcall; external winhttpdll;
function WinHttpCloseHandle(hInternet: HINTERNET): BOOL; stdcall; external winhttpdll;
function WinHttpAddRequestHeaders(hRequest: HINTERNET; pwszHeaders: PWideChar; dwHeadersLength: DWORD;
  dwModifiers: DWORD): BOOL; stdcall; external winhttpdll;
function WinHttpSendRequest(hRequest: HINTERNET; pwszHeaders: PWideChar;
  dwHeadersLength: DWORD; lpOptional: Pointer; dwOptionalLength: DWORD; dwTotalLength: DWORD;
  dwContext: DWORD): BOOL; stdcall; external winhttpdll;
function WinHttpReceiveResponse(hRequest: HINTERNET;
  lpReserved: Pointer): BOOL; stdcall; external winhttpdll;
function WinHttpQueryHeaders(hRequest: HINTERNET; dwInfoLevel: DWORD; pwszName: PWideChar;
  lpBuffer: Pointer; var lpdwBufferLength, lpdwIndex: DWORD): BOOL; stdcall; external winhttpdll;
function WinHttpReadData(hRequest: HINTERNET; lpBuffer: Pointer;
  dwNumberOfBytesToRead: DWORD; var lpdwNumberOfBytesRead: DWORD): BOOL; stdcall; external winhttpdll;
function WinHttpQueryDataAvailable(hRequest: HINTERNET; var lpdwNumberOfBytesAvailable: DWORD): BOOL; 
  stdcall; external winhttpdll;
function WinHttpSetOption(hInternet: HINTERNET; dwOption: DWORD; lpBuffer: Pointer; dwBufferLength: DWORD): BOOL; 
  stdcall; external winhttpdll;
function WinHttpQueryOption(hInternet: HINTERNET; dwOption: DWORD; var lpBuffer: Pointer; var lpdwBufferLength: DWORD): BOOL; 
  stdcall; external winhttpdll;
function WinHttpWriteData(hRequest: HINTERNET; lpBuffer: Pointer; dwNumberOfBytesToWrite: DWORD; 
  var lpdwNumberOfBytesWritten: DWORD): BOOL; stdcall; external winhttpdll;
function WinHttpCheckPlatform(): BOOL; stdcall; external winhttpdll;

还有几个缺失的:

WinHttpCrackUrl
WinHttpCreateUrl
WinHttpSetStatusCallback
WinHttpTimeFromSystemTime
WinHttpTimeToSystemTime

【问题讨论】:

第三个不是Delphi会在你导入类型库的时候自动为你创建的吗? 我还发现了有用的tek-tips.com/faqs.cfm?fid=7493 请在answer部分发布解决方案,而不是问题。 你对缺失的函数做了什么?是不是不能导入? 【参考方案1】: 项目 导入类型库 Microsoft WinHTTP 服务,版本 5.1(版本 5.1)C:\Windows\system32\winhttp.dll

然后使用它:

var
   http: IWinHttpRequest;
   szUrl: WideString;
begin
   szUrl := 'http://***.com/questions/6725348/winhttp-delphi-wrapper';

   http := CoWinHttpRequest.Create;
   http.open('GET', szUrl, False);
   http.send(EmptyParam);

   if (http.status = 200) then
       ShowMessage(http.responseText);

所以:

它是开箱即用的 - 使用开箱即用的工具 它是开源的 - 您可以随意修改源代码 这是 TLB

【讨论】:

... 并且使用 COM Wrapper 而不是 C WinHTTP 库会产生一些不必要的开销。 COM 可能是多线程服务中的噩梦。我宁愿在 Delphi 软件中调用 C API。 COM 接口已经改变(例如 5.0 已被弃用),所以您将来可能会遇到问题... 5.0 在 10 多年前被取代;这是5.1,一直很稳定。进程内 COM 对象的调用方法没有开销——它与在 Delphi 中调用对象的方法一样快。如果您不喜欢通过虚拟方法表调用函数的性能损失,那么您也不会使用 Delphi。 开销当然不在 asm call 本身,而是在 COM 实例创建和完成(例如处理 Vista/Seven 中增强的安全性)和参数转换(OleStr/WideString 做有成本)。而且您必须在每个线程中调用 CoInitialize,这在多线程服务中可能会很棘手。如果您有一个直接的类 C 包装器,它将比 COM 版本更快,后者旨在用于 Visual Basic 和脚本。 旋转 100,000 个线程我以 2.89µs 的速度对 CoInitializeCoWinHttpRequest.Create 进行基准测试(0.092µs 初始化单元,2.79µs 构建 WinHttp 对象)。 Delphi 中的字符串已经是 Wide(至少是作者正在谈论的 Delphi 版本)。但是,如果您将 AnsiStringWideString 的 100,000 次转换相加,那么每个字符串又需要 0.246µs。我不确定你从哪里得到 3.5µs 是一个巨大的负担的想法;足以证明不使用已经存在了十多年的标准、经过充分测试、支持、强化的辅助对象。 Delphi 2009+ 中的字符串是 Unicode 编码的,但仍然存在从 UnicodeStringWideString 的转换。重要的不是 Unicode 转换,而是 WideString 分配:BSTR 不使用 FastMM4,但要慢得多 SysAllocStringLen WinAPI 调用。在 Vista/7 下它比在 XP 下快,但它仍然比 Delphi string 慢。当然,在检索 HTTP 内容时并不重要!所以我同意你的观点,COM 在这里不是速度瓶颈,即使当我有一个普通的 C API 可用时我总是不喜欢使用 COM 接口。【参考方案2】:

如果您想在您的应用程序中实现 HTTP 客户端访问,您可以考虑以下几种选择:

使用提供的 Indy 组件; 使用第三方组件,例如 Synapse、ICS 或您自己的基于 WinSock 的包装器; 使用 WinINet; 使用 WinHTTP。

对于our ORM,对于它的 HTTP/1.1 连接层,我们尽量避免外部依赖,并且不需要所有 Indy 的特性和开销。

我们首先编写了自己的 WinSock 包装器,然后试用了 WinInet。在我们的测试基准中使用时,我们发现 WinINet 非常缓慢。

然后我们尝试了微软提供的新 API WinHTTP,我们发现它的速度非常快。与直接 WinSock 访问一样快,无需编写所有包装代码。

这是我们的 OpenSource WinHTTP 包装器,位于 unit named SynCrtSock 中。测试从 Delphi 5 到 XE。

您会看到我们为 WinINet 和 WinHTTP 使用了相同的泛型类。事实上,这两个库非常接近。

见this article for details。有一个关于自动代理检索的说明。

编辑: 使用即将推出的 Delphi XE2,您将能够交叉编译到 Mac OS X。在这种情况下,使用“抽象”类非常有意义,例如 @ 987654325@。在 Windows 下,它将使用 WinHTTP,但在 Mac OS X 下,它将调用套接字 API。要使您的代码编译,您只需调整类类型,而不是您的代码。

【讨论】:

正是您的博文让我想到了迁移到 WinHttp。我在您的 SynCrtSock 单元中找到了我需要的东西: 而且某些版本的 WinInet 有一个可怕的超时错误!不能信任 WinInet 可以在您的所有客户端 PC 上运行,除非您希望有 30-60 秒的冻结,否则请不要使用它,这只能通过让您的用户升级他们安装的 Internet Explorer 版本来解决。 @Warren P. Microsoft 确实建议人们使用IServerWinHttpRequest。这是一个更稳定、更安全的重写版本,具有更多用于控制代理设置和超时的功能。 (这就是为什么它被称为 server - 它更稳定,这是您在服务器上运行时想要的) @Ian 这是什么IServerWinHttpRequest?没有关于它的谷歌资源......从“服务器”这个词来看,这可能是因为 WinHTTP 可以在服务或服务器端使用,而 WinINet 是为客户端设计的。 @Arnaud Bouchez:对不起,我的意思是IServerXMLHTTPRequest (msdn.microsoft.com/en-us/library/ms762278(v=VS.85).aspx)(而不是IXMLHTTPRequest)。您可以使用 IXMLHttpRequest 获取常规 html 内容。 IXMLHttpRequest 建立在 WinInet API 之上。 “然而,与XMLHTTP 不同,ServerXMLHTTP 对象不依赖 WinInet 控件来对远程 XML 文档进行 HTTP 访问。ServerXMLHTTP 使用新的 HTTP 客户端堆栈。设计用于服务器应用程序,WinInet 的这个服务器安全子集提供以下优点: ..."

以上是关于WinHttp Delphi 包装器的主要内容,如果未能解决你的问题,请参考以下文章

wininet 或 winhttp,这是 POST 请求的首选

VBS通过WinHttp对象下载网页图片。

使用 WinHttp 发布表单

如何从 WinHttp 请求中获取 HTTP 状态码?

如何异步使用“WinHttp.WinHttpRequest.5.1”?

WinHttp 不会从 WinXP 上的 Amazon S3 下载