如何提高Web页面的性能

1. 尽可能的减少 HTTP 的请求数 [content]
2. 使用 CDN(Content Delivery Network) [server]
3. 添加 Expires 头(或者 Cache-control ) [server]
4. Gzip 组件 [server]
5. 将 CSS 样式放在页面的上方 [css]
6. 将脚本移动到底部(包括内联的) [javascript]
7. 避免使用 CSS 中的 Expressions [css]
8. 将 JavaScript 和 CSS 独立成外部文件 [javascript] [css]
9. 减少 DNS 查询 [content]
10. 压缩 JavaScript 和 CSS (包括内联的) [javascript] [css]
11. 避免重定向 [server]
12. 移除重复的脚本 [javascript]
13. 配置实体标签(ETags) [css]
14. 使 AJAX 缓存 [content]
15. 尽早清除缓冲区 [server]
16. AJAX 请求使用“GET”方法 [server]
17. 延迟加载组件 [content]
18. 预加载组件 [content]
19. 减少 DOM 元素的数量 [content]
20. 跨域分离组件 [content]
21. 减少 iframes 的数量 [content]
22. 不出现 404 [content]
23. 减小 cookie 的体积 [cookie]
24. 为组件使用 cookie-free 的域名 [cookie]
25.减少访问 DOM 的次数 [javascript]
26. 开发巧妙的事件处理程序 [javascript]
27. 优先选择使用 <link> 而非 @import [css]
28. 避免使用 filters [css]
29. 优化图片 [images]
30. 优化 CSS sprites [images] DL@bitsCN_com网管软件下载
32. 不要在 HTML 中缩放图片 [images]
33. 减小 favicon.ico 的体积并缓存 [images]
34. 保持组件在 25K 以下 [mobile]
35. 将组件分拆到多个文档中 [mobile]

面向站长和网站管理员的Web缓存加速指南


这是一篇知识性的文档,主要目的是为了让Web缓存相关概念更容易被开发者理解并应用于实际的应用环境中。为了简要起见,某些实现方面的细节被简化或省略了。如果你更关心细节实现则完全不必耐心看完本文,后面参考文档和更多深入阅读部分可能是你更需要的内容。


  1. 什么是Web缓存,为什么要使用它?
  2. 缓存的类型:

    1. 浏览器缓存;
    2. 代理服务器缓存;

  3. Web缓存无害吗?为什么要鼓励缓存?
  4. Web缓存如何工作:
  5. 如何控制(控制不)缓存:

    1. HTML Meta标签 vs. HTTP头信息;
    2. Pragma HTTP头信息(为什么不起作用);
    3. 使用Expires(过期时间)HTTP头信息控制保鲜期;
    4. Cache-Control(缓存控制) HTTP头信息;
    5. 校验参数和校验;

  6. 创建利于缓存网站的窍门;
  7. 编写利于缓存的脚本;
  8. 常见问题解答;
  9. 缓存机制的实现:Web服务器端配置;
  10. 缓存机制的实现:服务器端脚本;
  11. 参考文档和深入阅读;
  12. 关于本文档;


什么是Web缓存,为什么要使用它?

Web 缓存位于Web服务器之间(1个或多个,内容源服务器)和客户端之间(1个或多个):缓存会根据进来的请求保存输出内容的副本,例如html页面, 图片,文件(统称为副本),然后,当下一个请求来到的时候:如果是相同的URL,缓存直接使用副本响应访问请求,而不是向源服务器再次发送请求。

使用缓存主要有2大理由:


  • 减少相应延迟:因为请求从缓存服务器(离客户端更近)而不是源服务器被相应,这个过程耗时更少,让web服务器看上去相应更快;
  • 减少网络带宽消耗:当副本被重用时会减低客户端的带宽消耗;客户可以节省带宽费用,控制带宽的需求的增长并更易于管理。

缓存的类型


浏览器缓存


对 于新一代的Web浏览器来说(例如:IE,Firefox):一般都能在设置对话框中发现关于缓存的设置,通过在你的电脑上僻处一块硬盘空间用于存储你已 经看过的网站的副本。浏览器缓存根据非常简单的规则进行工作:在同一个会话过程中(在当前浏览器没有被关闭之前)会检查一次并确定缓存的副本足够新。这个 缓存对于用户点击“后退”或者点击刚访问过的链接特别有用,如果你浏览过程中访问到同一个图片,这些图片可以从浏览器缓存中调出而即时显现。

代理服务器缓存


Web代理服务器使用同样的缓存原理,只是规模更大。代理服务器群为成百上千用户服务使用同样的机制;大公司和ISP经常在他们的防火墙上假设代理缓存或者单独的缓存设备;

由 于带路服务器缓存并非客户端或者源服务器的一部分,而是位于原网络之外,请求必须路由到他们才能起作用。一个方法是手工设置你的浏览器:告诉浏览器使用 那个代理,另外一个是通过中间服务器:这个中间服务器处理所有的web请求,并将请求转发到后台网络,而用户不必配置代理,甚至不必知道代理的存在;

代理服务器缓存:是一个共享缓存,不只为一个用户服务,经常为大量用户使用,因此在减少相应时间和带宽使用方面很有效:因为同一个副本会被重用多次。

网关缓存


也被称为反向代理缓存或间接代理缓存,网关缓存也是一个中间服务器,和内网管理员部署缓存用于节省带宽不同:网关缓存一般是网站管理员自己部署:让他们的网站更容易扩展并获得更好的性能;
请求有几种方法被路由到网关缓存服务器上:其中典型的是让用一台或多台负载均衡服务器从客户端看上去是源服务器;

网络内容发布商 (Content delivery networks CDNs)分布网关缓存到整个(或部分)互联网上,并出售缓存服务给需要的网站,Speedera和Akamai就是典型的网络内容发布商(下文简称CDN)。

本问主要关注于浏览器和代理缓存,当然,有些信息对于网关缓存也同样有效;

Web缓存无害吗?为什么要鼓励缓存?


Web缓存在互联网上最容易被误解的技术之一:网站管理员经常怕对网站失去控制,由于代理缓存会“隐藏”他们的用户,让他们感觉难以监控谁在使用他们的网站。
不幸的是:就算不考虑Web缓存,互联网上也有很多网站使用非常多的参数以便管理员精确地跟踪用户如何使用他们的网站;如果这类问题也是你关心的,本文将告诉你如何获得精确的统计而不必将网站设计的非常缓存不友好。
另外一个抱怨是缓存会给用户过期或失效的数据;无论如何:本文可以告诉你怎样配置你的服务器来控制你的内容将被如何缓存。

CDN是另外一个有趣的方向,和其他代理缓存不同:CDN的网关缓存为希望被缓存的网站服务,没有以上顾虑。即使你使用了CDN,你也要考虑后续的代理服务器缓存和浏览器缓存问题。

另外一方面:如果良好地规划了你的网站,缓存会有助于网站服务更快,并节省服务器负载和互联网的链接请求。这个改善是显著的:一个难以缓存的网站可能需要几秒去载入页面,而对比有缓存的网站页面几乎是即时显现:用户更喜欢速度快的网站并更经常的访问;

这样想:很多大型互联网公司为全世界服务器群投入上百万资金,为的就是让用户访问尽可能快,客户端缓存也是这个目的,只不过更靠近用户一端,而且最好的一点是你甚至根本不用为此付费。

事实上,无论你是否喜欢,代理服务器和浏览器都回启用缓存。如果你没有配置网站正确的缓存,他们会按照缺省或者缓存管理员的策略进行缓存。

缓存如何工作


所有的缓存都用一套规则来帮助他们决定什么时候使用缓存中的副本提供服务(假设有副本可用的情况下);一些规则在协议中有定义(HTTP协议1.0和1.1),一些规则由缓存的管理员设置(浏览器的用户或者代理服务器的管理员);
一般说来:遵循以下基本的规则(不必担心,你不必知道所有的细节,细节将随后说明)


  1. 如果响应头信息:告诉缓存器不要保留缓存,缓存器就不会缓存相应内容;
  2. 如果请求信息是需要认证或者安全加密的,相应内容也不会被缓存;
  3. 如果在回应中不存在校验器(ETag或者Last-Modified头信息),缓存服务器会认为缺乏直接的更新度信息,内容将会被认为不可缓存。
  4. 一个缓存的副本如果含有以下信息:内容将会被认为是足够新的

    • 含有完整的过期时间和寿命控制头信息,并且内容仍在保鲜期内;
    • 浏览器已经使用过缓存副本,并且在一个会话中已经检查过内容的新鲜度;
    • 缓存代理服务器近期内已经使用过缓存副本,并且内容的最后更新时间在上次使用期之前;
    • 够新的副本将直接从缓存中送出,而不会向源服务器发送请求;

  5. 如果缓存的副本已经太旧了,缓存服务器将向源服务器发出请求校验请求,用于确定是否可以继续使用当前拷贝继续服务;
总之:新鲜度和校验是确定内容是否可用的最重要途径:
 

如果副本足够新,从缓存中提取就立刻能用了;
而经缓存器校验后发现副本的原件没有变化,系统也会避免将副本内容从源服务器整个重新传输一遍。

如何控制(控制不)缓存


有很多工具可以帮助设计师和网站管理员调整缓存服务器对待网站的方式,这也许需要你亲自下手对服务器的配置进行一些调整,但绝对值得;了解如何使用这些工具请参考后面的实现章节;

HTML meta标签和HTTP 头信息


HTML的编写者会在文档的<HEAD>区域中加入描述文档的各种属性,这些META标签常常被用于标记文档不可以被缓存或者标记多长时间后过期;
META 标签使用很简单:但是效率并不高,因为只有几种浏览器会遵循这个标记(那些真正会“读懂”HTML的浏览器),没有一种缓存代理服务器能遵循这个 规则(因为它们几乎完全不解析文档中HTML内容);有事会在Web页面中增加:Pragma: no-cache这个META标记,如果要让页面保持刷新,这个标签其实完全没有必要。
如果你的网站托管在ISP机房中,并且机房可能不给你权限去控制HTTP的头信息(如:Expires和Cache-Control),大声控诉:这些机制对于你的工作来说是必须的;
另 外一方面: HTTP头信息可以让你对浏览器和代理服务器如何处理你的副本进行更多的控制。他们在HTML代码中是看不见的,一般由Web服务器自动生成。但是,根据 你使用的服务,你可以在某种程度上进行控制。在下文中:你将看到一些有趣的HTTP头信息,和如何在你的站点上应用部署这些特性。

HTTP头信息发送在HTML代码之前,只有被浏览器和一些中间缓存能看到,一个典型的HTTP 1.1协议返回的头信息看上去像这样:

HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: "3e86-410-3596fbbc"
Content-Length: 1040
Content-Type: text/html


在头信息空一行后是HTML代码的输出,关于如何设置HTTP头信息请参考实现章节;

Pragma HTTP头信息 (为什么它不起作用)


很 多人认为在HTTP头信息中设置了Pragma: no-cache后会让内容无法被缓存。但事实并非如此:HTTP的规范中,响应型头信息没有任何关于Pragma属性的说明,而讨论了的是请求型头信息 Pragma属性(头信息也由浏览器发送给服务器),虽然少数集中缓存服务器会遵循这个头信息,但大部分不会。用了Pragma也不起什么作用,要用就使 用下列头信息:

使用Expires(过期时间)HTTP头信息来控制保鲜期


Expires(过期时间) 属性是HTTP控制缓存的基本手段,这个属性告诉缓存器:相关副本在多长时间内是新鲜的。过了这个时间,缓存器就会向源服务器发送请求,检查文档是否被修改。几乎所有的缓存服务器都支持Expires(过期时间)属性;

大部分Web服务器支持你用几种方式设置Expires属性;一般的:可以设计一个绝对时间间隔:基于客户最后查看副本的时间(最后访问时间)或者根据服务器上文档最后被修改的时间;

Expires 头信息:对于设置静态图片文件(例如导航栏和图片按钮)可缓存特别有用;因为这些图片修改很少,你可以给它们设置一个特别长的过期时间,这会使你的网站对 用户变得相应非常快;他们对于控制有规律改变的网页也很有用,例如:你每天早上6点更新新闻页,你可以设置副本的过期时间也是这个时间,这样缓存 服务器就知道什么时候去取一个更新版本,而不必让用户去按浏览器的“刷新”按钮。

过期时间头信息属性值只能是HTTP格式的日期时间,其他的都会被解析成当前时间“之前”,副本会过期,记住:HTTP的日期时间必须是格林威治时间(GMT),而不是本地时间。举例:

Expires: Fri, 30 Oct 1998 14:19:41 GMT

所以使用过期时间属性一定要确认你的Web服务器时间设置正确,一个途径是通过网络时间同步协议(Network Time Protocol NTP),和你的系统管理员那里你可以了解更多细节。
虽然过期时间属性非常有用,但是它还是有些局限,首先:是牵扯到了日期,这样Web服务器的时间和缓存服务器的时间必须是同步的,如果有些不同步,要么是应该缓存的内容提前过期了,要么是过期结果没及时更新。
还有一个过期时间设置的问题也不容忽视:如果你设置的过期时间是一个固定的时间,如果你返回内容的时候又没有连带更新下次过期的时间,那么之后所有访问请求都会被发送给源Web服务器,反而增加了负载和响应时间;

Cache-Control(缓存控制) HTTP头信息


HTTP 1.1介绍了另外一组头信息属性:Cache-Control响应头信息,让网站的发布者可以更全面的控制他们的内容,并定位过期时间的限制。
有用的 Cache-Control响应头信息包括:


  • max-age=[秒] — 执行缓存被认为是最新的最长时间。类似于过期时间,这个参数是基于请求时间的相对时间间隔,而不是绝对过期时间,[秒]是一个数字,单位是秒:从请求时间开始到过期时间之间的秒数。
  • s-maxage=[秒] — 类似于max-age属性,除了他应用于共享(如:代理服务器)缓存
  • public — 标记认证内容也可以被缓存,一般来说: 经过HTTP认证才能访问的内容,输出是自动不可以缓存的;
  • no-cache — 强制每次请求直接发送给源服务器,而不经过本地缓存版本的校验。这对于需要确认认证应用很有用(可以和public结合使用),或者严格要求使用最新数据的应用(不惜牺牲使用缓存的所有好处);
  • no-store — 强制缓存在任何情况下都不要保留任何副本
  • must-revalidate — 告诉缓存必须遵循所有你给予副本的新鲜度的,HTTP允许缓存在某些特定情况下返回过期数据,指定了这个属性,你高速缓存,你希望严格的遵循你的规则。
  • proxy-revalidate — 和 must-revalidate类似,除了他只对缓存代理服务器起作用

举例:

Cache-Control: max-age=3600, must-revalidate

如果你计划试用Cache-Control属性,你应该看一下这篇HTTP文档,详见参考和深入阅读;

校验参数和校验


在Web缓存如何工作: 我们说过:校验是当副本已经修改后,服务器和缓存之间的通讯机制;使用这个机制:缓存服务器可以避免副本实际上仍然足够新的情况下重复下载整个原件。
校验参数非常重要,如果1个不存在,并且没有任何信息说明保鲜期(Expires或Cache-Control)的情况下,缓存将不会存储任何副本;
最常见的校验参数是文档的最后修改时间,通过最后Last-Modified头信息可以,当一份缓存包含Last-Modified信息,他基于此信息,通过添加一个If-Modified-Since请求参数,向服务器查询:这个副本从上次查看后是否被修改了。
HTTP 1.1介绍了另外一个校验参数: ETag,服务器是服务器生成的唯一标识符ETag,每次副本的标签都会变化。由于服务器控制了ETag如何生成,缓存服务器可以通过If-None-Match请求的返回没变则当前副本和原件完全一致。
所有的缓存服务器都使用Last-Modified时间来确定副本是否够新,而ETag校验正变得越来越流行;
所有新一代的Web服务器都对静态内容(如:文件)自动生成ETag和Last-Modified头信息,而你不必做任何设置。但是,服务器对于动态内容(例如:CGI,ASP或数据库生成的网站)并不知道如何生成这些信息,参考一下编写利于缓存的脚本章节;

创建利于缓存网站的窍门


除了使用新鲜度信息和校验,你还有很多方法使你的网站缓存友好。


  • 保持URL稳定: 这是缓存的金科玉律,如果你给在不同的页面上,给不同用户或者从不同的站点上提供相同的内容,应该使用相同的URL,这是使你的网站缓存友好最简单,也是 最高效的方法。例如:如果你在页面上使用 "/index.html" 做为引用,那么就一直用这个地址;
  • 使用一个共用的库存放每页都引用的图片和其他页面元素;
  • 对于不经常改变的图片/页面启用缓存,并使用Cache-Control: max-age属性设置一个较长的过期时间;
  • 对于定期更新的内容设置一个缓存服务器可识别的max-age属性或过期时间;
  • 如果数据源(特别是下载文件)变更,修改名称,这样:你可以让其很长时间不过期,并且保证服务的是正确的版本;而链接到下载文件的页面是一个需要设置较短过期时间的页面。
  • 万不得已不要改变文件,否则你会提供一个非常新的Last-Modified日期;例如:当你更新了网站,不要复制整个网站的所有文件,只上传你修改的文件。
  • 只在必要的时候使用Cookie,cookie是非常难被缓存的,而且在大多数情况下是不必要的,如果使用cookie,控制在动态网页上;
  • 减少试用SSL,加密的页面不会被任何共享缓存服务器缓存,只在必要的时候使用,并且在SSL页面上减少图片的使用;
  • 使用可缓存性评估引擎,这对于你实践本文的很多概念都很有帮助;

编写利于缓存的脚本


脚本缺省不会返回校验参数(返回Last-Modified或ETag头信息)或其他新鲜度信息(Expires或Cache-Control),有些动态脚本的确是动态内容(每次相应内容都不一样),但是更多(搜索引擎,数据库引擎网站)网站还是能从缓存友好中获益的。
一般说来,如果脚本生成的输出在未来一段时间(几分钟或者几天)都是可重复复制的,那么就是可缓存的。如果脚本输出内容只随URL变化而变化,也是可缓存的;但如果输出会根据cookie,认证信息或者其他外部条件变化,则还是不可缓存的。


  • 最利于缓存的脚本就是将内容改变时导出成静态文件,Web服务器可以将其当作另外一个网页并生成和试用校验参数,让一些都变得更简单,只需要写入文件即可,这样最后修改时间也有了;
  • 另外一个让脚本可缓存的方法是对一段时间内能保持较新的内容设置一个相对寿命的头信息,虽然通过Expires头信息也可以实现,但更容易的是用Cache-Control: max-age属性,它会让首次请求后一段时间内缓存保持新鲜;
  • 如 果以上做法你都做不到,你可以让脚本生成一个校验属性,并对 If-Modified-Since 和/或If-None-Match请求作出反应,这些属性可以从解析HTTP头信息得到,并对符合条件的内容返回304 Not Modified(内容未改变),可惜的是,这种做法比不上前2种高效;

其他窍门:


  • 尽量避免使用POST,除非万不得已,POST模式的返回内容不会被大部分缓存服务器保存,如果你发送内容通过URL和查询(通过GET模式)的内容可以缓存下来供以后使用;
  • 不要在URL中加入针对每个用户的识别信息:除非内容是针对每个用户不同的;
  • 不要统计一个用户来自一个地址的所有请求,因为缓存常常是一起工作的;
  • 生成并返回Content-Length头信息,如果方便的话,这个属性让你的脚本在可持续链接模式时:客户端可以通过一个TCP/IP链接同时请求多个副本,而不是为每次请求单独建立链接,这样你的网站相应会快很多;
具体定义请参考实现章节。

常见问题解答


让网站变得可缓存的要点是什么?


好的策略是确定那些内容最热门,大量的复制(特别是图片)并针对这些内容先部署缓存。

如何让页面通过缓存达到最快相应?


缓存最好的副本是那些可以长时间保持新鲜的内容;基于校验虽然有助于加快相应,但是它不得不和源服务器联系一次去检查内容是否够新,如果缓存服务器上就知道内容是新的,内容就可以直接相应返回了。

我理解缓存是好的,但是我不得不统计多少人访问了我的网站!


如果你必须知道每次页面访问的,选择【一】个页面上的小元素,或者页面本身,通过适当的头信息让其不可缓存,例如: 可以在每个页面上部署一个1x1像素的透明图片。Referer头信息会有包含这个图片的每个页面信息;
明确一点:这个并不会给你一个关于你用户精确度很高的统计,而且这对互联网和你的用户这都不太好,消耗了额外的带宽,强迫用户去访问无法缓存的内容。了解更多信息,参考访问统计资料。

我如何能看到HTTP头信息的内容?


很多浏览器在页面属性或类似界面中可以让你看到Expires 和Last-Modified信息;如果有的话:你会找到页面信息的菜单和页面相关的文件(如图片),并且包含他们的详细信息;
看到完整的头信息,你可以用telnet手工连接到Web服务器;
为此:你可能需要用一个字段指定端口(缺省是80),或者链接到www.example.com:80 或者 www.example.com 80(注意是空格),更多设置请参考一下telnet客户端的文档;
打开网站链接:请求一个查看链接,如果你想看到http://www.example.com/foo.html 连接到www.example.com的80端口后,键入:

GET /foo.html HTTP/1.1 [回车]
GET /foo.html HTTP/1.1 [return]
Host: www.example.com [回车][回车]
Host: www.example.com [return][return]

在[回车]处按键盘的回车键;在最后,要按2次回车,然后,就会输出头信息及完整页面,如果只想看头信息,将GET换成HEAD。

我的页面是密码保护的,代理缓存服务器如何处理他们?


缺省的,网页被HTTP认证保护的都是私密内容,它们不会被任何共享缓存保留。但是,你可以通过设置Cache-Control: public让认证页面可缓存,HTTP 1.1标准兼容的缓存服务器会认出它们可缓存。
如果你认为这些可缓存的页面,但是需要每个用户认证后才能看,可以组合使用Cache-Control: public和no-cache头信息,高速缓存必须在提供副本之前,将将新客户的认证信息提交给源服务器。设置就是这样:

Cache-Control: public, no-cache

无论如何:这是减少认证请求的最好方法,例如: 你的图片是不机密的,将它们部署在另外一个目录,并对此配置服务器不强制认证。这样,那些图片会缺省都缓存。

我们是否要担心用户通过cache访问我的站点?


代理服务器上SSL页面不会被缓存(不推荐被缓存),所以你不必为此担心。但是,由于缓存保存了非SSL请求和从他们抓取的URL,你要意识到没有安全保护的网站,可能被不道德的管理员可能搜集用户隐私,特别是通过URL。
实际上,位于服务器和客户端之间的管理员可以搜集这类信息。特别是通过CGI脚本在通过URL传递用户名和密码的时候会有很大问题;这对泄露用户名和密码是一个很大的漏洞;
如果你初步懂得互联网的安全机制,你不会对缓存服务器有任何。

我在寻找一个包含在Web发布系统解决方案,那些是比较有缓存意识的系统?


这很难说,一般说来系统越复杂越难缓存。最差就是全动态发布并不提供校验参数;你无发缓存任何内容。可以向系统提供商的技术人员了解一下,并参考后面的实现说明。

 

 

 

 

我的图片设置了1个月后过期,但是我现在需要现在更新。


过期时间是绕不过去的,除非缓存(浏览器或者代理服务器)空间不足才会删除副本,缓存副本在过期之间会被一直使用。
最好的办法是改变它们的链接,这样,新的副本将会从源服务器上重新下载。记住:引用它们的页面本身也会被缓存。因此,使用静态图片和类似内容是很容易缓存的,而引用他们的HTML页面则要保持非常更新;
如果你希望对指定的缓存服务器重新载入一个副本,你可以强制使用“刷新”(在FireFox中在reload的时候按住shift键:就会有前面提到恶Pragma: no-cache头信息发出)。或者你可以让缓存的管理员从他们的界面中删除相应内容;

我运行一个Web托管服务,如何让我的用户发布缓存友好的网页?


如果你使用apahe,可以考虑允许他们使用.htaccess文件并提供相应的文档;
另外一方面: 你也可以考虑在各种虚拟主机上建立各种缓存策略。例如: 你可以设置一个目录 /cache-1m 专门用于存放访问1个月的访问,另外一个 /no-cache目录则被用提供不可存储副本的服务。
无论如何:对于大量用户访问还是应该用缓存。对于大网站,这方面的节约很明显(带宽和服务器负载);

我标记了一些网页是可缓存的,但是浏览器仍然每次发送请求给服务。如何强制他们保存副本?


缓存服务器并不会总保存副本并重用副本;他们只是在特定情况下会不保存并使用副本。所有的缓存服务器都回基于文件的大小,类型(例如:图片 页面),或者服务器空间的剩余来确定如何缓存。你的页面相比更热门或者更大的文件相比,并不值得缓存。
所以有些缓存服务器允许管理员根据文件类型确定缓存副本的优先级,允许某些副本被永久缓存并长期有效;

缓存机制的实现 - Web服务器端配置


一般说来,应该选择最新版本的Web服务器程序来部署。不仅因为它们包含更多利于缓存的功能,新版本往往在性能和安全性方面都有很多的改善。

Apache HTTP服务器


Apache有些可选的模块来包含这些头信息: 包括Expires和Cache-Control。 这些模块在1.2版本以上都支持;
这些模块需要和apache一起编译;虽然他们已经包含在发布版本中,但缺省并没有启用。为了确定相应模块已经被启用:找到httpd程序并运行httpd -l 它会列出可用的模块,我们需要用的模块是mod_expires和mod_headers


  • 如 果这些模块不可用,你需要联系管理员,重新编译并包含这些模块。这些模块有时候通过配置文件中把注释掉的配置启用,或者在编译的时候增加-enable -module=expires和-enable-module=headers选项(在apache 1.3和以上版本)。 参考Apache发布版中的INSTALL文件;

Apache一旦启用了相应的模块,你就可以在.htaccess文件或者在服务器的access.conf文件中通过mod_expires设置副本什 么时候过期。你可设置过期从访问时间或文件修改时间开始计算,并且应用到某种文件类型上或缺省设置,参考模块的文档获得更多信息,或者遇到问题的时候向你身边的apache专家讨教。
应用Cache-Control头信息,你需要使用mod_headers,它将允许你设置任意的HTTP头信息,参考mod_headers的文档可以获得更多资料;
这里有个例子说明如何使用头信息:


  • .htaccess文件允许web发布者使用命令只在配置文件中用到的命令。他影响到所在目录及其子目录;问一下你的服务器管理员确认这个功能是否启用了。

### 启用 mod_expires
ExpiresActive On
### 设置 .gif 在被访问过后1个月过期。
ExpiresByType image/gif A2592000
### 其他文件设置为最后修改时间1天后过期
### (用了另外的语法)
ExpiresDefault "modification plus 1 day"
### 在index.html文件应用 Cache-Control头属性
<Files index.html>
Header append Cache-Control "public, must-revalidate"
</Files>       


  • 注意: 在适当情况下mod_expires会自动计算并插入Cache-Control:max-age 头信息

Apache 2.0的配置和1.3类似,更多信息可以参考2.0的mod_expires和mod_headers文档;

Microsoft IIS服务器


Microsoft的IIS可以非常容易的设置头信息,注意:这只针对IIS 4.0服务器,并且只能在NT服务器上运行。
为网站的一个区域设置头信息,先要到管理员工具界面中,然后设置属性。选择HTTP Header选单,你会看到2个有趣的区域:启用内容过期和定制HTTP头信息。头一个设置会自动配置,第二个可以用于设置Cache-Control头信息;
设置asp页面的头信息可以参考后面的ASP章节,也可以通过ISAPI模块设置头信息,细节请参考MSDN。

Netscape/iPlanet企业服务器


3.6版本以后,Netscape/iPlanet已经不能设置Expires头信息了,他从3.0版本开始支持HTTP 1.1的功能。这意味着HTTP 1.1的缓存(代理服务器/浏览器)优势都可以通过你对Cache-Control设置来获得。
使用Cache-Control头信息,在管理服务器上选择内容管理|缓存设置目录。然后:使用资源选择器,选择你希望设置头信息的目录。设置完头信息后,点击“OK”。更多信息请参考Netscape/iPlanet企业服务器的手册。

缓存机制的实现:服务器端脚本


需要注意的一点是:也许服务器设置HTTP头信息比脚本语言更容易,但是两者你都应该使用。
因 为服务器端的脚本主要是为了动态内容,他本身不产生可缓存的文件页面,即使内容实际是可以缓存的。如果你的内容经常改变,但是不是每次页面请求都改变, 考虑设置一个Cache-Control: max-age头信息;大部分用户会在短时间内多次访问同一页面。例如: 用户点击“后退”按钮,即使没有新内容,他们仍然要再次从服务器下载内容查看。

CGI程序


CGI脚本是生成内容最流行的方式之一,你可以很容易在发送内容之前的扩展HTTP头信息;大部分CGI实现都需要你写 Content-Type头信息,例如这个Perl脚本:

#!/usr/bin/perl
print "Content-type: text/html\n";
print "Expires: Thu, 29 Oct 1998 17:04:19 GMT\n";
print "\n";
### 后面是内容体...

由于都是文本,你可以很容易通过内置函数生成Expires和其他日期相关的头信息。如果你使用Cache-Control: max-age;会更简单;

print "Cache-Control: max-age=600\n";

这样脚本可以在被请求后缓存10分钟;这样用户如果按“后退”按钮,他们不会重新提交请求;
CGI的规范同时也允许客户端发送头信息,每个头信息都有一个‘HTTP_’的前缀;这样如果一个客户端发送一个If-Modified-Since请求,就是这样的:

HTTP_IF_MODIFIED_SINCE = Fri, 30 Oct 1998 14:19:41 GMT


参考一下cgi_buffer库,一个自动处理ETag的生成和校验的库,生成Content-Length属性和对内容进行gzip压缩。在Python脚本中也只需加入一行;

服务器端包含 Server Side Includes


SSI(经常使用.shtml扩展名)是网站发布者最早可以生成动态内容的方案。通过在页面中设置特别的标记,也成为一种嵌入HTML的脚本;
大部分SSI的实现无法设置校验器,于是无法缓存。但是Apache可以通过对特定文件的组执行权限设置实现允许用户设置那种SSI可以被缓存;结合XbitHack调整整个目录。更多文档请参考mod_include文档。

PHP


PHP是一个内建在web服务器中的服务器端脚本语言,当做为HTML嵌入式脚本,很像SSI,但是有更多的选项,PHP可以在各种Web服务器上设置为CGI模式运行,或者做为Apache的模块;
缺省PHP生成副本没有设置校验器,于是也无法缓存,但是开发者可以通过Header()函数来生成HTTP的头信息;
例如:以下代码会生成一个Cache-Control头信息,并设置为3天以后过期的Expires头信息;

<?php
Header("Cache-Control: must-revalidate");

$offset = 60 * 60 * 24 * 3;
$ExpStr = "Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";
Header($ExpStr);
?>

记住: Header()的输出必须先于所有其他HTML的输出;
正如你看到的:你可以手工创建HTTP日期;PHP没有为你提供专门的函数(新版本已经让这个越来越容易了,请参考PHP的日期相关函数文档),当然,最简单的还是设置Cache-Control: max-age头信息,而且对于大部分情况都比较适用;
更多信息,请参考header相关的文档;
也请参考一下cgi_buffer库,自动处理ETag的生成和校验,Content-Length生成和内容的gzip压缩,PHP脚本只需包含1行代码;

Cold Fusion


Cold Fusion是Macromedia的商业服务器端脚本引擎,并且支持多种Windows平台,Linux平台和多种Unix平台。Cold Fusion通过CFHEADER标记设置HTTP头信息相对容易。可惜的是:以下的Expires头信息的设置有些容易误导;

<CFHEADER NAME="Expires" VALUE="#Now()#">

它并不像你想像的那样工作,因为时间(本例中为请求发起的时间)并不会被转换成一个符合HTTP时间,而且打印出副本的Cold fusion的日期/时间对象,大部分客户端会忽略或者将其转换成1970年1月1日。
但是:Cold Fusion另外提供了一套日期格式化函数, GetHttpTimeSTring. 结合DateAdd函数,就很容易设置过期时间了,这里我们设置一个Header声明副本在1个月以后过期;

<cfheader name="Expires" value="#GetHttpTimeString(DateAdd('m', 1, Now()))#">

你也可以使用CFHEADER标签来设置Cache-Control: max-age等其他头信息;
记住:Web服务器也会将头信息设置转给Cold Fusion(做为CGI运行的时候),检查你的服务器设置并确定你是否可以利用服务器设置代替Cold Fusion。

ASP和ASP.NET


在asp 中设置HTTP头信息是:确认Response方法先于HTML内容输出前被调用,或者使用 Response.Buffer暂存输出;同样的:注意某些版本的IIS缺省设置会输出Cache-Control: private 头信息,必须声明成public才能被共享缓存服务器缓存。
IIS的ASP和其他web服务器都允许你设置HTTP头信息,例如: 设置过期时间,你可以设置Response对象的属性;

<% Response.Expires=1440 %>

设置请求的副本在输出的指定分钟后过期,类似的:也可以设置绝对的过期时间(确认你的HTTP日期格式正确)

<% Response.ExpiresAbsolute=#May 31,1996 13:30:15 GMT# %>

Cache-Control头信息可以这样设置:

<% Response.CacheControl="public" %>

在ASP.NET中,Response.Expires 已经不推荐使用了,正确的方法是通过Response.Cache设置Cache相关的头信息;

Response.Cache.SetExpires ( DateTime.Now.AddMinutes ( 60 ) ) ;
Response.Cache.SetCacheability ( HttpCacheability.Public ) ;

参考MSDN文档可以找到更多相关新年系;

参考文档和深入阅读


HTTP 1.1 规范定义


HTTP 1.1的规范有大量的扩展用于页面缓存,以及权威的接口实现指南,参考章节:13, 14.9, 14.21, 以及 14.25.

Web-Caching.com


非常精彩的介绍缓存相关概念,并介绍其他在线资源。

关于非连续性访问统计


Jeff Goldberg内容丰富的演说告诉你为什么不应该过度依赖访问统计和计数器;

可缓存性检测引擎


可缓存的引擎设计,检测网页并确定其如何与Web缓存服务器交互, 这个引擎配合这篇指南是一个很好的调试工具,

cgi_buffer库


包含库:用于CGI模式运行的Perl/Python/PHP脚本,自动处理ETag生成/校验,Content-Length生成和内容压缩。正确地。 Python版本也被用作其他大量的CGI脚本。

关于本文档


本文版权属于Mark Nottingham <mnot@pobox.com>,本作品遵循创作共用版权。
如果你镜像本文,请通过以上邮件告知,这样你可以在更新时被通知;
所有的商标属于其所有人。
虽然作者确信内容在发布时的正确性,但不保证其应用或引申应用的正确性,如有误传,错误或其他需要澄清的问题请尽快告知作者;
本文最新版本可以从 http://www.mnot.net/cache_docs/ 获得;
翻译版本包括: 捷克语版,法语版和中文版。
版本: 1.81 - 2007年3月16日
创作共用版权声明
翻译: 车东 2007年9月6日

使用m3u文件连续播放音乐

1. 最简易的连续播放视频/音频的代码

<ASX version ="3.0">

<Entry>
    <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址1"/>
</Entry>

<Entry>
    <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址2"/>
</Entry>

<Entry>
    <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址3"/>
</Entry>


以下同样添加你想要的视频音频个数

</ASX>


 

 

 

 


2. 可以显示歌曲名称/演员名称的连续播放视频/音频的代码


<ASX version ="3.0">

<Entry>
  <Title>这里添加标题1 视频/音乐名称等等</Title>
  <Author>演员/歌手名1</Author>
  <Copyright>版权归属1</Copyright>
  <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址1"/>
</Entry>


<Entry>
  <Title>这里添加标题2 视频/音乐名称等等</Title>
  <Author>演员/歌手名2</Author>
  <Copyright>版权归属2</Copyright>
  <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址2"/>
</Entry>


<Entry>
  <Title>这里添加标题3 视频/音乐名称等等</Title>
  <Author>演员/歌手名3</Author>
  <Copyright>版权归属3</Copyright>
  <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址3"/>
</Entry>


以下同样添加你想要的视频音频个数

</ASX>


3. 可以显示自己的logo或广告以及歌曲名称/演员名称的连续播放视频/音频的代码


<ASX version ="3.0">


<Entry>
  <Title>这里添加标题1 视频/音乐名称等等</Title>
  <Author>演员/歌手名1</Author>
  <Copyright>版权归属1</Copyright>
    <Banner href = "你的个性图片在网络中的地址1">
    </Banner>
  <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址1"/>
</Entry>


<Entry>
  <Title>这里添加标题2 视频/音乐名称等等</Title>
  <Author>演员/歌手名2</Author>
  <Copyright>版权归属2</Copyright>
    <Banner href = "你的个性图片在网络中的地址2">
    </Banner>
  <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址2"/>
</Entry>


<Entry>
  <Title>这里添加标题 视频/音乐名称等等</Title>
  <Author>演员/歌手名</Author>
  <Copyright>版权归属</Copyright>
    <Banner href = "你的个性图片在网络中的地址3">
    </Banner>
  <Ref href = "以http或mms开头以mp3/wma/wmv/wav/midi/mpg/mpeg/avi结尾的视频音频地址3"/>
</Entry>


以下同样添加你想要的视频音频个数

</ASX>




将以上文件取名为"任意名.m3u"。

调用方式形如:


<embed src="test.m3u" type="application/x-mplayer2" autostart="true" loop="-1" controls="ControlPanel" width="600" height="500" >

软件版本Beta,RC,Demo,Build等含义总结

版本号:
V(Version):即版本,通常用数字表示版本号。(如:EVEREST Ultimate v4.20.1188 Beta )
Build:用数字或日期标示版本号的一种方式。(如:VeryCD eMule v0.48a Build 071112)
SP:Service Pack,升级包。(如:Windows XP SP 2/Vista SP 1)

授权和功能划分:
Trial:试用版,通常都有时间限制,有些试用版软件还在功能上做了一定的限制。可注册或购买成为正式版
Unregistered:未注册版,通常没有时间限制,在功能上相对于正式版做了一定的限制。可注册或购买成为正式版。
Demo:演示版,仅仅集成了正式版中的几个功能,不能升级成正式版。
Lite:精简版。
Full version:完整版,属于正式版。

语言划分:
SC:Simplified Chinese简体中文版。
CN : 简体中文版
GBK:简体中文汉字内码扩展规范版。
TC:Traditional Chinese繁体中文版。
CHT : 繁体中文版
BIG5:繁体中文大五码版。
EN : 英文版
Multilanguage : 多语言版
UTF8:Unicode Transformation Format 8 bit,对现有的中文系统不是好的解决方案。



开发阶段划分:
α(Alpha)版:内测版,内部交流或者专业测试人员测试用。Bug较多,普通用户最好不要安装。
β(Beta)版:公测版,专业爱好者大规模测试用,存在一些缺陷,该版本也不适合一般用户安装。
γ(Gamma)版:相当成熟的测试版,与即将发行的正式版相差无几。
RC版:Release Candidate。
RC 版。是 Release Candidate 的缩写,意思是发布倒计时,候选版本,处于Gamma阶段,该版本已经完成全部功能并清除大部分的BUG。到了这个阶段只会除BUG,不会对软件做任何大的更改。从Alpha到Beta再到Gamma是改进的先后关系,但RC1、RC2往往是取舍关系。
Final:正式版。

其他版本
Enhance :增强版或者加强版 属于正式版1
Free :自由版
Release :发行版 有时间限制
Upgrade :升级版
Retail  :零售版
Cardware :属共享软件的一种,只要给作者回复一封电邮或明信片即可。(有的作者并由此提供注册码等),目前这种形式已不多见。/ S
Plus :属增强版,不过这种大部分是在程序界面及多媒体功能上增强。
Preview :预览版
Corporation & Enterprise :企业版
Standard :标准版
Mini :迷你版也叫精简版只有最基本的功能
Premium : 贵价版
Professional : 专业版
Express : 特别版
Deluxe : 豪华版
Regged : 已注册版

Rip :是指从原版文件(一般是指光盘或光盘镜像文件)直接将有用的内容(核心内容)分离出来,剔除无用的文档,例如PDF说明文件啊,视频演示啊之类的东西,也可以算做是精简版吧…但主要内容功能是一点也不能缺少的!另:DVDrip是指将视频和音频直接从DVD光盘里以文件方式分离出来。



RTM 版 :这基本就是最终的版本,英文是 Release To Manufactur,意思是发布到生产商。
Original Equipment Manufacturer (OEM) 
You may license products through an Original Equipment Manufacturer (OEM). These products, such as Windows operating systems, come installed when you purchase a new computer. 
OEM软件是给电脑生产厂的版本,无需多说。 

Full Packaged Product (FPP)/Retail 
Physical, shrink-wrapped boxes of licensed product that can be purchased in a local retail store or any local software retailer. 
FPP就是零售版(盒装软件),这种产品的光盘的卷标都带有"FPP"字样,比如英文WXP Pro的FPP版本的光盘卷标就是WXPFPP_EN,其中WX表示是Windows XP,P是Professional(H是Home),FPP表明是零售版本,EN是表明是英语。获得途径除了在商店购买之外,某些MSDN用户也可以得到。
Volume Licensing for Organizations (VLO) 
You may enjoy potentially significant savings by acquiring multiple product licenses. Depending on the size and type of your organization. 
团体批量许可证(大量采购授权合约),这是为团体购买而制定的一种优惠方式。这种产品的光盘的卷标都带有"VOL"字样,取"Volume"前3个字母,以表明是批量,比如英文WXP Pro的VOL版本的光盘卷标就是WXPVOL_EN,其中WX表示是Windows XP,P是Professional(VOL没有Home版本),VOL表明是团体批量许可证版本,EN是表明是英语。获得途径主要是集团购买,某些MSDN用户也可以得到。

十年MFC经历认识的Microsoft技术

 
自从2005年3月8日下午16时“十年MFC经历认识的Microsoft技术”以帖子的方式发表于CSDN论坛后,引起了许多网友得好评,使得笔者诚惶诚恐,考虑到该贴过长(人气指数为5000),因此转移到Blog上,许多网友对此帖的评语只好省略,在此鄙人谢过了!为感谢网友的支持,本人希望今后能发出新的帖子以回报网友对我的鼓励,再一次谢谢!
 
 
 
初识MFC 
       我最初知道MFC大概是在1993年,那个时候Visual  C++还没面世,当时Microsoft的C++编译器还很弱,官方的名字是Microsoft  C/C++  7.0,MFC的版本是1.0,几乎没有引起什么反响,那个时期最好的C++开发环境是Borland  C++  3.1,其实,大概是1992年11月份,一个偶然的机会,我领略到Borland公司的厉害,记不得在什么地方,我看到一个绝妙的集成开发环境,即Turbo  C++  3.0  for  Windows,这是我记忆中第一个真正的Windows环境下的C++集成开发环境,那种激动的感觉至今仍记忆犹新,不客气的说,当时至少在C++方面,Microsoft与Borland不是一个水平的,Borland明显的要高于Microsoft  ,Borland的产品在技术上给我留下深刻的印象。那个时候Microsoft最好的开发平台是Visual  Basic  3.0,而Borland的Delphi正处于开发阶段(Delphi  的代码名称是:“VB  Killer”)……,想起这些十几年前的往事,我不禁感慨万千。 
十几年来,我用过许多开发环境,关于Visual  Basic,我用过最早的DOS版本,Windows版的Visual  Basic我基本上全都用过,至今我还记得每个版本的VB安装盘磁盘的盘数。同样,我用过各个版本的Delphi,特别是Delphi  2.0,给我留下极好的印象。Delphi提供真正编译的可视化开发环境,那个时候(1994年左右),Delphi就可以开发带有GUI的动态链接库,你可以想象,在Microsoft  Access  2.0的应用程序中可以加载一个Delphi  Form并进行程序交互,那种感觉真是棒极了。 
    Borland  C++是我心中无法抹掉的遗憾,从Turbo  C到C++  Builder,我深刻的体验到Borland的辉煌和无奈,Delphi从VB  Killer走到为VB护航(你可以想象Delphi一步到位的ActiveX  控件开发技术有多牛,早期的VB有多土,早期的VB不能开发动态链接库,因此无法开发ActiveX  控件,想起来真令人嘘唏不已),Borland  C++的命运也是不济。Borland  C++  3.1的辉煌永远不再了,十几年的开发工作中,我在C++上投入了大量的精力,Borland  C++曾经给我带来无数的激动,然而这个经典的名字却在与Microsoft的竞争中渐渐的流逝了……。 
MFC4.0的出现,使得人们感觉Microsoft在C++方面赶上来了,这一版的MFC是Win95推出后出现在Visual  C++  4中(Microsoft没有VC  3,VC4以前的版本是2.2、2.1、2.0、1.51、1.5、1.0)。也许是对Borland  C++的潜意识的失望,我不知不觉的接受了MFC,VC  4.2推出时,我通过正常渠道购买了这个编译器的企业版。
 
 
 
 关于Microsoft 
       关于Microsoft,有无数的人要对这个名字叙说感觉,这个令人讨厌的名字!不知道是喜欢还是憎恶,你是程序员,你的心思可能就要因Microsoft的存在而动,即使你用Linux,你可能也是因为Microsoft技术因素。多少年来,这个名字每天都出现在你、我、他的面前,因为你不得不面对Windows的存在,可是你憎恨这个名字吗?你讨厌这个名字吗?我不知道是否已经对这个名字麻木了。1998年我个人订了Microsoft  MSDN  Universal  版,我开始比较全面接触这个公司的开发技术,你可以想象,1998年当你面对上百张技术光盘的时候,你就知道什么叫做“厚度”,当我们有时说出“赶上”或  “达到”Microsoft某些产品的水平的时候,可能我们缺乏对这个公司“厚度”的真实了解。进入MSDN,我感觉Microsoft简直不是一个“公司”,而是(或者正在形成)一个“社会”。当时著名的技术网站http://www.codeguru.com全部的技术资料是可下载的(那个时候http://www.codeguru.com提供整个网站内容下载服务,大约3M左右),大名鼎鼎的www.codeproject.com还不存在。一开始,我始终潜意识在技术上对比Microsoft与Borland,应当说技术上Borland不比Microsoft弱,即使现在也有人持有这个看法,可是为什么Borland走到今天这个地步?而Microsoft却如日中天?若干年前,这两个公司竞争何等激烈,而现在却是另一番“合作”的景象?可能很多人想过,如果Borland不存在,对Microsoft不是更有力吗?其实Microsoft可能精通中国历史,读过《三国》、十分了解战国时期的中国,其实Borland形式上的存在,对Microsoft是十分有利的,至少形式上还有竞争对手,而事实上Borland已经受控于Microsoft(Microsoft是Borland的大股东)。你可以看到一些微妙的现象:Borland为Microsoft提供了大量的人才,其中包括Delphi总设计师以及Borland  C++编译器的核心成员;同时也为Microsoft  .NET提供强有力的护航服务(看看C#  Builder、Delphi  .NET)。1998年Microsoft  的COM技术基本已经成熟,这个技术使人感到震撼,当时Microsoft的对手们提出“OpenDoc”用于对抗“COM”,你看看“OpenDoc”阵营的几个成员:IBM、Apple、Borland、Novell,你会感到这个阵营十分豪华、强大。但结果却差强人意,“OpenDoc”无疾而终,而“COM”依然生机勃勃。 
    有人说“COM”没落了,那么就太不了解Microsoft了。在与“OpenDoc”的竞争中,“COM”是个彻底的胜利者,在与“Java”的竞争中,“COM”成功的进化了,在这个过程中Microsoft体现了强大的吸收能力、以及无法想象的韧劲。.NET只不过是COM的“别名”而已。对于一个经验丰富的C++程序员而言,.NET就是COM的进化,而Microsoft内部.NET就是“COM  3.0”(OLE2就是COM  2.0),而“CLR”就是一个不择不扣的COM对象。曾经有人问我,既然牛顿时代就奠定了基础(想想著名的牛顿-莱布尼茨公式),几百年后的今天,数学还研究“微积分”吗?回答当然是依然在研究!“微积分”早期是针对函数的,现代“微积分”是针对“流形(Manifold)、纤维丛(Fiber  Bundle)”的,概念深奥了,可是基本思想不变,只是“微积分”的思想得到合理的延拓与进化,你了解Microsoft吗?Microsoft  Research有一批超一流的数学家在为Microsoft工作,其中一些是斐尔兹奖的得主,Microsoft正在实现如同“微积分”进化到“微分流形”一样将“COM”进化到“.NET”。从科学概念角度上分析COM与Java,可能COM更全面、精确,从实现的成熟度上Java可能更成熟,可是你看到,Microsoft正在不紧不慢的追赶。Microsoft令人联想起战国时期的强秦。   
战国时期的秦国,采取“远交近攻”“抚弱掠强”等措施傲视六国,今天的Microsoft也是这样,VB1.0时,Microsoft推出“VBX”控件技术,众多的小公司得以生存,Microsoft自己不开发“VBX”组件,同样“VBX”进化为“OCX”时,Microsoft并不十分强大,可是这种试探得到众多小公司的响应。1997年Microsoft  Office  97、1998年Microsoft推出Visual  Studio  6.0,给众多中、小公司提供了生存、发展的机会,例如Microsoft  Office  97中集成了Visual  Basic  for  Application  5.0,这项技术使得几百家软件开发商与Microsoft签署了VBA技术许可协议,即使AutoDesk这样的公司都与Microsoft签署了这个协议,这个协议使得每个集成VBA的产品的给个用户许可为Microsoft付40$的许可费,如果你了解VSIP(Visual  Studio  Integration  Protocol)协议,以及有多少公司签订了VSIP协议,你就真正感觉到Microsoft的可怕;Microsoft  Office  97、Visual  Studio  6.0的用户界面十分漂亮,为什么Microsoft自己的开发工具不提供类似的软件组件?你看到众多第三方的Microsoft盟友纷纷推出自己的界面库以模仿Microsoft,他们不会反对Microsoft,因为他们已经形成了使得Microsoft以及这些公司得以生存的生态圈。 
    Microsoft的技术储备有多少,Microsoft之外的人很难说清楚,Microsoft中国公司也未必了解多少,1999年WTL类库刚刚出现的时候,人们就希望WTL能得到官方的支持,或授权给一个Microsoft之外的一个公司(你能想象出Borland  C++  5.0内置的ActiveX开发机制是基于Microsoft  ATL类库吗?),直到今天,WTL依然如故,我们完全相信,如果Microsoft强力推广WTL,WTL完全可以流行,可是Microsoft不缺类似的技术,类似的类库还有BCL(Base  Control  Library,一个用于开发轻量级ActiveX控件的类库),Microsoft还有一个基于ATL的类库,这个类库用于开发ActiveX  Designer,ActiveX  Designer是绝大多数程序员不了解得一类对象,如果你熟悉Office开发,你知道Office  VBA  中有一类对象,即Form2,此外VB6.0  中的报表设计器(以及著名的Active  Reporter),都属于此类对象,用这个类库,你可以为VB6.0以及集成VBA的系统提供定制化的可视化设计机制等等,如今ActiveX  Designer已经演化为集成于Visual  Studio  .NET中的设计器。
 
 
 
向Microsoft学习 
       无论从什么角度评价Microsoft,我觉得Microsoft是值得我们学习的,如果说生活在这个时代有Microsoft存在是一场灾难,你就应该痛恨这个家伙,但你首先要向这个家伙学习!我无意为Microsoft歌功颂德,我只是想说出十几年我对Microsoft技术的感受。 
       Microsoft在研究式的开发中受益极大,如果你有兴趣,你可以访问http://research.microsoft.com/,虽然部分中国公司也有研究院,但与Microsoft相比,真有“米粒之珠,也放光华?”的感觉。2003年,我在北京的一个地方现场体验了Microsoft亚洲研究院的招聘会,我看到中国的精英们进入Microsoft的渴望,事实上,在中国大陆,Microsoft亚洲研究院的人力资源已经延伸到各著名高校的相关专业的核心层,我感到,Microsoft几乎不需要“求贤”,因为,只要Microsoft需要,精英们会“蜂拥而至”,每个人都有“可以理解”的理由而向往那个地方,如果为搞数学研究蜂拥到加州大学,我觉得可以理解,因为那里有数学土壤,出了成果国人也会感到自豪,因为“科学无国界”。技术是否有国界?不知道是否有定论?!想想DVD等技术专利给国内业界带来的灾难,不知道应不应该痛定思痛,在Microsoft校园招聘现场的气氛中,我似乎明白了为什么国人“原创技术”少得可怜。我读过几本Microsoft亚洲研究院的高手写的书,明显可以看出,Bill  gate  是他们的精神领袖以及他们对Microsoft的虔诚,国内的研究机构应当研究一下Microsoft的用人之道,Microsoft好像是三国里的人物,不知是刘备还是曹操,或者二者的混合物。我经常路过西格玛大厦,第一次西格玛大厦进入真有“朝圣”的感觉,也与Microsoft中国的几个层次的人打过交道,各中滋味实在一言难尽。 
       在Office大战中,国产软件的确在一些方面与Microsoft进行较量,其实给人的感觉很勉强,界面上的似是而非,或用户习惯方面的接近并不能解决根本的问题,一个好的软件开发人员必须是一个软件使用的高手,很难想象一个软件操作水平很拙劣的开发人员能开发出高水平的软件,我最早使用的软件之一就是Microsoft  Word,当时的版本是2.0,大概是1992年的事情,给我留下深刻印象的是集成于Word中的Word  Basic,后来,我接触到Excel  3.0,不出所料,Excel中集成的是Excel  Basic,后来使用的Access中自然内置Access  Basic  1.0,在这些软件集成捆绑成Office之前,我就感觉这些产品的构思十分了不起,很具有Microsoft的风格,因为你知道,即使是一个DOS,Microsoft都要提供一个内置的QBasic或GW  Basic。虽然关于Microsoft的产品评论很多,作为一个技术人员,我认为Microsoft的产品构思绝对是第一流的,从1994年早期的Office系列到1997年形成的Office  4.2,我认为,技术构思上均领先于我国2002年以后的Office产品,你听说过如下说法吗?“Dos  作为操作系统的时代,Windows是应用软件;Windows是操作系统时,Office成为Dos时代的Windows;那么如果按此规律,Office会不会替代Windows而成为操作系统?”,现在在开发领域Visual  Studio(  .NET)正在成为另一个Office,你注意到了吗?控制Visual  Studio(  .NET)集成开发环境的仍然是一个Basic语言引擎(Visual  Basic  .NET)。 
       与许多公司不同的是,在技术体系上,Microsoft几乎所有的产品是息息相关的,Windows、Office、Visual  Studio  .NET虽然各不相同,但公共的核心即将形成,我们已经看到,核心组件方面,Office与Visual  Studio  .NET日渐趋于一致,例如Microsoft正在将Office  2003的核心组件VBA  6.X逐步用新的Visual  Studio  Tools  for  Office替代,而我们依然在一些似是而非的现象上与Microsoft的产品比较差距,国家采购或政府采购支持的公司,不去钻研核心技术,只是急功近利的采用短期行为急于与Microsoft相争,不知是否有蚍蜉撼树的感觉,个人的体验是,先学习Microsoft,踏踏实实的学,了解Microsoft,深入的了解,然后再喊口号。
 
 
 
为什么用MFC?             
       经过若干年的竞争,Borland  的OWL几乎消失了,这个OWL是个非常漂亮的C++类库,在Borland  C++  3.1风光无限的年代,OWL真正的做到了独领风骚。然而,Borland  C++  4.0错过了进入32位程序的最佳时机,BC  4.0推出后不久,迎来了Win95,Borland仓促上阵,以一个小的“Pack”使得BC4可以编译基于Win4的程序,当时的Visual  C++是2.0版,支持Window16的版本为Visual  C++1.51,有意思的是Borland可以用同一个编译器同时支持Win16、Win32,而Microsoft却不得不为Win16、Win32提供不同的编译器。然而,非正式版本的Visual  C++  2.1与Visual  C++  2.2却悄悄地支持了Win95的最新特征,即Win95新提供的一组公共控件,在我的印象中,Borland对Win95新特征的支持不利使得MFC与OWL的距离极大的缩短了。稍后到来的Borland  C++  4.5没有改变这个状况,尽管Borland  C++  5.0同时支持OWL与MFC,可是败象已经显露,Borland  C++非常遗憾的只走到了5.5版。C++  Builder虽然形式上引入了Delphi的VCL库,可是许多C++程序员并不买账,因为许多以C++为乐的人更喜欢以编辑的模式进行编码。Visual  C++  4.0的出现,在C++这个战场上,Borland开始落败了。 
       MFC发展到今天,已经十多年了,尽管褒贬不一,但可以肯定,十几年的技术积累已经奠定了MFC的生存基础,即使Microsoft的长角发布,MFC也不能推出Windows的舞台,事实上,长角(Longhorn)之后的Visual  Studio  .NET仍将MFC作为一个重要的组成部分,在今年的Visual  Studio  .NET  2005中,MFC在C++中的位置依然如故。MFC的未来,应该不必担心,只要你深入考察.NET类库,你会发现,MFC的许多思想机制正悄然进入.NET,与此同时,Microsoft的第三方盟友十多年来已为MFC开发了大量的扩展库,如果Microsoft是船,第三方盟友就是载舟之水。许多人认为MFC不发展了,其实是一种错觉,Visual  C++  6的界面十分经典,特别是其中的Docking控制条机制,其实Visual  C++  6的IDE完全就是MFC写的,可是MFC类库中控制条相关的类功能很弱,为什么?你会看到许多与Microsoft友好的公司,他们很快的在MFC基础上实现了Visual  C++  6  的Docking机制,这就是Microsoft的高明之处,Microsoft很会给盟友提供机会,其一贯的做法就是在自己的商品化产品中预先提供一些有趣的特征,使得其他一些公司进行模仿以带动用户群体。Borland不具备这样的储备。MFC第三方市场的繁荣,得益于Microsoft的策略与明智。MFC可否跨平台?理论上完全可以,Microsoft不做,也是策略,但是有许多重要的产品Microsoft却默许MFC移植到其他平台,事实上,Microsoft的合作伙伴之一Mainsoft公司(Windows源码就是从这家公司流失的),几年来就是负责移植MFC程序移植到UINIX、Linux、AIX等操作系统之上。 
       新版的Visual  C++中MFC已经支持.NET开发了,MFC与ATL的协作更好了。根据我的经验,MFC、ATL与.NET库三者完全可以融合在一起综合应用到实际的开发工作中去,如果你是MFC行家,我希望ATL与.NET库能成为你的忠实的左右手。那么有没有同时支持MFC、ATL与.NET库的程序?当然有,Visual  Studio  .NET  IDE就是!而且Visual  Studio  .NET  IDE还支持用ATL与.NET库扩展的Addin。
 
 
 
认识Application对象 
       如果你熟悉Microsoft  Office,你应该进一步的剖析这个大型软件,Microsoft  Office中几乎每个程序都是可二次开发的,这一点得益于Microsoft  Office内置的二次开发机制,一个是基于COM机制的VBA模型,另一个是基于.NET框架的托管模型:Visual  Studio  Tools  for  Office。作为一名程序员,你应当在技术角度解析Office的技术结构。Microsoft的大多数软件的对象结构可以通过Visual  Studio提供的工具OLE/COM  Object  Viewer考察其类型库得到,通过引用类型库,你甚至可以得到描述对象信息的C++头文件。这样做真是好处多多。一个典型的Office通常都有一个Application对象(或其他一个与之相当的对象),这个对象相当于软件枢纽,在这里,我们不讨论Office,借此话题说说Application对象。大多数支持扩展(Addin、Plugin)的软件都存在类似的构造。通常,一个系统得Application对象或者是一个COM对象,或者是一个.NET对象,如果你的系统存在这类对象,你的系统就基本具备支持Addin、Plugin的机制了。一个理想的做法就是在一个MFC系统中,内置一个ATL对象或.NET对象,稍后我们给出方案如何做到这一点。设计Application对象的关键是如何规划这个对象的属性、方法、事件。如果你希望系统具备良好的扩展性,Application对象是十分关键的,这也是构架艺术的体现。所谓Addin(Plugin),是系统运行时根据需要加载的对象库,Addin(Plugin)之所以可以扩展系统,关键的因素就是系统加载Addin(Plugin)时,将Application对象传递给Addin(Plugin)库,设想一下,如果Application恰到好处的触发了系统事件,而Addin(Plugin)库如愿的解释了事件,一个Addin(Plugin)库的任务不就OK了吗!因此Application对象是系统设计的关键。 
       如果你精通ATL对象,在你的MFC系统中添加一个ATL对象,这个任务可以用VC  Wizard完成。你已经接受了一个事实,就是MFC程序中存在一个CXXXApp对象(CWinApp的派生类),现在你要做的是增加一个对应得ATL对象。这个对象可以在CXXXApp::InitInstance()中创建,如果ATL对象的类是CXXXAppObject,建议你在CXXXApp对象对象中增加一个成员变量,例如:CComObject  <CXXXAppObject  >*  m_pAppObj,然后可以入下初始化m_pAppObj: 
                                 m_pAppObj  =  new  CComObject  <CXXXAppObject  >; 
注意程序结束时在CXXXApp::ExitInstance()中释放m_pAppObj,语句如下: 
                                 delete  m_pAppObj; 
你可以将系统得关键属性设置成CXXXAppObject的属性,例如系统得标题、是否为多文档等等。系统希望外部调用的功能可以实现为CXXXAppObject的方法,这一点取决于你的需要。系统需要外部扩展的功能,表现为CXXXAppObject的事件,关键是在恰当的位置触发事件以及提供的事件参数。例如,你可以在CXXXApp::InitInstance()触发应用程序开始的事件OnStartUp,Plugin捕获事件后,可以进行特定的初始化(身份确认、初始信息查询等等); 
你可以在CXXXApp::ExitInstance()触发应用程序结束事件,Plugin捕获事件后,处理用户需要的系统退出工作。所有的设计取决于具体设计。 
       如何加载Plugin,是一个有趣的问题,如果Plugin实现为一个COM范畴(Category),可以运用COM技术枚举这个Category;可以将Plugin安装到一个特定目录,也可以通过注册表。Plugin的实现可以用COM技术、也可以用.NET框架。适当的机会我会提供例子……
 
 
 
一些感想
 
 
 
一时心血来潮,就发了这个帖子,很难说是有心,还是无意。几天前我在新浪网上看应氏杯围棋决赛,我觉得该赢了吧,作为一个围棋迷,我们等了十几年,等到了属于国人的应氏杯。记得7、8年前在还在大学工作的时候,有一次,一位同事兴致冲冲的走道我面前对我说:“嗨,昨天马XX赢了李昌镐!”,当时我在系办公室正在看报纸,那位仁兄见我头都没抬,非常不满的抢下报纸,对我吼道:“喂!马XX赢了李昌镐!!你听到没有!!!”,我对他说:“你大惊小怪个啥?!马XX输了李昌镐多少盘,你知道吗?”,马XX几乎一直在输给李昌镐,人们已经不奇怪了,偶尔赢一次,国人就把他捧得北都找不到了,李昌镐弱冠17的时候就傲视这个世界了,可至今面孔不变,几天前的农心杯,中日联军5个人,被他打个落花流水,李昌镐是公认的世界第一,以至于有的高手知道下一个对手如果是他,就会去订回程机票。这次应氏杯,国人竟然感谢崔哲瀚,何也?因为这个弱冠19的小子,挡住了他的大哥李昌镐才使得应氏杯有了悬念。当国人媒体在说韩国仅李昌镐一人厉害的时候,不知道是出何居心还是自欺欺人,李昌镐年方30,不知道要力压中、日多少年!面对这个名字,真有点麻木了,这个太极虎!软件界又来了我们一向不齿的印度虎,2001年我们的软件出口额仅是印度的四十分之一,我们震惊了,怎么可能呢?这个四十分之一水分很大,很可能更可怜!当时我在大连参加一个关于“大连软件出口国内第一”的官方会议,那位大人在会上说:“据说,我们大连软件出口国内排名第一,市有关领导希望今天的会议给出这个第一的数字依据,希望你们把数据报上来,去年的数据也可申报,注意,我们要的只是数据,你们仔细体会,我们根据数据,有奖励,机会难得呀!”……。某一天,几个朋友在我家看央视的对话节目,对话一方为国内的软件大鳄们(用友、阿尔派等公司的老总们),另一方为印度软件的一个代表团。当问及中、印软件差距的时候,我们的刘老总(代表阿尔派)不以为然的说,据他的看法,我们已经快赶上(印度)了,……,言下之意颇有印度的水平不过如此的感觉,印度方的话我至今记忆犹新:“是否赶上,国际市场说的算!在中国看来,印度程序员的个性不足,技术也不怎么样,其实是个错觉,印度软件首先注重个性,许多重要的美国商品化软件都是在印度本土开发的……”,我们的舆论总是将印度程序员的水平描述的平庸至极,可是差距日渐拉开,……,围棋、足球(不好意思谈,谈不出口!)、软件,我们被近邻严酷的封锁了,乐坏了记者们、给媒体带来了生机…… 
       日本江户时代的围棋,如果一个人要想世袭一个称号(例如:本因坊),他必须战胜所有的师兄弟,然后,住进师父家的内室,你知道以后的事情吗?以后,这个棋手,就得为师父一家做饭、带孩子、搞卫生……,其余的门人则一心一意的下棋,这样的人、方式,造就了一代一代的本因坊,他们的棋谱大多数都流芳至今,这就是早期日本围棋的悟道模式。软件总共有多少语句?我最早接触的计算机软件教材是一本英文版的(影印的D版),不同于我们,那本书的作者构造了“X-语言”,他们不讲什么C、Pascal、Basic,一旦缺了什么机制,就给“X-语言”添加些成分。什么C、Pascal、Basic,你感觉差不多,但现在却分出了等级!我们驾驭语言的能力弱得很,可是我们在语言的细微之处却很讲究,不知道对不对,许多程序员也许是出于虚荣而用C++,事实上,地球人都知道,做数据库,Delphi、VB远比C++胜任,铺天盖地的C++的书,写的东西几乎雷同,因为,有用的或者作者不写、或者作者不懂。有时我在想,如果国内没有内需,会怎样?也许软件内需的存在,造就了中国软件的特色,我认为国内业界并没有充分利用中国软件内需的存在,也许中国软件内需的存在是软件落后的硬伤。 
       我记得一部电影《神辫》,那个英雄的大辫子被洋人炸掉了,最终他成了神枪手,战胜洋人用大刀、秘籍是不行的,用洋的东西战胜洋的技术才是正道。我觉得,一个好的程序员必须了解软件的历史,学习历史,你知道你为什么弱,别人是如何强大的。我们正在另一个战场上抗美(可笑的是我们却要赶超印度!),无论Microsoft、Borland如何争斗,无论他们谁统治谁,他们不影响美国的强大,朋友们,学习Microsoft,开发出让国人感到牛的软件! 
       这个帖子出乎本人的意料,愿意与大家共勉,希望这个帖子常在,与大家敞开心扉的交流!
 
 
 
FireFox与Microsoft 
       FireFox在一片赞扬、欢呼声中激情登场了,也许人们真的期待已久,平静的水面终于被扔进一块石头。我是IE的最早期的用户了,1996年首次Microsoft的TED(技术教育大会),IE4还没有发布时候,我们有机会目睹了内部版本的IE4(当时内部名称是:纳什维尔,英文名称忘记了),那真是一次令人激动的预览,当时IE3与Navigator  3激战正酣。当你第一次看到想象中的“Active  Desktop”,如果你没有身临其境,你不会激动。IE4本质上是一个Shell,其SDK是免费的,Navigator是基于Mozilla的浏览器,虽然是开源的,由于要照顾更大的共性(与操作系统无关),因此Mozilla不能充分的利用Windows的优势,Mozilla不能为广大的程序员带来所谓开发人员的“快感”,顶尖程序员可以驾驭Mozilla,以实现技术深度带来的乐趣,最早的Navigator同时提供17个版本(注意:不是17种自然语言,而是17种操作系统),从数学角度分析,Mozilla就像一组公理,你可以以此为基础开发不同操作系统上的浏览器,Navigator就是基于Mozilla的一个漂亮的结果,你能欣赏到代码结构的优美,然而失去的却是功能强大的个性(要知道,Windows用户在数量上远大于其他操作系统用户的总和)。普通用户不可能读懂Mozilla的代码,即使懂了也不能很好的运用,这也许是Mozilla(以及大多数开源代码)失败的致命原因之一。IE内核聪明的抓住了开发者,你想想:对数以万计的中、初级开发者而言,容易驾驭是首选的选择,也是明智的。我读过Mozilla,但我不会在开发过程中为一个具体的项目应用它。只要是浏览器,就不可能绝对的安全,无论是Mozilla,还是IE。当我了解到FireFox是基于Mozilla的一个新的浏览器,我基本上对其失去了信心,我有一个奇怪的观点:FireFox的推出,最大的受益者绝对是Microsoft,即使Microsoft失去20%的份额,但是会导致Microsoft强化IE,Microsoft正不知道如何促使IE进化的时候,FireFox的出现无疑为Microsoft提供了机会,物种进化的原则就是竞争,FireFox就是促进IE进一步强大的催化剂。FireFox的扩展机制的确十分灵活,如果对手不是Microsoft,就很难掀起波澜,而且当高级的开发者逐渐了解FireFox的时候,FireFox的漏洞就会渐渐暴露,试想想,如果某种Linux取代了Windows,那么,它的漏洞也会与Windows一样多,因为那个时候,会有与研究Windows漏洞一样多的人去研究对应得Linux的漏洞!从个人的角度上看,Microsoft也许有点“冤”,因为窥视Microsoft弱点的人实在太多了。从理论上看,计算机安全性是个永远的话题,就像任何社会都需要警察一样,没有了小偷、贼、犯罪,警察也就消失了,你想想,文明是什么?野蛮能消失吗?野蛮消失了,文明也就不存在了,高度文明就是更不存在了。人类克服了癌症,下一个疾病会比癌症更致命,但这并不意味着不必克服了癌症,进步真是一种挑战…… 
       IE的技术构思肯定是个卓越的构思,IE可扩展的机制,会给Windows开发者带来许许多多的益处。我正在计划一片文章,介绍如何将你的对象模型与MSHTML库实现对接,这样,在HTML文件中可以将你的指令系统与HTML对象模型融合在一起。 
 
 
 
话说“Hook” 
       在CSDN上时常看到关于“hook”,的问题,令我想起另一个话题,那就是游戏“外挂”。Hook提供一种改变一个Windows窗口消息处理的一种手段,通常的开发根本用不到,因此,谈不上“常用”,早期的Windows,由于不能很好的支持远东(当然包含汉字)地区的文字,因此出现了许多外挂的软件补充Windows的不足,中文之星是一个典型的、令国人自豪的软件,监控软件也许要运用hook技术,此外,很难想象什么软件会用到hook。有人问我,能不能改变一个进程的数据处理行为,我曾经告诉他:能,也不能!感觉告诉我,hook绝大多数场合下是一种“不礼貌”的行为。曾有一段时间,我的服务器,经常有人悄悄地近来,给我增加许多超级用户,肆意修改我的管理权限,我找到托管商,解决了这个问题,那时,我也买了几本服务器监听、安全方面的书,看了几天,我就放弃了,为什么?担心学坏(正、邪仅在一念之差),其实,每个服务器都很脆弱,对有经验的系统程序员而言,安全性与道德准则是联系在一起的,软件技术上走邪路很容易,有时我会想,如果我去设计病毒或者当黑客,会怎样?基础数学出身的我,数论、组合学、密码理论统统不是问题,Windows虚拟驱动程序开发,也不是问题!为什么那么多的人关心hook?国人的正道软件寥寥无几,可破解术却出神入化,可惜,可惜!hook是一种底层的编成机制,能理解好hook的人,完全具备掌握一流技术的底蕴,真希望回头…… 
 
 
 
MFC的批判 
       记得梁羽生先生笔下有一位正邪兼修的高手,名曰“乔北溟”(好像是这个名字),一次此人与大侠张丹枫在一个庙中相遇,乔北溟随手操起香案上的香炉,张丹枫问他:“你的家伙称手吗?”  ,乔北溟笑答:“以吾辈之见识,还在意手中之物是否为剑?”,张丹枫一愣,心中暗念,此人果然不同凡响…… 
       说起MFC,许多人都会撇撇嘴,高手们会对其提出许多尖锐的批评,例如,刻板的Document-View机制,繁复的框架结构,怪异的COM实现以及令人莫名其妙的宏,等等。MFC的大而全,不仅捆住了MFC开发组的手脚,也为全面掌握MFC的愿望设置了障碍。高手们批评之余,可能忽略了一个基本的事实,这个事实就是,你的批评来自于你对MFC的深入理解,当许多人指出MFC的种种弱点时,他们或许不愿意承认:他们的技高一筹、见识超人一等是MFC带来的,不止一次有人与我谈及:“MFC的COM实现,实在差劲,看看ATL(不容否认,ATL至今仍然是开发COM的最佳C++类库),你就会感觉MFC的臃肿……”,我们中的许多人潜意识里不知不觉的在作一件事:“当我们借助一部梯子登上一层楼的时候,我们会评价这个梯子是如何如何之糟糕。”1999年,我的一个项目中需要一个描述引擎,VBS(Visual  Basic  Script),是个免费的语言引擎,但功能局限极大,我联系了美国的Summit公司,他们很快寄来了Microsoft的Visual  Basic  for  Application  SDK  6.0,当时我的团队可谓很强,其中的几位研究生C++修养很好,拿到VBA  SDK时,他们对我说:“应当没问题,我们很快就会搞定VBA  SDK”,可是几天过去了,连个例子都没出来,原来,虽然VBA  SDK提供了MFC扩展类库(基于模版机制的MFC/ATL合成类库),可实现得极其别扭,我接手后的当天晚上,VBA  的IDE就集成到系统中,第二天可编程对象顺利出现在VBA  的IDE中,其余人觉得很奇怪,一看代码,原来我绕过Microsoft的例子,完全是另外的实现途径,那个时候,我感觉到,Microsoft这个家伙真的可恶,本来清晰的集成途径,却人为的让你绕来绕去增加技术难度,过后想想,也可以理解,不这样,第三方的Summit何以作技术支持?我经常想,如果没有商业利益,许多技术应当十分简洁、高效,这一点,Microsoft以及其他大公司都十分明白,如果一切都是最佳的实现模式,可能就另外一种局面了,复变函数论中有一个著名的定理:“复平面上处处解析的函数一定是常值函数。”,  学生们很难理解,当时我说,如果把一个省几十个县的最好学生组成一个班会怎样?结果是一定有一个较差的学生(除非这个班只有一个学生!),这是个无法抗拒的定则,你想想,用天下最好的20个菜形成的酒席是什么味道?那一定是最差的! 
       Microsoft的MFC是值得你学习和使用的,如果你讨厌这个东西或者你认为这是个邪恶的东西,你学学乔北溟,实现正邪归一…… 
 
 
 
有感于“鸡兔同笼” 
       小女初到北京时,对北京的教育颇为不适,铺天盖地的数学奥赛培训班向她压过来,孩子真是辛苦。她四年级时,就的对初等数论的基本内容进行强迫性的熟悉,还好,经过一段时间的努力,掌握了“鸡兔同笼”、“韩信点兵”等中国经典,马马虎虎的能证明费马小定理,有一天,她问我:“爸爸,大学数学什么样?还有‘鸡兔同笼’吗?”,我说,有,我特意找了本老外写的《Basic  Algebra》,找到其中的“中国剩余定理”,小孩子接着问道:“这本书中还有中国人的数学内容吗?”,我在习题中给她找到华罗庚老先生的“反同构定理”,小孩子又接着问:“还有吗?”,我感到很没面子,因为真的找不到了…… 
       曾经的一个梦,就是当一个数学家!为此,研究生时期买了大量的数学书,当时我们系的资料室是联合国教科文组织的藏书室,可以说,里面就是一个装满武功秘籍的宝库。有一天我们打扫资料室的一个仓库,仓库里全是鼓鼓囊囊的麻袋包,上面落满灰尘,手触摸一下,能粘出几毫米厚的灰尘,可以想象有几年没有打扫了。同学无意中揭开一个麻袋,我们惊呆了,里面是美国60年代各大学的数学杂志,每个杂志的名字都是响当当的,那真叫浩如烟海!当时我们就想,我们的论文能发表到其中吗?如果侥幸发了几篇,可想而知,我们就可以当博导了,这些比国内所谓核心期刊有分量得多的杂志,就像CSDN上的帖子一样,很快就会被淹没了,也许很久都不会有人参考、访问……,有一天,我也当了老师,面临着种种考核,于是,我们就成了论文机器,不论是否有价值,只要是核心的,你就高人一等。那个时候,我经常想起那些麻袋里的文献…… 
       我们整体水平的落后,导致整体的浮躁,数量上上去了,质量却下来了。若干年后,也许我成熟了,我们这些曾经站在大学讲坛上的人,没什么好的东西讲(谈不上  ‘教’)给年轻的学生,记得当年我校的计算中心计划招个培训班,几天过去,仅有7人报名,第8人来时,前7人就退了3人,主任感到奇怪,问学生,学生不语,其中原委并不复杂。我发此帖并没有精心策划,的确如某些网友所言是随感而发,“鸡兔同笼”、“勾股定理”已经有了历史地位,如果仅仅够用,我们住草房子一样保暖,为何建大厦呢?为什么放弃传统的长袍、马褂而去穿西装革履?病毒软件大战几乎是自杀性的内战,没有撼动国外产品的分毫,我们许多人喜欢对自己人说三道四,是不是很少想一致对外?人家卖我们打折的产品,条件是附加一份“忏悔书”,而执行者却是我们国人,为什么?因为我们的东西匮乏!当年别人用钢铁武器掠夺了我们的财富,他们强大了,地痞无赖换上了绅士面孔,讲起了法律,当你用D版时,人家文明的指责你,你的人力、财力、物力统统为人所用,取之于你用之于你,而我们却依然陶醉在“鸡兔同笼”、“勾股定理”的历史成就之中,我们依然喜欢争论“勾股定理”谁发现得更早,π是谁最先精确计算的,就像谈论C++谁的水平更高一样。
想起西太后……
    据说,西太后垂帘时期,洋人曾送给她一量火车,慈禧看过后,大骇,以为妖魔……。火车进入中国,最初可能也是引起争议的,大多数百姓都对此怀有惧怕的心理,时间长了,也就自然而然的接受了。
    想想Windows,最早也就是1983-1984年间出生的,到现在充其量也就20几岁,Linux大概比Windows小十岁。Windows来自一个贵族家庭,家长是Microsoft;Linux来自一个单身“母亲”,成长环境是一个社会,正可谓一个是贵族娇儿、一个是山野村夫。在洋人的世界里,Windows与Linux正如火如荼的争斗着,就跟人类历史上贵族与平民之间的争斗一样。我们很看不惯许多贵族娇儿,往往拿平民子弟的良好习惯与之比较,这一点都不奇怪。然而,我们平民阶层却有培养贵族的愿望,Linux社会也是如此,Linux社会已经分化出许多贵族了,为了各自的利益,面和心不和的联盟不知结了多少回,历史告诉我们,如果Windows死了而且如果Linux社会繁荣了,Linux社会就会出现一个贵族来接替Windows(叫不叫Windows无所谓,改朝换代吗)。某次,与国内一著名软件研究所的几个朋友一起进餐,谈到了Linux,话题自然很多,话题之一就是中国的Linux,朋友们笑曰:“……,自主知识产权的操作系统,也就是对开源代码内核的汉化,……”。人们可以列举许多关于Linux的强大之处,比如著名的电影《泰坦尼克》的特技制作,高性能计算Linux操作系统占据绝对主动等等……,其实对了解Linux的人而言,这一点并不奇怪:为某一特定的任务定制的Linux很容易剔除许多不必要的服务,这就好比轻装上阵,事实上,如果可以将许许多多的不必要的“虚拟”服务卸掉,Windows的性能并不差(Windows要加载许多虚拟服务),其实,即使Linux专家也承认,如果Windows允许向Linux一样可以按要求订制,许多弱点也许就不存在了,但市场也就是另一回事了。
    回到我们国人的立场,本人以为,没必要太多的对比Windows、Linux,如果我们Linux领域很强或者我们创造了Linux(毕竟这些都是洋玩意儿),倒也罢了,事实上,我们在Linux领域也不比Windows领域光彩多少,我们的许多人愿意用Linux说话,可是我们在Linux上拿出让我们立足的工作了吗?看看Linux社区就知道了!我们为什么不能把Windows、Linux都当作进入中国的两种新型火车而客观对待呢?在外交礼仪、航海公约以及种种标准等方面,我们已经潜移默化的接受了,为什么?因为我们弱势。Linux也是别人的,与Windows没什么区别。我们真正应该做的,是接受并使自己强大,许多网友给我回信,认为我的观点偏激或奉Microsoft为神,其实不对!只有认识一个强大的令你敬佩的敌人,你才有激情去强大自己。谈论Microsoft越多,其实客观上是对其承认越深,如果Microsoft不够强,就没必要拿它说话或对比,Microsoft给世界带来太多的负担,任何一个操作系统想战胜它,前提是必须兼容它给这个世界已经带来的积累,因此Microsoft很可能是无疾而终,铁木真的王国,是巨大无比的(据说他要讨伐一个对象,路上要在他的疆土内走上一年的时间),可并没有谁灭了这个王国,是自己灭的。我们认为铁木真是中国历史上的人物,可是有些国家认为是他们历史上的人物。其实只要客观对待Windows、Linux,能够正确定位他们的应用环节,Windows、Linux就都是好的东西!
    我们应当更多的看到,IBM、Microsoft等的崛起,是公司行为,这些公司为美国创造的不仅仅是辉煌,而是国力!我们的软件,依赖国家行为的投资(我们的软件研究所拿出什么来了?),靠国家的资助,却抱怨别人在垄断,多可笑!我们应当搞清楚我们在食物链中的层次,然后再指责别人。你不强大,你就受人欺负,拳头是硬道理。看看我们的北大青鸟,如果你看了他们的组件工厂的论述,你会觉得这是世界上最好的东西,可是你看到市场上北大青鸟在干嘛?我们有太多的自欺欺人的东西,有太多的世界领先、国内首创的鉴定,可是回头来,我们不得不清醒了……
 
 
后记:
 
为感谢CSDN网友的支持,本人拟定陆续增加几个新的帖子: 
一、十年MFC经历认识的Microsoft开发技术-多文档界面开发技术:此贴讨论一类多文档界面,主窗口是一个单文档界面,如果你愿意,你可以将多文档窗口作为主窗口的一个视图(CView)显示,这类多文档界面支持无限多个文档类型(即可以加载任意多个文档模板),支持(基于COM、.NET)二次开发技术以及VBA集成; 
二、十年MFC经历认识的Microsoft开发技术-可视化文档界面设计技术:此贴讨论MFC  Document/View  机制的可视化实现,将给出一种所见即所得的Document/View  设计机制;其中包含如何集成ActiveX  Ctrl、.NET  User  Control、MFC  CView类对象以形成一个MFC窗体; 
三、在MFC程序中如何有效的使用HTML、flash,例如,可以实现flash动画作为一个程序的Splash以增强程序的感染力,使用HTML、flash动画作为MDI程序的MDI用户区的背景等等……; 
四、十年MFC经历认识的Microsoft开发技术-MFC  .NET组件开发技术:介绍如何使用MFC类库开发.NET组件,例如可以用MFC开发WinForm对象,然后用于VB.NET、C#等等。 
 
如果大家有好的建议,请与我联系(sunhui@mail.apptemplate.com、sunhuizlz@yeah.net),如果有北京的朋友肯帮忙协助,在下不胜感激,希望得到大家的支持!
 

让Win32程序员更轻松的10个Perl模块

Windows特有Perl 模块的Perl档案库网络(CPAN)内容能够让很多人觉得满意。Windows用户喜欢它,因为它允许用户在Windows开发环境下高效而快速地开展相当复杂的工作。开源程序员喜欢它,因为相对于其他操作系统,Perl具有超强的兼容性。简言之,人人都喜欢。

 

这就是不管你站在那边,都有理由阅读它的原因。在文档中,我编制Perl模块目录,以便于在Win32 Perl编程中快速查阅。(见表A)如果你是个希望操纵Windows世界的Perl程序员,则应该将它们添加到工具箱中。

注意:可以直接从网络上安装CPAN模块。

 

 

表A

 





































包名

描述

Win32API::Registry

这个模块提供与Windows注册相关的Win32 API调用接口,允许读写注册值,创建删除注册键值、查询键值和从磁盘加载注册信息。

 

在需要读写Windows注册信息时使用此模块。

Win32::OLE

此模块允许通过使用OLE(对象连接和嵌入)的Perl脚本控制Win32应用程序。OLE对象参考与目标OLE类相关,并且可以通过Perl访问OLE类的方法。

 

在需要适应的OLE应用程序的界面时,例如Microsoft Office,使用此模块。

Win32::Internet

此模块提供访问WININET.DLL中库函数功能,在Windows中管理HTTP和FTP连接尤其实用。

 

当Perl程序需要与网络连接、URLs和FTP,HTTP发生作用时使用此模块。

Win32::Process::Info

此模块使得访问Windows进程信息变得容易。它提供了找回进程和子进程标记部分和获得详细进程信息的函数,注意它不允许杀死已有的进程。

 

在需要找回或监视当前进程表的时候使用此模块

Win32::Clipboard

此模块提供了Windows剪切板的优美接口。允许读剪切板中的内容,向剪切板上添加新信息或“等待”操作。

 

当需要使用剪切板在Windows和Perl应用程序之间转换数据或者显示当前剪切板内容时,使用此模块。

Win32::Service

此模块提供了与Windows服务管理器相互作用的函数,可以开始或停止Windows服务。

 

当需要开始或停止Windows服务时(例如,安装程序),使用此模块。

Win32::FileOp

此模块为许多Windows特有的文件操作提供API,这些文件操作包括:向Recycle Bin中移动文件,与Recent Documents作用,编辑INI文件,打开和关闭对话框和使用ShellExecute方法执行默认文件操作。

 

当需要快速执行不同的Windows特有的操作时使用此模块。

Win32::Pipe

此模块允许向Win32Perl程序中添加已命名的pipes.

 

当需要在应用程序和Perl服务后台程序中建立客户机-服务器连接时,使用此模块。

Win32::DriveInfo

此模块可以获得驱动器、文件系统、已用磁盘空间和Windows驱动器字符的广泛信息

 

在需要与Windows文件系统相互作用:得到可用驱动器字符、计算可用和已用磁盘空间或显示驱动器属性时,使用此模块。

Log::Dispatch::Win32EventLog

此模块提供与Windows事件日志相互作用的API。它允许注册事件和应用程序,将它们与特有日志联系和向日志中添加新信息。

 

使用此模块记录错误信息和使用Windows事件跟踪




Modified At 2008-06-04 13:35:58

Assembly Manifest 通俗简易手册[转]

 
[现象]
对这个问题的研究是起源于这么一个现象:当你用VC++2005(或者其它.NET)写程序后,在自己的计算机上能毫无问题地运行,但是当把此exe文件拷贝到别人电脑上时,便不能运行了,大致的错误提示如下:应用程序配置不正确,请重新安装程序……或者是MSVCR80D.dll 没有找到什么的(我记得不是很清楚,不过大致是这样的)
 
 
[分析]
看到这样的提示,当然不会傻到重装咯。第一反应应该是什么配置有问题、或者是缺少了什么依赖的库文件;于是我就根据以前Windows缺少库文件的经验,把所有库文件(××.DLL)统统一股脑地复制到当前文件夹下来,满心欢喜以为可以运行了,以运行……@#¥@#%¥……还是挂了。
 
 
[探索]
于是开始网上搜索,我Google,我摆渡;渐渐我发现,这一切都和一个叫做***.manifest 类型的文件发生关系,那么到底什么是 .manifest 文件呢?他有什么用,以前为什么没有?
后来,经过艰苦努力,终于得知,原来这一切都是Windows 的Assembly Manifest搞的鬼。这个东东的作用就是为了解决 以前windows上的“Dll 地狱” 问题才产生的新的DLL管理解决方案。大家知道,Dll是动态加载共享库,同一个Dll可能被多个程序所使用,而所谓“Dll 地狱”就是当不通程序依赖的Dll相同,但版本不同时,由于系统不能分辨到底哪个是哪个,所以加载错了Dll版本,然后就挂了。于是盖茨就吸取了教训,搞了一个程序集清单的东东,每个程序都要有一个清单,这个清单存再和自己应用程序同名的.manifest文件中,里面列出其所需要的所有依赖,这儿所列出的依赖可不是简单地靠文件明来区分的,而是根据一种叫做“强文件名”的东西区分的,那么什么是强文件明呢?我们来看一下这个.manifest文件便知道了。
 
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<dependency>
<dependentAssembly>
<assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
</dependentAssembly>
</dependency>
</assembly>
 
我们发现原来这是一个XML格式的文件,其中<dependency>这一部分指明了其依赖于一个名字叫做Microsoft.VC80.CRT的库。但是我们发现,<assemblyIdentity>属性里面还有其它的东东,分别是
type系统类型,version版本号,processorArchitecture平台环境,publicKeyToken公匙(一般用来标示一个公司)……把他们加在一起便成了“强文件名”了,有了这种“强文件名”,我们就可以根据其区分不同的版本、不同的平台……总之,有了这种强文件名,系统中可以有多个不同版本的相同的库共存而不会发生冲突。
 
 
[深入]
恩,那么现在,我们就来具体了解一下这一套机制。
首先是强弱文件名的问题。正如上面提到的那样,为了区分不同版本或不同厂商生成的相同的程序集,必须用一个Assembly Manifest程序清单来列出我这个程序集的强文件名--慢着,到这里你可能会问:刚才不是说Assembly Manifest程序清单是列出其所依赖的程序集的强文件名呢,怎么这里变成了当前文件的强文件明了呢?其实,Assembly Manifest程序清单有两部分功能,上面这个实例之所以标注了其所依赖的文件的强文件名是因为其是客户端的Assembly Manifest,在服务端有另外一个Manifest 来标注。
 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<noInheritable></noInheritable>
<assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.42" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
<file name="msvcr80.dll" hash="2a0d797a8c5eac76e54e98db9682e0938c614b45" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>phRUExlAeZ8BwmlD8VlO5udAnRE=</dsig:DigestValue></asmv2:hash></file>
<file name="msvcp80.dll" hash="cc4ca55fb6aa6b7bb8577ab4b649ab77e42f8f91" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>7AY1JqoUvK3u/6bYWbOagGgAFbc=</dsig:DigestValue></asmv2:hash></file>
<file name="msvcm80.dll" hash="55e8e87bbde00d1d96cc119ccd94e0c02c9a2768" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>hWq8zazTsMeKVxWFBa6bnv4hEOw=</dsig:DigestValue></asmv2:hash></file>
</assembly>
这个便是从WINDOWS\WinSxS\Manifests目录下取出来的一个manifest文件,再这个文件夹下有一陀子这种XML格式的manifest文件,其是服务端的程序清单。WinSxs是windows XP以上版本提供的[blue]非托管并行缓存(side-by-side catche)[/blue]里面安装了各种版本的经过强文件名签名的系统库,而上面这个文件<assemblyIdentity>正是标注了系统中Microsoft.VC80.CRT的一个版本的强文件名签名,如果其和客户端。.manifest 清单里面<dependentAssembly>所列出的依赖项对上的话,就会被加载。刚才说的side-by-side 是指各种不同的版本并行运行。
上面这个服务端manifest文件中<file>标签具体指明了当前强文件名签名的到底是哪一个文件,其中还有这个文件的Hash签名,以确保文件的完整性。
 
好了,有了这一套机制,就可以非常非常安全地进行库文件关联了,但是、但是貌似还有一个一直困扰我们的问题:这套机制安全是安全了,但是却失去了以前良好的前后版本兼容性,即如果你的系统库发生了升级,那么服务端的版本号发生了变化,那岂不是所有服务端程序都不能使用了吗?其实,windows还使用一个policy的策略文件来确认映射关系。
 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright ? 1981-2001 Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32-policy" name="policy.8.0.Microsoft.VC80.CRT" version="8.0.50727.42" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC80.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<bindingRedirect oldVersion="8.0.41204.256-8.0.50608.0" newVersion="8.0.50727.42"/>
</dependentAssembly>
</dependency>
</assembly>
 
这便是在WINDOWS\WinSxS\Policies目录下的一个Policy文件,其中<bindingRedirect>标签便指定了所有8.0.41204.256-8.0.50608.0变本的客户需求映射到8.0.50727.42这个我现在系统中安装的比较新的版本的库。当然我们也能对别的字段进行映射,这样便能很好解决系统升级带来的问题。
 
[应用]
经过以上的讲解,大家对整个依赖查找过程都有了一个整体的认识,那么在实际中问题就好解决了。
让我们回到实际问题中,我之前说了,把一个程序编译连接成可执行程序后,在别人的电脑上发现找不到其所依赖的库了,那么怎么办呢?聪明的你自然想到把其所依赖的库相应的版本拷贝到目标计算机上面,可是……当你在拼命寻找那个可执行文件的assembly manifests文件的时候,却突然发现找不到了,在执行目录下面明明只有一个exe文件嘛。是不是没有生成呢?显然不会,原来是资源连接器把那个assembly manifests文件连接到了可执行文件里面了;不信,你可以用你的vc++打开一个可执行文件看看,在其资源项里面就有一个叫做RT_MANIFEST的项目。这个里面就是二进制标示的manifests文件。那么根据这里面提供的要求,将相应版本的依赖文件(一般就是CRT运行库)拷贝到系统目录Windows\WinSxS\,记住一般会是连带着一个特殊命名的目录一起拷贝到那个文件夹下,比如CRT的运行库就是WinSxS\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50608.0_x-ww_b7acac55有这样一个目录,其标注了此库的版本号以及签名等信息,以防止多个版本重名时不能复制到同一WinSxS目录下。
 
这样就搞定了么?如果是以前,那么一切都解决了,系统会在这个目录下面找到这个运行库,可是现在单单这样可不行,系统可是要找到这个运行库的assembly manifests文件,并且对比强文件名之后才能加载,所以所以千万别忘了把相应的manifests文件拷贝到\WinSxS\Manifests目录下面。
 
当然,这样在目标的系统文件夹下面打动干戈,自然有些过于暴动了,还好,Windows还为我们提供了一种私有查找方式。这种方式会在前面的位置找不到合适库的时候在本地文件夹下面找。所以你只要把之前的库以及那个manifests文件一起拷贝到你的应用程序的路径下面,就可以使用啦。
 
根据MSDN的说明,在本地查找并加载遵循一下规则:
 
在应用程序本地文件夹中查找名为 <assemblyName>.manifest 的清单文件。在此示例中,加载程序试图在 appl.exe 所在的文件夹中查找 Microsoft.VC80.CRT.manifest。如果找到该清单,加载程序将从应用程序文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。
 
尝试在 appl.exe 本地文件夹中打开文件夹 <assemblyName>,如果存在此文件夹,则从中加载清单文件 <assemblyName>.manifest。如果找到该清单,加载程序将从 <assemblyName> 文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。
最后,我想补充的一点是,在你的VC++安装目录下面的“Microsoft Visual Studio 8\VC\redist”目录下,有着所有的提供发布的已经配备相应.manifest的库文件。所以你想要发布一个程序最简单最安全的做法(不用担心用户电脑是否包含你所需要的库)就是把这个目录下面的相应的库的文件夹和你的可执行文件放在一起发布。
比如在X86平台下如果你的可执行文件用到了CRT库(废话么),那么就拷贝Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT这个文件夹到你的程序所在的目录,一起发布,就万事大吉啦!

RSS详解

(Really Simple Syndication)是一种描述和同步网站内容的格式,是目前使用最广泛的XML应用。

RSS是一种起源于网景的推技术,将订户订阅的内容传送给他们的通讯协同格式(Protocol)。RSS可以是以下三个解释的其中一个:
Really Simple Syndication
RDF (Resource Description Framework) Site Summary
Rich Site Summary
但其实这三个解释都是指同一种Syndication的技术。 RSS目前广泛用于网上新闻频道,blog和wiki,主要的版本有0.91, 1.0, 2.0。

Continue reading "RSS详解"