在C#中,除了webclient我们还可以使用一组WindowsAPI来完成下载任务。这就是Windows Internet,简称 WinINet。本文通过一个demo来介绍WinINet的基本用法和一些实用技巧。

  • 系列文章

Winform文件下载之WebClient

  • 接口介绍

相比WebClient的用法,Win32API在使用时可能会烦琐一些。所以先把用到的API简单介绍一下。

资源的初始化和释放

InternetOpen

这是需要调用的第一个方法,它会初始化内部数据结构,为后面的调用做准备。

InternetCloseHandle

这个方法用来关闭使用中打开的Internet句柄,释放资源。

建立到服务器的连接

InternetOpenUrl

这是一个通用的函数,应用程序可以用它来请求数据(只要是WinINet支持的协议就可以)。尤其是当我们仅仅想要通过一个URL获取数据,而不关心通信协议相关的内容时,这个接口就特别合适。该方法会解析参数中的URL字符串,然后建立到服务器的连接,并准备下载由RUL标识的数据。

检查响应信息

HttpQueryInfo

检索与HTTP请求相关的报头信息。主要是查看请求是否成功。

读取响应内容

InternetReadFile

从 InternetOpenUrl打开的句柄中读取数据。

  • 下载过程

这里我们只介绍下载过程中的关键环节,完整的过程请参考本文的demo。

InternetOpenUrl

当请求与服务器建立连接时,我们重点考虑三个问题:请求的url,是否使用 RELOAD 标识, 客户端是否支持gzip压缩。

请求的url不用多说,这里直接请求一个http url.

我们不希望拿到客户端缓存中的数据,所以希望每次请求都能够从服务器重新下载。此时需要为InternetOpenUrl方法传入INTERNET_FLAG_RELOAD 标识。

当前绝大多数的web服务器都是支持gzip压缩的,我们的客户端当然也要能够解压缩服务器传回来的gzip格式的数据。所以我们要在请求中告诉服务器,客户端是能够处理gzip数据的。只有这样,服务器才会主动的返回gzip格式的数据。

代码如下:

  1. string referer = "Referer: xxxxxx\nAccept-Encoding: gzip";
  2.  
  3. // INTERNET_FLAG_RELOAD -> 0x80000000
  4.  
  5. // 跳过缓存,强制从原始的服务器下载数据
  6.  
  7. hInetFile = NativeMethods.InternetOpenUrl(this._hInet, uri.AbsoluteUri, referer, referer.Length, 0x80000000, IntPtr.Zero);

  

HttpQueryInfo

接下来我们开始检查前面发送的请求返回的header中的信息。主要是:请求的资源是否存在,返回的数据有多长,返回的文件的原始名称是什么,返回的数据是以什么格式被压缩的。

我们先要通过检查返回的状态码来确定请求是否成功,也就是返回的是不是200。

  1. byte[] content = new byte[BufferSize];
  2.  
  3. int count = BufferSize;
  4.  
  5. int temp = 0;
  6.  
  7. NativeMethods.HttpQueryInfo(hInetFile, 19, content, out count, out temp)
  8.  
  9. statuscode = Encoding.Unicode.GetString(content, 0, count);

  

正确返回时,statuscode应该是 “200”。

不要对HttpQueryInfo的第二个参数感到奇怪,为了获得请求的返回状态我们就得传入19。你可以参考Query Onfo Flags 。

用类似的方法可以得到返回数据的长度,原始的文件名称,返回数据的格式。

InternetReadFile

前面一切顺利的话就可以读取数据了。这个方法本身没什么可说的,但出于简化操作的目的,笔者对InternetReadFile进行了简单的封装。创建了一个继承自Stream的类MyInternetReadStream。在重写的 Read方法中调用InternetReadFile,并且添加了一个回调方法用来计算下载进度等信息。下面是代码概要,完整代码请参考demo。

  1. public override int Read(byte[] buffer, int offset, int count)
  2.  
  3. {
  4.  
  5. int dwNumberOfBytesToRead = Math.Min(BufferSize, count);
  6.  
  7. int length = 0;
  8.  
  9. NativeMethods.InternetReadFile(this._hInetFile, this._localBuffer,
    dwNumberOfBytesToRead, out length)
  10.  
  11. Array.Copy(this._localBuffer, 0, buffer, offset, length);
  12.  
  13. this._bytesReadCallback(length, this._contentLength);
  14.  
  15. return length;
  16.  
  17. }

  

Gzip stream

前面我们提到,服务器可能返回的是经过gzip压缩的数据,这就需要我们先检查数据的格式。如果是gzip格式的数据就需要把它解压缩。其实这在C#中是很简单的,我们只要把刚才创建的MyInternetReadStream的实例传给GZipStream的构造函数,创建一个新的GZipStream实例就可以了。

  1. private Stream GetInternetStream(IntPtr hInetFile)
  2.  
  3. {
  4.  
  5. //检查数据是不是gzip格式
  6.  
  7. string contentEncoding = MyWinInet.GetContentEncoding(hInetFile);
  8.  
  9. if (contentEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -1)
  10.  
  11. {
  12.  
  13. return new GZipStream(this.ForGZipReadStream(hInetFile), CompressionMode.Decompress, false);
  14.  
  15. }
  16.  
  17.  
  18. }
  19.  
  20. private Stream ForGZipReadStream(IntPtr hInetFile)
  21.  
  22. {
  23. return new MyWinInet.MyInternetReadStream(hInetFile,
    new MyWinInet.MyInternetReadStream.BytesReadCallback(this.BytesReadCallback));
  24.  
  25. }

  

至于计算下载进度,实时的下载速度的实现和Winform文件下载之WebClient 中的实现基本相同,请参考上文,或者直接看本文的demo。

总结:相比WebClient,使用WinINet接口要烦琐不少。当然也有一定的优势,比如前文中提到的代理问题,WinINet的默认设置就能处理好Credentials。不过在笔者看来,更重要的是我们可以选用不同的方式去处理下载问题。

  • Demo 下载:

WinInetDemo

Winform文件下载之WinINet的更多相关文章

  1. Winform文件下载之断点续传

    在本系列的前两篇文章中,分别向大家介绍了用于完成下载任务的 WebClinet 和 WinINet 的基本用法和一些实用技巧. 今天来为大家讲述下载过程中最常遇到的断点续传问题. 首先明确一点,本文所 ...

  2. Winform文件下载之WebClient

    最近升级了公司内部使用的一个下载小工具,主要提升了下面几点: 1. 在一些分公司的局域网中,连接不上外网 2. 服务器上的文件更新后,下载到的还是更新前的文件 3. 没有下载进度提示 4. 不能终止下 ...

  3. C# 文件下载 : WinINet

    在 C# 中,除了 WebClient 我们还可以使用一组 WindowsAPI 来完成下载任务.这就是 Windows Internet,简称 WinINet.本文通过一个 demo 来介绍 Win ...

  4. 基于Winform、WPF等的客户端文件下载

    有时候,我们用C#写一些客户端应用程序需要从服务器下载一些资源,如图片.dll.配置文件等.下面就来说一下,在Winform及WPF中如何下载文件. 我们的资源大多放在自己的网站上,或者从其他网站下载 ...

  5. Winform在线更新

    引言 2015年第一篇,Winform在线更新,算是重操旧业吧,09年刚到北京时一直做硬件联动编程,所以大多数时间都在搞Winform, 但是从来没做过在线更新这个功能,前几天参与部门另一个项目,上来 ...

  6. SNF开发平台WinForm之六-上传下载组件使用-SNF快速开发平台3.3-Spring.Net.Framework

    6.1运行效果: 6.2开发实现: 1.先在要使用的项目进行引用,SNF.WinForm.Attachments.dll文件. 2.在工具箱内新建选项卡->选择项,浏览找到文件SNF.WinFo ...

  7. 分享一个客户端程序(winform)自动升级程序,思路+说明+源码

    做winform的程序,不管用没用过自动更新,至少都想过自动更新是怎么实现的. 我这里共享一个自动更新的一套版本,给还没下手开始写的人一些帮助,也希望有大神来到,给指点优化意见. 本初我是通过sock ...

  8. c#winform使用WebBrowser 大全[超长文转载]

    1.主要用途:使用户可以在窗体中导航网页. 2.注意:WebBrowser 控件会占用大量资源.使用完该控件后一定要调用 Dispose 方法,以便确保及时释放所有资源.必须在附加事件的同一线程上调用 ...

  9. winform实现自动更新并动态调用form实现

    winform实现自动更新并动态调用form实现 标签: winform作业dllbytenull服务器 2008-08-04 17:36 1102人阅读 评论(0) 收藏 举报  分类: c#200 ...

随机推荐

  1. 深入研究C语言 第一篇

    一. 研究过程 1.第一章:创建编译环境: 我们首先下载TC2.0,找到其中与编译连接相关的程序和文件: (1) 编译器:TCC.exe (2) 连接器:tllike.exe (3) 相关文件:c0s ...

  2. 没有QQ的日子

    说来,也怪电脑不好,一开QQ就卡,年级也不小了,QQ上真的没啥话好说的,所以就想着关闭QQ. 其实做软件的知道,很多事情不是订下规则就可以做的到的,不过我还是给自己定个规则: 过完农历年后就不用QQ了 ...

  3. 解决Web部署 svg/woff/woff2字体 404错误

    在IIS上部署web项目的时候,发现浏览器报找不到woff.woff2字体的错误,导致浏览器加载字体报404错误,由于服务器IIS不认SVG,WOFF/WOFF2 这几个文件类型,只要在IIS上添加M ...

  4. FreeRTOS run on eclipse

    所需软件包: FreeRTOS.7.1.0.7zeclipse-cpp-helios-SR2-win32.zipTDM-GCC-32(版本任意吧..但同平台就选择一致的,32位系统就一致32位的软件, ...

  5. 每天记一些php函数,jQuery函数和linux命令(三)

    简介:学习完了php和jQuery之后,对函数的记忆不到位,导致很多函数没记住,所以为了促进自己的记忆,每天花一点时间来写这个博客. 时间:2016-12-21   地点:太原    天气:雨夹雪 一 ...

  6. 读Lua游戏开发实践指南

    11月11日开读,到今天正好一个月. 起因是被裁员之后,发现很多公司都在使用lua编写cocos2d-x游戏,原因是上手快,技术人员比较便宜. 如果引擎封装比较好,几乎在lua里写写基本逻辑就行了,不 ...

  7. 界面显示两个ListView

    1.List界面布局 <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:an ...

  8. Deploying JRE (Native Plug-in) for Windows Clients in Oracle E-Business Suite Release 12 (文档 ID 393931.1)

    In This Document Section 1: Overview Section 2: Pre-Upgrade Steps Section 3: Upgrade and Configurati ...

  9. [ASE][Daily Scrum]12.05

    占坑 最近大家都很忙所以工作准备放到周末来做……所以这两天进度会比较慢.

  10. openSUSE 11 上的配置可以Xmanager远程桌面

    openSUSE 11 上的配置(适用于默认图形环境为KDE的Linux): 1.配置KDM. openSUSE 11的默认图形环境为KDE,虽然可以同时安装GDM和KDM,但默认只启动了KDM.所以 ...