<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Computer, Electron and Technology &#187; 缓存</title>
	<atom:link href="http://www.donevii.com/post/tag/%e7%bc%93%e5%ad%98/feed" rel="self" type="application/rss+xml" />
	<link>http://www.donevii.com</link>
	<description>DoneVII CET &#38; CPPLITE</description>
	<lastBuildDate>Wed, 02 Jun 2010 10:45:52 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>清除指定squid缓存文件的脚本</title>
		<link>http://www.donevii.com/post/758.html</link>
		<comments>http://www.donevii.com/post/758.html#comments</comments>
		<pubDate>Mon, 09 Feb 2009 10:28:11 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/758.html</guid>
		<description><![CDATA[用wget来清空squid缓存 可以使用以下命令来清空squid的缓存： 代码:/usr/bin/wget -O squid.log -S &#8211;header=&#8221;ragma: no-cache&#8221;&#160; url 代码: squidclient -m PURGE -p 80 &#8220;http://you.video.sina.com.cn/index.html&#8... ]]></description>
			<content:encoded><![CDATA[<p>用wget来清空squid缓存</p>
<p>可以使用以下命令来清空squid的缓存：</p>
<p>代码:<br />/usr/bin/wget -O squid.log -S &#8211;header=&#8221;ragma: no-<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>&#8221;&nbsp; url</p>
<p>代码:</p>
<p>squidclient -m PURGE -p 80 &#8220;http://you.video.sina.com.cn/index.html&#8221;</p>
<p>代码:</p>
<p>http://www.cdn.com/api/purge.php?action=purge&amp;host=61.55.111.111&amp;url=http://www.md5.cn/</p>
<p>一款老外的程序，可以批量清除某类URL的Squid缓存，支持正则表达式。</p>
<p>　　下载网址：http://www.wa.apana.org.au/~dean/squidpurge/</p>
<p>　　编译：<br />代码:<br />wget http://www.wa.apana.org.au/~dean &#8230; 20040201-src.tar.gz<br />tar zxvf purge-20040201-src.tar.gz<br />cd purge<br />make<br />　　清除Squid缓存示例：<br />　　1、清除 URL 以“.mp3”结尾的缓存文件（例如 http://www.s135.com/abc.mp3、http://www.s135.com/01/a.mp3）<br />代码:<br />./purge -p localhost:80 -P 1 -se &#8216;\.mp3$&#8217;<br />　　2、清除URL中包含s135.com的所有缓存：<br />代码:<br />./purge -p localhost:80 -P 1 -se &#8216;s135.com&#8217;<br />　　我喜欢将程序推到后台去执行，让它慢慢地去清Squid缓存，同时将输出内容记录到purge.log文件：<br />代码:<br />./purge -p localhost:80 -P 1 -se &#8216;s135.com&#8217; &gt; purge.log 2&gt;&amp;1</p>
<p>　Squid <a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">web</a>缓存加速软件目前已经是新浪、搜狐、网易等各大网站广泛应用。Squid会在设置的缓存目录下建立多个目录，每一个目录下又建立多个目录，然后才在最里层的目录中存放缓存文件（object）。squid会根据用户请求网页的URL进行哈希，生成缓存文件，存放在某一个目录中。squid启动之后，将在内存中建立一个哈希表，记录硬盘中缓存文件配置的情形。</p>
<p>　　对于类似http://you.video.sina.com.cn/index.html之类的网页，squid只会生成一个缓存文件。可以用squid附带的squidclient工具清除：</p>
<p>代码:<br />squidclient -m PURGE -p 80 &#8220;http://you.video.sina.com.cn/index.html&#8221;<br />　　而对于带有参数的网页，例如新浪播客的Flash播放器http://vhead.blog.sina.com.cn/pl &#8230; 9852&amp;uid=1278987704，因“?”后面的参数不同，导致URL也不同，squid会生成多个缓存文件，哈希分散存放在不同的目录。如果修改了这个outer_player.swf文件，要更新squid缓存就要去清除不同目录下及内存中的很多个缓存文件，十分麻烦，于是我编写了一个Linux下的shell脚本，去完成这件麻烦的事：</p>
<p>　　脚本文件名：clear_squid_cache.sh（8月2日修正了UC网友“城市中的寂寞”反馈的BUG）</p>
<p>代码:<br />#!/bin/sh<br />squidcache_path=&#8221;/data1/squid/var/<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>&#8221;<br />squidclient_path=&#8221;/usr/local/squid/bin/squidclient&#8221;<br />grep -a -r $1 $squidcache_path/* | strings | grep &#8220;http:&#8221; | awk -F&#8217;http:&#8217; &#8216;{print &#8220;http:&#8221;$2;}&#8217; &gt; cache_list.txt<br />for url in `cat cache_list.txt`; do<br />$squidclient_path -m PURGE -p 80 $url<br />done<br />　　注意：请赋予clear_squid_cache.sh可执行权限（命令：chmod +x ./clear_squid_cache.sh）。请确保脚本所在目录可写。</p>
<p>　　设置：<br />　　squidcache_path= 表示squid缓存目录的路径<br />　　squidclient_path= 表示squidclient程序所在的路径，默认为squid安装目录下的bin/squidclient</p>
<p>　　用法：<br />　　1、清除所有Flash缓存（扩展名.swf）：<br />　　<br />代码:<br />./clear_squid_cache.sh swf<br />　　2、清除URL中包含sina.com.cn的所有缓存：<br />　　<br />代码:<br />./clear_squid_cache.sh sina.com.cn<br />　　3、清除文件名为zhangyan.jpg的所有缓存：<br />　　<br />代码:<br />./clear_squid_cache.sh zhangyan.jpg<br />　　效率：<br />　　经测试，在DELL 2950上清除26000个缓存文件用时2分钟左右。平均每秒可清除缓存文件177个。</p>
<p></p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/204.html" title="PHP &#038; memcached (2006-11-15)">PHP &#038; memcached</a> (0)</li>
	<li><a href="http://www.donevii.com/post/504.html" title="google背后的分布式架构 (2008-09-23)">google背后的分布式架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/129.html" title="大型高并发高负载网站的系统架构 (2006-10-26)">大型高并发高负载网站的系统架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/283.html" title="免费软件套装 (2007-01-08)">免费软件套装</a> (0)</li>
	<li><a href="http://www.donevii.com/post/203.html" title="使用memcached进行内存缓存 (2006-11-15)">使用memcached进行内存缓存</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/758.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>加快Flex应用启动速度的5种方式</title>
		<link>http://www.donevii.com/post/523.html</link>
		<comments>http://www.donevii.com/post/523.html#comments</comments>
		<pubDate>Tue, 30 Sep 2008 04:42:35 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[life]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/523.html</guid>
		<description><![CDATA[Jun Heider在O’Reilly的InsideRIA站点上发表了一篇精彩的文章，该文章就如何加快Flex应用的启动速度提出了很多建议，以帮助用户减少看见讨厌的“Loading”对话框的出现时间。他深入探讨了问题的... ]]></description>
			<content:encoded><![CDATA[<p>Jun Heider在O’Reilly的InsideRIA站点上发表了一篇精彩的文章，该文章就<a href="http://www.insideria.com/2008/04/flex-ria-performance-considera.html">如何加快Flex应用的启动速度提出了很多建议</a>，以帮助用户减少看见讨厌的“Loading”对话框的出现时间。他深入探讨了问题的不同方面，并对每种技术的优势和劣势进行了评判。     </p>
<p><strong>从外部加载媒体（Media）</strong>     <br />Heider提到了一个常用的Flex最佳实践——限制嵌入到应用/SWF文件中的媒体的数量，如图像、影片及mp3等资源都可以从外部的SWF文件加载。 Flex框架可以直接将图片、mp3及字体等资源编译到SWF中。当你想让最终用户获得全部资源时，这种方式确实能派上用场，但是这会导致你的应用长时间停留在“Loading”阶段。中国最大的RIA分享社区-与中国闪客一起成长和发展！</p>
<p><strong>在嵌入式字体中限制字符集</strong>     <br />Heider建议在嵌入式字体中限制字符集以降低SWF文件的总下载时间： 当你在Flex中嵌入一种字体时，你就会获得该字体的全部字符的支持。尽管这可能是你想要的，但你确信你需要全部字符么？例如，在一个只面向英文的应用中，你确信你真的想花时间下载中文字符数据么？     <br /><strong>缓存框架</strong></p>
<p>Heider回顾了<a href="http://labs.adobe.com/wiki/index.php/Flex_3:Feature_Introductions:Flex_3_RSLs">Flex 3 support for runtime-shared-libraries （RSL）</a>这篇文章：从Flex 3开始，你可以将Adobe签名的框架——RSLs缓存到Flash Player的<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>中。这有两个好处。首先，缓存在Flash Player <a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>中的签名的框架RSLs可由所有配置好的Flex应用共享。换句话说，如果某人的应用已经下载了500k的签名的框架RSL，并且该RSL仍旧 在Flash Player <a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>中，那么你的应用就可以使用缓存下来的RSL。其次，即使某人清空了其浏览器缓存，对Flash Player <a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>也没有任何影响。</p>
<p><strong>考虑模块化</strong>     <br />Heider谈到了将Flex应用划分成模块的好处：减少字体加载时间的另一种方式就是将你的Flex应用划分成模块。使用模块的一个好处在于当加载和卸载模块时你能完全操控它。     <br />之所以要划分成模块的最后一个原因是他们更快，而且我能即时加载它们。换句话说，在启动时唯一需要加载的模块就是 Step1.swf模块。因此，在使用模块的情况下，最终用户节省了启动时间，但是当他从一个模块切换到另一个模块时却需要花更多时间，因为每个模块都需 要以JIT形式加载。在我的应用中，只有当用户首次在steps 1-5之间切换时需要花更多时间。</p>
<p><strong>推迟实例化      <br /></strong>Heider围绕着Flex组件的“creationPolicy”属性及何时实例化应用的不同部分给出了很多建议。如果你想减少从数据下载到用户真正可以使用的总时间，当务之急就是推迟实例化。这项技术背后的理念就是直到应用真正使用的时候才在内存中创建对象。尽管推迟实例化技术会在应用的整个使用过程中导致少许——通常不那么明显——的延迟，但与长时间的启动延迟相比，它还是可接受的。推迟实例化的另一个好处在于内存使用的优化。 Heider还谈到了一个“实验性”的条款——“使用流”，这是他在讨论Dirk Eismann的帖子（<u>Building monolithic </u><a href="http://blog.csdn.net/lixinye0123/archive/2008/06/01/Flex">Flex</a><u> SWFs that still startup quickly.”</u>）时谈及的。Eismann提出一项技术以利用Flash Player中的多个frames以在部分应用中达到流的目的。查看<a href="http://www.insideria.com/2008/04/flex-ria-performance-considera.html">所有的帖子</a>以更多地了解该技术及关于加快Flex启动速度的建议。中国最大的RIA分享社区-与中国闪客一起成长和发展！) </p>
<p><strong>原文出处：</strong><a href="http://www.infoq.com/cn/news/2008/05/flex-startup-time">http://www.infoq.com/cn/news/2008/05/flex-startup-time</a></p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/9.html" title="flickr对javascript干的好事 (2006-08-17)">flickr对javascript干的好事</a> (0)</li>
	<li><a href="http://www.donevii.com/post/758.html" title="清除指定squid缓存文件的脚本 (2009-02-09)">清除指定squid缓存文件的脚本</a> (0)</li>
	<li><a href="http://www.donevii.com/post/129.html" title="大型高并发高负载网站的系统架构 (2006-10-26)">大型高并发高负载网站的系统架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/504.html" title="google背后的分布式架构 (2008-09-23)">google背后的分布式架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/128.html" title="应用加速 (2006-10-26)">应用加速</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/523.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>google背后的分布式架构</title>
		<link>http://www.donevii.com/post/504.html</link>
		<comments>http://www.donevii.com/post/504.html#comments</comments>
		<pubDate>Tue, 23 Sep 2008 08:48:29 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[life]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[unix]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>
		<category><![CDATA[芯片]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/504.html</guid>
		<description><![CDATA[　　Google是与众不同的。它的独特不仅仅表现于革新的思维和充满创意的应用 (比如那个大堂里的地球模型)，更在于其有别常规的IT策略…… 　　加利福尼亚州山景城(Mountain View)Google公司(Google... ]]></description>
			<content:encoded><![CDATA[<p>　　Google是与众不同的。它的独特不仅仅表现于革新的思维和充满创意的应用  (比如那个大堂里的地球模型)，更在于其有别常规的IT策略……</p>
<p>　　加利福尼亚州山景城(Mountain  View)Google公司(Google，下称Google)总部有一个43号大楼，该建筑的中央大屏幕上显示着一个与Google地球(Google  Earth)相仿的世界地图，一个转动的地球上不停地闪动着五颜六色的光点，恍如罗马宫廷的千万烛灯，每一次闪动标志着地球的这个角落一名Google用  户发起了一次新的搜索。</p>
<p>　　这同时意味着Google又一次满足了人们对未知信息的好奇与渴望。</p>
<p>　　Google是与众不同的。它的独特不仅仅表现于革新的思维和充满创意的应用  (比如那个大堂里的地球模型)，更在于其有别常规的IT策略。从人们的常理来看，简单的硬件商品和免费软件是无法构建出一个帝国的，但是Google做到  了。在性能调整后，Google把它们变成一个无可比拟的分布式计算平台，该平台能够支持大规模的搜索和不断涌现的新兴应用。我们原本认为这些应用都是个  人消费级别的，但是Google改变了这一切。现在商业世界也在使用它们，这就令这家搜索公司显得那么与众不同。</p>
<p>　　GoogleWeb  服务背后的IT架构对无数使用搜索引擎的用户来说也许并不是非常重要，但它是Google几百位致力于把全球信息组织起来，实现“随处可达，随时可用”目  标的工程师们的最核心工作。这就需要一个在覆盖范围和野心上都与Google的商业愿景完全相符的IT蓝图作为支撑。</p>
<p>　　Google  的经理们一直对公司的IT策略话题保持沉默，他们厌恶谈及特定的厂商或者产品，当被问到他们的服务器和数据中心时，他们总是闭口不谈。但与几位  Google的IT领导一起呆了一天后，我们最终得以揭示该公司的IT是如何运作的，那可不仅仅是一个运行在无数服务器集群上的、表面看来非常简单的搜索  引擎。在其简单的外表下，蕴涵着许多内部研发软件、定制硬件、人工智能，以及对性能的执着追求和打破常规的人力管理模式。</p>
<p>　　IT理念方面，Google对同行有一条建议：尽量避免那些人人都在使用的系统和软件，以自己的方式做事会更有独特的竞争优势。</p>
<p>　　“企业文化决定了你的做事方式。”道格拉斯&#8221;美林(Douglas  Merrill)，这位Google工程副总裁和事实上的首席信息官(CIO)  指出，“到了我们这样的发展阶段，企业观念和文化非常与众不同，这也反过来鞭策我们必须要采用与众不同的方式来运行那些他人看来很常规的系统。”<br />
　　Google  最大的IT优势在于它能建造出既富于性价比(并非廉价)又能承受极高负载的高性能系统。因此IT顾问史蒂芬&#8221;阿诺德(Stephen  Arnold)指出，Google与竞争对手，如亚马逊网站(Amazon)、电子港湾公司(eBay)、微软公司(Microsoft，下称微软)和雅 虎公司  (Yahoo，下称雅虎)等公司相比，具有更大的成本优势。Google程序员的效率比其他<a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">Web</a>公司同行们高出50%～100%，原因是Google已  经开发出了一整套专用于支持大规模并行系统编程的定制软件库。据他估算，其他竞争公司可能要花上四倍的时间才能获得同等的效果。</p>
<p>　　打造服务器</p>
<p>　　Google  究竟是怎样做到这点的呢?其中一个手段，美林认为，“是因为我们自己动手打造硬件。”Google并不制造计算机系统，但它根据自己的参数定制硬件，然后  像MTV的节目“靓车打造”(Pimp My Ride)那样自己安装和调整硬件系统。开源程序经理克里斯&#8221;迪博纳(Chris  DiBona)评论道：“我们很善于购买商业服务器，并且改造他们为我们所用，最后把性能压榨和发挥到极致，以致有时候他们热得像要融化了似的。”</p>
<p>　　这种亲手打造的方式，来源于Google从车库诞生时与生俱来的节俭风格，更与Google那超大型的系统规模息息相关，良好的习惯一直延续至  今。据说 Google在65个数据中心拥有20万～45万台服务器—这个数目会有偏差(取决于你如何定义服务器和由谁来做这项统计)。但是，不变的是持续上升的趋势。</p>
<p>　　Google不会去讨论这些资产，因为它认为保密也是一种竞争优势。事实上，Google之所以喜欢开源软件也是因为它的私密性。“如果我们购  买了软件许可或代码许可，人们只要对号入座，就可以猜出Google的IT基础架构。”迪博纳分析说，  “使用开源软件，就使我们多了一条把握自己命运的途径。”</p>
<p>　　Google喜欢规模化的服务器运行方式。当有成百上千台机器时，定制服务器的优势也会成倍增加，效果也会更趋明显。Google正在俄勒冈州  哥伦比亚河边的达勒斯市建造一个占地30亩的数据中心，在那儿它可以获得运算和降温需要的低价水力电力能源(参见边栏《Google数据中心自有一套》)。</p>
<p>　　Google以“单元”(Cell)的形式组织这些运行  Linux操作系统的服务器，迪博纳把这种形式比喻成互联网服务的“磁盘驱动器”(但别和一直谣传的Google存储服务Gdrive混淆了，“并没有  Gdrive这回事。”一位Google女发言人明确表示。)，公司的软件程序都驻扎在这些并不昂贵的电脑机箱里，由程序员决定它们的冗余工作量。这种由  很多单元组成的文件系统代替了商业存储设备;迪博纳表示Google这些单元设备更易于建造和维护，他还暗示他们能处理更大规模的数据。</p>
<p>　　Google  不会漏过对任何技术细节的关注。多年来，公司的工程师就在研究微处理器的内部工作机制，随着Google规模的持续壮大，必然会用到特别定制和调节过的芯  片。知名工程师路易斯&#8221;巴罗索(Luiz  Barroso)去年在一篇发表在工业杂志上的论文中证实，近年来Google的主要负荷都由单核设计的系统承担着。但许多服务器端的应用，如  Google搜索索引服务，所需的并行计算在单核芯片的指令级别上执行得并不好。</p>
<p>　　曾在数据设备公司(Digital  Equipment)和康柏公司(Compaq)当过芯片设计师的巴罗索认为，随着AMD公司、英特尔公司(Intel)、太阳计算机系统公司(Sun)开始制造多核芯片，必将会出现越来越多芯片级别的并行计算。</p>
<p>　　Google  也曾考虑过自己制造计算机芯片，但从业界潮流来看，这个冒险的举动似乎不是很必要。“微处理器的设计非常复杂而且成本昂贵，”运营高级副总裁乌尔斯&#8221;霍尔 茨勒(Urs  Holzle)表示。Google宁愿与芯片制造商合作，让他们去理解自己的应用并设计适合的芯片。这是一种客户建议式的设计，其关注点在于总体吞吐量、  效能，以及耗电比，而不是看单线程的峰值性能。霍尔茨勒表示，“这也是最近多核CPU的设计潮流与未来方向。”<br />
裁缝般地定制软件</p>
<p>　　为了能尽量压榨硬件性能，Google开发了相当数量的定制软件。创新产品主要包括用于简化处理和创建大规模数据集的编程模型  MapReduce;用于存储和管理大规模数据的系统BigTable;分析分布式运算环境中大规模数据集的解释编程语言Sawzall;用于数据密集型  应用的分布式文件系统的 “Google文件系统”(Google File  System);还有为处理分布式系统队列分组和任务调度的“Google工作队列”(Google  Workqueue)。</p>
<p>　　正是从Sawzall这些工具里体现出Google对计算效率的执著关注。并不是每家公司都能从底层去解决效率问题，但是对Google来说，  为常规关系型数据库无法容纳的大规模数据集专门设计一种编程语言是完全合理的。即使其他编程工具可以解决问题，Google的工程师们仍然会为了追求效率  而另外开发一套定制方案。Google工程师认为，Sawzall能与C++中的MapReduce相媲美，而且它更容易编写一些。</p>
<p>　　Google  对效率的关注使它不可能对标准Linux内核感到满意;Google会根据自己的需要运行修改过的内核版本。通过调整Linux的底层性能，Google  工程师们在提高了整体系统可靠性的基础上，还一并解决了数据损坏和数据瓶颈等一系列棘手问题。对内核的修改也使Google的计算机集群系统因为通信效率  的提高而运行得更快。</p>
<p>　　当然，Google偶尔也会出现系统故障，情况一旦发生，无数的用户就会受到影响了。三年前一次持续30分钟的系统故障使20%的搜索流量受到影响。</p>
<p>　　Google  开发了自己的网站服务器却没有使用开源的Apache服务器，尽管它在网站服务器的市场占有率超过60%。迪博纳认为，Google的网站服务器可以运行  在更多数量的主机上，对Google站点上内容庞大又彼此互相依赖的应用程序来说，这种服务器的负载均衡能力远比Apache的能力更高。同时，在用标准  公共网关接口(CGI)访问数据库动态网页方面，Google服务器的编程难度要比  Apache更高，但是最终运行速度却更快。“如果我们能够压榨出10%～20%的性能，我们就可以节省出更多系统资源、电量和人力了。”迪博纳在总结中指出。</p>
<p>　　Google还设计了自己的客户关系管理(CRM)系统用于支持自己基于竞价和点击的互联网广告收费业务。但对是否需要设计自己的工具，Google的态度也不是一成不变的。比如在财会软件上，它就使用了甲骨文公司(Oracle)的Financials软件。</p>
<p>　　美林拿着一只叉子举例说明现成的产品也可以带来价值。但在有些场合现成的软件产品就不一定适用了。“我们的文化在各个层面对我们的运作都有深远影响，”他表示，“所以我们不想让购买所得的工具改变我们的工作方式和文化层面。”<br />
Google&#8217;s  BigTable 原理 （翻译）</p>
<p>    题记：google 的成功除了一个个出色的创意外，还因为有 Jeff Dean  这样的软件架构天才。<br />
                                                  &#8212;&#8212; 编者</p>
<p>官方的  Google Reader blog 中有对BigTable 的解释。这是Google 内部开发的一个用来处理大数据量的系统。这种系统适合处理半结构化的数据比如  RSS 数据源。 以下发言  是 Andrew Hitchcock  在 2005 年10月18号 基于： Google 的工程师 Jeff Dean  在华盛顿大学的一次谈话 (Creative Commons License).</p>
<p>首先，BigTable 从 2004  年初就开始研发了，到现在为止已经用了将近8个月。（2005年2月）目前大概有100个左右的服务使用BigTable，比如： Print,Search  History,Maps和 Orkut。根据Google的一贯做法，内部开发的BigTable是为跑在廉价的PC机上设计的。BigTable  让Google在提供新服务时的运行成本降低，最大限度地利用了计算能力。</p>
<p>BigTable 是建立在 GFS ，Scheduler ，Lock Service 和  MapReduce 之上的。</p>
<p>每个Table都是一个多维的稀疏图 sparse map。Table 由行和列组成，并且每个存储单元 cell  都有一个时间戳。在不同的时间对同一个存储单元cell有多份拷贝，这样就可以记录数据的变动情况。在他的例子中，行是URLs  ，列可以定义一个名字，比如：contents。Contents  字段就可以存储文件的数据。或者列名是：”language”，可以存储一个“EN”的语言代码字符串。</p>
<p>为了管理巨大的Table，把Table根据行分割，这些分割后的数据统称为：Tablets。每  个Tablets大概有 100-200 MB，每个机器存储100个左右的  Tablets。底层的架构是：GFS。由于GFS是一种分布式的文件系统，采用Tablets的机制后，可以获得很好的负载均衡。比如：可以把经常响应  的表移动到其他空闲机器上，然后快速重建。</p>
<p>Tablets在系统中的存储方式是不可修改的 immutable  的SSTables，一台机器一个日志文件。当系统的内存满后，系统会压缩一些Tablets。由于Jeff在论述这点的时候说的很快，所以我没有时间把听到的都记录下来，因此下面是一个大概的说明：</p>
<p>压缩分为：主要和次要的两部分。次要的压缩仅仅包括几个Tablets，而主要的压缩时关于整个系统的压缩。主压缩有回收硬盘空间的功能。Tablets的位置实际上是存储在几个特殊的BigTable的存储单元cell中。看起来这是一个三层的系统。<br />
客户端有一个指向METAO的Tablets的指针。如果METAO的Tablets被频繁使用，那个这台机器就会放弃其他的tablets专门支持  METAO这个Tablets。METAO tablets  保持着所有的META1的tablets的记录。这些tablets中包含着查找tablets的实际位置。（老实说翻译到这里，我也不太明白。）在这个系统中不存在大的瓶颈，因为被频繁调用的数据已经被提前获得并进行了缓存。</p>
<p>     现在我们返回到对列的说明：列是类似下面的形式：  family:optional_qualifier。在他的例子中，行：www.search-analysis.com   也许有列：”contents:其中包含html页面的代码。 “ anchor:cnn.com/news” 中包含着  相对应的url，”anchor:www.search-analysis.com/” 包含着链接的文字部分。列中包含着类型信息。<br />
     (翻译到这里我要插一句，以前我看过一个关于万能数据库的文章，当时很激动，就联系了作者，现在回想起来，或许google的 bigtable  才是更好的方案，切不说分布式的特性，就是这种建华的表结构就很有用处。)</p>
<p>     注意这里说的是列信息，而不是列类型。列的信息是如下信息，一般是：属性/规则。 比如：保存n份数据的拷贝或者保存数据n天长等等。当 tablets  重新建立的时候，就运用上面的规则，剔出不符合条件的记录。由于设计上的原因，列本身的创建是很容易的，但是跟列相关的功能确实非常复杂的，比如上文提到 的  类型和规则信息等。为了优化读取速度，列的功能被分割然后以组的方式存储在所建索引的机器上。这些被分割后的组作用于 列 ,然后被分割成不同的  SSTables。这种方式可以提高系统的性能，因为小的，频繁读取的列可以被单独存储，和那些大的不经常访问的列隔离开来。</p>
<p>在一台机器上的所有的  tablets  共享一个log，在一个包含1亿的tablets的集群中，这将会导致非常多的文件被打开和写操作。新的log块经常被创建，一般是64M大小，这个GFS的块大小相等。当一个机器down掉后，控制机器就会重新发布他的log块到其他机器上继续进行处理。这台机器重建tablets然后询问控制机器处理结构的存储位置，然后直接对重建后的数据进行处理。这个系统中有很多冗余数据，因此在系统中大量使用了压缩技术。</p>
<p>     Dean 对压缩的部分说的很快，我没有完全记下来，所以我还是说个大概吧：压缩前先寻找相似的 \行，列，和时间数据。</p>
<p>    他们使用不同版本的：  BMDiff 和 Zippy 技术。</p>
<p>   BMDiff 提供给他们非常快的写速度： 100MB/s – 1000MB/s 。Zippy 是和 LZW  类似的。Zippy 并不像 LZW 或者 gzip 那样压缩比高，但是他处理速度非常快。</p>
<p>    Dean 还给了一个关于压缩 <a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">web</a>  蜘蛛数据的例子。这个例子的蜘蛛 包含 2.1B 的页面，行按照以下的方式命名：“com.cnn.www/index.html:http”.在未压缩前的<a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">web</a>  page 页面大小是：45.1 TB ，压缩后的大小是：4.2 TB ， 只是原来的 9.2%。Links 数据压缩到原来的 13.9% ,  链接文本数据压缩到原来的 12.7%。<br />
<span id="more-504"></span><br />
Google 还有很多没有添加但是已经考虑的功能。<br />
    1.   数据操作表达式，这样可以把脚本发送到客户端来提供修改数据的功能。<br />
    2. 多行数据的事物支持。<br />
    3.   提高大数据存储单元的效率。<br />
    4. BigTable 作为服务运行。<br />
    好像：每个服务比如： maps 和 search history  历史搜索记录都有他们自己的集群运行 BigTable。<br />
    他们还考虑运行一个全局的 BigTable  系统，但这需要比较公平的分割资源和计算时间。</p>
<p>大表(Bigtable):结构化数据的分布存储系统</p>
<p>http://labs.google.com/papers/bigtable-osdi06.pdf</p>
<p>｛中是译者评论,程序除外｝<br />
{本文的翻译可能有不准确的地方,详细资料请参考原文.}</p>
<p>摘要<br />
bigtable是设计来分布存储大规模结构化数据的，从设计上它可以扩展到上２^50字节，分布存储在几千个普通服务器上．google的很多项目使用  bt来存储数据，包括网页查询，google  earth和google金融．这些应用程序对bt的要求各不相同：数据大小（从URL到网页到卫星图象）不同，反应速度不同（从后端的大批处理到实时数  据服务）．对于不同的要求，bt都成功的提供了灵活高效的服务．在本文中，我们将描述bt的数据模型．这个数据模型让用户动态的控制数据的分布和结构．我  们还将描述BT的设计和实现．<br />
１．介绍<br />
在过去两年半里，我们设计，实现并部署了BT．BT是用来分布存储和管理结构化数据的．BT的设计使它能够管理2^50  bytes(petabytes)数据，并可以部署到上千台机器上．BT完成了以下目标：应用广泛，可扩展，高性能和高可用性（high availability）.  包括google analytics, google finance, orkut, personalized search, writely和google  earth在内的60多个项目都使用BT.这些应用对BT的要求各不相同，有的需要高吞吐量的批处理，有的需要快速反应给用户数据．它们使用的BT集群也各不相同，有的只有几台机器，有的有上千台，能够存储2^40字节(terabytes)数据．<br />
BT在很多地方和数据库很类似：它使用了很多数据库的实现策略．并行数据库[14]和内存数据库[13]有可扩展性和高性能，但是BT的界面不同．BT不支持完全的关系数据模型；而是为客户提供了简单的数据模型，让客户来动态控制数据的分布和格式{就是只存储字串，格式由客户来解释}，并允许客户推断底层存储数据的局部性｛以提高访问速度｝．数据下标是行和列的名字，数据本身可以是任何字串．BT的数据是字串，没有解释｛类型等｝．客户会在把各种结构或者半结构化的数据串行化｛比如说日期串｝到数据中．通过仔细选择数据表示，客户可以控制数据的局部化．最后，可以使用BT模式来控制数据是放在内存里还是在硬盘上．｛就是说用模式，你可以把数据放在离应用最近的地方．毕竟程序在一个时间只用到一块数据．在体系结构里，就是：locality,  locality,  locality｝<br />
第二节描述数据模型细节．第三节关于客户API概述．第四节简介BT依赖的google框架．第五节描述BT的实现关键部分．第6节叙述提高BT性  能的一些调整．第7节提供BT性能的数据．在第8节，我们提供BT的几个使用例子，第9节是经验教训．在第10节，我们列出相关研究．最后是我们的结论．<br />
２．数据模型<br />
BT是一个稀疏的，长期存储的｛存在硬盘上｝，多维度的，排序的映射表．这张表的索引是行关键字，列关键字和时间戳．每个值是一个不解释的字符数组．｛数据都是字符串，没类型，客户要解释就自力更生吧｝．<br />
(row:string,  column:string,time:int64)-&gt;string  {能编程序的都能读懂，不翻译了}<br />
我们仔细查看过好些类似bigtable的系统之后定下了这个数据模型。举一个具体例子（它促使我们做出某些设计决定），  比如我们想要存储大量网页及相关信息，以用于很多不同的项目；我们姑且叫它Webtable。在Webtable里，我们将用URL作为行关键字，用网页  的某些属性作为列名，把网页内容存在contents:列中并用获取该网页的时间戳作为标识，如图一所示。<br />
图一：一个存储<a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">Web</a>网页的范例列表片断。行名是一个反向URL｛即com.cnn.www｝。contents列族｛原文用  family，译为族，详见列族｝ 存放网页内容，anchor列族存放引用该网页的锚链接文本。CNN的主页被Sports  Illustrater｛即所谓SI，CNN的王牌体育节目｝和MY-look的主页引用，因此该行包含了名叫“anchor:cnnsi.com”和  “anchhor:my.look.ca”的列。每个锚链接只有一个版本｛由时间戳标识，如t9，t8｝；而contents列则有三个版本，分别由时间  戳t3，t5，和t6标识。<br />
行<br />
表中的行关键字可以是任意字符串（目前支持最多64KB，多数情况下10－100字节足够了）。在一个行关键字下的每一个读写操作都是原子操作（不管读写这一行里多少个不同列），这是一个设计决定，这样在对同一行进行并发操作时，用户对于系统行为更容易理解和掌控。<br />
Bigtable通过行关键字的字典序来维护数据。一张表可以动态划分成多个连续行。连续行在这里叫做“子表”｛tablet｝，是数据分布和负载  均衡的单位。这样一来，读较少的连续行就比较有效率，通常只需要较少机器之间的通信即可。用户可以利用这个属性来选择行关键字，从而达到较好数据访问地域  性｛locality｝。举例来说，在Webtable里，通过反转URL中主机名的方式，可以把同一个域名下的网页组织成连续行。具体来说，可以把  maps.google.com/index.html中的数据存放在关键字com.google.maps/index.html下。按照相同或属性相  近的域名来存放网页可以让基于主机和基于域名的分析更加有效。<br />
列族<br />
一组列关键字组成了“列族”，这是访问控制的基本单位。同一列族下存放的所有数据通常都是同一类型（同一列族下的数据可压缩在一起）。列族必须先创  建，然后在能在其中的列关键字下存放数据；列族创建后，族中任何一个列关键字均可使用。我们希望，一张表中的不同列族不能太多（最多几百个），并且列族在  运作中绝少改变。作为对比，一张表可以有无限列。<br />
列关键字用如下语法命名：列族：限定词。  列族名必须是看得懂｛printable｝的字串，而限定词可以是任意字符串。比如，Webtable可以有个列族叫language，存放撰写网页的语  言。我们在language列族中只用一个列关键字，用来存放每个网页的语言标识符。该表的另一个有用的列族是anchor；给列族的每一个列关键字代表  一个锚链接，如图一所示。而这里的限定词则是引用该网页的站点名；表中一个表项存放的是链接文本。<br />
访问控制，磁盘使用统计，内存使用统计，均可在列族这个层面进行。在Webtable举例中，我们可以用这些控制来管理不同应用：有的应用添加新的基本数据，有的读取基本数据并创建引申的列族，有的则只能浏览数据（甚至可能因为隐私权原因不能浏览所有数据）。<br />
时间戳<br />
Bigtable表中每一个表项都可以包含同一数据的多个版本，由时间戳来索引。Bigtable的时间戳是64位整型。可以由Bigtable来  赋值，表示准确到毫秒的“实时”；或者由用户应用程序来赋值。需要避免冲突的应用程序必须自己产生具有唯一性的时间戳。不同版本的表项内容按时间戳倒序排  列，即最新的排在前面。<br />
为了简化对于不同数据版本的数据的管理，我们对每一个列族支持两个设定，以便于Bigtable对表项的版本自动进行垃圾清除。用户可以指明只保留表项的最后n个版本，或者只保留足够新的版本（比如，只保留最近7天的内容）。<br />
在Webtable举例中，我们在contents:列中存放确切爬行一个网页的时间戳。如上所述的垃圾清除机制可以让我们只保留每个网页的最近三个版本。<br />
3.API<br />
BT的API提供了建立和删除表和列族的函数．还提供了函数来修改集群，表和列族的元数据，比如说访问权限．<br />
//  Open the table<br />
Table *T = OpenOrDie(”/bigtable/<a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">web</a>/webtable”);<br />
// Write a  new anchor and delete an old anchor<br />
RowMutation r1(T,  “com.cnn.www”);<br />
r1.Set(”anchor:www.c-span.org”,  “CNN”);<br />
r1.Delete(”anchor:www.abc.com”);<br />
Operation op;<br />
Apply(&amp;op,  &amp;r1);<br />
图 2:  写入Bigtable.<br />
在BT中，客户应用可以写或者删除值，从每个行中找值，或者遍历一个表中的数据子集．图2的c++代码是使用RowMutation抽象表示来进行一系列的更新（为保证代码精简，没有包括无关的细节）．调用Apply函数，就对Ｗebtable进行了一个原子修改：它为http://www.cnn.com/增加了一个锚点，并删除了另外一个锚点．<br />
Scanner  scanner(T);<br />
ScanStream *stream;<br />
stream =  scanner.FetchColumnFamily(”anchor”);<br />
stream-&gt;SetReturnAllVersions();<br />
scanner.Lookup(”com.cnn.www”);<br />
for  (; !stream-&gt;Done(); stream-&gt;Next()) {<br />
printf(”%s %s %lld  %s\n”,<br />
scanner.RowName(),<br />
stream-&gt;ColumnName(),<br />
stream-&gt;MicroTimestamp(),<br />
stream-&gt;Value());<br />
}<br />
图3:  从Bigtable读数据.<br />
图3的C++代码是使用Scanner抽象来遍历一个行内的所有锚点．客户可以遍历多个列族．有很多方法可以限制一次扫描中产生的行，列和时间戳．  例如，我们可以限制上面的扫描，让它只找到那些匹配正则表达式*.cnn.com的锚点，或者那些时间戳在当前时间前10天的锚点．<br />
BT还支持其他一些更复杂的处理数据的功能．首先，BT支持单行处理．这个功能可以用来对存储在一个行关键字下的数据进行原子的读-修改-写操作．  BT目前不支持跨行关键字的处理，但是它有一个界面，可以用来让客户进行批量的跨行关键字处理操作．其次，BT允许把每个表项用做整数记数器．最后，BT  支持在服务器的地址空间内执行客户端提供的脚本程序．脚本程序的语言是google开发的Sawzall[28]数据处理语言．目前，我们基于的  Sawzall的API还不允许客户脚本程序向BT内写数据，但是它允许多种形式的数据变换，基于任何表达式的过滤和通过多种操作符的摘要．<br />
BT可以和MapReduce[12]一起使用．MapReduce是google开发的大规模并行计算框架．我们为编写了一套外层程序，使BT可以作为MapReduce处理的数据源头和输出结果．<br />
4.建立BT的基本单元<br />
BT是建立在其他数个google框架单元上的．BT使用google分布式文件系统(GFS)[17]来存储日志和数据文件{yeah,  right, what else can it use,  FAT32?}．一个BT集群通常在一个共享的机器池中工作，池中的机器还运行其他的分布式应用{虽然机器便宜的跟白菜似的，可是一样要运行多个程序，命苦的象小白菜}，BT和其他程序共享机器｛BT的瓶颈是ＩＯ/内存，可以和CPU要求高的程序并存｝．BT依赖集群管理系统来安排工作，在共享的机器上管理资源，处理失效机器并监视机器状态｛典型的server  farm结构，BT是上面的应用之一｝．<br />
BT内部存储数据的格式是google  SSTable格式．一个SSTable提供一个从关键字到值的映射，关键字和值都可以是任意字符串．映射是排序的，存储的｛不会因为掉电而丢失｝，不可改写的．可以进行以下操作：查询和一个关键字相关的值；或者根据给出的关键字范围遍历所有的关键字和值．在内部，每个SSTable包含一列数据块（通常每个块的大小是64KB,但是大小是可以配置的｛索引大小是16  bits，应该是比较好的一个数｝）．块索引（存储在SSTable的最后）用来定位数据块；当打开SSTable的时候，索引被读入内存｛性能｝．每次查找都可以用一个硬盘搜索完成｛根据索引算出数据在哪个道上，一个块应该不会跨两个道，没必要省那么点空间｝：首先在内存中的索引里进行二分查找找到数据块的位置，然后再从硬盘读去数据块．最佳情况是：整个SSTable可以被放在内存里，这样一来就不必访问硬盘了．｛想的美，前面是谁口口声声说要跟别人共享机器来着？你把内存占满了别人上哪睡去？｝<br />
BT还依赖一个高度可用的，存储的分布式数据锁服务Chubby[8]｛看你怎么把这个high  performance给说圆喽｝．一个Chubby服务由5个活的备份｛机器｝构成，其中一个被这些备份选成主备份，并且处理请求．这个服务只有在大多数备份都活着并且互相通信的时候才是活的｛绕口令？去看原文吧，是在有出错的前提下的冗余算法｝．当有机器失效的时候，Chubby使用Paxos算法[9,23]来保证备份的一致性｛这个问题还是比较复杂的，建议去看引文了解一下问题本身｝．Chubby提供了一个名字空间，里面包括了目录和小文件｛万变不离其宗｝．每个目录或者文件可以当成一个锁来用，读写文件操作都是原子化的．Chubby客户端的程序库提供了对Chubby文件的一致性缓存｛究竟是提高性能还是降低性能？如果访问是分布的，就是提高性能｝．每个Chubby客户维护一个和Chubby服务的会话．如果一个客户不能在一定时间内更新它的会话，这个会话就过期失效了｛还是针对大server  farm里机器失效的频率设计的｝．当一个会话失效时，其拥有的锁和打开的文件句柄都失效｛根本设计原则：失效时回到安全状态｝．Chubby客户可以在文件和目录上登记回调函数，以获得改变或者会话过期的通知．｛翻到这里，有没有人闻到<a href="http://www.donevii.com/post/tag/java" class="st_tag internal_tag" rel="tag" title="Posts tagged with java">java</a>的味道了？｝<br />
BT使用Chubby来做以下几个任务：保证任何时间最多只有一个活跃的主备份；来存储BT数据的启动位置（参考5.1节）；发现小表  （tablet）服务器，并完成tablet服务器消亡的善后（5.2节）；存储BT数据的模式信息（每张表的列信息）；以及存储访问权限列表．如果有相当长的时间Chubby不能访问，BT就也不能访问了｛任何系统都有其弱点｝．最近我们在使用11个Chubby服务实例的14个BT集群中度量了这个效果，由于Chubby不能访问而导致BT中部分数据不能访问的平均百分比是0.0047%,这里Chubby不能访问的原因是Chubby本身失效或者网络问题．单个集群里，受影响最大的百分比是0.0326%｛基于文件系统的Chubby还是很稳定的｝.<br />
GFS是一个可扩展的分布式文件系统，用于大型的、分布式的、对大量数据进行访问的应用。它运行于廉价的普通硬件上，但可以提供容错功能。它可以给大量的用户提供总体性能较高的服务。<br />
出处：http://labs.google.com/papers/gfs.html<br />
1、设计概览<br />
（1）设计想定<br />
GFS与过去的分布式文件系统有很多相同的目标，但GFS的设计受到了当前及预期的应用方面的工作量及技术环境的驱动，这反映了它与早期的文件系统明显不同的设想。这就需要对传统的选择进行重新检验并进行完全不同的设计观点的探索。<br />
GFS与以往的文件系统的不同的观点如下：<br />
1、部件错误不再被当作异常，而是将其作为常见的情况加以处理。因为文件系统由成百上千个用于存储的机器构成，而这  些机器是由廉价的普通部件组成并被大量的客户机访问。部件的数量和质量使得一些机器随时都有可能无法工作并且有一部分还可能无法恢复。所以实时地监控、错  误检测、容错、自动恢复对系统来说必不可少。<br />
2、按照传统的标准，文件都非常大。长度达几个GB的文件是很平常的。每个文件通常包含很多应用对象。当经常要处理  快速增长的、包含数以万计的对象、长度达TB的数据集时，我们很难管理成千上万的KB规模的文件块，即使底层文件系统提供支持。因此，设计中操作的参数、  块的大小必须要重新考虑。对大型的文件的管理一定要能做到高效，对小型的文件也必须支持，但不必优化。<br />
3、大部分文件的更新是通过添加  新数据完成的，而不是改变已存在的数据。在一个文件中随机的操作在实践中几乎不存在。一旦写完，文件就只可读，很多数据都有这些特性。一些数据可能组成一  个大仓库以供数据分析程序扫描。有些是运行中的程序连续产生的数据流。有些是档案性质的数据，有些是在某个机器上产生、在另外一个机器上处理的中间数据。  由于这些对大型文件的访问方式，添加操作成为性能优化和原子性保证的焦点。而在客户机中缓存数据块则失去了吸引力。<br />
4、工作量主要由两种读操作构成：对大量数据的流方式的读操作和对少量数据的随机方式的读操作。在前一种读操作中，  可能要读几百KB，通常达  1MB和更多。来自同一个客户的连续操作通常会读文件的一个连续的区域。随机的读操作通常在一个随机的偏移处读几个KB。性能敏感的应用程序通常将对少量  数据的读操作进行分类并进行批处理以使得读操作稳定地向前推进，而不要让它来来回回的读。<br />
5、工作量还包含许多对大量数据进行的、连续的、向文件添加数据的写操作。所写的数据的规模和读相似。一旦写完，文件很少改动。在随机位置对少量数据的写操作也支持，但不必非常高效。<br />
6、系统必须高效地实现定义完好的大量客户同时向同一个文件的添加操作的语义。<br />
（2）系统接口<br />
GFS提供了一个相似地文件系统界面，虽然它没有向POSIX那样实现标准的API。文件在目录中按层次组织起来并由路径名标识。<br />
（3）体系结构：<br />
一个GFS集群由一个master和大量的chunkserver构成，并被许多客户（Client）访问。如图1  所示。Master和  chunkserver通常是运行用户层服务进程的Linux机器。只要资源和可靠性允许，chunkserver和client可以运行在同一个机器  上。<br />
文件被分成固定大小的块。每个块由一个不变的、全局唯一的64位的chunk－handle标识，chunk－ handle是在块创建时由  master分配的。ChunkServer将块当作Linux文件存储在本地磁盘并可以读和写由chunk－handle和位区间指定的数据。出于可靠  性考虑，每一个块被复制到多个chunkserver上。默认情况下，保存3个副本，但这可以由用户指定。<br />
Master维护文件系统所以的元数据（metadata），包括名字空间、访问控制信息、从文件到块的映射以及块  的当前位置。它也控制系统范围的活动，如块租约（lease）管理，孤儿块的垃圾收集，chunkserver间的块迁移。Master定期通过  HeartBeat消息与每一个  chunkserver通信，给chunkserver传递指令并收集它的状态。<br />
与每个应用相联的GFS客户代码实现了文件系统的API并与master和chunkserver通信以代表应用程序读和写数据。客户与master的交换只限于对元数据（metadata）的操作，所有数据方面的通信都直接和chunkserver联系。<br />
客户和chunkserver都不缓存文件数据。因为用户缓存的益处微乎其微，这是由于数据太多或工作集太大而无法  缓存。不缓存数据简化了客户程序和整个系统，因为不必考虑缓存的一致性问题。但用户缓存元数据（metadata）。Chunkserver也不必缓存文  件，因为块时作为本地文件存储的。<br />
（4）单master。<br />
只有一个master也极大的简化了设计并使得master可以根据全局情况作出先进的块放置和复制决定。但是我们  必须要将master对读和写的参与减至最少，这样它才不会成为系统的瓶颈。Client从来不会从master读和写文件数据。Client只是询问  master它应该和哪个  chunkserver联系。Client在一段限定的时间内将这些信息缓存，在后续的操作中Client直接和chunkserver交互。<br />
以图1解释一下一个简单的读操作的交互。<br />
1、client使用固定的块大小将应用程序指定的文件名和字节偏移转换成文件的一个块索引（chunk  index）。<br />
2、给master发送一个包含文件名和块索引的请求。<br />
3、master回应对应的chunk  handle和副本的位置（多个副本）。<br />
4、client以文件名和块索引为键缓存这些信息。（handle和副本的位置）。<br />
5、Client  向其中一个副本发送一个请求，很可能是最近的一个副本。请求指定了chunk handle（chunkserver以chunk  handle标识chunk）和块内的一个字节区间。<br />
6、除非缓存的信息不再有效（<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a> for a limited  time）或文件被重新打开，否则以后对同一个块的读操作不再需要client和master间的交互。<br />
通常Client可以在一个请求中询问多个chunk的地址，而master也可以很快回应这些请求。<br />
（5）块规模：<br />
块规模是设计中的一个关键参数。我们选择的是64MB，这比一般的文件系统的块规模要大的多。每个块的副本作为一个普通的Linux文件存储，在需要的时候可以扩展。<br />
块规模较大的好处有：<br />
1、减少client和master之间的交互。因为读写同一个块只是要在开始时向master请求块位置信息。对于读写大型文件这种减少尤为重要。即使对于访问少量数据的随机读操作也可以很方便的为一个规模达几个TB的工作集缓缓存块位置信息。<br />
2、Client在一个给定的块上很可能执行多个操作，和一个chunkserver保持较长时间的TCP连接可以减少网络负载。<br />
3、这减少了master上保存的元数据（metadata）的规模，从而使得可以将metadata放在内存中。这又会带来一些别的好处。<br />
不利的一面：<br />
一个小文件可能只包含一个块，如果很多Client访问改文件的话，存储这些块的chunkserver将成为访问的热点。但在实际应用中，应用程序通常顺序地读包含多个块的文件，所以这不是一个主要问题。<br />
（6）元数据（metadata）：<br />
master存储了三中类型的metadata：文件的名字空间和块的名字空间，从文件到块的映射，块的副本的位  置。所有的metadata都放在内存中。前两种类型的metadata通过向操作日志登记修改而保持不变，操作日志存储在master的本地磁盘并在几  个远程机器上留有副本。使用日志使得我们可以很简单地、可靠地更新master的状态，即使在master崩溃的情况下也不会有不一致的问题。相反，  mater在每次启动以及当有  chuankserver加入的时候询问每个chunkserver的所拥有的块的情况。<br />
A、内存数据结构：<br />
因为metadata存储在内存中，所以master的操作很快。进一步，master可以轻易而且高效地定期在后台扫描它的整个状态。这种定期地扫描被用于实现块垃圾收集、chunkserver出现故障时的副本复制、为平衡负载和磁盘空间而进行的块迁移。<br />
这种方法的一个潜在的问题就是块的数量也即整个系统的容量是否受限与master的内存。实际上，这并不是一个严重  的问题。Master为每个  64MB的块维护的metadata不足64个字节。除了最后一块，文件所有的块都是满的。类似的，每个文件的名字空间数据也不足64个字节，因为文件名  是以一种事先确定的压缩方式存储的.如果要支持更大的文件系统，那么增加一些内存的方法对于我们将元数据（metadata）保存在内存种所获得的简单  性、可靠性、高性能和灵活性来说，这只是一个很小的代价。<br />
B、块位置：<br />
master并不为chunkserver所拥有的块的副本的保存一个不变的记录。它在启动时通过简单的查询来获得这些信息。Master可以保持这些信息的更新，因为它控制所有块的放置并通过HeartBeat消息来监控chunkserver的状态。<br />
这样做的好处：因为chunkserver可能加入或离开集群、改变路径名、崩溃、重启等，一个集群重有成百个server，这些事件经常发生，这种方法就排除了master与chunkserver之间的同步问题。<br />
另一个原因是：只有chunkserver才能确定它自己到底有哪些块，由于错误，chunkserver中的一些块可能会很自然的消失，这样在master中就没有必要为此保存一个不变的记录。<br />
C、操作日志：<br />
操作日志包含了对metadata所作的修改的历史记录。它作为逻辑时间线定义了并发操作的执行顺序。文件、块以及它们的版本号都由它们被创建时的逻辑时间而唯一地、永久地被标识。<br />
操作日志是如此的重要，我们必须要将它可靠地保存起来，并且只有在metadata的改变固定下来之后才将变化呈现给用户。所以我们将操作日志复制到数个远程的机器上，并且只有在将相应的日志记录写到本地和远程的磁盘上之后才回答用户的请求。<br />
Master可以用操作日志来恢复它的文件系统的状态。为了将启动时间减至最小，日志就必须要比较小。每当日志的长度增长到超过一定的规模后，master就要检查它的状态，它可以从本地磁盘装入最近的检查点来恢复状态。<br />
创建一个检查点比较费时，master的内部状态是以一种在创建一个检查点时并不耽误即将到来的修改操作的方式来组  织的。Master切换到一个新的日子文件并在一个单独的线程中创建检查点。这个新的检查点记录了切换前所有的修改。在一个有数十万文件的集群中用一分钟  左右就能完成。创建完后，将它写入本地和远程的磁盘。<br />
（7）数据完整性<br />
名字空间的修改必须是原子性的，它们只能有master处理：名字空间锁保证了操作的原子性和正确性，而master的操作日志在全局范围内定义了这些操作的顺序。<br />
文  件区间的状态在修改之后依赖于修改的类型，不论操作成功还是失败，也不论是不是并发操作。如果不论从哪个副本上读，所有的客户都看到同样的数据，那么文件  的这个区域就是一致的。如果文件的区域是一致的并且用户可以看到修改操作所写的数据，那么它就是已定义的。如果修改是在没有并发写操作的影响下完成的，那  么受影响的区域是已定义的，所有的client都能看到写的内容。成功的并发写操作是未定义但却是一致的。失败的修改将使区间处于不一致的状态。<br />
Write操作在应用程序指定的偏移处写入数据，而record  append操作使得数据（记录）即使在有并发修改操作的情况下也至少原子性的被加到GFS指定的偏移处，偏移地址被返回给用户。<br />
在一系列成功的修改操作后，最后的修改操作保证文件区域是已定义的。GFS通过对所有的副本执行同样顺序的修改操作并且使用块版本号检测过时的副本（由于chunkserver退出而导致丢失修改）来做到这一点。<br />
因为用户缓存了会位置信息，所以在更新缓存之前有可能从一个过时的副本中读取数据。但这有缓存的截止时间和文件的重新打开而受到限制。<br />
在修改操作成功后，部件故障仍可以是数据受到破坏。GFS通过master和chunkserver间定期的handshake，借助校验和来检测对数据的破坏。一旦检测到，就从一个有效的副本尽快重新存储。只有在GFS检测前，所有的副本都失效，这个块才会丢失。<br />
2、系统交互<br />
（1）租约（lease）和修改顺序<br />
（2）数据流<br />
我们的目标是充分利用每个机器的网络带宽，避免网络瓶颈和延迟<br />
为了有效的利用网络，我们将数据流和控制流分离。数据是以流水线的方式在选定的chunkerserver链上线性的传递的。每个机器的整个对外带宽都被用作传递数据。为避免瓶颈，每个机器在收到数据后，将它收到数据尽快传递给离它最近的机器。<br />
（3）原子性的record  Append：<br />
GFS提供了一个原子性的添加操作：record  append。在传统的写操作中，client指定被写数据的偏移位置，向同一个区间的并发的写操作是不连续的：区间有可能包含来自多个client的数  据碎片。在record append中，  client只是指定数据。GFS在其选定的偏移出将数据至少原子性的加入文件一次，并将偏移返回给client。<br />
在分布式的应用中，不同机器上的许多client可能会同时向一个文件执行添加操作，添加操作被频繁使用。如果用传  统的write操作，可能需要额外的、复杂的、开销较大的同步，例如通过分布式锁管理。在我们的工作量中，这些文件通常以多个生产者单个消费者队列的方式  或包含从多个不同 client的综合结果。<br />
Record  append和前面讲的write操作的控制流差不多，只是在primary上多了一些逻辑判断。首先，client将数据发送到文件最后一块的所有副本  上。然后向primary发送请求。Primary检查添加操作是否会导致该块超过最大的规模（64M）。如果这样，它将该块扩充到最大规模，并告诉其它  副本做同样的事，同时通知client该操作需要在下一个块上重新尝试。如果记录满足最大规模的要求，primary就会将数据添加到它的副本上，并告诉  其它的副本在在同样的偏移处写数据，最后primary向client报告写操作成功。如果在任何一个副本上record  append操作失败，client将重新尝试该操作。这时候，同一个块的副本可能包含不同的数据，因为有的可能复制了全部的数据，有的可能只复制了部  分。GFS不能保证所有的副本每个字节都是一样的。它只保证每个数据作为一个原子单元被写过至少一次。这个是这样得出的：操作要是成功，数据必须在所有的  副本上的同样的偏移处被写过。进一步，从这以后，所有的副本至少和记录一样长，所以后续的记录将被指定到更高的偏移处或者一个不同的块上，即使另一个副本  成了primary。根据一致性保证，成功的record  append操作的区间是已定义的。而受到干扰的区间是不一致的。<br />
（4）快照（snapshot）<br />
快照操作几乎在瞬间构造一个文件和目录树的副本，同时将正在进行的其他修改操作对它的影响减至最小。<br />
我们使用copy-on-write技术来实现snapshot。当master受到一个snapshot请求时，  它首先将要snapshot的文件上块上的lease。这使得任何一个向这些块写数据的操作都必须和master交互以找到拥有lease的副本。这就给  master一个创建这个块的副本的机会。<br />
副本被撤销或终止后，master在磁盘上登记执行的操作，然后复制源文件或目录树的metadata以对它的内存状态实施登记的操作。这个新创建的snapshot文件和源文件（其metadata）指向相同的块（chunk）。<br />
Snapshot之后，客户第一次向chunk  c写的时候，它发一个请求给master以找到拥有lease的副本。Master注意到chunk c的引用记数比1大，它延迟对用户的响应，选择一个chunk  handle C’,然后要求每一有chunk c的副本的chunkserver创建一个块C’。每个chunkserver在本地创建chunk  C’避免了网络开销。从这以后和对别的块的操作没有什么区别。<br />
3、MASTER操作<br />
MASTER执行所有名字空间的操作，除此之外，他还在系统范围管理数据块的复制：决定数据块的放置方案，产生新数据块并将其备份，和其他系统范围的操作协同来确保数据备份的完整性，在所有的数据块服务器之间平衡负载并收回没有使用的存储空间。<br />
3.1  名字空间管理和加锁<br />
与传统文件系统不同的是，GFS没有与每个目录相关的能列出其所有文件的数据结构，它也不支持别名（unix中的硬连接或符号连接），不管是对文件或是目录。GFS的名字空间逻辑上是从文件元数据到路径名映射的一个查用表。<br />
MASTER在执行某个操作前都要获得一系列锁，例如，它要对/d1/d2…/dn/leaf执行操作，则它必须获  得/d1，/d1/d2，…，  /d1/d2/…/dn的读锁，/d1/d2…/dn/leaf的读锁或写锁（其中leaf可以使文件也可以是目录）。MASTER操作的并行性和数据的  一致性就是通过这些锁来实现的。<br />
3.2  备份存储放置策略<br />
一个GFS集群文件系统可能是多层分布的。一般情况下是成千上万个文件块服务器分布于不同的机架上，而这些文件块服  务器又被分布于不同机架上的客户来访问。因此，不同机架上的两台机器之间的通信可能通过一个或多个交换机。数据块冗余配置策略要达到连个目的：最大的数据  可靠性和可用性，最大的网络带宽利用率。因此，如果仅仅把数据的拷贝置于不同的机器上很难满足这两个要求，必须在不同的机架上进行数据备份。这样即使整个  机架被毁或是掉线，也能确保数据的正常使用。这也使数据传输，尤其是读数据，可以充分利用带宽，访问到多个机架，而写操作，则不得不涉及到更多的机架。<br />
3.3  产生、重复制、重平衡数据块<br />
当MASTER产生新的数据块时，如何放置新数据块，要考虑如下几个因素：（1）尽量放置在磁盘利用率低的数据块服  务器上，这样，慢慢地各服务器的磁盘利用率就会达到平衡。（2）尽量控制在一个服务器上的“新创建”的次数。（3）由于上一小节讨论的原因，我们需要把数  据块放置于不同的机架上。<br />
MASTER在可用的数据块备份低于用户设定的数目时需要进行重复制。这种情况源于多种原因：服务器不可用，数据被  破坏，磁盘被破坏，或者备份数目被修改。每个被需要重复制的数据块的优先级根据以下几项确定：第一是现在的数目距目标的距离，对于能阻塞用户程序的数据  块，我们也提高它的优先级。最后，  MASTER按照产生数据块的原则复制数据块，并把它们放到不同的机架内的服务器上。<br />
MASTER周期性的平衡各服务器上的负载：它检查chunk分布和负载平衡，通过这种方式来填充一个新的服务器而  不是把其他的内容统统放置到它上面带来大量的写数据。数据块放置的原则与上面讨论的相同，此外，MASTER还决定那些数据块要被移除，原则上他会清除那  些空闲空间低于平均值的那些服务器。<br />
3.4  垃圾收集<br />
在一个文件被删除之后，GFS并不立即收回磁盘空间，而是等到垃圾收集程序在文件和数据块级的的检查中收回。<br />
当一个文件被应用程序删除之后，MASTER会立即记录下这些变化，但文件所占用的资源却不会被立即收回，而是重新  给文件命了一个隐藏的名字，并附上了删除的时间戳。在MASTER定期检查名字空间时，它删除超过三天（可以设定）的隐藏的文件。在此之前，可以以一个新  的名字来读文件，还可以以前的名字恢复。当隐藏的文件在名字空间中被删除以后，它在内存中的元数据即被擦除，这就有效地切断了他和所有数据块的联系。<br />
在一个相似的定期的名字空间检查中，MASTER确认孤儿数据块（不属于任何文件）并擦除他的元数据，在和MASTER的心跳信息交换中，每个服务器报告他所拥有的数据块，MASTER返回元数据不在内存的数据块，服务器即可以删除这些数据块。<br />
3.5  过时数据的探测<br />
在数据更新时如果服务器停机了，那么他所保存的数据备份就会过时。对每个数据块，MASTER设置了一个版本号来区别更新过的数据块和过时的数据块。<br />
当MASTER授权一个新的lease时，他会增加数据块的版本号并会通知更新数据备份。MASTER和备份都会记  录下当前的版本号，如果一个备份当时不可用，那么他的版本号不可能提高，当ChunkServer重新启动并向MASTER报告他的数据块集时，  MASTER就会发现过时的数据。<br />
MASTER在定期的垃圾收集程序中清除过时的备份，在此以前，处于效率考虑，在各客户及英大使，他会认为根本不存  在过时的数据。作为另一个安全措施，  MASTER在给客户及关于数据块的应答或是另外一个读取数据的服务器数据是都会带上版本信息，在操作前客户机和服务器会验证版本信息以确保得到的是最新  的数据。<br />
4、容错和诊断<br />
4.1 高可靠性<br />
4.1.1  快速恢复<br />
不管如何终止服务，MASTER和数据块服务器都会在几秒钟内恢复状态和运行。实际上，我们不对正常终止和不正常终止进行区分，服务器进程都会被切断而终止。客户机和其他的服务器会经历一个小小的中断，然后它们的特定请求超时，重新连接重启的服务器，重新请求。<br />
4.1.2  数据块备份<br />
如上文所讨论的，每个数据块都会被备份到放到不同机架上的不同服务器上。对不同的名字空间，用户可以设置不同的备份级别。在数据块服务器掉线或是数据被破坏时，MASTER会按照需要来复制数据块。<br />
4.1.3  MASTER备份<br />
为确保可靠性，MASTER的状态、操作记录和检查点都在多台机器上进行了备份。一个操作只有在数据块服务器硬盘上  刷新并被记录在MASTER和其备份的上之后才算是成功的。如果MASTER或是硬盘失败，系统监视器会发现并通过改变域名启动它的一个备份机，而客户机  则仅仅是使用规范的名称来访问，并不会发现MASTER的改变。<br />
4.2  数据完整性<br />
每个数据块服务器都利用校验和来检验存储数据的完整性。原因：每个服务器随时都有发生崩溃的可能性，并且在两个服务器间比较数据块也是不现实的，同时，在两台服务器间拷贝数据并不能保证数据的一致性。<br />
每个Chunk按64kB的大小分成块，每个块有32位的校验和，校验和和日志存储在一起，和用户数据分开。<br />
在读数据时，服务器首先检查与被读内容相关部分的校验和，因此，服务器不会传播错误的数据。如果所检查的内容和校验  和不符，服务器就会给数据请求者返回一个错误的信息，并把这个情况报告给MASTER。客户机就会读其他的服务器来获取数据，而MASTER则会从其他的  拷贝来复制数据，等到一个新的拷贝完成时，MASTER就会通知报告错误的服务器删除出错的数据块。<br />
附加写数据时的校验和计算优化了，因为这是主要的写操作。我们只是更新增加部分的校验和，即使末尾部分的校验和数据已被损坏而我们没有检查出来，新的校验和与数据会不相符，这种冲突在下次使用时将会被检查出来。<br />
相反，如果是覆盖现有数据的写，在写以前，我们必须检查第一和最后一个数据块，然后才能执行写操作，最后计算和记录校验和。如果我们在覆盖以前不先检查首位数据块，计算出的校验和则会因为没被覆盖的数据而产生错误。<br />
在空闲时间，服务器会检查不活跃的数据块的校验和，这样可以检查出不经常读的数据的错误。一旦错误被检查出来，服务器会拷贝一个正确的数据块来代替错误的。<br />
4.3  诊断工具<br />
广泛而细致的诊断日志以微小的代价换取了在问题隔离、诊断、性能分析方面起到了重大的作用。GFS服务器用日志来记  录显著的事件（例如服务器停机和启动）和远程的应答。远程日志记录机器之间的请求和应答，通过收集不同机器上的日志记录，并对它们进行分析恢复，我们可以  完整地重现活动的场景，并用此来进行错误分析。<br />
5 测量<br />
5.1  测试环境<br />
一台主控机，两台主控机备份，16台数据块服务器，16台客户机。<br />
每台机器：2块PIII1.4G处理器，2G内存，2块80G5400rpm的硬盘，1块100Mbps全双工网卡<br />
19台服务器连接到一个HP2524交换机上，16台客户机俩接到领外一台交换机上，两台交换机通过1G的链路相连。</p>
<p>原文转自：http://bigweb.group.javaeye.com/group/topic/5749</p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/78.html" title="[转]C/C++/Perl/汇编/Java效率比较 (2006-10-11)">[转]C/C++/Perl/汇编/Java效率比较</a> (0)</li>
	<li><a href="http://www.donevii.com/post/44.html" title="ACE自适配通信环境 (2006-08-23)">ACE自适配通信环境</a> (0)</li>
	<li><a href="http://www.donevii.com/post/129.html" title="大型高并发高负载网站的系统架构 (2006-10-26)">大型高并发高负载网站的系统架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/9.html" title="flickr对javascript干的好事 (2006-08-17)">flickr对javascript干的好事</a> (0)</li>
	<li><a href="http://www.donevii.com/post/128.html" title="应用加速 (2006-10-26)">应用加速</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/504.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>squid日志TCP命中率字段祥解</title>
		<link>http://www.donevii.com/post/489.html</link>
		<comments>http://www.donevii.com/post/489.html#comments</comments>
		<pubDate>Wed, 17 Sep 2008 11:20:04 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[life]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/489.html</guid>
		<description><![CDATA[squid的日志很重要。常常要了解的，其中最重要的就是命中率啦，不然反向代理做的用就不大。 #cat access.log&#124;gawk &#8216;{print $4}&#8217;&#124;sort&#124;uniq -c&#124;sort -nr 9568 TCP_IMS_HIT/304 6313 TCP_HIT/200 21... ]]></description>
			<content:encoded><![CDATA[<p>squid的日志很重要。常常要了解的，其中最重要的就是命中率啦，不然反向代理做的用就不大。    <br />#cat access.log|gawk &#8216;{print $4}&#8217;|sort|uniq -c|sort -nr     <br />9568 TCP_IMS_HIT/304     <br />6313 TCP_HIT/200     <br />2133 TCP_MISS/200     <br />1568 TCP_MISS/206     <br />587 TCP_MEM_HIT/200     <br />531 TCP_MISS/304     <br />207 TCP_REFRESH_HIT/200     <br />152 TCP_REFRESH_HIT/304     <br />86 TCP_NEGATIVE_HIT/404     <br />69 TCP_MISS/404     <br />9 TCP_MISS/000     <br />4 TCP_MISS/503     <br />1 TCP_REFRESH_MISS/000     <br />1 TCP_DENIED/400     <br />可以使用上面的方法，大约的分析一下命令中比。什么意思就看下面的详解.     <br />#cat /var/log/squid/access.log |grep TCP_MEM_HIT     <br />如果看到很多的TCP_MEM_HIT ，这表明该文件是从内存缓存读取的，squid已经起作用了！你再用浏览器打开该文件，应该是快如闪电了。。呵呵，大功告成了！还有其他类型的HIT，如TCP_HIT等等，这些是从磁盘读取的，我� ��得加速的意义不大，只不过缓解了apache的压力而已。     <br />相应于HTTP请求，下列标签可能出现在access.log文件的第四个域。     <br />TCP_HIT     <br />Squid发现请求资源的貌似新鲜的拷贝，并将其立即发送到客户端。     <br />TCP_MISS     <br />Squid没有请求资源的<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>拷贝。     <br />TCP_REFERSH_HIT     <br />Squid发现请求资源的貌似陈旧的拷贝，并发送确认请求到原始服务器。原始服务器返回304（未修改）响应，指示squid的拷贝仍旧是新鲜的。     <br />TCP_REF_FAIL_HIT     <br />Squid发现请求资源的貌似陈旧的拷贝，并发送确认请求到原始服务器。然而，原始服务器响应失败，或者返回的响应Squid不能理解。在此情形下，squid发送现有<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>拷贝（很可能是陈旧的）到� �户端。     <br />TCP_REFRESH_MISS     <br />Squid发现请求资源的貌似陈旧的拷贝，并发送确认请求到原始服务器。原始服务器响应新的内容，指示这个<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>拷贝确实是陈旧的。     <br />TCP_CLIENT_REFRESH_MISS     <br />Squid发现了请求资源的拷贝，但客户端的请求包含了<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">Cache</a>-Control: no-<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>指令。Squid转发客户端的请求到原始服务器，强迫<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>确认。     <br />TCP_IMS_HIT     <br />客户端发送确认请求，Squid发现更近来的、貌似新鲜的请求资源的拷贝。Squid发送更新的内容到客户端，而不联系原始服务器。     <br />TCP_SWAPFAIL_MISS     <br />Squid发现请求资源的有效拷贝，但从磁盘装载它失败。这时squid发送请求到原始服务器，就如同这是个<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>丢失一样。     <br />TCP_NEGATIVE_HIT     <br />在对原始服务器的请求导致HTTP错误时，Squid也会<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>这个响应。在短时间内对这些资源的重复请求，导致了否命中。 negative_ttl指令控制这些错误被<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>的时间数量。请注意这些错误只在内存<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>，不会写往磁盘。下列HTTP状态码可能导致否定 <a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>（也遵循于其他约束）： 204, 305, 400, 403, 404, 405, 414, 500, 501, 502, 503, 504。     <br />TCP_MEM_HIT     <br />Squid在内存<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>里发现请求资源的有效拷贝，并将其立即发送到客户端。注意这点并非精确的呈现了所有从内存服务的响应。例如，某些<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>在内存里，但要求确认的响应，会以TCP_REFRESH_HIT, TCP_REFRESH_MISS等形式记录。     <br />TCP_DENIED     <br />因为http_access或http_reply_access规则，客户端的请求被拒绝了。注意被http_access拒绝的请求在第9域的值是NONE/-，然而被http_reply_access拒绝的请求，在相应地方有一个有效值。     <br />TCP_OFFLINE_HIT     <br />当offline_mode激活时，Squid对任何<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>响应返回<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>命中，而不用考虑它的新鲜程度。     <br />TCP_REDIRECT     <br />重定向程序告诉Squid产生一个HTTP重定向到新的URI（见11.1节）。正常的，Squid不会记录这些重定向。假如要这样做，必须在编译squid前，手工定义LOG_TCP_REDIRECTS预处理指令。     <br />NONE     <br />无分类的结果用于特定错误，例如无效主机名。     <br />相应于ICP查询，下列标签可能出现在access.log文件的第四域。     <br />UDP_HIT     <br />Squid在<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>里发现请求资源的貌似新鲜的拷贝。     <br />UDP_MISS     <br />Squid没有在<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>里发现请求资源的貌似新鲜的拷贝。假如同一目标通过HTTP请求，就可能是个<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>丢失。请对比UDP_MISS_NOFETCH。     <br />UDP_MISS_NOFETCH     <br />跟UDP_MISS类似，不同的是这里也指示了Squid不愿去处理相应的HTTP请求。假如使用了-Y命令行选项，Squid在启动并编译其内存索引时，会返回这个标签而不是UDP_MISS。     <br />UDP_DENIED     <br />因为icp_access规则，ICP查询被拒绝。假如超过95% 的到某客户端的ICP响应是UDP_DENIED，并且客户端数据库激活了（见附录A），Squid在1小时内，停止发送任何ICP响应到该客户端。若这点发生，你也可在<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a>.log里见到一个警告。     <br />UDP_INVALID     <br />Squid接受到无效查询（例如截断的消息、无效协议版本、URI里的空格等）。Squid发送UDP_INVALID响应到客户端。</p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/758.html" title="清除指定squid缓存文件的脚本 (2009-02-09)">清除指定squid缓存文件的脚本</a> (0)</li>
	<li><a href="http://www.donevii.com/post/128.html" title="应用加速 (2006-10-26)">应用加速</a> (0)</li>
	<li><a href="http://www.donevii.com/post/129.html" title="大型高并发高负载网站的系统架构 (2006-10-26)">大型高并发高负载网站的系统架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/203.html" title="使用memcached进行内存缓存 (2006-11-15)">使用memcached进行内存缓存</a> (0)</li>
	<li><a href="http://www.donevii.com/post/388.html" title="[转]内存种类知多少 (2008-01-14)">[转]内存种类知多少</a> (1)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/489.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转]AT指令集及S寄存器</title>
		<link>http://www.donevii.com/post/389.html</link>
		<comments>http://www.donevii.com/post/389.html#comments</comments>
		<pubDate>Wed, 16 Jan 2008 03:46:35 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[mobile]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[command]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mtk]]></category>
		<category><![CDATA[register]]></category>
		<category><![CDATA[ror]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/389.html</guid>
		<description><![CDATA[AT命令使计算机或终端与调制解调器通讯,所有命令行必须由ASCII字符“AT”开始并由 &#60;Enter&#62; 结束。除了A/指令和推出(缺省为+++)。这些将在后面讨论。字母&#8221;AT&#8221;用以提醒调制解调器注... ]]></description>
			<content:encoded><![CDATA[<p id="textboxContent" class="p_entry2">AT命令使计算机或终端与调制解调器通讯,所有命令行必须由ASCII字符“AT”开始并由 &lt;Enter&gt; 结束。除了A/指令和推出(缺省为+++)。这些将在后面讨论。字母&#8221;AT&#8221;用以提醒调制解调器注意，其后将有一条或多条命令出现, &#8220;AT&#8221;及其后的字母可以是大写或小写。</p>
<h5>
<p align="center"><font color="#ff0000">AT必须同为大写或小写。如&#8221;At&#8221;或&#8221;aT&#8221;是不允许的。</font></p>
</h5>
<blockquote><p>    一串命令可以写在一行里。为了便于阅读可以加或不加空格。命令中或命令间的空格会被忽略，命令行的最多字符数为39(包括&#8221;AT&#8221;)。在输入一条命令期间，可以用退格键(backspace)改正除&#8221;AT&#8221;以外的错误。若命令行中任一处出现语法错误，本行其后的内容将被忽略，并返回ERROR。大数带有超出正常范围的参数的命令将不被接收并返回 ERROR.本章列出所有设置调制解调器的命令。包括控制ACTIVE调制解调器的贺氏标准AT命令集。贺氏V系列命令集和扩展命令集</p>
<p><strong>AT命令集的描述</strong></p>
<p>  <strong>    符号 * 表明该命令的设置可用AT&amp;Wn命令存于两个用户方案中的一个</strong></p>
<p><strong>A/        重执行命令</strong></p>
<blockquote>
<blockquote><p>重执行前一AT命令行，主要用于连接时占线，无应答或号码错误。这一命令必须单独构成一命令行并由&#8221;/&#8221;字符结束,(&lt;Enter&gt; 不能用于结束命令)。</p></blockquote>
</blockquote>
<p><strong>+++       退出字符 缺省:+</strong></p>
<blockquote>
<blockquote><p>切换调制解调器从在线状态到命令状态，而不会中断数据连接。可以通过改变S寄存器S2的值来改变这一字符。</p></blockquote>
</blockquote>
<p><strong>AT=x      写入被选的S寄存器</strong></p>
<blockquote>
<blockquote><p>这一命令将数值x写入当前被选的S寄存器，一个S寄存器可由ATSn命令选择，若 x 是一个数字,所有S 寄存器将返回 OK 响应。</p></blockquote>
</blockquote>
<p><strong>AT?       读被选的S寄存器</strong></p>
<blockquote><p>  这一命令读并且显示被选的S寄存器的内容。一个S寄存器可由ATSn命令选择。</p></blockquote>
<p><strong>ATA       应答</strong></p>
<blockquote>
<blockquote><p>它必须是命令行中的最后一条指令。调制解调器在应答方式下继续执行连接程序。在与远端调制解调器交换载波后进入连接状态,如果在由寄存器S7规定的时间内(缺省值=50秒)没有检测到载波, 调制解调器将挂机。在连接过程中，通过DTE输入的任何一个字母都将中断这一命令。</p></blockquote>
</blockquote>
<p><strong>ATBn*     选择ITU-T或Bell模式 缺省=0</strong></p>
<blockquote>
<blockquote><p> ATB0 选择在1200和300bps速率下通讯的ITU-T V.22和V.21协议<br />
 ATB1 选择在1200和300bps速率下通讯的Bell 212A和103协议</p></blockquote>
</blockquote>
<p><strong>ATCn      载波控制缺省=1</strong></p>
<blockquote>
<blockquote><p>包含这一命令只是为了保证兼容性，执行号只是返回一结果码而没有其它作用。<br />
ATC1 正常传输载波切换</p></blockquote>
</blockquote>
<p><strong>ATDn      拨号</strong></p>
<blockquote>
<blockquote><p>它必须是命令行中的最后一条指令, ATD命令使调制解调器摘机后, 根据输入的参数拨号,以建立连接。如果不带参数，调制解调器摘机后，不拨号进入发起方式。</p>
<p>使用标点可使命令更易读懂。圆括号,连字符和空格符会被忽略。拔号命令行中如果出现了非法字符，则该字符及其后的内容将被忽略。调制解调器允许的拨号命令长度为36个字符。</p>
<p>参数：0-9 A B C D * # L P T R ! @ W , ; ^ S=n<br />
0-9     DTMF 符号0到9<br />
A-D     DTMF 符号A,B,C和D。在一些国家中不使用这些符号<br />
*       &#8220;星&#8221;号(仅用于音频拨号)<br />
#       &#8220;#&#8221;号(仅用于音频拨号)<br />
J       为本次呼叫执行在可提供的最高速率下的MNP10链路协商(可选)<br />
K       使本次呼叫MNP10链路协商期间电源电平可调(可选)<br />
L       重拨上一次拨过的号码<br />
P       脉冲拨号<br />
T       双音频拨号<br />
R       逆叫方式。允许调制解调器使用应答方式呼叫只能作为发起使用的调制解调        器, 必须作为命令行中的最后一个字符输入。<br />
!       使调制解调器按照S29中规定的值挂机一段时间再摘机。<br />
@       使调制解调器等待5秒钟的无声回答<br />
w       按照寄存器S7中规定的时间，在拨号前等待拨号音。<br />
,       在拨号过程中，按照寄存器S8中规定的时间,暂停<br />
;       拨号后返回命令状态<br />
^       打开呼叫音<br />
()      被忽视，用于格式化号码串<br />
-       被忽视，用于格式化号码串<br />
&lt;space&gt; 被忽视,用于格式化号码串<br />
S=n     用AT&amp;Zn 命令存在地址n处的号码拨号</p></blockquote>
</blockquote>
<p><strong>ATE*     命令回应           缺省:1</strong></p>
<blockquote>
<blockquote><p>ATE0 关闭命令回应<br />
ATE1 打开 命令回应</p></blockquote>
</blockquote>
<p><strong>ATHn     摘挂机控制       缺省:0</strong></p>
<blockquote>
<blockquote><p>ATH0 使调制解调器挂机<br />
ATH1 当调制解调器处于挂机状态，使调制解调器摘机，返回响 OK，等待进一步的命令。</p></blockquote>
</blockquote>
<p><strong>ATIn     识别</strong></p>
<blockquote>
<blockquote><p>I0 报告产品代码<br />
I1 报告ROM中预先计算的校验和<br />
I2 计算校验和并与ROM中的校验和比较,返回&#8221;OK&#8221;或&#8221;ERROR&#8221;结果码<br />
I3 报告固件修正<br />
I4 报告OEM定义的识别串<br />
I5 报告国家代码参数<br />
I6 报告固件修正<br />
I7 报告调制解调器数据泵类型</p></blockquote>
</blockquote>
<p><strong>ATLn*    扬声器音量       缺省:2</strong></p>
<blockquote>
<blockquote><p>ATL0 扬声器低音量<br />
ATL1 扬声器低音量<br />
ATL2 扬声器中音量<br />
ATL3 扬声器高音量</p></blockquote>
</blockquote>
<p><strong>ATMn*    扬声器控制       缺省:1</strong></p>
<blockquote>
<blockquote><p>ATM0 关闭扬声器<br />
ATM1 扬声器在呼叫建立握手阶段打开至检测到来自于远端调制解调器的载波后关闭<br />
ATM2 扬声器持续开<br />
ATM3 扬声器在应答期间打开。当检测到来自于远端的调制解调器的载波和拨号时关闭</p></blockquote>
</blockquote>
<p><strong>ATNn*     调制握手       缺省:1</strong></p>
<blockquote>
<blockquote><p><strong> </strong>ATN0 要求调制解调器S37选择连接速率,若S37＝0,则连接速率必须与发出的上一条AT命令的速率相匹配。如果所选择的速率可用不止一个通讯标准实现(如Bell212A或ITU-T V.22 速率在 1200bps)调制解调器同时参考ATB 命令选择。ATN1 允许时使用双方调制解调器都支持的任一速率握手，使能够自动检测。在这一方式下，ATB命令被忽视，调制解调器只用ITU-T方式连接。</p></blockquote>
</blockquote>
<p><strong>ATOn     进入数据在现状态 缺省:0</strong></p>
<blockquote>
<blockquote><p>ATO0 使调制解调器从命令在现状态直接返回数据在线状态,不经过自动均衡。<br />
ATO1 使调制解调器从命令在现状态返回数据在状态,经过自动均衡。</p></blockquote>
</blockquote>
<p><strong>ATP*     设脉冲拨号为缺省</strong><br />
 </p>
<p><strong>ATQn*    结果码显示        缺省:0</strong></p>
<blockquote>
<blockquote><p>ATQ0 调制解调器向DTE发送结果码<br />
ATQ1 禁止调制解调器向DTE发送结果码</p></blockquote>
</blockquote>
</blockquote>
<p align="center"><a target="_blank" href="http://www.lsllhd.com/gksmpale/sspek.htm"><font color="#ff0000">点击查看S寄存器详解！</font></a></p>
<blockquote><p><strong>ATSn     设S寄存器n为缺省寄存器</strong><br />
 <strong>ATSn?    读S寄存器</strong></p>
<blockquote>
<blockquote><p>读S寄存器中的内容，所有的S寄存器都可以读</p></blockquote>
</blockquote>
<p><strong>ATSn=x   写入S寄存器</strong></p>
<blockquote>
<blockquote><p>将 x值写入指定的S寄存器n</p></blockquote>
</blockquote>
<p><strong>ATT*     设音频拔号为缺省</strong><br />
 </p>
<p><strong>ATVn*    结束码类型 (消息控制)        缺省</strong>:1</p>
<blockquote>
<blockquote><p>ATV0 发送短型 (数字型) 结果码<br />
ATV1 发送长型 (字符型) 结果码</p></blockquote>
</blockquote>
<p><strong>ATWn*    协商进程报告                缺省:0</strong></p>
<blockquote>
<blockquote><p>ATW0 不报告纠错呼叫进程<br />
ATW1 报告纠错呼叫进程<br />
ATW2 不报告纠错呼叫进程，CONNECT xxxx指示DCE速率。</p></blockquote>
</blockquote>
<p><strong>ATXn*     扩展结果码            缺省:4</strong></p>
<blockquote>
<blockquote><p>ATX0<strong> </strong>调制解调器忽视拨号音和忙音。当由盲拨建立连接时，发送CONNECT信息。ATX1 调制解调器忽视拨号音和忙音。当由盲拨建立连接时，CONNECT XXXX 反映的是比特速率</p>
<p>ATX2 调制解调器忽视忙音，但在拨号前等待拨号音，如果5秒钟内检测不到拨号音，则发送NO DIAL TONE 信息，连接建立后 发送 CONNECT xxxx反映比特速率。</p>
<p>ATX3 调制解调器忽视拨号音,若检测到忙音,发送BUSY信息,当由盲拨建立起连接时, CONNECT XXXX 反映的是比特速率。</p>
<p>ATX4 如果5秒钟内检测不到拨号音，发送NO DIAL TONE 讯息,检测到忙音, 发送BUSY信息。连接建立后发送CONNECT XXXX 反映比特速率。</p></blockquote>
</blockquote>
<p><strong>ATYn*     控制长间隔拆接         缺省:0</strong></p>
<blockquote>
<blockquote><p>ATY0 不允许长间隔拆接<br />
ATY1 允许长间隔拆接</p></blockquote>
</blockquote>
<p><strong>ATZn      复位                缺省:0</strong></p>
<blockquote>
<blockquote><p>重新调出由用户方案规定的动态配置<br />
ATZ0 软复位并重新调出用户方案0<br />
ATZ1 软复位并重新调出用户方案1</p></blockquote>
</blockquote>
<p><strong>AT&amp;An*    握手异常终止(备选)    缺省:1</strong></p>
<blockquote>
<blockquote><p><strong> </strong>AT&amp;A0 在握手时禁止用户进行异常终止。当拨号或应答时，握手不能异常终止,只有DTR 信号下降。AT&amp;A1 用户可以在握手时进行异常终止.在接收到DTE的字符后,发起和应答可以在握手期间随时进行异常终止.</p></blockquote>
</blockquote>
<p><strong>AT&amp;Cn*     RS232-C DCD          设置缺省:1</strong></p>
<blockquote>
<blockquote><p>AT&amp;C0 DCD为ON，不论来自远端的调制解调器的数据载波的状态为何。<br />
AT&amp;C1 DCD 跟随来自于远端调制解调器的数据载波的状态</p></blockquote>
</blockquote>
<p><strong>AT&amp;Dn*    RS232-C DTR          设置缺省:2</strong></p>
<blockquote>
<blockquote><p>决定了调制解调器与来自串口的DTR信号相关的操作。由于跟踪DTR的下降引起的操作在下表列出:</p></blockquote>
</blockquote>
</blockquote>
<table border="1" width="72%" height="201">
<tr>
<td width="20%"> </td>
<td width="21%"><center>&amp;D0 </center></td>
<td width="20%"><center>&amp;D1 </center></td>
<td width="20%"><center>&amp;D2 </center></td>
<td width="19%"><center>&amp;D3 </center></td>
</tr>
<tr>
<td width="20%"><center>&amp;Q0 </center></td>
<td width="21%"><center>NONE </center></td>
<td width="20%"><center>2 </center></td>
<td width="20%"><center>3 </center></td>
<td width="19%"><center>4 </center></td>
</tr>
<tr>
<td height="22" width="20%"><center>&amp;Q1 </center></td>
<td height="22" width="21%"><center>1 </center></td>
<td height="22" width="20%"><center>2 </center></td>
<td height="22" width="20%"><center>3 </center></td>
<td height="22" width="19%"><center>4 </center></td>
</tr>
<tr>
<td width="20%"><center>&amp;Q2 </center></td>
<td width="21%"><center>3 </center></td>
<td width="20%"><center>3 </center></td>
<td width="20%"><center>3 </center></td>
<td width="19%"><center>3 </center></td>
</tr>
<tr>
<td width="20%"><center>&amp;Q3 </center></td>
<td width="21%"><center>3 </center></td>
<td width="20%"><center>3 </center></td>
<td width="20%"><center>3 </center></td>
<td width="19%"><center>3 </center></td>
</tr>
<tr>
<td width="20%"><center>&amp;Q4 </center></td>
<td width="21%"><center>1 </center></td>
<td width="20%"><center>2 </center></td>
<td width="20%"><center>3 </center></td>
<td width="19%"><center>4 </center></td>
</tr>
<tr>
<td width="20%"><center>&amp;Q5 </center></td>
<td width="21%"><center>NONE </center></td>
<td width="20%"><center>2 </center></td>
<td width="20%"><center>3 </center></td>
<td width="19%"><center>4 </center></td>
</tr>
<tr>
<td width="20%"><center>&amp;Q6 </center></td>
<td width="21%"><center>NONE </center></td>
<td width="20%"><center>2 </center></td>
<td width="20%"><center>3 </center></td>
<td width="19%"><center>4 </center></td>
</tr>
</table>
<p><dir><dir></p>
<blockquote><p>1 调制解调器断开连接并发送结果码OK<br />
2 若在数据状态下,则进入命令状态,并发送结果码OK<br />
3 调制解调器断开连接并发送结果码OK, DTR 为 OFF时不能自动应答<br />
4 调制解调器执行热启动(即与ATZ命令相同)</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Fn     重新调用工厂            设置缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>&amp;F0 重新调用作为V.42bis自动可靠方式的出厂缺省设置<br />
&amp;F1 重新调用作为MNP5自动可靠方式的出厂缺省设置<br />
&amp;F2 重新调用作为DIRECT方式的出厂缺省设置<br />
&amp;F3 重新调用作为MNP10方式自动可靠方式的出厂缺省设置(可选)</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Gn*    设置保护音            缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;G0 无保护音<br />
AT&amp;G1 无保护音<br />
AT&amp;G2 1800HZ保护音</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Jn*    电话插头选择          缺省:0</strong><br />
 </p>
<blockquote>
<blockquote><p>包含这一命令只是基于兼容性的考虑，没有任何功能<br />
AT&amp;J0 不操作任何功能<br />
AT&amp;J1 不操作任何功能</p></blockquote>
</blockquote>
</blockquote>
<blockquote><p><strong>AT&amp;Kn*    DTE/调制解调器流    控制缺省:3</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;K0 关闭流控制<br />
AT&amp;K3 使用RTS/CTS流控<br />
AT&amp;K4 使用XON/XOFF流控<br />
AT&amp;K5 使用透明XON/XOFF流控<br />
AT&amp;K6 使用RTS/CTS和XON/XOFF流控(作为传真方式下的缺省)</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Ln*    传输线类型            缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;L0 拨号线<br />
AT&amp;L1 二线专线 （备选）<br />
AT&amp;L2 四线专线 （备选）</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Mn*    通讯方式</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>与AT&amp;Q0-3相同</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Pn*    拨号脉冲占空比        缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;P0 39％61％占空比@10PPS<br />
AT&amp;P1 33％67％占空比@10PPS<br />
AT&amp;P2 39％61％占空比@20PPS<br />
AT&amp;P3 33％67％占空比@20PPS</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Qn*    通讯方式             缺省:5</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;Q0 选择直接异步操作<br />
AT&amp;Q1 选择同步模式一操作<br />
AT&amp;Q2 选择同步模式二操作<br />
AT&amp;Q3 选择同步模式三操作<br />
AT&amp;Q4 选择自动同步模式操作<br />
AT&amp;Q5 选择纠错模式操作<br />
AT&amp;Q6 选择标准模式下的异步操作</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Rn*    RS232-C RTS/CTS   设置缺省:0</strong></p>
<blockquote>
<blockquote><p>AT&amp;R0 CTS跟踪RTS, 本地DTE发送的RTS由OFF变为ON经过由寄存器S26所规定的以10微秒为增量的延迟后,CTS变为ONAT&amp;R1 调制解调器忽视RTS,除非使用了AT&amp;K3命令,CTS保持为ON</p></blockquote>
</blockquote>
<p><strong>AT&amp;Sn*    RS232-C DSR       设置缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;S0 DSR始终为ON<br />
AT&amp;S1 DSR根据EIA-232-C的规定操作</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Tn*    测试和诊断            缺省:4</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>测试只能在非纠错方式下(标准或直接模式)下的异步操作中进行，除参数7和8以外，要中止正在进行中的测试必须首先敲入退出符。若S18非零，则测试经由S18规定的时间后自动中止并显示OK。AT&amp;T0 终止进行中的测试<br />
AT&amp;T1 启动本地模拟回环<br />
AT&amp;T3 在本地启动远端数字回环·,若连接未建通,返回ERROR<br />
AT&amp;T4 允许调制解调器响应来自远端的进行远程数字环回测试的请求<br />
AT&amp;T5 拒绝调制解调器响应来自远端的进行远程数字环回测试的求<br />
AT&amp;T6 启动远端数字环回测试,若连接未通,返回ERROR<br />
T&amp;T7 启动远端数字环回自测试,若连接未建通,返回ERROR<br />
AT&amp;T8 启动本地模拟环回自测试<br />
 </p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;V     看当今配置及用户参数</strong></p>
<blockquote>
<blockquote><p>AT&amp;V0 查看当前配置、用户方案和存储的电话号码<br />
AT&amp;V1 显示最后一次数据连接的详细情况</p></blockquote>
</blockquote>
</blockquote>
<blockquote><p><strong>AT&amp;Wn    储存用户参数              缺省：0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;W0 作为用户0存贮<br />
AT&amp;W1 作为用户1存贮</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Xn*</strong>   <strong> 选择同步时钟源             缺省：0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT&amp;X0 调制解调器提供传输时钟，内部时钟。 AT&amp;X1 DTE提供传输时钟，外部时钟。<br />
AT&amp;X2 由调制解调器从接外载波信号中提供传输时钟，从属接收时钟</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT&amp;Yn*    指示缺省用户参数            缺省：0</strong></p>
<blockquote>
<blockquote><p>在硬复位后可选择将使用的用户方案。<br />
AT&amp;Y0 选择用户方案0<br />
AT&amp;Y1 选择用户方案1</p></blockquote>
</blockquote>
</blockquote>
<blockquote><p><strong>AT&amp;Zn=x   储存电话号码(n=0-3)         缺省：0</strong></p>
<blockquote>
<blockquote><p>将一36位数字电话号码(x)存放在一指定电话号码表中(n), 作以后拨号用(参见命令ATDS=n)</p></blockquote>
</blockquote>
</blockquote>
<p><dir><dir></p>
<blockquote><p>AT\An 最大MNP块的大小缺省:2<br />
AT\A0 设最大块为64个字符<br />
AT\A1 设最大块为128个字符<br />
AT\A2 设最大块为192个字符<br />
AT\A3 设最大块为256个字符</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT\Bn     发送中断信号(n=1-9)        缺省:3</strong></p>
<blockquote>
<blockquote><p>当在非MNP连接期间输入此命令,调制解调器向远端调制解器发送一中断信号,中断信号长度参数为n值的100倍（以毫秒            为单位）,在MNP模式下,输入此命令,调制解调器向远端调制解调器发送一链路注意码PDU</p></blockquote>
</blockquote>
</blockquote>
<blockquote><p><strong>AT\Gn     调制解调器到调制解调器的流控制    缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT\G0 关闭流控(XON/XOFF)<br />
AT\G1 打开流控(XON/XOFF)<br />
 </p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT\Jn     DTE速率自动调整控制            缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT\J0 关闭匹配线路速率的DTE速率调整功能<br />
AT\J1 打开匹配线路速率的DTE速率调整功能<br />
 </p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT\Kn     中断控制                     缺省:5</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>在数据传输期间收到来自DTE的中断信号时,调制解调器作出如下响应AT\K0,2,4 调制解调器进入连机命令状态,而不向远端发送中断信号<br />
AT\K1 调制解调器清空终端的缓冲器并向远端调制解调器发送中断信号<br />
AT\K3 调制解调器不清空终端的缓冲器,但向远端调制解调器发送中断信号<br />
AT\K5 调制解调器随发送的数据发送中断信号. 调制解调器在连机命令状态时数据传输过程中，做如下操作<br />
AT\K0,1 调制解调器清空终端的缓冲器，并向远端调制解调器发送中断信号<br />
AT\K2,3 调制解调器不清空缓冲器，但向远端调制解调器发送中断信号<br />
AT\K4,5 调制解调器随传输的数据按顺序发送中断信号 在非纠错模式下收到来自DTE的中断信号时,调制解调器做如下操作<br />
AT\K0,1 调制解调器清除终端的缓冲器,并向本地DTE发送中断信号<br />
AT\K2,3 调制解调器不清除缓冲器,但向本地DTE发送中断信号<br />
AT\K4,5 调制解调器随接收的数据按顺序发送中断信号<br />
 <br />
 </p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT\Ln     MNP块传输控制                 缺省:0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT\L0 对于MNP链路连接使用流模式<br />
AT\L1 对于MNP链路连接使用块模式<br />
 </p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT\Nn     操作模式控制                 缺省:3</strong></p>
<blockquote>
<blockquote><p>AT\N0 选择标准速度缓存模式(无纠错)<br />
AT\N1 选择直接模式(等效于&amp;M0,&amp;Q0)<br />
AT\N2 选择可靠模式,可靠连接失败会使调制解调器挂机<br />
AT\N3 选择自动可靠模式<br />
AT\N4 选择LAPM纠错模式,LAPM纠错连接失败会使调制解调器挂机<br />
AT\N5 选择MNP纠错模式,MNP纠错连接失败会使调制解调器挂机</p></blockquote>
</blockquote>
</blockquote>
<blockquote><p><strong>AT\Vn     单线连接信息                 缺省：0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT\V0 关闭单线连接信息。<br />
AT\V1 打开单线连接信息。</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT％C*    压缩控制                    缺省: 3</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT%C0 关闭数据压缩 AT%C1 打开MNP5数据压缩<br />
AT%C2 打开V.42bis数据压缩<br />
AT%C3 打开MNP5和V.42bis数据压缩</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT％En    开/关自动均衡                缺省：2</strong><br />
 </p>
<blockquote>
<blockquote><p>控制是使调制解调器自动监听线路质量并请求均衡(％E1)还是当线路质量不好时降速，线路质量好时升速。</p></blockquote>
</blockquote>
</blockquote>
<p><dir><dir></p>
<blockquote><p>AT%E0 关闭线路质量监听和自动均衡。<br />
AT%E1 打开线路质量监听和自动均衡。<br />
AT%E2 打开线路质量监听和速率自动调整上升或下降。<br />
AT%E3 打开线路质量监听和采用快速挂机的自动均衡。</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT％L     报告接收灵敏度</strong></p>
<blockquote>
<blockquote><p>返回接收信号的电平值,提供以下数值</p></blockquote>
</blockquote>
</blockquote>
<p><dir><dir></p>
<blockquote><p>001=-1dBm接收电平<br />
002=-2dBm接收电平<br />
: :<br />
043=-43dBm接收电平</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT%On     选择应答或呼叫模式             缺省：1</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT%O0 选择应答式模<br />
AT%O1 选择发起式模</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT%Rn     选择接收灵敏度 (</strong>适用於专线型号)<strong> 缺省：0</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>AT%R0 -43dBm<br />
AT%R1 -33dBm<br />
<strong>备选：</strong>适用於拔号线型号,JP2跳线：-33dBM 连接1-2 针；-43 连接2-3针</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT％Q     显示线路信号质量</strong></p></blockquote>
<p><dir><dir></p>
<blockquote><p>返回眼图指标(EQM)值的高字节,该字节的表示范围为0到127,当这一数值为70DC±10(依赖于线路速率)或更大时,若已使用了AT％E1命令则调制解调器将自动均衡,标准连接时这一数在0到15之间。到60时则为较差连接。</p></blockquote>
<p></dir></dir></p>
<blockquote><p><strong>AT#CIDn   呼叫者身份鉴定                 缺省：0</strong></p>
<blockquote>
<blockquote><p>AT#CID=0关闭呼叫者身份鉴定<br />
AT#CID=1打开DTE格式化形式的呼叫者身份鉴定<br />
AT#CID=2打开DTE非格式化形式的呼叫者身份鉴定<br />
AT#CID? 从调制解调器中恢复当前呼叫者身份鉴定方式<br />
AT#CID=? 返回调制解调器允许模式的列表,表中各部分间用逗号隔开</p></blockquote>
</blockquote>
<p><strong>AT-SDR=n  鉴别性振铃                    缺省：0</strong></p>
<blockquote>
<blockquote><p>AT-SDR=0 允许任何振铃、并报告&#8221;RING&#8221;<br />
AT-SDR=1 允许一类型振铃<br />
AT-SDR=2 允许二类型振铃<br />
AT-SDR=3 允许一及二类型振铃<br />
AT-SDR=4 允许三类型振铃<br />
AT-SDR=5 允许一及三类型振铃<br />
AT-SDR=6 允许二及三类型振铃<br />
AT-SDR=7 允许一、二及三类型振铃</p></blockquote>
</blockquote>
</blockquote>
<table border="1" width="556" cellPadding="9">
<tr>
<td width="22%" vAlign="top"><center>振铃类型 </center></td>
<td width="78%" vAlign="top"><center>振铃时段模式 </center></td>
</tr>
<tr>
<td width="22%" vAlign="top"><center>1 </center></td>
<td width="78%" vAlign="top">响2秒、停4秒</td>
</tr>
<tr>
<td height="21" width="22%" vAlign="top"><center>2 </center></td>
<td height="21" width="78%" vAlign="top">响0.8秒、停0.4秒、响0.8秒、停4秒</td>
</tr>
<tr>
<td height="20" width="22%" vAlign="top"><center>3 </center></td>
<td height="20" width="78%" vAlign="top">响0.4秒、停0.2秒、响0.4秒、停0.2秒、响0.8秒、停4秒</td>
</tr>
</table>
<blockquote><p><strong>AT+MS*     选择线路调制方式</strong></p>
<blockquote>
<blockquote><p>命令格式为（336型号）:<br />
AT+MS=&lt;模式&gt;,&lt;自动模式&gt;,&lt;最小速率&gt;,&lt;最大速率&gt;<br />
缺省值为 AT+MS=11,1,300,33600 （336型号）命令格式为（560型号）:<br />
AT+MS=&lt;模式&gt;,&lt;自动模式&gt;,&lt;最小速率&gt;,&lt;最大速率&gt;,<br />
&lt;x_law&gt;,&lt;rb_signal&gt;,&lt;maxup_rate&gt;<br />
缺省值为 AT+MS=12,1,300,56000,33600 （560型号）</p>
<p><strong>AT+MS?  </strong>向包含所选选项的DTE发送一信息流</p>
<p><strong>AT+MS=?</strong> 向包含所提供选项的DTE发送一信息流<br />
 <br />
 </p></blockquote>
</blockquote>
</blockquote>
<table border="1" width="100%" cellPadding="9">
<tr>
<td width="22%" vAlign="top"><center>自动模式 </center></td>
<td width="78%" vAlign="top"><center>选 项 </center></td>
</tr>
<tr>
<td width="22%" vAlign="top"><center>0 </center></td>
<td width="78%" vAlign="top"><center>关闭自动模式 </center></td>
</tr>
<tr>
<td height="21" width="22%" vAlign="top"><center>1 </center></td>
<td height="21" width="78%" vAlign="top"><center>打开自动模式 </center></td>
</tr>
</table>
<blockquote>
<blockquote>
<blockquote></blockquote>
</blockquote>
</blockquote>
<table border="1" width="100%" cellPadding="9">
<tr>
<td height="42" width="7%" vAlign="top"><center>模式 </center></td>
<td height="42" width="16%" vAlign="top">  调制方式选择</td>
<td height="42" width="77%" vAlign="top"><center>可能 波特率(bps) &lt;最小 波特率&gt; &lt;最大 波特率&gt; </center></td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>0 </center></td>
<td width="16%" vAlign="top">V.21</td>
<td width="77%" vAlign="top">300</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>1 </center></td>
<td width="16%" vAlign="top">V.22</td>
<td width="77%" vAlign="top">1200</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>2 </center></td>
<td width="16%" vAlign="top">V.22bis</td>
<td width="77%" vAlign="top">2400或1200</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>3 </center></td>
<td width="16%" vAlign="top">V.23</td>
<td width="77%" vAlign="top">1200</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>9 </center></td>
<td width="16%" vAlign="top">V.32</td>
<td width="77%" vAlign="top">9600或4800</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>10 </center></td>
<td width="16%" vAlign="top">V.32bis</td>
<td width="77%" vAlign="top">14400,12000,9600,7200 或4800</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>11 </center></td>
<td width="16%" vAlign="top">V.34</td>
<td width="77%" vAlign="top">33600,31200,28800,26400,24000,21600,19200, 16800,14400,12000, <br />
9600,7200,4800或2400</td>
</tr>
<tr>
<td height="83" width="7%" vAlign="top"><center>12 </center></td>
<td height="83" width="16%" vAlign="top">V.90</td>
<td height="83" width="77%" vAlign="top">56000,54667,53333,52000,50667,49333,48000,46667,45333,42667, <br />
41333,40000,38667,37333,36000,34667,33333,32000,30667,29333, <br />
28000 (560型号适用)</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>56 </center></td>
<td width="16%" vAlign="top">K56flex</td>
<td width="77%" vAlign="top">56000,54000,52000,50000,48000,46000,44000,42000,40000,38000, <br />
36000,34000,32000 (560型号适用)</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>64 </center></td>
<td width="16%" vAlign="top">Bell 103</td>
<td width="77%" vAlign="top">300</td>
</tr>
<tr>
<td width="7%" vAlign="top"><center>69 </center></td>
<td width="16%" vAlign="top">Bell 212</td>
<td width="77%" vAlign="top">1200</td>
</tr>
</table>
<blockquote>
<blockquote><p>&lt;x_law&gt; 是一个可选的数字，用来确定码类型，选择是：</p>
<blockquote><p>0 = u-Law 1 = A-Law注意：ATZ命令将复位&lt;x_law&gt;值为0 (u-Law)。<br />
 </p>
<p>&lt;rb_signaling&gt; 是一个可选的数字，用于配置一个发送数据的调制解调器产生“丢失位”信号或不产生“丢               失位”信号；或配置一台接收数据的调制解调器检测“丢失位”信号或不检测“丢失位”信               号。选择是：</p></blockquote>
</blockquote>
</blockquote>
<p><dir></p>
<blockquote>
<blockquote><p>0 = 发送数据的调制解调器产生丢失位信号。接收数据的调制解调器检测丢失位信号。1= 发送数据的调制解调器不产生丢失位信号。接收数据的调制解调器不检测丢失位信号。</p>
<p>注意：ATZ命令将复位&lt;rb_signaling&gt;值为0。<br />
 </p></blockquote>
</blockquote>
<p></dir></p>
<blockquote>
<blockquote><p>Maxup_rate : 连接速率的最大值。</p></blockquote>
</blockquote>
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1535176</p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/387.html" title="[转]嵌入式系统 Boot Loader 技术内幕 (2008-01-14)">[转]嵌入式系统 Boot Loader 技术内幕</a> (0)</li>
	<li><a href="http://www.donevii.com/post/290.html" title="PHP5 效率优化 (2007-01-13)">PHP5 效率优化</a> (0)</li>
	<li><a href="http://www.donevii.com/post/758.html" title="清除指定squid缓存文件的脚本 (2009-02-09)">清除指定squid缓存文件的脚本</a> (0)</li>
	<li><a href="http://www.donevii.com/post/73.html" title="常用的HR相关英文词汇 (2006-10-10)">常用的HR相关英文词汇</a> (1)</li>
	<li><a href="http://www.donevii.com/post/221.html" title="什么是bootloader程序及其功能和特点 (2006-11-22)">什么是bootloader程序及其功能和特点</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/389.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转]内存种类知多少</title>
		<link>http://www.donevii.com/post/388.html</link>
		<comments>http://www.donevii.com/post/388.html#comments</comments>
		<pubDate>Mon, 14 Jan 2008 08:39:12 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[doc]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[mtk]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[RAM]]></category>
		<category><![CDATA[SDRAM]]></category>
		<category><![CDATA[介绍]]></category>
		<category><![CDATA[历史]]></category>
		<category><![CDATA[图片]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>
		<category><![CDATA[芯片]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/388.html</guid>
		<description><![CDATA[终于知道为什么SDRAM都被CPU当做Internal RAM来用。 凡是对电脑有所了解的朋友都知道内存这玩意，可是，可能有不少朋友对内存的认识仅仅局限在SDRAM和DDR SDRAM这两种类型，事实上，内存的种类是... ]]></description>
			<content:encoded><![CDATA[<pre>    <em>终于知道为什么SDRAM都被CPU当做Internal RAM来用。</em></pre>
<pre>    凡是对电脑有所了解的朋友都知道内存这玩意，可是，可能有不少朋友对内存的认识仅仅局限在SDRAM和DDR SDRAM这两种类型，事实上，内存的种类是非常多的，从能否写入的角度来分，就可以分为RAM(随机存取存储器)和ROM(只读存储器)这两大类。每一类别里面有分别有许多种类的内存。以下就让我们看看内存到底有些什么种类吧！</pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_01.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　一、RAM(Random Access Memory，随机存取存储器)</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_02.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　RAM的特点是：电脑开机时，操作系统和应用程序的所有正在运行的数据和程序都会放置其中，并且随时可以对存放在里面的数据进行修改和存取。它的工作需要由持续的电力提供，一旦系统断电，存放在里面的所有数据和程序都会自动清空掉，并且再也无法恢复。</pre>
<pre>
 </pre>
<pre>　　根据组成元件的不同，RAM内存又分为以下十八种：</pre>
<pre>
 </pre>
<pre>　　01.DRAM（Dynamic RAM，动态随机存取存储器）：</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_03.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　这是最普通的RAM，一个电子管与一个电容器组成一个位存储单元，DRAM将每个内存位作为一个电荷保存在位存储单元中，用电容的充放电来做储存动作，但因电容本身有漏电问题，因此必须每几微秒就要刷新一次，否则数据会丢失。存取时间和放电时间一致，约为2~4ms。因为成本比较便宜，通常都用作计算机内的主存储器。</pre>
<pre>
 </pre>
<pre>　　02.SRAM（Static RAM，静态随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_04.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　静态，指的是内存里面的数据可以长驻其中而不需要随时进行存取。每6颗电子管组成一个位存储单元，因为没有电容器，因此无须不断充电即可正常运作，因此它可以比一般的动态随机处理内存处理速度更快更稳定，往往用来做高速缓存。</pre>
<pre>
 </pre>
<pre>　　03.VRAM（Video RAM，视频内存）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_05.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　它的主要功能是将显卡的视频数据输出到数模转换器中，有效降低绘图显示芯片的工作负担。它采用双数据口设计，其中一个数据口是并行式的数据输出入口，另一个是串行式的数据输出口。多用于高级显卡中的高档内存。</pre>
<pre>
 </pre>
<pre>　　04.FPM DRAM（Fast Page Mode DRAM，快速页切换模式动态随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_06.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　改良版的DRAM，大多数为72Pin或30Pin的模块。传统的DRAM在存取一个BIT的数据时，必须送出行地址和列地址各一次才能读写数据。而FRM DRAM在触发了行地址后，如果CPU需要的地址在同一行内，则可以连续输出列地址而不必再输出行地址了。由于一般的程序和数据在内存中排列的地址是连续的，这种情况下输出行地址后连续输出列地址就可以得到所需要的数据。FPM将记忆体内部隔成许多页数Pages，从512B到数KB不等，在读取一连续区域内的数据时，就可以通过快速页切换模式来直接读取各page内的资料，从而大大提高读取速度。在96年以前，在486时代和PENTIUM时代的初期，FPM DRAM被大量使用。</pre>
<pre>
 </pre>
<pre>　　05.EDO DRAM（Extended Data Out DRAM，延伸数据输出动态随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_07.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　这是继FPM之后出现的一种存储器，一般为72Pin、168Pin的模块。它不需要像FPM DRAM那样在存取每一BIT 数据时必须输出行地址和列地址并使其稳定一段时间，然后才能读写有效的数据，而下一个BIT的地址必须等待这次读写操作完成才能输出。因此它可以大大缩短等待输出地址的时间，其存取速度一般比FPM模式快15%左右。它一般应用于中档以下的Pentium主板标准内存，后期的486系统开始支持EDO DRAM，到96年后期，EDO DRAM开始执行。。</pre>
<pre>
 </pre>
<pre>　　06.BEDO DRAM（Burst Extended Data Out DRAM，爆发式延伸数据输出动态随机存取存储器）</pre>
<pre>
 </pre>
<pre>　　这是改良型的EDO DRAM，是由美光公司提出的，它在芯片上增加了一个地址计数器来追踪下一个地址。它是突发式的读取方式，也就是当一个数据地址被送出后，剩下的三个数据每一个都只需要一个周期就能读取，因此一次可以存取多组数据，速度比EDO DRAM快。但支持BEDO DRAM内存的主板可谓少之又少，只有极少几款提供支持（如VIA APOLLO VP2），因此很快就被DRAM取代了。</pre>
<pre>
 </pre>
<pre>　　07.MDRAM（Multi-Bank DRAM，多插槽动态随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_09.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　MoSys公司提出的一种内存规格，其内部分成数个类别不同的小储存库 (BANK)，也即由数个属立的小单位矩阵所构成，每个储存库之间以高于外部的资料速度相互连接，一般应用于高速显示卡或加速卡中，也有少数主机板用于L2高速缓存中。</pre>
<pre>
 </pre>
<pre>　　08.WRAM（Window RAM，窗口随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_10.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　韩国Samsung公司开发的内存模式，是VRAM内存的改良版，不同之处是它的控制线路有一、二十组的输入/输出控制器，并采用EDO的资料存取模式,因此速度相对较快，另外还提供了区块搬移功能（BitBlt），可应用于专业绘图工作上。</pre>
<pre>
 </pre>
<pre>　　09.RDRAM（Rambus DRAM，高频动态随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_11.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　Rambus公司独立设计完成的一种内存模式，速度一般可以达到500~530MB/s，是DRAM的10倍以上。但使用该内存后内存控制器需要作相当大的改变，因此它们一般应用于专业的图形加速适配卡或者电视游戏机的视频内存中。</pre>
<pre>
 </pre>
<pre>　　10.SDRAM（Synchronous DRAM，同步动态随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_12.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　这是一种与CPU实现外频Clock同步的内存模式，一般都采用168Pin的内存模组，工作电压为3.3V。 所谓clock同步是指内存能够与CPU同步存取资料，这样可以取消等待周期，减少数据传输的延迟，因此可提升计算机的性能和效率。</pre>
<pre>
 </pre>
<pre>　　11.SGRAM（Synchronous Graphics RAM，同步绘图随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_13.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　SDRAM的改良版，它以区块Block，即每32bit为基本存取单位，个别地取回或修改存取的资料，减少内存整体读写的次数，另外还针对绘图需要而增加了绘图控制器，并提供区块搬移功能（BitBlt），效率明显高于SDRAM。</pre>
<pre>
 </pre>
<pre>　　12.SB SRAM（Synchronous Burst SRAM，同步爆发式静态随机存取存储器）</pre>
<pre>
 </pre>
<pre>　　一般的SRAM是非同步的，为了适应CPU越来越快的速度，需要使它的工作时脉变得与系统同步，这就是SB SRAM产生的原因。</pre>
<pre>
 </pre>
<pre>　　13.PB SRAM（Pipeline Burst SRAM，管线爆发式静态随机存取存储器）</pre>
<pre>
 </pre>
<pre>　　CPU外频速度的迅猛提升对与其相搭配的内存提出了更高的要求，管线爆发式SRAM取代同步爆发式SRAM成为必然的选择，因为它可以有效地延长存取时脉，从而有效提高访问速度。</pre>
<pre>
 </pre>
<pre>　　14.DDR SDRAM（Double Data Rate二倍速率同步动态随机存取存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_16.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　作为SDRAM的换代产品，它具有两大特点：其一，速度比SDRAM有一倍的提高；其二，采用了DLL（Delay Locked Loop：延时锁定回路）提供一个数据滤波信号。这是目前内存市场上的主流模式。</pre>
<pre>
 </pre>
<pre>　　15.SLDRAM （Synchronize Link，同步链环动态随机存取存储器）</pre>
<pre>
 </pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_17.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre> </pre>
<pre>
 </pre>
<pre>　　这是一种扩展型SDRAM结构内存，在增加了更先进同步电路的同时，还改进了逻辑控制电路，不过由于技术显示，投入实用的难度不小。</pre>
<pre>
 </pre>
<pre>　　16.CDRAM（CACHED DRAM，同步缓存动态随机存取存储器）</pre>
<pre>
 </pre>
<pre>　　这是三菱电气公司首先研制的专利技术，它是在DRAM芯片的外部插针和内部DRAM之间插入一个SRAM作为二级<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">CACHE</a>使用。当前，几乎所有的CPU都装有一级<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">CACHE</a>来提高效率，随着CPU时钟频率的成倍提高，<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">CACHE</a>不被选中对系统性能产生的影响将会越来越大，而<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">CACHE</a> DRAM所提供的二级<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">CACHE</a>正好用以补充CPU一级<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">CACHE</a>之不足，因此能极大地提高CPU效率。</pre>
<pre>
 </pre>
<pre>　　17.DDRII (Double Data Rate Synchronous DRAM，第二代同步双倍速率动态随机存取存储器)</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_19.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　DDRII 是DDR原有的SLDRAM联盟于1999年解散后将既有的研发成果与DDR整合之后的未来新标准。DDRII的详细规格目前尚未确定。</pre>
<pre>
 </pre>
<pre>　　18.DRDRAM (Direct Rambus DRAM)</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_20.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　是下一代的主流内存标准之一，由Rambus 公司所设计发展出来，是将所有的接脚都连结到一个共同的Bus，这样不但可以减少控制器的体积，已可以增加资料传送的效率。</pre>
<pre>
 </pre>
<pre>　　二、ROM(READ Only Memory，只读存储器)</pre>
<pre>
 </pre>
<pre>　　ROM是线路最简单半导体电路，通过掩模工艺，一次性制造，在元件正常工作的情况下，其中的代码与数据将永久保存，并且不能够进行修改。一般应用于PC系统的程序码、主机板上的 BIOS (基本输入/输出系统Basic Input/Output System)等。它的读取速度比RAM慢很多。</pre>
<pre>
 </pre>
<pre>　　根据组成元件的不同，ROM内存又分为以下五种：</pre>
<pre>
 </pre>
<pre>　　1.MASK ROM（掩模型只读存储器）</pre>
<pre>
 </pre>
<pre>　　制造商为了大量生产ROM内存，需要先制作一颗有原始数据的ROM或EPROM作为样本，然后再大量复制，这一样本就是MASK ROM，而烧录在MASK ROM中的资料永远无法做修改。它的成本比较低。</pre>
<pre>
 </pre>
<pre>　　2.PROM（Programmable ROM，可编程只读存储器）</pre>
<pre>
 </pre>
<pre>　　这是一种可以用刻录机将资料写入的ROM内存，但只能写入一次，所以也被称为“一次可编程只读存储器”(One Time Progarmming ROM，OTP-ROM)。PROM在出厂时，存储的内容全为1，用户可以根据需要将其中的某些单元写入数据0(部分的PROM在出厂时数据全为0，则用户可以将其中的部分单元写入1)， 以实现对其“编程”的目的。</pre>
<pre>
 </pre>
<pre>　　3.EPROM（Erasable Programmable，可擦可编程只读存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_24.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　这是一种具有可擦除功能，擦除后即可进行再编程的ROM内存，写入前必须先把里面的内容用紫外线照射它的IC卡上的透明视窗的方式来清除掉。这一类芯片比较容易识别，其封装中包含有“石英玻璃窗”，一个编程后的EPROM芯片的“石英玻璃窗”一般使用黑色不干胶纸盖住， 以防止遭到阳光直射。</pre>
<pre>
 </pre>
<pre>　　4.EEPROM（Electrically Erasable Programmable，电可擦可编程只读存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_25.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　功能与使用方式与EPROM一样，不同之处是清除数据的方式，它是以约20V的电压来进行清除的。另外它还可以用电信号进行数据写入。这类ROM内存多应用于即插即用（PnP）接口中。</pre>
<pre>
 </pre>
<pre>　　5.Flash Memory（快闪存储器）</pre>
<pre>
 </pre>
<p><center></p>
<pre><img src="http://www.ithard.com/pics/newspic/2004/07/21/040719_hd_kr_memory_26.jpg" /></pre>
<p></center></p>
<pre>
 </pre>
<pre>　　这是一种可以直接在主机板上修改内容而不需要将IC拔下的内存，当电源关掉后储存在里面的资料并不会流失掉，在写入资料时必须先将原本的资料清除掉，然后才能再写入新的资料，缺点为写入资料的速度太慢。</pre>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/504.html" title="google背后的分布式架构 (2008-09-23)">google背后的分布式架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/128.html" title="应用加速 (2006-10-26)">应用加速</a> (0)</li>
	<li><a href="http://www.donevii.com/post/129.html" title="大型高并发高负载网站的系统架构 (2006-10-26)">大型高并发高负载网站的系统架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/387.html" title="[转]嵌入式系统 Boot Loader 技术内幕 (2008-01-14)">[转]嵌入式系统 Boot Loader 技术内幕</a> (0)</li>
	<li><a href="http://www.donevii.com/post/9.html" title="flickr对javascript干的好事 (2006-08-17)">flickr对javascript干的好事</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/388.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Shell编程 &#8211; 傻瓜教程2</title>
		<link>http://www.donevii.com/post/315.html</link>
		<comments>http://www.donevii.com/post/315.html#comments</comments>
		<pubDate>Sat, 24 Mar 2007 13:41:29 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[doc]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[windows]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/?p=315</guid>
		<description><![CDATA[&#160;本文例子代码下载(19K)(codeproject.com) &#160;&#160;&#160; 在这篇指南的第一部分，我给了大家一个编写外壳扩展的介绍，并且演示了一个一次操作一个简单文件的上下文菜单扩展。在第二部分中... ]]></description>
			<content:encoded><![CDATA[<p>&nbsp;<a href="http://263.csdn.net/FileBBS/files/2001_9/T_618_1.zip">本文例子代码下载(19K)(codeproject.com)</a>
<p>&nbsp;&nbsp;&nbsp; 在这篇指南的<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>，我给了大家一个编写外壳扩展的介绍，并且演示了一个一次操作一个简单文件的上下文菜单扩展。在第二部分中，我将要演示如何在一个操作中操作多个文件。这个扩展是一个注册和卸载COM服务器的实用工具。它还演示了如何使用ATL对话框类<code>CDialogImpl</code>。我将通过解释一些特殊的注册表键来讲第二部分，通过这些键你可以使你的扩展被<em>任何</em>文件调用，而不是那些预先选择的类型(.TXT)。</p>
<p>&nbsp;&nbsp;&nbsp; 第二部分假设你已经阅读了<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>，所以你知道了上下文菜单扩展的基本知识。除此之外，你还必须理解COM，ATL和STL的基本知识。</p>
<h2>上下文菜单扩展的开始-它能做什么？</h2>
<p>&nbsp;&nbsp;&nbsp; 这个外壳扩展将使你注册和卸载那些在EXE,DLL和OCX里的COM服务器。不像我们在<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>所做的那样，这个扩展将对所有你右键点击事件发生时所有选中的文件进行操作。</p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">使用</span><span lang="EN-US">AppWizard </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">开始</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">运行</span><span lang="EN-US">AppWizard </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，做一个新的</span><span lang="EN-US">ATL COM wizard app</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。我们叫它</span><code>DllReg</code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。在向导中保持所有默认选项，单击完成。我们现在就有了一个空的将会生成</span><span lang="EN-US">DLL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">项目，但是我们必须添加自己的外壳扩展</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">对象。在</span><span lang="EN-US">ClassView</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">树中，右键单击</span> <code>DllReg classes</code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">项，选择</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">New ATL Object</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在</span><span lang="EN-US">ATL Object </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">向导，第一面板已经选择了</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">Simple Object</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，只要单击下一步就行了。在第二面板中，在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">Short Name</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">编辑控件中输入</span><code>DllRegShlExt</code> <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，然后单击确定（面板中的其它的编辑框将会自动完成）。这就创建了一个类名为</span><code>CDllRegShlExt</code> <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的类，它包含了实现一个</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">对象的基本代码。我们将向这个类添加我们的代码。</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">初始化接口</span></h2>
<p>&nbsp;&nbsp;&nbsp; 在这个扩展中，我们的<code>IShellExtInit::Initialize()</code>实现将会相当不同。有两个原因，第一我们将列举所有选中的文件；第二，我们将测试我们选中的文件是否有注册(<a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">register</a>)和卸载(unregister)函数的出口。我们将仅仅考虑那些同时有<code>DllRegisterServer()</code>和<code>DllUnregisterServer()</code>出口的文件。其他的都将被忽略掉。</p>
<p>&nbsp;&nbsp;&nbsp; 我们将使用列表控件和STL的字符串与列表类，所以你必须首先在<code>stdafx.h</code> 文件中添加如下行：</p>
<pre>#include &lt;commctrl.h&gt;#include &lt;string&gt;#include &lt;list&gt;#include &lt;atlwin.h&gt;typedef std::list&lt;std::basic_string&lt;TCHAR&gt; &gt; string_list;</pre>
<p>&nbsp;&nbsp;&nbsp; 我们的<code>CDllRegShlExt</code>类也将需要一些成员变量：</p>
<pre>protected:    HBITMAP     m_hRegBmp;    HBITMAP     m_hUnregBmp;    string_list m_lsFiles;    TCHAR       m_szDir[MAX_PATH];</pre>
<p><code>&nbsp;&nbsp;&nbsp;</code> <code>CDllRegShlExt</code>的构造函数中将加载两幅上下文菜单中使用的位图：</p>
<pre>CDLLRegShlExt::CDLLRegShlExt(){    m_hRegBmp = LoadBitmap ( _Module.GetModuleInstance(),                             MAKEINTRESOURCE(IDB_REGISTERBMP) );    m_hUnregBmp = LoadBitmap ( _Module.GetModuleInstance(),                               MAKEINTRESOURCE(IDB_UNREGISTERBMP) );}</pre>
<p>&nbsp;&nbsp;&nbsp; 在你将<code>IShellExtInit</code>添加到了被<code>CDllRegShlExt</code>实现的接口列表中之后(关于这个操作请参见<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>)，我们开始写<code>Initialize()</code>函数。</p>
<p><code>&nbsp; Initialize()</code>将执行如下步骤：</p>
<ol>
<li>改变当前目录为在资源浏览器窗口中正在被查看的目录； </li>
<li>列举所有被选中的文件； </li>
<li>对每一个文件，试图使用<code>LoadLibrary()</code>加载它； </li>
<li>如果<code>LoadLibrary()</code>成功，看该文件是否有<code>DllRegisterServer()</code>和<code>DllUnregisterServer()</code>的函数出口； </li>
<li>如果两个出口都被找到了，添加该文件的的文件名到我们能操作的文件列表中，<code>m_lsFiles</code>。 </li>
</ol>
<pre>HRESULT CDllRegShlExt::Initialize (     LPCITEMIDLIST pidlFolder,    LPDATAOBJECT pDataObj,    HKEY hProgID ){TCHAR     szFile    [MAX_PATH];TCHAR     szFolder  [MAX_PATH];TCHAR     szCurrDir [MAX_PATH];TCHAR*    pszLastBackslash;UINT      uNumFiles;HDROP     hdrop;FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };STGMEDIUM stg = { TYMED_HGLOBAL };HINSTANCE hinst;bool      bChangedDir = false;HRESULT (STDAPICALLTYPE* pfn)();</pre>
<p>&nbsp;&nbsp;&nbsp; 非常多的局部变量！第一步是从传进去的<code>pDataObj</code>参数中获得一个HDROP。这和<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>的扩展有些相似。</p>
<pre>    // Read the list of folders from the data object.  They're stored in HDROP    // format, so just get the HDROP handle and then use the drag 'n' drop APIs    // on it.    if ( FAILED( pDO-&gt;GetData ( &amp;etc, &amp;stg )))        return E_INVALIDARG;    // Get an HDROP handle.    hdrop = (HDROP) GlobalLock ( stg.hGlobal );    if ( NULL == hdrop )        {        ReleaseStgMedium ( &amp;stg );        return E_INVALIDARG;        }    // Determine how many files are involved in this operation.    uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 );</pre>
<p>&nbsp;&nbsp;&nbsp; 接下来要做的就是一个获取下一个文件名的for循环(使用<code>DragQueryFile()</code>)并试图用<code>LoadLibrary()</code>装载它。在该文的实际例子工程当中预先做了一些目录改变的工作。在这儿我把它忽略了，因为它有点儿太长了。</p>
<pre>    for ( UINT uFile = 0; uFile &lt; uNumFiles; uFile++ )        {        // Get the next filename.        if ( 0 == DragQueryFile ( hdrop, uFile, szFile, MAX_PATH ))            continue;        // Try &amp; load the DLL.        hinst = LoadLibrary ( szFile );                if ( NULL == hinst )            continue;</pre>
<p>&nbsp;&nbsp;&nbsp; 下一步，我们将看看它是否有两个必要函数的出口。</p>
<pre>        // Get the address of DllRegisterServer();        (FARPROC&amp;) pfn = GetProcAddress ( hinst, &quot;DllRegisterServer&quot; );        // If it wasn't found, skip the file.        if ( NULL == pfn )            {            FreeLibrary ( hinst );            continue;            }        // Get the address of DllUnregisterServer();        (FARPROC&amp;) pfn = GetProcAddress ( hinst, &quot;DllUnregisterServer&quot; );        // If it was found, we can operate on the file, so add it to        // our list of files (m_lsFiles).        if ( NULL != pfn )            {            m_lsFiles.push_back ( szFile );            }        FreeLibrary ( hinst );        }   // end for</pre>
<p>&nbsp;&nbsp;&nbsp; 最后一步就是(在最后一个if块中)添加文件名到<code>m_lsFiles</code>中。<code>m_lsFiles</code>是一个保存字符串的STL列表集。这个列表在稍后，就是当我们遍历所有文件注册和卸载的时候将被用到。</p>
<p>&nbsp;&nbsp;&nbsp; 在<code>Initialize()</code>的最后要做的就是释放资源并返回恰当的值给Explorer。</p>
<pre>    // Release resources.    GlobalUnlock ( stg.hGlobal );    ReleaseStgMedium ( &amp;stg );    // If we found any files we can work with, return S_OK.  Otherwise,    // return E_INVALIDARG so we don't get called again for this right-click    // operation.    return ( m_lsFiles.size() &gt; 0 ) ? S_OK : E_INVALIDARG;}</pre>
<p>&nbsp;&nbsp;&nbsp; 如果你看一眼例子项目代码的话，你会发现我不得不都过查看文件的文件名来计算出哪个目录正在被查看。你也许会纳闷为什么我不用<code>pidlFolder</code>参数。虽然在<code>pidlFolder</code>的文档中，它被解释为&ldquo;包含正在显示上下文菜单的项目的文件夹的项目标志列表(the item identifier list for the folder that contains the item whose context menu is being displayed)(很抱歉我不能把这句话翻译好，贴出原文供参考)&rdquo;。可是，我在Windows 98 上测试的时候，这个参数总是为NULL，所以它毫无用处。</p>
<h2>添加我们的菜单项</h2>
<p>&nbsp;&nbsp;&nbsp; 接下来的是<code>IContextMenu</code>方法。和以前一样，你需要添加<code>IContextMenu</code>到<code>CDllRegShlExt</code>实现的接口列表中。再一次的，做这些的步骤在<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>中。</p>
<p>&nbsp;&nbsp;&nbsp; 我们将添加两个菜单项到菜单中，一个为注册选中的文件，一个是卸载它们。这些项看起来如下：</p>
<p><img height="72" src="http://www.codeproject.com/shell/ShellExtGuide2/ShellExtGuide2_1.jpg" width="233" border="0" alt="" /></p>
<p>&nbsp;&nbsp;&nbsp; 我们的<code>QueryContextMenu()</code>实现和<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>一样开始。我们检查<code>uFlags</code>，如果<code>CMF_DEFAULTONLY</code>标志存在的话立即返回。&nbsp;</p>
<pre>HRESULT CDLLRegShlExt::QueryContextMenu (    HMENU hmenu,    UINT  uMenuIndex,    UINT  uidFirstCmd,    UINT  uidLastCmd,    UINT  uFlags ){UINT uCmdID = uidFirstCmd;    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.    if ( uFlags &amp; CMF_DEFAULTONLY )        {        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );        }</pre>
<p>&nbsp;&nbsp;&nbsp; 下面，我们添加&quot;<a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">Register</a> servers&quot;菜单项。这儿有些新东西：我们给菜单项设置了位图。这和WinZip的菜单有点儿相似，也在菜单命令旁边显示一个小图标。</p>
<pre>    // Add our <a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">register</a>/unregister items.    InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uCmdID++,                 _T(&quot;<a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">Register</a> server(s)&quot;) );    // Set the bitmap for the <a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">register</a> item.    if ( NULL != m_hRegBmp )        {        SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, m_hRegBmp, NULL );        }    uMenuIndex++;</pre>
<p>&nbsp;&nbsp;&nbsp; <code>SetMenuItemBitmaps()</code>API函数我们宰菜单旁显示一个小齿轮的方法。注意<code>uCmdID</code>是自增加的，这样下一次我们调用<code>InsertMenu()</code>，它的命令ID将会比前一个大1。在这步骤的最后，<code>uMenuIndex</code> 被增加是因为我们的第二个菜单项将被显示在第一个之后。</p>
<p>&nbsp;&nbsp;&nbsp; 接着说第二个菜单项。它和第一个非常相似。</p>
<pre>    InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uCmdID++,                 _T(&quot;Unregister server(s)&quot;) );    // Set the bitmap for the unregister item.    if ( NULL != m_hUnregBmp )        {        SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, m_hUnregBmp, NULL );        }</pre>
<p>&nbsp;&nbsp;&nbsp; 最后，我们告诉Explorer我们添加了几个菜单项。</p>
<pre>    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 2 );</pre>
<h2>提供敏感帮助和动词</h2>
<p>&nbsp;&nbsp;&nbsp; 和前面的一样，当Explorer需要显示敏感帮助或者获得动词的时候，<code>GetCommandString()</code>方法被调用。和前一个稍稍不同的是我们添加了2个菜单项，所以我们必须首先检查<code>uCmdID</code>参数来知道Explorer正在呼叫哪个菜单项。</p>
<pre>#include &lt;atlconv.h&gt;HRESULT CDLLRegShlExt::GetCommandString (     UINT  uCmdID,    UINT  uFlags,     UINT* puReserved,    LPSTR szName,    UINT  cchMax ){LPCTSTR szPrompt;    USES_CONVERSION;    if ( uFlags &amp; GCS_HELPTEXT )        {        switch ( uCmdID )            {            case 0:                szPrompt = _T(&quot;<a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">Register</a> all selected COM servers&quot;);            break;            case 1:                szPrompt = _T(&quot;Unregister all selected COM servers&quot;);            break;            default:                return E_INVALIDARG;            break;            }</pre>
<p>&nbsp;&nbsp;&nbsp; 如果<code>uCmdID</code>为0，那么我们正在调用第一个(<a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">register</a>)。如果为1，则在调用第二个(unregister)。在确定帮助字符串之后，我们将它拷贝到提供的缓存当中，必要的话，先将它转换成Unicode形式。</p>
<pre>        // Copy the help text into the supplied buffer.  If the shell wants        // a Unicode string, we need to case szName to an LPCWSTR.        if ( uFlags &amp; GCS_UNICODE )            {            lstrcpynW ( (LPWSTR) szName, T2CW(szPrompt), cchMax );            }        else            {            lstrcpynA ( szName, T2CA(szPrompt), cchMax );            }        }</pre>
<p>&nbsp;&nbsp;&nbsp; 在这个扩展中，我同样也写了代码提供一个动词。然而，我在Windows 98下测试的时候，Explorer从来不调用<code>GetCommandString()</code>来获取一个动词。我甚至写了一个在DLL中调用<code>ShellExecute()</code>的测试程序然后试图使用动词，但是它还是不工作。我不知道这个情况在Windows NT下是否会有不同。这儿，我忽略了那些动词相关的代码，如果你感兴趣的话，你可以在例子项目中找到。</p>
<h2>执行用户的选择</h2>
<p>&nbsp;&nbsp;&nbsp; 当用户点击我们菜单中的一个时，Explorer调用我们的<code>InvokeCommand()</code>方法。<code>InvokeCommand()</code>首先检查了<code>lpVerb</code>的高位字(high word)。如果它是非零的，则它是被调用的动词的名称，因为我们知道动词不能正常工作(至少在Win 98下)，我们将它忽略了。否则，如果它的低位字(low word)是0或1，那么我们知道其中有一个菜单项目被点击了。</p>
<pre>HRESULT CDllRegShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo ){    // If lpVerb really points to a string, ignore this function call and bail out.    if ( 0 != HIWORD( pInfo-&gt;lpVerb ))        return E_INVALIDARG;    // Check that lpVerb is one of our commands (0 or 1)    switch ( LOWORD( pInfo-&gt;lpVerb ))        {        case 0:        case 1:            {            CProgressDlg dlg ( &amp;m_lsFiles, pInfo );            dlg.DoModal();            return S_OK;            }        break;        default:            return E_INVALIDARG;        break;        }}</pre>
<p>&nbsp;&nbsp;&nbsp; 如果<code>lpVerb</code>是0或1，我们创建一个对话框（从ATL类CDialogImpl继承），然后向它传递文件名列表。</p>
<p>&nbsp;&nbsp;&nbsp; 所有实际的工作都在<code>CProgressDlg</code>类中完成。它的<code>OnInitDialog()</code>函数初始化列表控件，然后调用<code>CProgressDlg::DoWork()</code>。<code>DoWork()</code>遍历在<code>CDllRegShlExt::Initialize()</code>中生成的文件名列表，然后调用文件中的合适的函数。基本代码如下；并不完全，因为我删掉了错误检查和填充列表控件的部分。不过这已经足够向大家演示如何遍历一个文件列表并执行每一个。</p>
<pre>void CProgressDlg::DoWork(){HRESULT (STDAPICALLTYPE* pfn)();string_list::const_iterator it, itEnd;HINSTANCE hinst;LPCSTR    pszFnName;HRESULT   hr;WORD      wCmd;    wCmd = LOWORD ( m_pCmdInfo-&gt;lpVerb );    // We only support 2 commands, so check the value passed in lpVerb.    if ( wCmd &gt; 1 )        return;    // Determine which function we'll be calling.  Note that these strings are    // not enclosed in the _T macro, since GetProcAddress() only takes an    // ANSI string for the function name.    pszFnName = wCmd ? &quot;DllUnregisterServer&quot; : &quot;DllRegisterServer&quot;;    for ( it = m_pFileList-&gt;begin(), itEnd = m_pFileList-&gt;end();          it != itEnd;          it++ )        {        // Try to load the next file.        hinst = LoadLibrary ( it-&gt;c_str() );        if ( NULL == hinst )            continue;        // Get the address of the <a href="http://www.donevii.com/post/tag/register" class="st_tag internal_tag" rel="tag" title="Posts tagged with register">register</a>/unregister function.        (FARPROC&amp;) pfn = GetProcAddress ( hinst, pszFnName );        // If it wasn't found, go on to the next file.        if ( NULL == pfn )            continue;        // Call the function!        hr = pfn();</pre>
<p>&nbsp;&nbsp;&nbsp; 我需要解释一下那个<code>for</code>循环，因为STL集合类是有点令人胆战心惊的，如果你不习惯使用它的话。<code>m_pFileList</code>是一个指向在<code>CDllRegShlExt</code>类中的<code>m_lsFiles</code>列表的指针。（这个指针被传递给了<code>CProgressDlg</code>的构造器。）STL<code>列表</code>集有一个类型叫做<code>const_iterator</code>，这是一个相似于MFC中<code>POSITION</code>类型的抽象实体。一个<code>const_iterator</code>变量像一个列表中常量对象的指针，所以这个遍历器(iterator)可以使用<code>-&gt;</code>访问它本身。遍历器可以使用<code>++</code> 来自增实现在列表中前进。</p>
<p>&nbsp;&nbsp;&nbsp; 所以，这个<code>for</code>循环的初始化表达式调用<code>list::begin()</code>来获取一个遍历器&ldquo;指向&rdquo;列表中的第一个字符串，然后调用<code>list::end()</code>来获取一个遍历器&ldquo;指向&rdquo;列表的&ldquo;末尾&rdquo;，最后一个字符串的位置。 (我把这些词语放在引号里是为了强调指向，开始，和末尾的概念都是被<code>const_iterator</code>类型抽象了的，而且必须通过<code>const_iterator</code>的方法[像<code>begin()</code>]或者操作符[像<code>++</code>]。)这些遍历器被分别地赋给<code>it</code>和<code>itEnd</code>。 这个循环一直进行着，直到<code>it</code>等于<code>itEnd</code>；意思是，当<code>it</code>还没有到达列表的&ldquo;末尾&rdquo;时。这个遍历器<code>it</code>将会在循环过程每次自增加，这样它可以每次到达一个列表中的字符串。</p>
<p>&nbsp;&nbsp;&nbsp; 在遍历其中，表达式<code>it-&gt;c_str()</code> 使用<code>-&gt;</code>操作符。因为<code>it</code>像一个指向<code>string</code>的指针(记住，<code>m_pFileList</code>是一个STL <code>string</code>的列表)，<code>it-&gt;c_str()</code>在<code>it</code>当前所指向的<code>string</code>中调用<code>c_str()</code>函数。<code>c_str()</code>返回一个C类型的字符串指针，既然这样，就是一个<code>LPCTSTR</code>。</p>
<p>&nbsp;&nbsp;&nbsp; <code>DoWork()</code>的余下部分是释放内存和错误检查。你可以从例子项目的<code>ProgressDlg.cpp</code>文件中获取所有代码。</p>
<p>&nbsp;&nbsp;&nbsp; (我刚刚意识到叫一个变量为&quot;it&quot;，是多么的奇怪。很抱歉！) :)</p>
<h2>注册外壳扩展</h2>
<p>&nbsp;&nbsp;&nbsp; 这个<code>DllReg</code>扩展对可执行文件进行操作，所以我们注册它被EXE,DLL和OCX文件调用。和<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>中一样，我们可以在RGS脚本中做这些事，<code>DllRegShlExt.rgs</code>。下面是注册我们的扩展为一个对于上述扩展名文件的上下文菜单扩展必需的脚本。</p>
<pre>HKCR{    NoRemove dllfile    {        NoRemove shellex        {            NoRemove ContextMenuHandlers            {                ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}'            }        }    }    NoRemove exefile    {        NoRemove shellex        {            NoRemove ContextMenuHandlers            {                ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}'            }        }    }    NoRemove ocxfile    {        NoRemove shellex        {            NoRemove ContextMenuHandlers            {                ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}'            }        }    }}</pre>
<p>&nbsp;&nbsp;&nbsp; RGS文件的格式，以及关键词<code>NoRemove</code>和<code>ForceRemove</code>在<a href="http://www.csdn.net/develop/read_article.asp?id=10683">第一部分</a>已经解释过了，如果你忘了他们的意思的话。&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 正如我们前一个扩展一样，在NT/2000下，我们需要添加我们的扩展到&ldquo;认证的(approved)&rdquo;扩展列表当中。实现这个过程的代码在 <code>DllRegisterServer()</code>和<code>DllUnregisterServer()</code>函数中。我不想展示这些代码，因为它仅仅是一些简单的注册表访问，但是你可以从例子项目中中到这些代码。</p>
<h2>看起来应该什么样呢？</h2>
<p>&nbsp;&nbsp;&nbsp; 当你点击我们其中的一个菜单的时候，对话框就会出现，显示操作的结果：</p>
<p><img height="273" src="http://www.codeproject.com/shell/ShellExtGuide2/ShellExtGuide2_2.jpg" width="450" border="0" alt="" /></p>
<p>&nbsp;&nbsp;&nbsp; 列表控件显示了每个文件的文件名和操作是否成功。当你选中一个项时，一个更详细的消息将被显示在下方。如果失败的话，将有调用失败的描述。</p>
<h2>注册扩展的其他方法</h2>
<p>&nbsp;&nbsp;&nbsp; 到现在为止，我们的扩展只被确定的文件类型调用。通过在<code>HKCR\*</code>下注册成为一个上下文菜单扩展使得被任何文件操作调用成为可能：</p>
<pre>HKCR{    NoRemove *    {        NoRemove shellex        {            NoRemove ContextMenuHandlers            {                ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}'            }        }    }}</pre>
<p>&nbsp;&nbsp;&nbsp; <code>HKCR\*</code>键被所有文件调用的外壳扩展。注意文档中所说的该扩展也被其他外壳对象所调用（文件，目录，虚拟文件夹，控制面板项等等），但是我在测试中所见到的并不是这样。这些扩展仅仅被文件系统中的文件所调用。</p>
<p>&nbsp;&nbsp;&nbsp; 在外壳版本4.71以上，还有一个叫做<code>HKCR\AllFileSystemObjects</code>的键。如果我们在这个键下注册，我们的扩展将被文件系统中所有的文件和目录调用，根目录除外。（根目录调用的扩展在<code>HKCR\Drive</code>下注册。)然而，当我在这个键下注册的时候我看到一些奇怪的现象。&ldquo;发送到&rdquo;菜单也使用这个键，而它混在了<code>DllReg</code>菜单中间：</p>
<p><img height="102" src="http://www.codeproject.com/shell/ShellExtGuide2/ShellExtGuide2_3.jpg" width="231" border="0" alt="" /></p>
<p>&nbsp;&nbsp;&nbsp; 你同样可以编写一个操作目录的上下文菜单扩展。要这样的例子的话，请看我的文章：<br />&nbsp;&nbsp; <a href="http://www.codeproject.com/tips/DirClean.asp" target="_blank">A Utility to Clean Up Compiler Temp Files(一个清除编译临时文件的实用工具)</a>。（译者：确实是个好玩艺 :)）</p>
<p>&nbsp;&nbsp;&nbsp; 最后，在外壳版本4.71以上，你可以使你的上下文菜单在用户右键单击一个正在查看目录（包括桌面）的资源管理器窗口背景时被调用。为了使你的扩展被这么调用，你必须在<code>HKCR\Directory\Background\shellex\ContextMenuHandlers</code>键下注册。使用这个方法，你可以向你的桌面上下文菜单中添加菜单项目或者其他任何目录。传递给<code>IShellExtInit::Initialize()</code>的参数有点儿不同，我会在我以后的文章中加以说明。</p>
<h2>待续&hellip;&hellip;</h2>
<p>&nbsp;&nbsp;&nbsp; 接着的第三部分我们将实践一种新的扩展，查询。它将显示一些外壳对象的弹出式描述。我还将向你展示如何在外壳扩展中使用MFC。</p>
<p>&nbsp;&nbsp;&nbsp; <span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: Times New Roman; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"></span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">你可以从下面的网址获得这个和其他文章的最新版本：</span><span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><a href="http://home.inreach.com/mdunn/code/" target="_blank">http://home.inreach.com/mdunn/code/</a></span> </p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/314.html" title="Shell编程 &#8211; 傻瓜教程1 (2007-03-24)">Shell编程 &#8211; 傻瓜教程1</a> (0)</li>
	<li><a href="http://www.donevii.com/post/283.html" title="免费软件套装 (2007-01-08)">免费软件套装</a> (0)</li>
	<li><a href="http://www.donevii.com/post/78.html" title="[转]C/C++/Perl/汇编/Java效率比较 (2006-10-11)">[转]C/C++/Perl/汇编/Java效率比较</a> (0)</li>
	<li><a href="http://www.donevii.com/post/290.html" title="PHP5 效率优化 (2007-01-13)">PHP5 效率优化</a> (0)</li>
	<li><a href="http://www.donevii.com/post/204.html" title="PHP &#038; memcached (2006-11-15)">PHP &#038; memcached</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/315.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Shell编程 &#8211; 傻瓜教程1</title>
		<link>http://www.donevii.com/post/314.html</link>
		<comments>http://www.donevii.com/post/314.html#comments</comments>
		<pubDate>Sat, 24 Mar 2007 13:39:08 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[doc]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[debug]]></category>
		<category><![CDATA[ror]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[windows]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/?p=314</guid>
		<description><![CDATA[&#160;&#160;&#160; 外壳扩展(Shell Extention)是一个能向Windows外壳(资源管理器)添加一些功能的COM对象。这有很多的内容，但是却很少有关于它们的易懂的文档告诉我们如何去编写这些外壳(Shell)程序。... ]]></description>
			<content:encoded><![CDATA[<div class="postTitle"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">&nbsp;&nbsp;&nbsp; 外壳扩展</span><span lang="EN-US">(Shell Extention)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是一个能向</span><span lang="EN-US">Windows</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">外壳</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">资源管理器</span><span lang="EN-US">)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">添加一些功能的</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">对象。这有很多的内容，但是却很少有关于它们的易懂的文档告诉我们如何去编写这些外壳</span><span lang="EN-US">(Shell)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">程序。如果你想做对外壳很深入的了解，我极力向你推荐</span><span lang="EN-US">Dino Esposito </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的非常好的一本书《</span><cite><span lang="EN-US">Visual C++ Windows Shell Programming</span></cite><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">》。但是对于那些没有这本书并且仅仅关心如何去编写外壳扩展的人，我写的一指南将会令你非常惊讶，如果并非如此的话也能给你理解如何编写外壳扩展提供很好的帮助。要阅读这一指南，确保你对</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">要相当熟悉。</span> </div>
<div class="postText">
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">第一部分包括了对外壳扩展的概要的介绍，并提供了一个上下文菜单扩展的例程来使你对以后的部分中充满兴趣。</span></p>
<h2 style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">什么是<em>外壳扩展</em>呢？</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">这有两部分，外壳和扩展</span><span lang="EN-US">(<em>extension</em>)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。外壳指的是资源管理器</span><span lang="EN-US">(Explorer)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，而扩展是指当一个预订的事件（如：右键单击一个</span><span lang="EN-US">.doc</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">文档）发生时，被资源管理器调用的你写的代码。所以以个外壳扩展是一个向资源管理器添加特色的</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">对象。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">一个外壳扩展是一个进程中服务器，它实现了一些与资源管理器通信的借口。而在我看来，</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是快速实现一个扩展并使它运行的最简单的方法，因为你不用为一遍又一遍的写</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryInterface()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">AddRef()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">而大伤脑筋。而且在</span><span lang="EN-US">Windows NT/2000</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下调试扩展也变得更为容易。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">有很多种的扩展，每种扩展在不同的事件发生时被调用。下面是一些比较通用的类型和它们被调用的情况：</span></p>
<table style="WIDTH: 100%; mso-cellspacing: 1.5pt" cellpadding="0" width="100%" border="1">
<tbody>
<tr>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p style="TEXT-ALIGN: center" align="center"><strong><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">类型</span><span lang="EN-US"> <o:p></o:p></span></strong></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p style="TEXT-ALIGN: center" align="center"><strong><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">什么时候被调用</span><span lang="EN-US"> <o:p></o:p></span></strong></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 34%; PADDING-TOP: 0.75pt" width="34%">
<p style="TEXT-ALIGN: center" align="center"><strong><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">可以做什么</span><span lang="EN-US"><o:p></o:p></span></strong></p>
</td>
</tr>
<tr>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">上下文菜单</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">用户在文件或目录右键单击时。在外壳扩展</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">4.71</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">版本以上，在目录窗口的背景上右键单击也将被调用。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 34%; PADDING-TOP: 0.75pt" width="34%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">向上下文菜单添加项目。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
</tr>
<tr>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">属性单</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">文件的属性单被显示时。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 34%; PADDING-TOP: 0.75pt" width="34%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">向属性单添加一个属性页。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
</tr>
<tr>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">拖扔</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">用户右键拖动项目并把它扔在一个目录窗口活着桌面上时。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 34%; PADDING-TOP: 0.75pt" width="34%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">添加项目至上下文菜单。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
</tr>
<tr>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">扔</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">用户拖一个项目并把它扔到一个文件上时。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 34%; PADDING-TOP: 0.75pt" width="34%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">任何你想做的事。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
</tr>
<tr>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">查询信息</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">(</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">外壳版本</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">4.71+)<span style="COLOR: black"><o:p></o:p></span></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 33%; PADDING-TOP: 0.75pt" width="33%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">用户鼠标在一个文件或像我的电脑一样的其他外壳对象上悬停时。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
<td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; WIDTH: 34%; PADDING-TOP: 0.75pt" width="34%">
<p class="MsoNormal"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">返回一个资源管理器在工具条提示上的字符串。</span><span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"><o:p></o:p></span></p>
</td>
</tr>
</tbody>
</table>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">到现在为止你可能为什么一个扩展看起来想在资源管理器里。如果你安装了</span><span lang="EN-US">WinZip</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">（有谁没有吗？），它就包括了许多种的外壳扩展，其中一个就是上下文句柄。下面世</span><span lang="EN-US">WinZip 8 </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">为了压缩文件添加到上下文菜单的截图：</span></p>
<p><img alt="" hspace="" src="http://www.codeproject.com/shell/ShellExtGuide1/ShellExGuide1_1.jpg" align="baseline" border="0" /></p>
<p style="TEXT-INDENT: 21pt"><span lang="EN-US">WinZip</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">包含了添加菜单项目的代码，并提供敏感帮助（显示在资源管理器状态条的文本），并在用户选择</span><span lang="EN-US">WinZip</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">命令之一时起作用。</span></p>
<p style="TEXT-INDENT: 21pt"><span lang="EN-US">WinZip</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">还包含了拖和扔的句柄。这个类型和上下文菜单扩展非常类似，但是它仅仅在用户通过鼠标右键拖动一个文件时才被调用。下面是</span><span lang="EN-US">WinZip</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的拖扔句柄如何添加上下文菜单：</span></p>
<p><img alt="" hspace="" src="http://www.codeproject.com/shell/ShellExtGuide1/ShellExGuide1_2.jpg" align="baseline" border="0" /></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">还有很多种其他类型（微软一直往新版本的</span><span lang="EN-US">Windows</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">里添加更多内容）。到现在，我们已经看到了上下文菜单扩展，因为它非常容易编写，我们将很容易的看到它的结果（很快就能满意）。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在我们开始编码以前，有一些提示，它将使我们做起来更加容易。当你促成一个外壳扩展被资源管理器调用的时候，它将在内存中呆上一小会儿，从而使它不能立即被重建</span><span lang="EN-US">(rebuild)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。为了使资源管理器更加频繁的卸载这些扩展，创建这个注册表键：</span></p>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.0pt">HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\AlwaysUnloadDLL</span></code><span lang="EN-US" style="FONT-SIZE: 9pt; mso-bidi-font-size: 10.0pt"><o:p></o:p></span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">并把它的默认值设为</span><span lang="EN-US">&rdquo;1&rdquo;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。在</span><span lang="EN-US">Windows 9x</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">系列中，这是最好的方法。在</span><span lang="EN-US">NT/2000</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，到如下的键：</span></p>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.0pt">HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer</span></code><span lang="EN-US" style="FONT-SIZE: 9pt; mso-bidi-font-size: 10.0pt"><o:p></o:p></span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">创建一个叫做</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">DesktopProcess</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的双字节值，使它的值为</span><span lang="EN-US">1</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。这使得桌面和任务栏运行在一个进程中，后发的资源管理器运行在它自己的进程里。这就意味着你可以使用一个单独的资源管理器窗口来调试，并且当你关掉它的时候，你的</span><span lang="EN-US">DLL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">也会自动的被卸载，避免了该文件仍然在使用得问题。要使得你的注册表修改生效的话，你必须注销并且重新登录。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我将稍晚一些解释如何在</span><span lang="EN-US">Win 9x</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下进行调试。</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">开始一个上下文菜单扩展</span><span lang="EN-US"> &ndash; </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">它能做什么</span><span lang="EN-US">?</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">让我们开始简单的做一个扩展，它仅仅弹出一个消息框表示它已经在工作了。我们将对扩展名为</span><span lang="EN-US">.txt</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的文件设置一个钩子，这样当用户右键单击一个文本文件的时候，我们的扩展就能被调用了。</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">使用</span><span lang="EN-US">AppWizard </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">开始</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">好了，现在是我们开始的时候了。那是什么？我还没有告诉你如何使用神秘的外壳扩展接口？不要担心，我将在接下来的过程中为你解释。我发现如果一个概念被解释，有一个例子更容易明白，通过例子代码你很快就能理解。我将会先解释任何东西，然后给出代码，但是我发现还是不容易吸收。总之，启动你的</span><span lang="EN-US">MSVC</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">吧，我们要开始了。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">运行</span><span lang="EN-US">AppWizard </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，做一个新的</span><span lang="EN-US">ATL COM wizard app</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。我们叫它</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">SimpleExt</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。在向导中保持所有默认选项，单击完成。我们现在就有了一个空的将会生成</span><span lang="EN-US">DLL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">项目，但是我们必须添加自己的外壳扩展</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">对象。在</span><span lang="EN-US">ClassView</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">树中，右键单击</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">SimpleExt classes</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">项，选择</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">New ATL Object</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在</span><span lang="EN-US">ATL Object </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">向导，第一面板已经选择了</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">Simple Object</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，只要单击下一步就行了。在第二面板中，在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">Short Name</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">编辑控件中输入</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">SimpleShlExt</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，然后单击确定（面板中的其它的编辑框将会自动完成）。这就创建了一个类名为</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">CSimpleShlExt</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的类，它包含了实现一个</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">对象的基本代码。我们将向这个类添加我们的代码。</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">初始化接口</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">当我们的外壳扩展被装载的时候，资源管理器调用我们的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryInterface()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数获取一个指向</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IShellExtInit</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接口的指针。这个接口只有一个方法，</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">Initialize()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，它的原型如下：</span></p>
<pre><span lang="EN-US">HRESULT IShellExtInit::Initialize (</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>LPCITEMIDLIST pidlFolder,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>LPDATAOBJECT pDataObj,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp;</span>HKEY hProgID );</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">资源管理器使用这个方法给我们不同的信息。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">pidlFolder</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是包含有正在被作用的文件的文件夹的</span><span lang="EN-US">PIDL(PIDL[<strong>p</strong>ointer to an <strong>ID</strong> <strong>l</strong>ist]</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是唯一标志外壳中任一对象（无论是否是文件系统对象）的数据结构。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">pDataObj</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是一个</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IDataObject</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接口指针，通过它我们可以获得被作用的文件的文件名。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">hProgID</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是一个打开的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">HKEY</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，通过它我们可以访问包含有我们的</span><span lang="EN-US">DLL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">注册数据的注册表键。在这个简单的扩展中，我们只需要用到</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">pDataObj</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">参数。</span></p>
<p><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">添加这个接口方法到我们的</span><span lang="EN-US">COM </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">对象中，先打开</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">SimpleShlExt.h</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">文件，并添加如下用红色书写的代码行：</span></p>
<pre><span lang="EN-US" style="COLOR: red">#include &lt;shlobj.h&gt;<o:p></o:p></span></pre>
<pre><span lang="EN-US" style="COLOR: red">#include &lt;comdef.h&gt;</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><a href="http://www.donevii.com/post/tag/class" class="st_tag internal_tag" rel="tag" title="Posts tagged with class">class</a> ATL_NO_VTABLE CSimpleShlExt : </span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</span>public CComObjectRootEx&lt;CComSingleThreadModel&gt;,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public CComCoClass&lt;CSimpleShlExt, &amp;CLSID_SimpleShlExt&gt;,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public IDispatchImpl&lt;ISimpleShlExt, &amp;IID_ISimpleShlExt, &amp;LIBID_SIMPLEEXTLib&gt;,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="COLOR: red">public IShellExtInit</span></pre>
<pre><span lang="EN-US">{</span></pre>
<pre><span lang="EN-US">BEGIN_COM_MAP(CSimpleShlExt)</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>COM_INTERFACE_ENTRY(ISimpleShlExt)</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>COM_INTERFACE_ENTRY(IDispatch)</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="COLOR: red">COM_INTERFACE_ENTRY(IShellExtInit)</span></pre>
<pre><span lang="EN-US">END_COM_MAP()</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">这个</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">COM_MAP</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是说明了</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">如何实现它的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryInterface()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。这个列表告诉</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">其他使用</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryInterface()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的程序可以从我们这儿获得什么。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接着，在类的声明当中，添加</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">Initialize()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数。此外，我们还需要一个保存文件名的变量：</span></p>
<pre><span lang="EN-US">protected:</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>TCHAR m_szFile [MAX_PATH];</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US">public:</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// IShellExtInit</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>STDMETHOD(Initialize)(LPCITEMIDLIST, LPDATAOBJECT, HKEY);</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接下来，在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">SimpleShlExt.cpp</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">文件中，添加该函数的定义：</span></p>
<pre><span lang="EN-US">HRESULT CSimpleShlExt::Initialize ( </span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</span>LPCITEMIDLIST pidlFolder,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>LPDATAOBJECT pDataObj,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>HKEY hProgID )</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我们所要做的就是获得被右键单击的文件的文件名，并把它显示在一个消息框中。如果有很多个文件被选中，你可以通过</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">pDataObj</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接口指针访问它们。但是为了保持该例子的简单性，我只要获得第一个文件的文件名。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">文件名被保存为与你使用</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">WS_EX_ACCEPTFILES</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">样式拖和扔一个文件到窗口时用到的一个相同的格式。那就意味着，我们获得文件名使用了相同的</span><span lang="EN-US">API:</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'"> DragQueryFile()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。我们通过获得包含在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IDataObject</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">中的数据的句柄开始这个函数：</span></p>
<pre><span lang="EN-US">{</span></pre>
<pre><span lang="EN-US">FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };</span></pre>
<pre><span lang="EN-US">STGMEDIUM stg = { TYMED_HGLOBAL };</span></pre>
<pre><span lang="EN-US">HDROP<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>hDrop;</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// Look for CF_HDROP data in the data object.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>if ( FAILED( pDataObj-&gt;GetData ( &amp;fmt, &amp;stg )))</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Nope! Return an &quot;invalid argument&quot; error back to Explorer.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return E_INVALIDARG;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// Get a pointer to the actual data.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>hDrop = (HDROP) GlobalLock ( stg.hGlobal );</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// Make sure it worked.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>if ( NULL == hDrop )</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return E_INVALIDARG;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">要注意，错误检查是极其重要的，尤其是指针。因为我们的扩展运行在资源管理器的进程空间当中，如果我们的程序毁坏的话，同样会让资源管理器也毁坏的。在</span><span lang="EN-US">Win 9x</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下，这可能就意味着重新启动。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">现在，我们有了一个</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">HDROP</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">句柄，我们可以获得我们需要的文件名了。</span></p>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// Sanity check &ndash; make sure there is at least one filename.</span></pre>
<pre><span lang="EN-US">UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"> <span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;</span>if ( 0 == uNumFiles )</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>GlobalUnlock ( stg.hGlobal );</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ReleaseStgMedium ( &amp;stg );</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return E_INVALIDARG;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US">HRESULT hr = S_OK;</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// Get the name of the first file and store it in our member variable m_szFile.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH ))</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hr = E_INVALIDARG;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>GlobalUnlock ( stg.hGlobal );</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>ReleaseStgMedium ( &amp;stg );</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>return hr;</span></pre>
<pre><span lang="EN-US">}</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">如果我们返回</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">E_INVALIDAR</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，在右键单击事件发生时，资源管理器将不会再调用我们的扩展。如果我们返回</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">S_OK</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，那么资源管理器将会再次调用</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryInterface()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">来获得我们将要添加的另一个接口指针：</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IContextMenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和上下文菜单交互的接口</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">一旦资源管理器初始化了我们的扩展，它将会调用</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IContextMenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的方法让我们添加菜单项目、敏感帮助并完成用户的选择。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">向我们的扩展中添加</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IContextMenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接口和添加</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IShellExtInit</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">相类似。打开</span><span lang="EN-US">SimpleShlExt.h</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">并添加一下红颜色的代码：</span></p>
<pre><span lang="EN-US"><a href="http://www.donevii.com/post/tag/class" class="st_tag internal_tag" rel="tag" title="Posts tagged with class">class</a> ATL_NO_VTABLE CSimpleShlExt : </span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</span>public CComObjectRootEx&lt;CComSingleThreadModel&gt;,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public CComCoClass&lt;CSimpleShlExt, &amp;CLSID_SimpleShlExt&gt;,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public IDispatchImpl&lt;ISimpleShlExt, &amp;IID_ISimpleShlExt, &amp;LIBID_SIMPLEEXTLib&gt;,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public IShellExtInit,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="COLOR: red">public IContextMenu</span></pre>
<pre><span lang="EN-US">{</span></pre>
<pre><span lang="EN-US">BEGIN_COM_MAP(CSimpleShlExt)</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>COM_INTERFACE_ENTRY(ISimpleShlExt)</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>COM_INTERFACE_ENTRY(IDispatch)</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>COM_INTERFACE_ENTRY(IShellExtInit)</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span lang="EN-US" style="COLOR: red">COM_INTERFACE_ENTRY(IContextMenu)</span></pre>
<pre><span lang="EN-US">END_COM_MAP()</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接着添加</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IContextMenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">方法的原型：</span></p>
<pre><span lang="EN-US">public:</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// IContextMenu</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>STDMETHOD(GetCommandString)(UINT, UINT, UINT*, LPSTR, UINT);</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO);</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>STDMETHOD(QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);</span></pre>
<h3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial">更改上下文菜单</span></h3>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IContextMenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">有</span><span lang="EN-US">3</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">个方法。第一个，</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryContextMenu()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，让我们更改菜单。它的原型为：</span></p>
<pre><span lang="EN-US">HRESULT IContextMenu::QueryContextMenu (</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>HMENU hmenu,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uMenuIndex, </span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uidFirstCmd,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uidLastCmd,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uFlags );</span></pre>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">hmenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是上下文菜单的句柄。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uMenuIndex</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是我们开始添加我们的菜单项目的开始位置。</span> <code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uidFirstCmd</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span> <code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uidLastCmd</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是我们可以给菜单项目使用的命令</span><span lang="EN-US">ID</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">值的范围。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uFlags</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">指出为什么资源管理器正在调用</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryContextMenu()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，这我们将在以后看到。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">有关它的返回值你将会得到不同的答案，如果你问不同的人的话。</span><span lang="EN-US">Dino Esposito </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的书上说它使被</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryContextMenu()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">添加的菜单项目的号码。</span><span lang="EN-US">MSDN</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">上关于</span><span lang="EN-US">VC 6</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">部分说它是最后一个被添加的菜单项目的命令</span><span lang="EN-US">ID</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">加上</span><span lang="EN-US">1</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。最新的</span><span lang="EN-US">MSDN</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">文档有如下说明：</span></p>
<p style="MARGIN-LEFT: 36pt; MARGIN-RIGHT: 36pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">设置代码的值</span><span lang="EN-US">[</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">由</span><span lang="EN-US">HRESULT</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">返回的</span><span lang="EN-US">]</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">为被分配的最大的命令</span><span lang="EN-US">ID</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">偏移加上</span><span lang="EN-US">1</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。例如，假定</span><span lang="EN-US">idCmdFirst</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">被设置为</span><span lang="EN-US">5</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，你添加了</span><span lang="EN-US">3</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">个菜单项目分别使用命令</span><span lang="EN-US">ID</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">为</span><span lang="EN-US">5</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">、</span><span lang="EN-US">7</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><span lang="EN-US">8</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。它的返回值将是</span><span lang="EN-US">MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 &#8211; 5 + 1)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span><span lang="EN-US"> </span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">到我现在所写的所有代码中，我接受了</span><span lang="EN-US">Dino</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的解释，这样工作的很好。事实上，他的制作返回值的方法和在线</span><span lang="EN-US">MSDN</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的方法是相同的，在你使用</span><span lang="EN-US">uidFirstCmd</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">开始添加你的菜单项目时开始计数，每添加一个增加</span><span lang="EN-US">1</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span><span lang="EN-US"> </span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我们的简单的扩展将仅仅添加一个菜单项目，所以</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">QueryContextMenu()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数相当简单：</span></p>
<pre><span lang="EN-US">HRESULT CSimpleShlExt::QueryContextMenu (</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>HMENU hmenu,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uMenuIndex, </span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uidFirstCmd,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uidLastCmd,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uFlags )</span></pre>
<pre><span lang="EN-US">{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// If the flags include CMF_DEFAULTONLY then we shouldn't do anything.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>if ( uFlags &amp; CMF_DEFAULTONLY )</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, _T(&quot;SimpleShlExt Test Item&quot;) );</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 );</span></pre>
<pre><span lang="EN-US">}</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我们首先要做的就是检查</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uFlags</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。你可以在</span><span lang="EN-US">MSDN</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">查询所有的标志列表，但是对于上下文菜单扩展来说，只有一样是重要的：</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">CMF_DEFAULTONLY</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。这个标志告诉名字空间扩展仅仅添加默认菜单项目。如果这个标志在的话，外壳扩展将不会添加任何菜单项目。这就是为什么当</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">CMF_DEFAULTONLY</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">存在的时候我们立即返回</span><span lang="EN-US">0</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的原因。如果该标志不存在，我们更改菜单（使用</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">hmenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">句柄），然后返回</span><span lang="EN-US">1</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">告诉外壳我们添加了一个菜单项目。</span></p>
<h3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial">在状态条显示敏感帮助</span></h3>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IContextMenu</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">中下一个可以调用的方法是</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GetCommandString()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。如果用户在资源管理器窗口中右键单击了一个文本文件的时候，或者选中一个文本文件，然后单击&ldquo;文件&rdquo;菜单，状态条上将显示敏感帮助。我们的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GetCommandString()</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数将会返回一个让资源管理器显示得字符串。</span></p>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GetCommandString()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数原型如下：</span></p>
<pre><span lang="EN-US">HRESULT IContextMenu::GetCommandString (</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT idCmd,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT uFlags,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT *pwReserved,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>LPSTR pszName,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT cchMax );</span></pre>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">idCmd</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是一个基于</span><span lang="EN-US">0</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的指明哪个菜单项目被选中的数。因为我们仅仅添加了一个菜单项，</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">idCmd</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">将总是为</span><span lang="EN-US">0</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。但是如果我们添加了，我是说，</span><span lang="EN-US">3</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">个的话，</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">idCmd</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">将会是</span><span lang="EN-US">0,1</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">或者</span><span lang="EN-US">2</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uFlags</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是另一个标志组。我将会在后面进行描述。我们可以忽略</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">pwReserved</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">pszName</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是一个指向一个被外壳所拥有的缓存的指针，该缓存保存被显示的帮助字符串。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">cchMax</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是缓存的大小。返回值是</span><span lang="EN-US">HRESULT</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">常量，例如</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">S_OK</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">或</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">E_FAIL</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span></p>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GetCommandString()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">同样能被用来获得菜单项的&ldquo;动词&rdquo;。&ldquo;动词&rdquo;是一个标志作用于文件的动作的字符串，它是独立于语言的。有关</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">ShellExecute()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的文档作了更多的说明，有关&ldquo;动词&rdquo;的主题更适合在另一篇文章说明，这儿简要说明的是列在注册表中的动词（比如说</span><span lang="EN-US">&rdquo;open&rdquo;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><span lang="EN-US">&rdquo;print&rdquo;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">），或者那些有上下文菜单扩展动态创建的&ldquo;动词&rdquo;。这使得在外壳扩展中实现的行为可以被</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">ShellExecute()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">调用。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">总之，我提及所有这些的原因是我们不得不确定为什么</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GetCommandString()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">被调用。如果资源管理器需要一个敏感帮助字符串的时候，我们就提供。如果资源管理器请求一个&ldquo;动词&rdquo;的话，我们将忽略它。这是</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uFlags</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">起作用的地方。如果</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">uFlags</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GCS_HELPTEXT</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的位被设置的话，那么资源管理器将请求敏感帮助。附加的，如果</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GCS_UNICODE</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">位被设置，我们必须返回一个</span><span lang="EN-US">Unicode</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">字符串。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我们的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">GetCommandString()</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的代码看起来应该像下面这样：</span></p>
<pre><span lang="EN-US">#include &lt;atlconv.h&gt;<span style="mso-spacerun: yes">&nbsp; </span>// for ATL string conversion macros</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US">HRESULT CSimpleShlExt::GetCommandString (</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>idCmd,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>uFlags,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT* pwReserved,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>LPSTR pszName,</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>UINT<span style="mso-spacerun: yes">&nbsp; </span>cchMax )</span></pre>
<pre><span lang="EN-US">{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>USES_CONVERSION;</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// Check idCmd, it must be 0 since we have only one menu item.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>if ( 0 != idCmd )</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return E_INVALIDARG;</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// If Explorer is asking for a help string, copy our string into the</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// supplied buffer.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>if ( uFlags &amp; GCS_HELPTEXT )</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>LPCTSTR szText = _T(&quot;This is the simple shell extension's help&quot;);</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( uFlags &amp; GCS_UNICODE )</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// We need to cast pszName to a Unicode string, and then use the</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Unicode string copy API.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lstrcpynW ( (LPWSTR) pszName, T2CW(szText), cchMax );</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Use the ANSI string copy API to return the help string.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lstrcpynA ( pszName, T2CA(szText), cchMax );</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return S_OK;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>return E_INVALIDARG;</span></pre>
<pre><span lang="EN-US">}</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">没什么奇特的；我只是把字符串编码并且把它转换为合适的字符集。如果你以前从来都没有使用过</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">变换宏，你干脆先看看它们，因为这将我使更容易理解传递一个</span><span lang="EN-US">Unicode</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">字符串到</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">方法和</span><span lang="EN-US">OLE</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数中。在上面的代码中，我使用了</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">T2CW</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">T2CA</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">分别将</span><span lang="EN-US">TCHAR</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">字符串转换为</span><span lang="EN-US">Unicode</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><span lang="EN-US">ANSI</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。在函数头部的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">USES_CONVERSION</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">宏声明了一个变换宏使用的局部变量。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">一个需要注意的重要事项是</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">lstrcpyn()</span></code><span lang="EN-US">API</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数保证了目标字符串是以</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">结束的。这是它和</span><span lang="EN-US">CRT</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">strncpy()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的不同之处。如果源字符串的长度大于或等于</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">cchMax</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">时，</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'"> strncpy()</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">并不添加结束符</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。我建议你总是使用</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">lstrcpyn()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，这样你就不用不得不在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">strncpy()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">之后添加检查来保证字符串是</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">结束的。</span></p>
<h3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial">执行用户的选择</span></h3>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">最后一个</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">IContextMenu</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">方法是</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">InvokeCommand()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。这个方法将在用户单击我们添加的那个菜单项目时被调用。它的原型如下：</span></p>
<pre><span lang="EN-US">HRESULT IContextMenu::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo );</span></pre>
<p style="TEXT-INDENT: 21pt"><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">CMINVOKECOMMANDINFO</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">结构里有很多的信息，但是根据我们现在的意图，我们只需要关心</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">lpVerb</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">hwnd</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">lpVerb</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">有双重的任务&mdash;&mdash;它既可以是被调用的&ldquo;动词&rdquo;的名称，也可以是一个用以告诉我们哪个菜单项被选中地索引。</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">hwnd</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是资源管理器窗口的句柄，在那儿，用户调用了我们的扩展。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我们检查</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">lpVerb</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，因为我们只添加了一个菜单项，所以如果它为</span><span lang="EN-US">0</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，则我们的菜单被点击了。我能想到的最简单的事就是弹出一个消息框，所以我们就这么做。这个消息框显示了选中的文件的文件名，证明它的确是在工作。</span></p>
<pre><span lang="EN-US">HRESULT CSimpleShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )</span></pre>
<pre><span lang="EN-US">{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// If lpVerb really points to a string, ignore this function call and bail out.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>if ( 0 != HIWORD( pCmdInfo-&gt;lpVerb ))</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return E_INVALIDARG;</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>// Get the command index - the only valid one is 0.</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>switch ( LOWORD( pCmdInfo-&gt;lpVerb ))</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>case 0:</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>TCHAR szMsg [MAX_PATH + 32];</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wsprintf ( szMsg, _T(&quot;The selected file was:\n\n%s&quot;), m_szFile );</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>MessageBox ( pCmdInfo-&gt;hwnd, szMsg, _T(&quot;SimpleShlExt&quot;),</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>MB_ICONINFORMATION );</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return S_OK;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>break;</span></pre>
<pre><span lang="EN-US">&nbsp;<o:p></o:p></span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>default:</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return E_INVALIDARG;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>break;</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">}</span></pre>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">注册外壳扩展</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">到现在为止，我们已经实现了我们所有的</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接口。但是&hellip;&hellip;如何使资源管理器使用我们的扩展呢？</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">自动生成了注册我们的</span><span lang="EN-US">DLL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">为一个</span><span lang="EN-US">COM</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">服务器的代码，但是它仅仅是让其它程序来使用我们的</span><span lang="EN-US">DLL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。为了告诉资源管理器我们的扩展存在，我们必须在保持文本文件的注册表键下注册它：</span></p>
<p><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">HKEY_CLASSES_ROOT\txtfile</span></code></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在那个键下面，一个叫做</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">ShellEx</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的键保存了一个对于文本文件将被调用的外壳扩展列表。在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">ShellEx</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下，</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">ContextMenuHandlers</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">键保存了一个上下文菜单扩展的列表。每一个扩展在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">ContextMenuHandlers</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下创建一个字键，并把他的默认值设置为它的</span><span lang="EN-US">GUID</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。所以，为我们的扩展，我们创建如下键：</span></p>
<p><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">HKEY_CLASSES_ROOT\txtfile\ShellEx\ContextMenuHandlers\SimpleShlExt</span></code></p>
<p><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">并把它的默认值设置为我们的</span><span lang="EN-US">GUID</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">：</span></p>
<p><span lang="EN-US">&quot;{5E2121EE-0300-11D4-8D3B-444553540000}&quot;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">然而，你不用自己做这件事。如果你在</span><span lang="EN-US">FileView</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">页查看你得文件列表时，你会发现</span><span lang="EN-US">SimpleShlExt.rgs</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。这是一个由</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">解析的文本文件，它告诉</span><span lang="EN-US">ATL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">当这个服务器被注册时该添加什么键，当被反注册时又该删除什么键。下面我们指定了要添加的注册表入口：</span></p>
<pre><span lang="EN-US">HKCR</span></pre>
<pre><span lang="EN-US">{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>NoRemove txtfile</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>NoRemove ShellEx</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>NoRemove ContextMenuHandlers</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ForceRemove SimpleShlExt = s '{5E2121EE-0300-11D4-8D3B-444553540000}'</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;</span>}</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></pre>
<pre><span lang="EN-US">}</span></pre>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">它以</span><span lang="EN-US">&quot;HKCR&quot;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">&mdash;&mdash;</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">HKEY_CLASSES_ROOT</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的缩写&mdash;&mdash;开头，每一行是注册表键名称。关键词</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">NoRemove</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">意味着当该服务器被反注册时该键不能被删除。最后一行有点复杂。关键词</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">ForceRemove</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">意思是如果该键存在，那么在该键被写之前先删除它。这一行剩下的部分指定了一个将被保存在</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">SimpleShlExt</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">键的默认值中的字符串</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">那就是</span><span lang="EN-US">&rdquo;s&rdquo;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的意思</span><span lang="EN-US">)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span></p>
<p style="TEXT-INDENT: 18pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在这儿，我需要说明一点。我们注册扩展时的键是</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">HKCR\txtfile</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。然而，这个名称</span><span lang="EN-US">&quot;txtfile&quot; </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">并不是一个永久的或预先知道的。如果你查看一下</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">HKCR\.txt</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，那个键的默认值是这个名称被保存的地方。这就两个侧面效果：</span></p>
<ul type="disc">
<li class="MsoNormal" style="mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; tab-stops: list 36.0pt; mso-list: l0 level1 lfo1"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我们将不能可靠的使用</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">RGS</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">脚本，因为</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">&quot;txtfile&quot;</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">可能不是正确的键名。</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana"> <o:p></o:p></span></li>
<li class="MsoNormal" style="mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; tab-stops: list 36.0pt; mso-list: l0 level1 lfo1"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">其他的一些文本编辑器可能被安装，它们同</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">.TXT</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">文件相关联。如果它们改变了</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana"> </span><code><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'">HKCR\.txt</span></code><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">键的默认值，所有存在的外壳扩展都将会停止工作。</span><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana"> <o:p></o:p></span></li>
</ul>
<p style="TEXT-INDENT: 18pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">看起来，这的确是我设计的缺陷。我想微软也在考虑同样的事，因为最近创建的扩展，像</span><span lang="EN-US">QueryInfo</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">扩展，是在</span><span lang="EN-US">.txt</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">键下注册的。</span></p>
<p style="TEXT-INDENT: 18pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">好了，说明到这儿。有一个最终的注册细节。在</span><span lang="EN-US">Win NT/2000</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下，我们必须自己将我们的扩展方到一个&ldquo;被认可的&rdquo;扩展列表当中。如果我们不这么做的话，那些非管理员用户将不会壮在我们的扩展。这个列表被保存在：</span></p>
<p><code><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.0pt">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved</span></code><span lang="EN-US" style="FONT-SIZE: 9pt; mso-bidi-font-size: 10.0pt"><o:p></o:p></span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在这个键下，我们创建一个字符串值它的名称是我们的</span><span lang="EN-US">GUID</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。字符串的内容可以是任何东西。做这些事情的代码在我们的</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">DllRegisterServer()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">和</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">DllUnregisterServer()</span></code><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">函数当中。我并不想把这些呆马列在这儿，因为这只是简单的注册表访问。你可以从本文的例子项目当中找到它们。</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">调试外壳扩展</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">最终，你写成了这个相当不容易的扩展，然后你将会调试它。打开你的项目设置</span><span lang="EN-US">(Project-&gt;Settings)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，到</span><span lang="EN-US">Debug</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">栏，在</span><span lang="EN-US">&quot;Executable for debug session&quot;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">编辑框中输入资源管理器的全路径，例如：</span><span lang="EN-US">&quot;C:\windows\explorer.exe&quot;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。如果你使用的是</span><span lang="EN-US">NT</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">或</span><span lang="EN-US">2000</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，而且你已经设置过了</span><code><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">DesktopProcess</span></code><span lang="EN-US"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">注册表项，那么在你按</span><span lang="EN-US">F5</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">开始调试的时候，会有一个新的资源管理器窗口打开。只要你在那个窗口工作，以后重建</span><span lang="EN-US">DLL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">时你将不会有问题，因为当你关掉窗口时，你的扩展也被卸载了。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在</span><span lang="EN-US">Windows 9x</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下，恐怕你不得不在调试之前关闭你的外壳。单击&ldquo;开始&rdquo;</span><span lang="EN-US">-&gt;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">&ldquo;关闭系统&rdquo;。按住</span><span lang="EN-US">Ctrl+Alt+Shift</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">然后点击&ldquo;取消&rdquo;。这将关闭资源管理器，然后你看见任务栏消失了。切换到</span><span lang="EN-US">MSVC</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">然后按</span><span lang="EN-US">F5</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">开始调试。按</span><span lang="EN-US">Shift+F5</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">关闭资源管理器停止调试。当你做完调试的时候，你可以运行</span><span lang="EN-US">Explorer</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">重新正常启动你的外壳。</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">它看起来是什么样的？</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下面是我们添加的项目看起来的样子：</span></p>
<p><img alt="" hspace="" src="http://www.codeproject.com/shell/ShellExtGuide1/ShellExGuide1_3.jpg" align="baseline" border="0" /></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">这就是我们的菜单！</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下面是有敏感帮助时资源管理器的状态栏的样子：</span></p>
<p><img alt="" hspace="" src="http://www.codeproject.com/shell/ShellExtGuide1/ShellExGuide1_4.jpg" align="baseline" border="0" /></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">而下面是消息框的样子，它显示了被选中的文件的文件名：</span></p>
<p><img alt="" hspace="" src="http://www.codeproject.com/shell/ShellExtGuide1/ShellExGuide1_5.jpg" align="baseline" border="0" /></p>
<p><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">本例程代码下载地址</span><span lang="EN-US">(11K)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">：</span><span lang="EN-US">http://www.codeproject.com/shell/ShellExtGuide1/ShellExtGuide1_demo.zip</span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下一部分&hellip;&hellip;</span></h2>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">接着的第二部分，一个新的上下文菜单扩展将会告诉你如何同时对多个文件进行操作。</span></p>
<p style="MARGIN-LEFT: 21pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">你可以从下面的网址获得这个和其他文章的最新版本：</span><span lang="EN-US"><a href="http://home.inreach.com/mdunn/code/" target="_blank">http://home.inreach.com/mdunn/code/</a> </span></p>
<p><span lang="EN-US">&nbsp;<o:p></o:p></span></p>
<h2><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">关于翻译：</span></h2>
<p><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">这是我第一次翻译文章，文章来自著名的</span><span lang="EN-US">http://www.codeproject.com/</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，翻译之前我看了</span><span lang="EN-US">csdn</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的开发文档，发现还是空白，所以就像把它翻译了，也许有对它感兴趣的人。文章总共有</span><span lang="EN-US">9</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">个部分。我没有太多的时间，只翻译了第一部分，也许能起到抛砖引玉的作用，让那些对外壳扩展不了解的人入个门，入了门的多个参考。更多的文章大家可以从</span><span lang="EN-US"><a href="http://www.codeproject.com/shell/">http://www.codeproject.com/shell/</a> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">找到。例子代码，原文也可以从那儿找到。我的</span><span lang="EN-US">email</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">是</span><span lang="EN-US">mefish@163.net</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">，头一次翻译，做得不好，任何意见、建议、鲜花、掌声、石头、带酒的啤酒瓶都将受到热烈欢迎&hellip;&hellip;</span></p>
</div>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/283.html" title="免费软件套装 (2007-01-08)">免费软件套装</a> (0)</li>
	<li><a href="http://www.donevii.com/post/312.html" title="IE编程 &#8211; ToolBar (2007-03-24)">IE编程 &#8211; ToolBar</a> (0)</li>
	<li><a href="http://www.donevii.com/post/203.html" title="使用memcached进行内存缓存 (2006-11-15)">使用memcached进行内存缓存</a> (0)</li>
	<li><a href="http://www.donevii.com/post/78.html" title="[转]C/C++/Perl/汇编/Java效率比较 (2006-10-11)">[转]C/C++/Perl/汇编/Java效率比较</a> (0)</li>
	<li><a href="http://www.donevii.com/post/315.html" title="Shell编程 &#8211; 傻瓜教程2 (2007-03-24)">Shell编程 &#8211; 傻瓜教程2</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/314.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP5 效率优化</title>
		<link>http://www.donevii.com/post/290.html</link>
		<comments>http://www.donevii.com/post/290.html#comments</comments>
		<pubDate>Sat, 13 Jan 2007 10:53:44 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[doc]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ror]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/?p=290</guid>
		<description><![CDATA[静态调用的成员一定要定义成 static&#160;&#160;(PHP5 ONLY) PHP 5 引入了静态成员的概念，作用和 PHP 4 的函数内部静态变量一致，但前者是作为类的成员来使用。静态变量和 Ruby 的类变量(class variable)... ]]></description>
			<content:encoded><![CDATA[<p><strong><font color="#0000ff" size="2">静态调用的成员一定要定义成 static&nbsp;&nbsp;(PHP5 ONLY)</font></strong></p>
<p>PHP 5 引入了静态成员的概念，作用和 PHP 4 的函数内部静态变量一致，但前者是作为类的成员来使用。静态变量和 Ruby 的类变量(<a href="http://www.donevii.com/post/tag/class" class="st_tag internal_tag" rel="tag" title="Posts tagged with class">class</a> variable)差不多，所有类的实例共享同一个静态变量。</p>
<p><font face="Courier New"><font color="#008080">&lt;?php<br /></font><font color="#0000ff"><a href="http://www.donevii.com/post/tag/class" class="st_tag internal_tag" rel="tag" title="Posts tagged with class">class</a> </font><font color="#008080">foo </font></font><font face="Courier New"><font color="#0000ff">{<br />&nbsp; &nbsp; function </font><font color="#008080">bar</font></font><font face="Courier New"><font color="#0000ff">() {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;echo </font><font color="#008000">&#8216;foobar&#8217;</font></font><font face="Courier New"><font color="#0000ff">;<br />&nbsp; &nbsp; }<br />}</p>
<p></font><font color="#008080">$foo </font><font color="#0000ff">= new </font><font color="#008080">foo</font></font><font face="Courier New" color="#0000ff">;</p>
<p></font><font face="Courier New"><font color="#808080">// instance way</p>
<p></font><font color="#008080">$foo</font><font color="#0000ff">-&gt;</font><font color="#008080">bar</font></font><font face="Courier New" color="#0000ff">();</p>
<p></font><font face="Courier New"><font color="#808080">// static way</p>
<p></font><font color="#008080">foo</font><font color="#0000ff">::</font><font color="#008080">bar</font></font><font face="Courier New"><font color="#0000ff">();<br /></font><font color="#008080">?&gt;</font></p>
<p></font>静态地调用非 static 成员，效率会比静态地调用 static 成员慢 50-60%。主要是因为前者会产生 E_STRICT 警告，内部也需要做转换。</p>
<p><strong><font color="#0000ff">使用类常量 (PHP5 ONLY)</font></strong></p>
<p>PHP 5 新功能，类似于 C++ 的 const。</p>
<p>使用类常量的好处是：</p>
<p>- 编译时解析，没有额外开销<br />- 杂凑表更小，所以内部查找更快<br />- 类常量仅存在于特定「命名空间」，所以杂凑名更短<br />- 代码更干净，使除错更方便</p>
<p><strong><font color="#0000ff">(暂时)不要使用 require/include_once</font></strong></p>
<p>require/include_once 每次被调用的时候都会打开目标文件！</p>
<p>- 如果用绝对路径的话，PHP 5.2/6.0 不存在这个问题<br />- 新版的 APC 缓存系统已经解决这个问题</p>
<p>文件 I/O 增加 =&gt; 效率降低</p>
<p>如果需要，可以自行检查文件是否已被 require/include。</p>
<p><strong><font color="#0000ff">不要调用毫无意义的函数</font></strong></p>
<p>有对应的常量的时候，不要使用函数。</p>
<p><font face="Courier New"><font color="#008080">&lt;?php<br />php_uname</font><font color="#0000ff">(</font><font color="#008000">&#8216;s&#8217;</font><font color="#0000ff">) == </font><font color="#008080">PHP_OS</font></font><font face="Courier New"><font color="#0000ff">;<br /></font><font color="#008080">php_version</font><font color="#0000ff">() == </font><font color="#008080">PHP_VERSION</font></font><font face="Courier New"><font color="#0000ff">;<br /></font><font color="#008080">php_sapi_name</font><font color="#0000ff">() == </font><font color="#008080">PHP_SAPI</font></font><font face="Courier New"><font color="#0000ff">;<br /></font><font color="#008080">?&gt;</font><br /></font>虽然使用不多，但是效率提升大概在 3500% 左右。</p>
<p><strong><font color="#0000ff">最快的 Win32 检查</font></strong></p>
<p><font face="Courier New"><font color="#008080">&lt;?php<br />$is_win </font><font color="#0000ff">= </font><font color="#008080">DIRECTORY_SEPARATOR </font><font color="#0000ff">== &#8216;\\&#8217;</font></font><font face="Courier New"><font color="#008080">;<br />?&gt;</font></p>
<p></font>- 不用函数<br />- Win98/NT/2000/XP/Vista/Longhorn/Shorthorn/Whistler&#8230;通用<br />- 一直可用</p>
<p><strong><font color="#ffa500">时间问题 (PHP&gt;5.1.0 ONLY)</font></strong></p>
<p>你如何在你的软件中得知现在的时间？简单，「time() time() again, you ask me&#8230;」。</p>
<p>不过总归会调用函数，慢。</p>
<p>现在好了，用 $_SERVER['REQUEST_TIME']，不用调用函数，又省了。</p>
<p><strong><font color="#0000ff">加速 PCRE</font></strong></p>
<p>&nbsp;对于不用保存的结果，不用 ()，一律用 (?:)</p>
<p>这样 PHP 不用为符合的内容分配内存，省。效率提升 15% 左右。</p>
<p>- 能不用正则，就不用正则，在分析的时候仔细阅读手册「字符串函数」部分。有没有你漏掉的好用的函数？</p>
<p><strong><font color="#0000ff">加速 strtr</font></strong></p>
<p>如果需要转换的全是单个字符的时候，用字符串而不是数组来做 strtr：</p>
<p><font face="Courier New"><font color="#008080">&lt;?php<br />$addr </font><font color="#0000ff">= </font><font color="#008080">strtr</font><font color="#0000ff">(</font><font color="#008080">$addr</font><font color="#0000ff">, </font><font color="#008000">&quot;abcd&quot;</font><font color="#0000ff">, </font><font color="#008000">&quot;efgh&quot;</font><font color="#0000ff">); </font></font><font face="Courier New"><font color="#808080">// good<br /></font><font color="#008080">$addr </font><font color="#0000ff">= </font><font color="#008080">strtr</font><font color="#0000ff">(</font><font color="#008080">$addr</font><font color="#0000ff">, array(</font><font color="#008000">&#8216;a&#8217; </font><font color="#0000ff">=&gt; </font><font color="#008000">&#8216;e&#8217;</font></font><font face="Courier New" color="#0000ff">,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</font><font face="Courier New"><font color="#808080">// &#8230;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</font><font color="#0000ff">)); </font></font><font face="Courier New"><font color="#808080">// bad<br /></font><font color="#008080">?&gt;</font></font><br />效率提升：10 倍。</p>
<p><strong><font color="#ffa500">不要做无谓的替换</font></strong></p>
<p>即使没有替换，str_replace 也会为其参数分配内存。很慢！解决办法：</p>
<p>- 用 strpos 先查找(非常快)，看是否需要替换，如果需要，再替换</p>
<p>效率：</p>
<p>- 如果需要替换：效率几乎相等，差别在 0.1% 左右。<br />- 如果不需要替换：用 strpos 快 200%。</p>
<p><strong><font color="#0000ff">邪恶的 @ 操作符</font></strong></p>
<p>不要滥用 @ 操作符。虽然 @ 看上去很简单，但是实际上后台有很多操作。用 @ 比起不用 @，效率差距：3 倍。</p>
<p>特别不要在循环中使用 @，在 5 次循环的测试中，即使是先用 error_reporting(0) 关掉错误，在循环完成后再打开，都比用 @ 快。</p>
<p><strong><font color="#0000ff">善用 strncmp</font></strong></p>
<p>当需要对比「前 n 个字符」是否一样的时候，用 strncmp/strncasecmp，而不是 substr/strtolower，更不是 PCRE，更千万别提 ereg。strncmp/strncasecmp 效率最高(虽然高得不多)。</p>
<p><strong><font color="#0000ff">慎用 substr_compare (PHP5 ONLY)</font></strong></p>
<p>按照上面的道理，substr_compare 应该比先 substr 再比较快咯。答案是否定的，除非：</p>
<p>- 无视大小写的比较<br />- 比较较大的字符串</p>
<p><strong><font color="#0000ff">不要用常量代替字符串</font></strong></p>
<p>为什么：</p>
<p>- 需要查询杂凑表两次<br />- 需要把常量名转换为小写(进行第二次查询的时候)<br />- 生成 E_NOTICE 警告<br />- 会建立临时字符串</p>
<p>效率差别：700%。</p>
<p><strong><font color="#0000ff">不要把 count/strlen/sizeof 放到 for 循环的条件语句中</font></strong></p>
<p>贴士：我的个人做法</p>
<p><font face="Courier New"><font color="#008080">&lt;?php<br /></font><font color="#0000ff">for (</font><font color="#008080">$i </font><font color="#0000ff">= </font><font color="#008080">0</font><font color="#0000ff">, </font><font color="#008080">$max </font><font color="#0000ff">= </font><font color="#008080">count</font><font color="#0000ff">(</font><font color="#008080">$array</font><font color="#0000ff">);</font><font color="#008080">$i </font><font color="#0000ff">&lt; </font><font color="#008080">$max</font><font color="#0000ff">; ++</font><font color="#008080">$i</font></font><font face="Courier New"><font color="#0000ff">);<br /></font><font color="#008080">?&gt;</font></font></p>
<p>效率提升相对于：</p>
<p>- count 50%<br />- strlen 75%</p>
<p><strong><font color="#ffa500">短的代码不一定快</font></strong></p>
<p><font face="Courier New" color="#008080">&lt;?php<br /></font><font face="Courier New"><font color="#808080">// longest<br /></font><font color="#0000ff">if (</font><font color="#008080">$a </font><font color="#0000ff">== </font><font color="#008080">$b</font></font><font face="Courier New"><font color="#0000ff">) {<br />&nbsp; &nbsp; </font><font color="#008080">$str </font><font color="#0000ff">.= </font><font color="#008080">$a</font></font><font face="Courier New"><font color="#0000ff">;<br />} else {<br />&nbsp; &nbsp; </font><font color="#008080">$str </font><font color="#0000ff">.= </font><font color="#008080">$b</font></font><font color="#0000ff"><font face="Courier New">;<br />}</font><br /></font><font face="Courier New"><font color="#808080">// longer<br /></font><font color="#0000ff">if (</font><font color="#008080">$a </font><font color="#0000ff">== </font><font color="#008080">$b</font></font><font face="Courier New"><font color="#0000ff">) {<br />&nbsp; &nbsp; </font><font color="#008080">$str </font><font color="#0000ff">.= </font><font color="#008080">$a</font></font><font face="Courier New"><font color="#0000ff">;<br />}<br /></font><font color="#008080">$str </font><font color="#0000ff">.= </font><font color="#008080">$b</font></font><font face="Courier New" color="#0000ff">;</p>
<p></font><font face="Courier New"><font color="#808080">// short<br /></font><font color="#008080">$str </font><font color="#0000ff">.= (</font><font color="#008080">$a </font><font color="#0000ff">== </font><font color="#008080">$b </font><font color="#0000ff">? </font><font color="#008080">$a </font><font color="#0000ff">: </font><font color="#008080">$b</font></font><font face="Courier New"><font color="#0000ff">);<br /></font><font color="#008080">?&gt;</font></font><br />你觉得哪个快？</p>
<p>效率比较：</p>
<p>- longest: 4.27<br />- longer: 4.43<br />- short: 4.76</p>
<p>不可思议？再来一个：<br /><font face="Courier New" color="#008080">&lt;?php<br /></font><font face="Courier New"><font color="#808080">// original<br /></font><font color="#008080">$d </font><font color="#0000ff">= </font><font color="#008080">dir</font><font color="#0000ff">(</font><font color="#008000">&#8216;.&#8217;</font></font><font face="Courier New"><font color="#0000ff">);<br />while ((</font><font color="#008080">$entry </font><font color="#0000ff">= </font><font color="#008080">$d</font><font color="#0000ff">-&gt;</font><font color="#008080">read</font><font color="#0000ff">()) !== </font><font color="#008080">false</font></font><font face="Courier New"><font color="#0000ff">) {<br />&nbsp; &nbsp; if (</font><font color="#008080">$entry </font><font color="#0000ff">== </font><font color="#008000">&#8216;.&#8217; </font><font color="#0000ff">|| </font><font color="#008080">$entry </font><font color="#0000ff">== </font><font color="#008000">&#8216;..&#8217;</font></font><font face="Courier New" color="#0000ff">) {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;continue;<br />&nbsp; &nbsp; }<br />}</p>
<p></font><font face="Courier New"><font color="#808080">// versus<br /></font><font color="#008080">glob</font><font color="#0000ff">(</font><font color="#008000">&#8216;./*&#8217;</font></font><font face="Courier New" color="#0000ff">);</p>
<p></font><font face="Courier New"><font color="#808080">// versus (include . and ..)<br /></font><font color="#008080">scandir</font><font color="#0000ff">(</font><font color="#008000">&#8216;.&#8217;</font></font><font face="Courier New"><font color="#0000ff">);<br /></font><font color="#008080">?&gt;</font></font><br />哪个快？</p>
<p>效率比较：</p>
<p>- original: 3.37<br />- glob: 6.28<br />- scandir: 3.42<br />- original without OO: 3.14<br />- SPL (PHP5): 3.95</p>
<p>画外音：从此也可以看出来 PHP5 的面向对象效率提高了很多，效率已经和纯函数差得不太多了。</p>
<p><strong><font color="#0000ff">提高 PHP 文件访问效率</font></strong></p>
<p>需要包含其他 PHP 文件的时候，使用完整路径，或者容易转换的相对路径。</p>
<p><font face="Courier New"><font color="#008080">&lt;?php</p>
<p></font><font color="#0000ff">include </font><font color="#008000">&#8216;file.php&#8217;</font><font color="#0000ff">; </font></font><font face="Courier New"><font color="#808080">// bad approach</p>
<p></font><font color="#008080">incldue </font><font color="#008000">&#8216;./file.php&#8217;</font><font color="#0000ff">; </font></font><font face="Courier New"><font color="#808080">// good</p>
<p></font><font color="#0000ff">include </font><font color="#008000">&#8216;/path/to/file.php&#8217;</font><font color="#0000ff">; </font></font><font face="Courier New"><font color="#808080">// ideal</p>
<p></font><font color="#008080">?&gt;</font></font></p>
<p><strong><font color="#0000ff">物尽其用</font></strong></p>
<p>PHP 有很多扩展和函数可用，在实现一个功能的之前，应该看看 PHP 是否有了这个功能？是否有更简单的实现？</p>
<p><font face="Courier New"><font color="#008080">&lt;?php<br />$filename </font><font color="#0000ff">= </font><font color="#008000">&quot;./somepic.gif&quot;</font></font><font face="Courier New"><font color="#0000ff">;<br /></font><font color="#008080">$handle </font><font color="#0000ff">= </font><font color="#008080">fopen</font><font color="#0000ff">(</font><font color="#008080">$filename</font><font color="#0000ff">, </font><font color="#008000">&quot;rb&quot;</font></font><font face="Courier New"><font color="#0000ff">);<br /></font><font color="#008080">$contents </font><font color="#0000ff">= </font><font color="#008080">fread</font><font color="#0000ff">(</font><font color="#008080">$handle</font><font color="#0000ff">, </font><font color="#008080">filesize</font><font color="#0000ff">(</font><font color="#008080">$filename</font></font><font face="Courier New"><font color="#0000ff">));<br /></font><font color="#008080">fclose</font><font color="#0000ff">(</font><font color="#008080">$handle</font></font><font face="Courier New" color="#0000ff">);</p>
<p></font><font face="Courier New"><font color="#808080">// vs. much simpler</p>
<p></font><font color="#008080">file_get_contents</font><font color="#0000ff">(</font><font color="#008000">&#8216;./somepic.gif&#8217;</font></font><font face="Courier New"><font color="#0000ff">);<br /></font><font color="#008080">?&gt;</font></font></p>
<p><strong><font color="#ffa500">关于引用的技巧</font></strong></p>
<p>引用可以：</p>
<p>- 简化对复杂结构数据的访问<br />- 优化内存使用<br /><font face="Courier New"><font color="#008080">&lt;?php<br />$a</font><font color="#0000ff">[</font><font color="#008000">'b'</font><font color="#0000ff">][</font><font color="#008000">'c'</font></font><font face="Courier New" color="#0000ff">] = array();</p>
<p></font><font face="Courier New"><font color="#808080">// slow 2 extra hash lookups per access<br /></font><font color="#0000ff">for (</font><font color="#008080">$i </font><font color="#0000ff">= </font><font color="#008080">0</font><font color="#0000ff">; </font><font color="#008080">$i </font><font color="#0000ff">&lt; </font><font color="#008080">5</font><font color="#0000ff">; ++</font><font color="#008080">$i</font></font><font face="Courier New"><font color="#0000ff">)<br />&nbsp; &nbsp; </font><font color="#008080">$a</font><font color="#0000ff">[</font><font color="#008000">'b'</font><font color="#0000ff">][</font><font color="#008000">'c'</font><font color="#0000ff">][</font><font color="#008080">$i</font><font color="#0000ff">] = </font><font color="#008080">$i</font></font><font face="Courier New" color="#0000ff">;</p>
<p></font><font face="Courier New"><font color="#808080">// much faster reference based approach<br /></font><font color="#008080">$ref </font><font color="#0000ff">=&amp; </font><font color="#008080">$a</font><font color="#0000ff">[</font><font color="#008000">'b'</font><font color="#0000ff">][</font><font color="#008000">'c'</font></font><font face="Courier New"><font color="#0000ff">];<br />for (</font><font color="#008080">$i </font><font color="#0000ff">= </font><font color="#008080">0</font><font color="#0000ff">; </font><font color="#008080">$i </font><font color="#0000ff">&lt; </font><font color="#008080">5</font><font color="#0000ff">; ++</font><font color="#008080">$i</font></font><font face="Courier New"><font color="#0000ff">)<br />&nbsp; &nbsp; </font><font color="#008080">$ref</font><font color="#0000ff">[</font><font color="#008080">$i</font><font color="#0000ff">] = </font><font color="#008080">$i</font></font><font face="Courier New"><font color="#0000ff">;<br /></font><font color="#008080">?&gt;</font></font><br /><font face="Courier New"><font color="#008080">&lt;?php<br />$a </font><font color="#0000ff">= </font><font color="#008000">&#8216;large string&#8217;</font></font><font face="Courier New" color="#0000ff">;</p>
<p></font><font face="Courier New"><font color="#808080">// memory intensive approach<br /></font><font color="#0000ff">function </font><font color="#008080">a</font><font color="#0000ff">(</font><font color="#008080">$str</font></font><font face="Courier New"><font color="#0000ff">)<br />{<br />&nbsp; &nbsp; return </font><font color="#008080">$str</font><font color="#0000ff">.</font><font color="#008000">&#8216;something&#8217;</font></font><font face="Courier New" color="#0000ff">;<br />}</p>
<p></font><font face="Courier New"><font color="#808080">// more efficient solution<br /></font><font color="#0000ff">function </font><font color="#008080">a</font><font color="#0000ff">(&amp;</font><font color="#008080">$str</font></font><font face="Courier New"><font color="#0000ff">)<br />{<br />&nbsp; &nbsp; </font><font color="#008080">$str </font><font color="#0000ff">.= </font><font color="#008000">&#8216;something&#8217;</font></font><font face="Courier New"><font color="#0000ff">;<br />}<br /></font><font color="#008080">?&gt;</font></font><br />==============================================<br />参考资料<br /><a href="http://ilia.ws/" target="_blank" snap_preview_added="spa">http://ilia.ws</a></p>
<p>Ilia 的个人网站，Blog，他参与的开发以及出版的一些稿物链接等等。<br /><a href="http://ez.no/" target="_blank" snap_preview_added="spa">http://ez.no</a></p>
<p>eZ components 官方网站，eZ comp 是针对 PHP5 的开源通用库，以效率为己任，Ilia 也参与了开发。<br /><a href="http://phparch.com/" target="_blank" snap_preview_added="spa">http://phparch.com</a></p>
<p>php|architect，不错的 php 出版商/培训组织。买不起或者买不到的话，网上可以下到很多经典的盗版。</p>
<p><a href="http://talks.php.net/" target="_blank" snap_preview_added="spa">http://talks.php.net</a></p>
<p></p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/204.html" title="PHP &#038; memcached (2006-11-15)">PHP &#038; memcached</a> (0)</li>
	<li><a href="http://www.donevii.com/post/283.html" title="免费软件套装 (2007-01-08)">免费软件套装</a> (0)</li>
	<li><a href="http://www.donevii.com/post/78.html" title="[转]C/C++/Perl/汇编/Java效率比较 (2006-10-11)">[转]C/C++/Perl/汇编/Java效率比较</a> (0)</li>
	<li><a href="http://www.donevii.com/post/504.html" title="google背后的分布式架构 (2008-09-23)">google背后的分布式架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/110.html" title="翻译：On having layout (2006-10-24)">翻译：On having layout</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/290.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>网页 Meta 标记全面介绍</title>
		<link>http://www.donevii.com/post/235.html</link>
		<comments>http://www.donevii.com/post/235.html#comments</comments>
		<pubDate>Wed, 29 Nov 2006 08:37:00 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[web]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.donevii.com/?p=235</guid>
		<description><![CDATA[META标签是HTML语言HEAD区的一个辅助性标签，它位于HTML文档头部的&#60;HEAD&#62;标记和&#60;TITLE&#62;标记之间，它提供用户不可见的信息。meta标签通常用来为搜索引擎robots定义页面主题，或者是定义... ]]></description>
			<content:encoded><![CDATA[<p>META标签是HTML语言HEAD区的一个辅助性标签，它位于HTML文档头部的&lt;HEAD&gt;标记和&lt;TITLE&gt;标记之间，它提供用户不可见的信息。meta标签通常用来为搜索引擎robots定义页面主题，或者是定义用户浏览器上的cookie；它可以用于鉴别作者，设定页面格式，标注内容提要和关键字；还可以设置页面使其可以根据你定义的时间间隔刷新自己,以及设置RASC内容等级，等等。<br />下面介绍一些有关 标记的例子及解释。<br />　　META标签分两大部分：HTTP标题信息(HTTP-EQUIV)和页面描述信息(NAME)。<br />※ HTTP-EQUIV<br />　　HTTP-EQUIV类似于HTTP的头部协议，它回应给浏览器一些有用的信息，以帮助正确和精确地显示网页内容。常用的HTTP-EQUIV类型有：<br />　　1、Content-Type和Content-Language (显示字符集的设定)<br />　　说明：设定页面使用的字符集，用以说明主页制作所使用的文字已经语言，浏览器会根据此来调用相应的字符集显示page内容。<br />　　用法：&lt;Meta http-equiv=&quot;Content-Type&quot; Content=&quot;text/html; Charset=gb2312&quot;&gt;<br />　　　　　　&lt;Meta http-equiv=&quot;Content-Language&quot; Content=&quot;zh-CN&quot;&gt;<br />　　注意：　该META标签定义了HTML页面所使用的字符集为GB2132，就是国标汉字码。如果将其中的&ldquo;charset=GB2312&rdquo;替换成&ldquo;BIG5&rdquo;，则该页面所用的字符集就是繁体中文Big5码。当你浏览一些国外的站点时，IE浏览器会提示你要正确显示该页面需要下载xx语支持。这个功能就是通过读取HTML页面META标签的Content-Type属性而得知需要使用哪种字符集显示该页面的。如果系统里没有装相应的字符集，则IE就提示下载。其他的语言也对应不同的charset，比如日文的字符集是&ldquo;iso-2022-jp &rdquo;，韩文的是&ldquo;ks_c_5601&rdquo;。<br />　　Content-Type的Content还可以是：text/xml等文档类型；<br />　　Charset选项：ISO-8859-1(英文)、BIG5、UTF-8、SHIFT-Jis、Euc、Koi8-2、us-ascii, x-mac-roman, iso-8859-2, x-mac-ce, iso-2022-jp, x-sjis, x-euc-jp,euc-kr, iso-2022-kr, gb2312, gb_2312-80, x-euc-tw, x-cns11643-1,x-cns11643-2等字符集；Content-Language的Content还可以是：EN、FR等语言代码。<br />　　2、Refresh (刷新)<br />　　　说明：让网页多长时间（秒）刷新自己，或在多长时间后让网页自动链接到其它网页。<br />　　　用法：&lt;Meta http-equiv=&quot;Refresh&quot; Content=&quot;30&quot;&gt;<br />　　　　　　&lt;Meta http-equiv=&quot;Refresh&quot; Content=&quot;5; Url=http://www.xia8.net&quot;&gt;<br />　　　注意：其中的5是指停留5秒钟后自动刷新到URL网址。<br />　　3、Expires (期限)<br />　　　说明：指定网页在缓存中的过期时间，一旦网页过期，必须到服务器上重新调阅。<br />　　　用法：&lt;Meta http-equiv=&quot;Expires&quot; Content=&quot;0&quot;&gt;<br />　　　　　　&lt;Meta http-equiv=&quot;Expires&quot; Content=&quot;Wed, 26 Feb 1997 08:21:57 GMT&quot;&gt;<br />　　　注意：必须使用GMT的时间格式，或直接设为0(数字表示多少时间后过期)。<br />　　4、Pragma (cach模式)<br />　　　说明：禁止浏览器从本地机的缓存中调阅页面内容。<br />　　　用法：&lt;Meta http-equiv=&quot;Pragma&quot; Content=&quot;No-cach&quot;&gt;<br />　　　注意：网页不保存在缓存中，每次访问都刷新页面。这样设定，访问者将无法脱机浏览。<br />　　5、Set-Cookie (cookie设定)<br />　　说明：浏览器访问某个页面时会将它存在缓存中，下次再次访问时就可从缓存中读取，以提高速度。
<p>当你希望访问者每次都刷新你广告的图标，或每次都刷新你的计数器，就要禁用缓存了。通常HTML文件没有必要禁用缓存，对于ASP等页面，就可以使用禁用缓存，因为每次看到的页面都是在服务器动态生成的，缓存就失去意义。如果网页过期，那么存盘的cookie将被删除。<br />　　　用法：&lt;Meta http-equiv=&quot;Set-Cookie&quot; Content=&quot;cookievalue=xxx; expires=Wednesday, 21-Oct-98 16:14:21 GMT; path=/&quot;&gt;<br />　　　注意：必须使用GMT的时间格式。<br />　　6、Window-target (显示窗口的设定)<br />　　　说明：强制页面在当前窗口以独立页面显示。<br />　　　用法：&lt;Meta http-equiv=&quot;Widow-target&quot; Content=&quot;_top&quot;&gt;<br />　　　注意：这个属性是用来防止别人在框架里调用你的页面。Content选项：_blank、_top、_self、_parent。<br />　　7、Pics-label (网页RSAC等级评定)<br />　　　说明：在IE的Internet选项中有一项内容设置，可以防止浏览一些受限制的网站，而网站的限制级别就是通过该参数来设置的。<br />　　　用法：&lt;META http-equiv=&quot;Pics-label&quot; Contect=&quot;(PICS－1.1&#8242;http://www.rsac.org/ratingsv01.html&#8217; I gen comment &#8216;RSACi North America Sever&#8217; by <a href="mailto:'inet@microsoft.com'"><font color="#4563b9">&#8216;inet@microsoft.com&#8217;</font></a>　for &#8216;http://www.microsoft.com&#8217; on &#8217;1997.06.30T14:21－0500&#8242; r(n0 s0 v0 l0))&quot;&gt;<br />　　　注意：不要将级别设置的太高。RSAC的评估系统提供了一种用来评价<a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">Web</a>站点内容的标准。用户可以设置Microsoft Internet Explorer（IE3.0以上）来排除包含有色情和暴力内容的站点。上面这个例子中的HTML取自Microsoft的主页。代码中的（n 0 s 0 v 0 l 0）表示该站点不包含不健康内容。级别的评定是由RSAC，即美国娱乐委员会的评级机构评定的，如果你想进一步了解RSAC评估系统的等级内容，或者你需要评价自己的网站，可以访问RSAC的站点：<a href="http://www.rsac.org/"><font color="#4563b9">http://www.rsac.org/</font></a>。<br />　　8、Page-Enter、Page-Exit (进入与退出)<br />　　　说明：这个是页面被载入和调出时的一些特效。<br />　　　用法：&lt;Meta http-equiv=&quot;Page-Enter&quot; Content=&quot;blendTrans(Duration=0.5)&quot;&gt;<br />　　　　　　&lt;Meta http-equiv=&quot;Page-Exit&quot; Content=&quot;blendTrans(Duration=0.5)&quot;&gt;<br />　　　注意：blendTrans是动态滤镜的一种，产生渐隐效果。另一种动态滤镜RevealTrans也可以用于页面进入与退出效果:<br />　　　　　　&lt;Meta http-equiv=&quot;Page-Enter&quot; Content=&quot;revealTrans(duration=x,transition=y)&quot;&gt;<br />　　　　　　&lt;Meta http-equiv=&quot;Page-Exit&quot; Content=&quot;revealTrans(duration=x,ransition=y)&quot;&gt;<br />　　　　　　　Duration　　表示滤镜特效的持续时间(单位：秒)<br />　　　　　　　Transition　滤镜类型。表示使用哪种特效，取值为0-23。<br />　　　　　　　0 矩形缩小　　　　　　　1 矩形扩大<br />　　　　　　　2 圆形缩小　　　　　　　3 圆形扩大<br />　　　　　　　4 下到上刷新　　　　　　　5 上到下刷新<br />　　　　　　　6 左到右刷新　　　　　　　7 右到左刷新<br />　　　　　　　8 竖百叶窗　　　　　　　9 横百叶窗<br />　　　　　　 10 错位横百叶窗　　　　　　 11 错位竖百叶窗<br />　　　　　　 12 点扩散　　　　　　 13 左右到中间刷新<br />　　　　　　 14 中间到左右刷新　　　　　　 15 中间到上下<br />　　　　　　 16 上下到中间　　　　　　 17 右下到左上<br />　　　　　　 18 右上到左下　　　　　　 19 左上到右下<br />　　　　　　 20 左下到右上　　　　　　 21 横条<br />　　　　　　 22 竖条　　　　　　 23 以上22种随机选择一种<br />　　9、MSThemeCompatible (XP主题)<br />　　　说明：是否在IE中关闭 xp 的主题<br />　　　用法：&lt;Meta http-equiv=&quot;MSThemeCompatible&quot; Content=&quot;Yes&quot;&gt;<br />　　　注意：关闭 xp 的蓝色立体按钮系统显示样式，从而和win2k 很象。<br />　　10、IE6 (页面生成器)<br />　　　说明：页面生成器generator，是ie6<br />　　　用法：&lt;Meta http-equiv=&quot;IE6&quot; Content=&quot;Generator&quot;&gt;<br />　　　注意：用什么东西做的，类似商品出厂厂商。<br />　　11、Content-Script-Type (脚本相关)<br />　　　说明：这是近来W3C的规范，指明页面中脚本的类型。<br />　　　用法：&lt;Meta http-equiv=&quot;Content-Script-Type&quot; Content=&quot;text/javascript&quot;&gt;<br />　　　注意：<br />※NAME变量<br />　　name是描述网页的，对应于Content（网页内容），以便于搜索引擎机器人查找、分类（目前几乎所有的搜索引擎都使用网上机器人自动查找meta值来给网页分类）。<br />　　name的value值（name=&quot;&quot;）指定所提供信息的类型。有些值是已经定义好的。例如description(说明)、keyword(关键字)、refresh(刷新)等。还可以指定其他任意值，如：creationdate(创建日期) 、document ID(文档编号)和level(等级)等。<br />　　name的content指定实际内容。如：如果指定level(等级)为value(值)，则Content可能是beginner(初级)、intermediate(中级)、advanced(高级)。 <br />　　1、Keywords (关键字)<br />　　　说明：为搜索引擎提供的关键字列表<br />　　　用法：&lt;Meta name=&quot;Keywords&quot; Content=&quot;关键词1,关键词2，关键词3,关键词4,&hellip;&hellip;&quot;&gt;<br />　　　注意：各关键词间用英文逗号&ldquo;,&rdquo;隔开。META的通常用处是指定搜索引擎用来提高搜索质量的关键词。当数个META元素提供文档语言从属信息时，搜索引擎会使用lang特性来过滤并通过用户的语言优先参照来显示搜索结果。例如：<br />　　　　　　&lt;Meta name=&quot;Kyewords&quot; Lang=&quot;EN&quot; Content=&quot;vacation,greece,sunshine&quot;&gt;<br />　　　　　　&lt;Meta name=&quot;Kyewords&quot; Lang=&quot;FR&quot; Content=&quot;vacances,gr&egrave;:ce,soleil&quot;&gt;<br />　　2、Description (简介)<br />　　　说明：Description用来告诉搜索引擎你的网站主要内容。<br />　　　用法：&lt;Meta name=&quot;Description&quot; Content=&quot;你网页的简述&quot;&gt;<br />　　　注意：<br />　　3、Robots (机器人向导)<br />　　　说明：Robots用来告诉搜索机器人哪些页面需要索引，哪些页面不需要索引。Content的参数有all、none、index、noindex、follow、nofollow。默认是all。<br />　　　用法：&lt;Meta name=&quot;Robots&quot; Content=&quot;All|None|Index|Noindex|Follow|Nofollow&quot;&gt;<br />　　　注意：许多搜索引擎都通过放出robot/spider搜索来登录网站，这些robot/spider就要用到meta元素的一些特性来决定怎样登录。<br />　　　 all：文件将被检索，且页面上的链接可以被查询；<br />　　　 none：文件将不被检索，且页面上的链接不可以被查询；(和 &quot;noindex, no follow&quot; 起相同作用)<br />　　　 index：文件将被检索；（让robot/spider登录）<br />　　　 follow：页面上的链接可以被查询；<br />　　　 noindex：文件将不被检索，但页面上的链接可以被查询；(不让robot/spider登录)<br />　　　nofollow：文件将不被检索，页面上的链接可以被查询。(不让robot/spider顺着此页的连接往下探找)<br />　　4、Author (作者)<br />　　　说明：标注网页的作者或制作组<br />　　　用法：&lt;Meta name=&quot;Author&quot; Content=&quot;张三，<a href="mailto:abc@sohu.com"><font color="#4563b9">abc@sohu.com</font></a>&quot;&gt;<br />　　　注意：Content可以是：你或你的制作组的名字,或Email<br />　　5、Copyright (版权)<br />　　　说明：标注版权<br />　　　用法：&lt;Meta name=&quot;Copyright&quot; Content=&quot;本页版权归Zerospace所有。All Rights Reserved&quot;&gt;<br />　　　注意：<br />　　6、Generator (编辑器)<br />　　　说明：编辑器的说明<br />　　　用法：&lt;Meta name=&quot;Generator&quot; Content=&quot;PCDATA|FrontPage|&quot;&gt;<br />　　　注意：Content=&quot;你所用编辑器&quot;<br />　　7、revisit-after (重访)<br />　　　说明：<br />　　　用法：&lt;META name=&quot;revisit-after&quot; CONTENT=&quot;7 days&quot; &gt;<br />　　　注意：<br />※Head中的其它一些用法<br />　　1、scheme (方案)<br />　　　说明：scheme can be used when name is used to specify how the value of content should<br />　　　　　　be interpreted.<br />　　　用法：&lt;meta scheme=&quot;ISBN&quot; name=&quot;identifier&quot; content=&quot;0-14-043205-1&quot; /&gt;<br />　　　注意：<br />　　2、Link (链接)<br />　　　说明：链接到文件<br />　　　用法：&lt;Link href=&quot;soim.ico&quot; rel=&quot;Shortcut Icon&quot;&gt;<br />　　　注意：很多网站如果你把她保存在收件夹中后，会发现它连带着一个小图标，如果再次点击进入之后还会发现地址栏中也有个小图标。现在只要在你的页头加上这段话，就能轻松实现这一功能。&lt;LINK&gt; 用来将目前文件与其它 URL 作连结，但不会有连结按钮，用於 &lt;HEAD&gt; 标记间， 格式如下： <br />　　　　　　　&lt;link href=&quot;URL&quot; rel=&quot;relationship&quot;&gt; <br />　　　　　　　&lt;link href=&quot;URL&quot; rev=&quot;relationship&quot;&gt;<br />　　3、Base (基链接)<br />　　　说明：插入网页基链接属性<br />　　　用法：&lt;Base href=&quot;<a href="http://www.csdn.net/"><font color="#4563b9">http://www.csdn.net/</font></a>&quot; target=&quot;_blank&quot;&gt;<br />　　　注意：你网页上的所有相对路径在链接时都将在前面加上&ldquo;http://www.cn8cn.com/&rdquo;。其中</p>
<p>target=&quot;_blank&quot;是链接文件在新的窗口中打开，你可以做其他设置。将&ldquo;_blank&rdquo;改为&ldquo;_parent&rdquo;是链接文件将在当前窗口的父级窗口中打开；改为&ldquo;_self&rdquo;链接文件在当前窗口（帧）中打开；改为&ldquo;_top&rdquo;链接文件全屏显示。</p>
<p>&lt;head&gt;　　<br />&lt;title&gt;文件头，显示在浏览器标题区&lt;/title&gt;<br />&lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;zh-cn&quot;&gt;<br />　&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=gb2312&quot;&gt;<br />　&lt;meta name=&quot;GENERATOR&quot; content=&quot;Microsoft FrontPage 4.0&quot;&gt;<br />　&lt;meta name=&quot;ProgId&quot; content=&quot;FrontPage.Editor.Document&quot;&gt;<br />　&lt;meta name=&quot;制作人&quot; content=&quot;Simonzy&quot;&gt;<br />　&lt;meta name=&quot;主题词&quot; content=&quot;HTML 网页制作 C# .NET JavaScript JS&quot;&gt;<br />&lt;/head&gt;</p>
<p>以上是META标签的一些基本用法，其中最重要的就是：Keywords和Description的设定。为什么呢？道理很简单，这两个语句可以让搜索引擎能准确的发现你，吸引更多的人访问你的站点!根据现在流行搜索引擎(Google，Lycos，AltaVista等)的工作原理，搜索引擎先派机器人自动在WWW上搜索，当发现新的网站时，便于检索页面中的Keywords和Description，并将其加入到自己的数据库，然后再根据关键词的密度将网站排序。</p>

	<h4>相关文章</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.donevii.com/post/9.html" title="flickr对javascript干的好事 (2006-08-17)">flickr对javascript干的好事</a> (0)</li>
	<li><a href="http://www.donevii.com/post/110.html" title="翻译：On having layout (2006-10-24)">翻译：On having layout</a> (0)</li>
	<li><a href="http://www.donevii.com/post/61.html" title="有漏洞的Web 2.0 (2006-09-01)">有漏洞的Web 2.0</a> (0)</li>
	<li><a href="http://www.donevii.com/post/129.html" title="大型高并发高负载网站的系统架构 (2006-10-26)">大型高并发高负载网站的系统架构</a> (0)</li>
	<li><a href="http://www.donevii.com/post/640.html" title="二十一世纪让人悲伤的七个原因 (2008-12-01)">二十一世纪让人悲伤的七个原因</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/235.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
