﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-首页原创精华区</title><link>http://www.cnblogs.com/</link><description>技术改变世界</description><language>zh-cn</language><lastBuildDate>Tue, 09 Feb 2010 18:00:29 GMT</lastBuildDate><pubDate>Tue, 09 Feb 2010 18:00:29 GMT</pubDate><ttl>60</ttl><item><title>【翻译作品】JavaScript Event学习补遗：addEventSimple</title><link>http://www.cnblogs.com/beiyu/archive/2010/02/10/eventSimple.html</link><dc:creator>北玉</dc:creator><author>北玉</author><pubDate>Tue, 09 Feb 2010 17:06:00 GMT</pubDate><guid>http://www.cnblogs.com/beiyu/archive/2010/02/10/eventSimple.html</guid><description><![CDATA[<p>阅读: 35 评论: 1 作者: <a href="http://www.cnblogs.com/beiyu/" target="_blank">北玉</a> 发表于 2010-02-10 01:06 <a href="http://www.cnblogs.com/beiyu/archive/2010/02/10/eventSimple.html" target="_blank">原文链接</a></p><p>这里有个addEventSimple和removeEventSimple函数，有时候需要不那么显眼的事件处理程序的时候我就用这两个函数注册。</p>
<pre><pre class="brush:javascript">function addEventSimple(obj,evt,fn) {
	if (obj.addEventListener)
		obj.addEventListener(evt,fn,false);
	else if (obj.attachEvent)
		obj.attachEvent('on'+evt,fn);
}

function removeEventSimple(obj,evt,fn) {
	if (obj.removeEventListener)
		obj.removeEventListener(evt,fn,false);
	else if (obj.detachEvent)
		obj.detachEvent('on'+evt,fn);
}
</pre>
<br /></pre>
<p>用法如下：</p>
<pre><pre class="brush:javascript">addEventSimple(element,'click',functionname);
addEventSimple(document,'load',functionname2);
</pre>
<br /></pre>
<p>现在如果元素element被点击的话就会触发functionaname，同样的document也有了onload的事件处理程序functionname2。</p>
<p>缺陷：this关键字在IE下面不是你想象的指向触发事件的元素，而是指向整个文档。</p>
<p>原文地址：<a title="http://www.quirksmode.org/js/eventSimple.html" href="http://www.quirksmode.org/js/eventSimple.html">http://www.quirksmode.org/js/eventSimple.html</a></p>
<p>&nbsp;</p>
<p>回老家过年了，不能及时发布了，来年继续，大家新年快乐！</p><img src="http://www.cnblogs.com/beiyu/aggbug/1666892.html?type=1" width="1" height="1" alt=""/><p>评论: 1　<a href="http://www.cnblogs.com/beiyu/archive/2010/02/10/eventSimple.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/beiyu/archive/2010/02/10/eventSimple.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>.NET 4.0 环境下无法直接运行 .NET 2.0 程序的问题，当然目前解决了</title><link>http://www.cnblogs.com/hongcing/archive/2010/02/10/1666880.html</link><dc:creator>洪星</dc:creator><author>洪星</author><pubDate>Tue, 09 Feb 2010 16:09:00 GMT</pubDate><guid>http://www.cnblogs.com/hongcing/archive/2010/02/10/1666880.html</guid><description><![CDATA[<p>阅读: 149 评论: 0 作者: <a href="http://www.cnblogs.com/hongcing/" target="_blank">洪星</a> 发表于 2010-02-10 00:09 <a href="http://www.cnblogs.com/hongcing/archive/2010/02/10/1666880.html" target="_blank">原文链接</a></p><p>由于 .NET 4.0 使用新版本的 CLR，变化也不少，</p>
<p>例如：</p>
<p>mscoree.dll 变为 mscoree.dll 和 mscoreei.dll，</p>
<p>mscorwks.dll 变为 clr.dll，</p>
<p>mscorjit.dll 变为 clrjit.dll。</p>
<p>这导致在没有安装 .NET 2.0 的情况下，.NET 2.0 程序默认无法在 .NET 4.0 环境下运行（未安装 .NET 2.0/3.0/3.5）。（<span style="background-color: #ffffff;"><span style="color: #0000ff;">纠正：不是文件名的变化导致无法运行，而是微软的新策略是强烈建议程序在开发时使用的运行时版本下运行，这样可以保证最大的兼容性</span></span>）</p>
<p>那么怎么办呢？</p>
<p>Visual Studio 2010 对项目可以设置&ldquo;目标框架&rdquo;，在没有安装 .NET 3.5 的情况下可以选择 .NET Framework 4、.NET Framework 4 Client Profile，在改变选择之后，会生成一个配置文件，内容如下：</p>
<p>&nbsp;</p>
<div class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">&lt;?</span><span style="color: #ff00ff;">xml version="1.0"</span><span style="color: #0000ff;">?&gt;</span><span style="color: #000000;"><br /></span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">configuration</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />  </span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">startup</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />    </span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">supportedRuntime </span><span style="color: #ff0000;">version</span><span style="color: #0000ff;">="v4.0"</span><span style="color: #ff0000;"> sku</span><span style="color: #0000ff;">=".NETFramework,Version=v4.0"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />  </span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">startup</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br /></span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">configuration</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br /></span></div></pre>
</div>
<p>把这个文件名改成你需要运行的程序的配置名，同时把程序与配置文件放到一起，这个时候就可以打开 .NET 2.0 的程序了。</p>
<p>&nbsp;</p>
<p>以上只是我当时想出此方法的思路，当然你没有必要这样做。</p>
<p>你只要搞一个配置文件或者将以上配置文件内容添加到已有的配置文件中。</p>
<p>&nbsp;</p>
<p><span style="color: #0000ff;">supportedRuntime version="v4.0"</span> 强制当前程序在&nbsp;CLR&nbsp;4.0 环境下运行，当然你可以设置 1.0、1.1、2.0，前提是运行环境内有对于版本的运行时。（第二次纠正：如果强制在 CLR&nbsp;2.0 下运行，必须设置 <span style="color: #0000ff;">supportedRuntime version="v2.0.50727"</span>，如果设置 v2.0 无效。）</p>
<p>&nbsp;</p>
<p>顺便说明<span style="color: #0000ff;">目前的运行时版本只有 1.0、1.1、2.0、4.0</span>，没有3.0、3.5。</p>
<p>&nbsp;</p>
<p>实际上仅对 Windows XP 需要这样做，因为 XP 默认情况下没有安装任何 .NET 版本，Windows&nbsp;Vista/7 默认情况下分别安装了 .NET 3.0、.NET 3.5，具有 CLR 2.0。</p>
<p>以上做法可能会有兼容性问题，大型复杂程序不推荐这样做。小工具或者自行编写的程序（例如我）可以这样做，出了问题自行解决。这样<span style="color: #0000ff;">在 XP&nbsp;下，就不用安装 N 个 .NET 版本</span>。</p>
<p>本文没有说明的内容，可以自行摸索。</p>
<p>&nbsp;</p><img src="http://www.cnblogs.com/hongcing/aggbug/1666880.html?type=1" width="1" height="1" alt=""/><p>评论: 0　<a href="http://www.cnblogs.com/hongcing/archive/2010/02/10/1666880.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/hongcing/archive/2010/02/10/1666880.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>【翻译作品】JavaScript Event学习第十一章：按键的检测</title><link>http://www.cnblogs.com/beiyu/archive/2010/02/09/keys_detected.html</link><dc:creator>北玉</dc:creator><author>北玉</author><pubDate>Tue, 09 Feb 2010 15:26:00 GMT</pubDate><guid>http://www.cnblogs.com/beiyu/archive/2010/02/09/keys_detected.html</guid><description><![CDATA[<p>阅读: 105 评论: 0 作者: <a href="http://www.cnblogs.com/beiyu/" target="_blank">北玉</a> 发表于 2010-02-09 23:26 <a href="http://www.cnblogs.com/beiyu/archive/2010/02/09/keys_detected.html" target="_blank">原文链接</a></p><p><b>检测用户的按键是事件处理程序的一个很特别的环节。这一章我们着力解决一些非常棘手的问题，并且制定一个完备的表格。</b></p>
<p>第一个问题是对于按键事件来说根本就没有标准，按照<a href="http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-eventgroupings-keyevents">规范</a>说的：包含键盘等输入设备的事件模型会在以后的DOM规范中作出解释。</p>
<p>
正如我们了解到的，浏览器在最开始设计的时候并没有一个标准，大家都像做实验似的，最后的成品虽然是有用的，但是肯定造成了兼容性的问题。按键问题也不例外：这里有两个属性能检测到用户按下了什么键，虽然有足够的理由为什么需要两个属性，但是依然并不是所有的浏览器都支持。</p>
<p>另外，在keypress与keydown和keyup之间还有一些很重要的区别。</p>
<p>最后就是windows和mac的区别了，在mac上要检测用户按下了什么键简直比在windows下难了N倍。 </p>
<h2></h2>
<h2>keyCode和charCode</h2>
<p>能检测到用户按下了什么键的两个属性就是keyCode和charCode了。简单说来：keyCode是用来检测用户真正按下了键盘上那个键的，而charCode则是给出键入字符的ASCII码。有一些小问题需要注意：大写的A和小写的a的keyCode是一样的，因为他们在键盘上就是一个键；但是charCode不一样，因为他俩是两个不同的字符。</p>
<p>IE和Opera不支持charCode。然而他们会在keyCode里面保存字符信息，但是只是在onkeypress的情况下，在onkeydown/up情况下包含的是键的信息。 </p>
<h2></h2>
<h2></h2>
<h2>字符和数字键</h2>
<p>让我们以一个简单的例子开始。小写a的ASCII码是97，大写的是65。那么在两种情况下，当用户在键盘上敲下相同的键的时候，什么时候的键值是65呢（相当于大写的A）</p>
<h3>keyCode</h3>
<p>&nbsp;</p>
<p> <a href="http://images.cnblogs.com/cnblogs_com/beiyu/WindowsLiveWriter/JavaScriptEvent_11AF9/image_2.png"><img style="display: inline; border: 0px;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/beiyu/WindowsLiveWriter/JavaScriptEvent_11AF9/image_thumb.png" border="0" height="248" width="799" /></a></p>
<p>&nbsp;</p>
<h3>charCode</h3>
<p><a href="http://images.cnblogs.com/cnblogs_com/beiyu/WindowsLiveWriter/JavaScriptEvent_11AF9/image_4.png"><img style="display: inline; border: 0px;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/beiyu/WindowsLiveWriter/JavaScriptEvent_11AF9/image_thumb_1.png" border="0" height="239" width="807" /></a> </p>
<p>&nbsp;</p>
<p>因此，在onkeydown/up的情况下，你能够从keyCode里面得到键值。在onkeypress情况下，想要得到字符值就要使用：<code>evt.charCode || evt.keyCode</code>.</p>
<h2></h2>
<h2></h2>
<h2>标点符号</h2>
<p>我还是决定不对标点符号键做测试了。我怀疑这不仅跟浏览器和操作系统有关，还可能跟键盘设置和默认语言有关。我一般用的是荷兰语版的windows，如果跟美国版的101键的键盘相比有很大出入，我一点都不会觉得意外。</p>
<p>比如说shift+,键出来的应该是&lt;，但是我测试的ASCII码的结果却是&rsquo;?&rsquo;的。当我发现了这个问题，我决定还是不在标点符号键的问题上浪费时间了.</p>
<h2></h2>
<h2></h2>
<h2>特别键</h2>
<p>功能键就是只那些不能打印出来的但是却具有一定功能的键。比如shift、ESC、enter等等都是功能键。</p>
<h3>一些说明：</h3>
<p>1、一般，mac的可靠性比windows要差，有些键可能检测不到</p>
<p>2、IE不会触发下面这键的keypress事件：delete, end, enter, escape, 功能键, home, insert, pageUp/Down 和 tab。</p>
<p>3、在onkeypress事件下，Safari会给下面这些键给出很奇怪的keyCode值：delete, end, 功能键, home 和 pageUp.Down。但是在onkeydown/up下面就很正常。</p>
<p>4、Alt,Cmd,Ctrl和shfit键在mac上无法探测，不过Opera下面例外。然而你却可以使用altKey，ctrlKey，shfitKey这些属性。</p>
<p>&nbsp;</p>
<p><b>如果你需要探测这些键，你就探测一下载onkeydown/up下面的keyCode就行了，算是给自己帮忙了，onkeypress和charCode就忘掉吧。</b></p>
<p>&nbsp;</p>
<h2></h2>
<h2>原文后面有个大的键值的列表，还有一个测试框，有兴趣的童鞋可以移步。</h2>
<p>翻译地址：<a href="http://www.quirksmode.org/js/keys.html">http://www.quirksmode.org/js/keys.html</a></p><img src="http://www.cnblogs.com/beiyu/aggbug/1666860.html?type=1" width="1" height="1" alt=""/><p>评论: 0　<a href="http://www.cnblogs.com/beiyu/archive/2010/02/09/keys_detected.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/beiyu/archive/2010/02/09/keys_detected.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Silverlight 代码创建动画 示例</title><link>http://www.cnblogs.com/hongcing/archive/2010/02/09/1666789.html</link><dc:creator>洪星</dc:creator><author>洪星</author><pubDate>Tue, 09 Feb 2010 12:11:00 GMT</pubDate><guid>http://www.cnblogs.com/hongcing/archive/2010/02/09/1666789.html</guid><description><![CDATA[<p>阅读: 384 评论: 4 作者: <a href="http://www.cnblogs.com/hongcing/" target="_blank">洪星</a> 发表于 2010-02-09 20:11 <a href="http://www.cnblogs.com/hongcing/archive/2010/02/09/1666789.html" target="_blank">原文链接</a></p><p>代码中使用了 C# 3.0 语法</p>
<p>效果是一个红色矩形从右下角移动到左上角</p>
<p>仅仅是示例，演示如何在代码中动态创建动画</p>
<p>&nbsp;</p>
<div onclick="cnblogs_code_show('07e7df3c-cdb2-4172-88c2-df29b67d3854')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" class="code_img_closed" id="code_img_closed_07e7df3c-cdb2-4172-88c2-df29b67d3854" style="display: none;" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('07e7df3c-cdb2-4172-88c2-df29b67d3854',event)" class="code_img_opened" id="code_img_opened_07e7df3c-cdb2-4172-88c2-df29b67d3854" /><span class="cnblogs_code_collapse">MainPage.xaml</span>
<div id="cnblogs_code_open_07e7df3c-cdb2-4172-88c2-df29b67d3854">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">UserControl </span><span style="color: #ff0000;">x:Class</span><span style="color: #0000ff;">="Hongcing.Silverlight.Create_And_Run_Animation"</span><span style="color: #ff0000;"><br />        xmlns</span><span style="color: #0000ff;">="http://schemas.microsoft.com/winfx/2006/xaml/presentation"</span><span style="color: #ff0000;"><br />        xmlns:x</span><span style="color: #0000ff;">="http://schemas.microsoft.com/winfx/2006/xaml"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />    </span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Canvas </span><span style="color: #ff0000;">Loaded</span><span style="color: #0000ff;">="LayoutRoot_Loaded"</span><span style="color: #ff0000;"> </span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br /></span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">UserControl</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br /></span></div></pre>
</div>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<div onclick="cnblogs_code_show('148d33a9-0f4c-4455-82e5-e85513ea4217')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" class="code_img_closed" id="code_img_closed_148d33a9-0f4c-4455-82e5-e85513ea4217" style="display: none;" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('148d33a9-0f4c-4455-82e5-e85513ea4217',event)" class="code_img_opened" id="code_img_opened_148d33a9-0f4c-4455-82e5-e85513ea4217" /><span class="cnblogs_code_collapse">MainPage.xaml.cs</span>
<div id="cnblogs_code_open_148d33a9-0f4c-4455-82e5-e85513ea4217">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">using</span><span style="color: #000000;"> System;<br /></span><span style="color: #0000ff;">using</span><span style="color: #000000;"> System.Windows;<br /></span><span style="color: #0000ff;">using</span><span style="color: #000000;"> System.Windows.Controls;<br /></span><span style="color: #0000ff;">using</span><span style="color: #000000;"> System.Windows.Media;<br /></span><span style="color: #0000ff;">using</span><span style="color: #000000;"> System.Windows.Media.Animation;<br /></span><span style="color: #0000ff;">using</span><span style="color: #000000;"> System.Windows.Shapes;<br /><br /></span><span style="color: #0000ff;">namespace</span><span style="color: #000000;"> Hongcing.Silverlight<br />{<br />    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">partial</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Create_And_Run_Animation : UserControl<br />    {<br />        </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Create_And_Run_Animation()<br />        {<br />            InitializeComponent();<br />        }<br /><br />        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> LayoutRoot_Loaded(</span><span style="color: #0000ff;">object</span><span style="color: #000000;"> sender, RoutedEventArgs e)<br />        {<br />            var redRectangle </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Rectangle<br />            {<br />                Width </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">300</span><span style="color: #000000;">,<br />                Height </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">200</span><span style="color: #000000;">,<br />                Fill </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> SolidColorBrush(Colors.Red),<br />                Stroke </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> SolidColorBrush(Colors.Black)<br />            };<br /><br />            (sender </span><span style="color: #0000ff;">as</span><span style="color: #000000;"> Panel).Children.Add(redRectangle);<br /><br />            var leftAnimation </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DoubleAnimation<br />            {<br />                Duration </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Duration(TimeSpan.FromSeconds(</span><span style="color: #800080;">5</span><span style="color: #000000;">)),<br />                From </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">700</span><span style="color: #000000;">,<br />                To </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;"><br />            };<br /><br />            var topAnimation </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DoubleAnimation<br />            {<br />                Duration </span><span style="color: #000000;">=</span><span style="color: #000000;"> leftAnimation.Duration,<br />                From </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">350</span><span style="color: #000000;">,<br />                To </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;"><br />            };<br /><br />            Storyboard.SetTarget(leftAnimation, redRectangle);<br />            Storyboard.SetTarget(topAnimation, redRectangle);<br />            Storyboard.SetTargetProperty(leftAnimation, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> PropertyPath(</span><span style="color: #800000;">"</span><span style="color: #800000;">(Canvas.Left)</span><span style="color: #800000;">"</span><span style="color: #000000;">));<br />            Storyboard.SetTargetProperty(topAnimation, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> PropertyPath(</span><span style="color: #800000;">"</span><span style="color: #800000;">(Canvas.Top)</span><span style="color: #800000;">"</span><span style="color: #000000;">));<br /><br />            </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Storyboard { Children </span><span style="color: #000000;">=</span><span style="color: #000000;"> { leftAnimation, topAnimation } }.Begin();<br />        }<br />    }<br />}<br /></span></div></pre>
</div>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p><img src="http://www.cnblogs.com/hongcing/aggbug/1666789.html?type=1" width="1" height="1" alt=""/><p>评论: 4　<a href="http://www.cnblogs.com/hongcing/archive/2010/02/09/1666789.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/hongcing/archive/2010/02/09/1666789.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>做一个优秀的PM（1）--论赞美和尊重</title><link>http://www.cnblogs.com/Rousef/archive/2010/02/09/1666786.html</link><dc:creator>星际迷茫</dc:creator><author>星际迷茫</author><pubDate>Tue, 09 Feb 2010 12:09:00 GMT</pubDate><guid>http://www.cnblogs.com/Rousef/archive/2010/02/09/1666786.html</guid><description><![CDATA[<p>阅读: 577 评论: 7 作者: <a href="http://www.cnblogs.com/Rousef/" target="_blank">星际迷茫</a> 发表于 2010-02-09 20:09 <a href="http://www.cnblogs.com/Rousef/archive/2010/02/09/1666786.html" target="_blank">原文链接</a></p><p>最近一个进项目组时间不长的员工完成一个较为复杂的js代码给我查看，我看了以后，提出了些待改进的问题就走了。刚走了没有多久，听到这个员工小声对旁边的人说：X 哥都不表扬我。</p>
<p>成功学大师卡耐基的著作&#8220;人性的弱点&#8221;前几章重点讲述了赞美的意义，（第一章，做个批评的吝啬鬼，第二章，给别人留足面子，<strong>第三章，用赞美来激励他人</strong>，第四章不要对别人的优点视而不见）。他在文章里说：&#8220;我们都希望得到认可和赏识，而且会尽一切努力去得到它。&#8221;&#8220;一个重要的原则是，称赞最微小的进步并称赞其每一次进步。&#8221;现在，我能真切地体会到赞美的重要性。我们都能记住小时候，老师对自己的某次表扬，某次考试或者表现在班级里出类拔萃。。如果你还对赞美有怀疑的话，我想列举下面几个事实：</p>
<ul><li>为什么玩家对网游上瘾？因为游戏中的每一次战斗获得的东西和经验，每一次升级的能力提高都让玩家有莫大的满足感。这会让你去做一些自己都不知道为什么要做的事情，只为了让自己愉悦和满足，去获得其他玩家的尊重。现实社会对你努力的漠然导致只有游戏才能最好激励你继续付出，每一次的付出，每一次的金币和经验的积累。一些貌似毫无意义的虚拟经验和财富驱动着无数玩家在废寝忘食中如痴如醉。</li><li>玫琳凯是美国历史上最伟大的女性企业家，在玫琳凯早期职业生涯中，一些不愉快的经验教给她很多待人之道。一次她参加了一整天销售讲习，有位销售经理做了一场激励士气的演讲，玫琳凯很渴望和他握手。&#8220;我在队伍中排了3个小时，好不容易轮到我和他见面，但他从未正眼瞧过我一眼，只是从我肩膀上望过去，看看队伍还有多长，他甚至没有察觉到我正在和他握手。虽然我明白他很累，但我也是一样&#8212;&#8212;在队伍中等待了3个小时，我的疲惫并不亚于他！我觉得受到了伤害和侮辱，因为他根本没有把我看在眼里。从那时起，我便下定决心，如果有一天人们排队来和我握手，我将给每一位来到我面前的人全然的关注，不管我自己是多么疲劳！&#8221;在玫琳凯公司成为一家大公司后，玫琳凯曾多次站在长长的队伍前，和上百位人士握手长达数小时。一旦她感到累了，她总是想起自己从前排队和那位销售经理握手的情形，并立即打起精神，直视握手者的眼睛，尽可能地说些比较亲切的话。也许只是几句简短的闲谈，如&#8220;我喜欢你的发型&#8221;或是&#8220;你的衣裳漂亮极了&#8221;，但她尽可能给予对方全然的注意，而且决不允许其他事情打扰自己。&#8220;在握手的同时，我都将对方视为最重要的人。&#8221;</li><li>比尔盖茨在１９７５年母亲节时给母亲寄了一张卡片，他用斜体英文写了这么一段：我爱您！，妈妈，您从来不说我比别的孩子差，您总是从我在干得事情当中，不断寻找值得赞许的地方。我怀念和您一起的所有时光。</li></ul>
<p>　下次我一定会找个机会对这个同事说：你这次写的代码非常出色，完全实现了想要的效果，我在你这个年龄的时候远达不到这个能力。当然这里面对日后的重用性没有考虑，程序写的比较固定。我相信以后能看到你在这方面有更大的进步。</p><img src="http://www.cnblogs.com/Rousef/aggbug/1666786.html?type=1" width="1" height="1" alt=""/><p>评论: 7　<a href="http://www.cnblogs.com/Rousef/archive/2010/02/09/1666786.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/Rousef/archive/2010/02/09/1666786.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>[WPF]使用自定义Panel更好地控制Resize时的行为——之二</title><link>http://www.cnblogs.com/nankezhishi/archive/2010/02/09/customPanel2.html</link><dc:creator>南柯之石</dc:creator><author>南柯之石</author><pubDate>Tue, 09 Feb 2010 12:06:00 GMT</pubDate><guid>http://www.cnblogs.com/nankezhishi/archive/2010/02/09/customPanel2.html</guid><description><![CDATA[<p>阅读: 218 评论: 2 作者: <a href="http://www.cnblogs.com/nankezhishi/" target="_blank">南柯之石</a> 发表于 2010-02-09 20:06 <a href="http://www.cnblogs.com/nankezhishi/archive/2010/02/09/customPanel2.html" target="_blank">原文链接</a></p><p>考虑下面这样的界面布局。 <p><a href="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_2.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_thumb.png" width="508" height="208"></a>  <p>对于这个小窗口而言，东西少。可以把窗口定死在500*300这样。但是如果这个表单是属于一个大窗体的一个部分。情况就比较复杂了。 <p>我们并不能把窗口定死大小。因为窗口有可能会需要在1920*1200到1024*768等不同的分辨率下运行。这样这个表单在不同情况下的大小就有可能有很大的差别。 <p>一个比较好的方案是，当窗口变小，第一行放不下所有控件时。换到第二行。当窗口变大，第一行有多余地方时，把第二行的内容放在第一行上，使界面的右边不留空白。 <p>有人会大叫这不就是WrapPanel做的事情吗？没有错，但是WrapPanel的每一行都是左对齐的，右边会有大片的留白，这在想要设计有良好用户体验的软件中，是不可接受的。 <p>也就是说WPF自带的WrapPanel没有处理好下面这种情况。 <p><b>第一行有多余的空间，但是又不足以把第二行的什么控件放上来。</b> <p>这时，<b>第一行的现有的控件应该充分利用第一行的所有可用空间，把第一行填满。</b>这个是用WrapPanel做不到的。WrapPanel主要用于内部的Item的大小基本一样的情况。如果大小不一，WrapPanel就会造成不好的用户体验。还不如用Grid做等比例缩放好。 <p>很可惜，现在我们又要自己写一个WrapPanel了。如下图所示。 <p><a href="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_4.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_thumb_1.png" width="508" height="118"></a>  <p>图1. FillWrapPanel Demo <p>在这个Panel里的所有的Button都有 20的MinWidth和55的MaxWidth。并特意为3号Button设置了30的MaxWidth，为6号Button设置了100的MaxWidth。我们来看一下这个Panel在Resize时的行为。 <p><a href="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_6.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_thumb_2.png" width="429" height="118"></a>  <p>图2. 再小就要换行了（原则是让所有控件都尽可能大，但是右边又不能留白） <p><a href="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_8.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_thumb_3.png" width="274" height="118"></a>  <p>图3. 缩到6号Button <p><a href="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_10.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_thumb_4.png" width="178" height="145"></a>  <p>图4. 缩到4号Button <p><a href="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_12.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_thumb_5.png" width="140" height="145"></a>  <p>图5. 最小 <p>如果用WPF的WrapPanel呢？结果很简单。 <p><a href="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_14.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/nankezhishi/WindowsLiveWriter/WPFPanelResize_119EF/image_thumb_6.png" width="298" height="115"></a>  <p>图6. WPF的WrapPanel（Item的大小是确定的，不会变化，而且右边会留白） <p>系统的WrapPanel很适于实现Explorer里浏览文件、文件夹或是看图软件的缩略图模式。而本文中介绍的FillWrapPanel更适于实现类似表单的窗体。因为里面的东西大小很可能是各不相同的。   <img src="http://www.cnblogs.com/nankezhishi/aggbug/1666784.html?type=1" width="1" height="1" alt=""/><p>评论: 2　<a href="http://www.cnblogs.com/nankezhishi/archive/2010/02/09/customPanel2.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/nankezhishi/archive/2010/02/09/customPanel2.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Ubuntu 9.10 Server 配置基于SSL的Subversion</title><link>http://www.cnblogs.com/basilwang/archive/2010/02/09/1666785.html</link><dc:creator>BAsil</dc:creator><author>BAsil</author><pubDate>Tue, 09 Feb 2010 12:06:00 GMT</pubDate><guid>http://www.cnblogs.com/basilwang/archive/2010/02/09/1666785.html</guid><description><![CDATA[<p>阅读: 253 评论: 0 作者: <a href="http://www.cnblogs.com/basilwang/" target="_blank">BAsil</a> 发表于 2010-02-09 20:06 <a href="http://www.cnblogs.com/basilwang/archive/2010/02/09/1666785.html" target="_blank">原文链接</a></p><p>1 配置准备</p>  <p>&nbsp; Ubuntu 9.10 Server 英文版。服务器版还是以稳定高效为主，也没有安装图形界面，另外主要是通过<a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/" target="_blank">putty.exe</a>远程访问。没有采用中文版的原因是，在shell下，中文字体可能没有安装总显示乱码，拜过谷哥大神，未解决。</p>  <p>2 安装软件及模块一览 </p>  <p>&nbsp; （1）Apache2.2.14&nbsp; 自己下载编译。未用ubuntu 9.10 server 下apt-get安装的apache2 是因为这个版本的apache2在启用ssl的时候总是报undefined symbol apr_ldap_ssl_init错误，此处我怀疑可能是ubuntu9.10 server的问题,我在虚拟机ubuntu 8.04 desktop下配置成功。</p>  <p>&nbsp; （2）OpenSSL 通过apt-get install openssl 安装即可</p>  <p>&nbsp; （3）Subversion 自己编译下载。很不幸，我无法使用ubuntu的subversion来成功配置SSL。</p>  <p>3 Ubuntu &amp; Linux&nbsp;&nbsp; </p>  <p>鉴于大家可能对不同发行版的linux不太了解，我简单介绍一下，并比较apache2的Ubuntu版本和自主安装的安装及配置文件路径</p>  <p>&nbsp;&nbsp; 以下摘自Wiki</p>  <blockquote>   <p>&nbsp;&nbsp;&nbsp;&nbsp; One can distinguish between commercially backed distributions, such as <a href="http://en.wikipedia.org/wiki/Fedora_%28operating_system%29" target="_blank">Fedora</a> (<a href="http://en.wikipedia.org/wiki/Red_Hat" target="_blank">Red Hat</a>), <a href="http://en.wikipedia.org/wiki/OpenSUSE" target="_blank">openSUSE</a> (<a href="http://en.wikipedia.org/wiki/Novell" target="_blank">Novell</a>), <a href="http://en.wikipedia.org/wiki/Ubuntu_%28operating_system%29" target="_blank">Ubuntu</a> (<a href="http://en.wikipedia.org/wiki/Canonical_Ltd." target="_blank">Canonical Ltd.</a>), and <a href="http://en.wikipedia.org/wiki/Mandriva_Linux" target="_blank">Mandriva Linux</a> (<a href="http://en.wikipedia.org/wiki/Mandriva" target="_blank">Mandriva</a>) and entirely community-driven distributions such as <a href="http://en.wikipedia.org/wiki/Debian" target="_blank">Debian</a> and <a href="http://en.wikipedia.org/wiki/Gentoo_Linux" target="_blank">Gentoo</a>, though there are other distributions that are driven neither by a corporation nor a community, perhaps most famously <a href="http://en.wikipedia.org/wiki/Slackware" target="_blank">Slackware</a>.</p> </blockquote>  <p>简单翻译一下，就是说linux有商业支持的发行版如Fedora,openSUSE,Ubuntu和社区推动的版本如Debian和Gentoo。另外Ubuntu本身又是基于Debian的，这点大家应该清楚。</p>  <p>&nbsp;&nbsp;&nbsp; </p>  <table border="0" cellspacing="0" cellpadding="2" width="747"><tbody>     <tr>       <td valign="top" width="142">&nbsp;</td>        <td valign="top" width="282">Ubuntu Apache2</td>        <td valign="top" width="321">自主安装Apache2</td>     </tr>      <tr>       <td valign="top" width="142">安装目录</td>        <td valign="top" width="282">/etc/apache2          <br /></td>        <td valign="top" width="321">指定路径          <br />例如：           <br />/usr/local/apache2           <br /></td>     </tr>      <tr>       <td valign="top" width="142">应用程序</td>        <td valign="top" width="282">/etc/sbin/apache2          <br />/etc/lib/apache2</td>        <td valign="top" width="321">/usr/local/apache2/bin/httpd</td>     </tr>      <tr>       <td valign="top" width="142">服务</td>        <td valign="top" width="282">/etc/init.d/apache2&nbsp; restart</td>        <td valign="top" width="321">/usr/local/apache2/bin/httpd &#8211;k restart&nbsp; <br />（不知还有没有别的方式？)</td>     </tr>      <tr>       <td valign="top" width="142">配置文件</td>        <td valign="top" width="282">/etc/apache2/apache2.conf          <br />/etc/apache2/httpd.conf (通常为空）           <br />/etc/apache2/ports.conf           <br />/etc/apache2/mods_available/           <br />/etc/apache2/sites_available/           <br />apache2.conf负责调用其他的配置文件</td>        <td valign="top" width="321">/usr/local/apache2/conf/httpd.conf          <br />/usr/local/apache2/conf/extra/           <br />httpd.conf负责调用extra文件夹下的配置文件</td>     </tr>   </tbody></table>  <p>4 自主安装Apache2</p>  <p>#sudo wget <a title="http://apache.etoak.com/httpd/httpd-2.2.14.tar.gz" href="http://apache.etoak.com/httpd/httpd-2.2.14.tar.gz" target="_blank">http://apache.etoak.com/httpd/httpd-2.2.14.tar.gz</a>     <br />#sudo tar -zxvf httpd-2.2.14.tar.gz&nbsp;&nbsp;&nbsp;&nbsp; <br />#cd httpd-2.2.14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />#sudo ./configure&nbsp;&nbsp; --enable-dav --enable-dav-fs --enable-so&nbsp;&nbsp; --enable-ssl --enable-maintainer-mode -prefix=/usr/local/apache2&nbsp;&nbsp; --enable-mods-shared=all&nbsp;&nbsp;&nbsp; <br />#sudo make&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />#sudo make install</p>  <p>&nbsp;&nbsp; 注意./configure 时确保enable-dav和enable-ssl，其中dav模块是通过apache2存储文件的基础。    <br />5 安装OpenSSL</p>  <p>#sudo apt-get install openssl</p>  <p>6 安装Subversion</p>  <p># sudo apt-get install build-essential openssl ssh expat libxyssl-dev libssl-dev&nbsp;&nbsp;&nbsp; <br /># sudo apt-get remove subversion&nbsp;&nbsp;&nbsp; <br /># sudo dpkg --purge subversion&nbsp;&nbsp;&nbsp; <br /># wget <a href="http://subversion.tigris.org/downloads/subversion-1.6.2.tar.gz" target="_blank">http://subversion.tigris.org/downloads/subversion-1.6.2.tar.gz</a>     <br /># wget <a href="http://subversion.tigris.org/downloads/subversion-deps-1.6.2.tar.gz" target="_blank">http://subversion.tigris.org/downloads/subversion-deps-1.6.2.tar.gz</a>     <br /># tar xvfz subversion-1.6.2.tar.gz&nbsp;&nbsp;&nbsp; <br /># tar xvfz subversion-deps-1.6.2.tar.gz&nbsp;&nbsp;&nbsp; <br /># cd subversion-1.6.2/neon/&nbsp;&nbsp;&nbsp; <br /># ./configure --prefix=/usr/local --with-ssl --with-pic&nbsp;&nbsp; <br /># make&nbsp;&nbsp;&nbsp; <br /># sudo make install&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br /># ./configure --prefix=/usr/local --with-ssl --with-neon=/usr/local&nbsp;&nbsp;&nbsp; <br /># make&nbsp;&nbsp;&nbsp; <br /># sudo make install&nbsp;&nbsp; </p>  <p>&nbsp; 注意：首先移除ubuntu自带的subversion，随后进行编译安装。其中<a href="http://subversion.tigris.org/downloads/subversion-deps-1.6.2.tar.gz" target="_blank">subversion-deps-1.6.2.tar.gz</a>是Subversion客户端使用的Neon库，它不仅仅可以用来验证服务器证书，也可以必要时提供客户端证书，因此也要安装上</p>  <p>7 配置http访问subversion</p>  <p>&nbsp; 参考了<a href="http://www.javaeye.com/topic/293540" target="_blank">主题：ubuntu下安装svn提供HTTP版本管理服务</a>和<a href="http://www.howtogeek.com/howto/ubuntu/install-subversion-with-web-access-on-ubuntu/" target="_blank">Install Subversion with Web Access on Ubuntu</a></p>  <p>(1)#sudo svnadmin create /svn&nbsp;&nbsp; </p>  <p>&nbsp;&nbsp; 可以看一下/svn下是不是增加了些文件&nbsp; <br />(2)#sudo vim /usr/local/apache2/conf/httpd.conf</p>  <p>&nbsp;&nbsp; 增加&nbsp; <br />&nbsp;&nbsp; &lt;Location /svn&gt;     <br />&nbsp;&nbsp;&nbsp;&nbsp; DAV svn     <br />&nbsp;&nbsp;&nbsp;&nbsp; SVNPath /svn     <br />&nbsp;&nbsp;&nbsp;&nbsp; AuthzSVNAccessFile /svn/conf/authz     <br />&nbsp;&nbsp;&nbsp;&nbsp; AuthType Basic     <br />&nbsp;&nbsp;&nbsp;&nbsp; AuthName "Subversion.Tracker"     <br />&nbsp;&nbsp;&nbsp;&nbsp; AuthUserFile /svn/conf/passwd&nbsp;&nbsp;&nbsp;&nbsp; Require valid-user     <br />&nbsp;&nbsp; &lt;/Location&gt;</p>  <p>&nbsp;&nbsp;&nbsp; 这部分应该也可以放置到 conf/extra/httpd_dav.conf文件中，然后将httpd.conf中的 include conf/extra/httpd_dav.conf 注释去掉，不过奇怪的是httpd_dav.conf文件中的示例使用&lt;Directory&gt;而不是&lt;Location&gt;,我未作尝试。</p>  <p>(3)#sudo htpasswd -cm /svn/conf/passwd &lt;username&gt;    <br />&nbsp;&nbsp; 增加一个用户名,可以打开passwd文件查看是否创建成功     <br />&nbsp;&nbsp; 同时在Ubuntu上创建同名用户和相同密码&nbsp; (此处待验证，网络上有的文章没有强调这一点     <br />&nbsp;&nbsp; sudo useradd &lt;username&gt;     <br />&nbsp;&nbsp; sudo passwd &lt;password&gt;</p>  <p>(4)#sudo chown www-data:www-data&nbsp; -R /svn&nbsp; </p>  <p>&nbsp;&nbsp;&nbsp; 将/svn的所有者和所在的组更改为www-data（ubuntu下apache2的账户)</p>  <p>&nbsp;&nbsp;&nbsp; #sudo chmod &#8211;R g+ws /svn</p>  <p>(5)#sudo /usr/local/apache2/bin/httpd &#8211;k restart</p>  <p>&nbsp;&nbsp;&nbsp; 看看<a href="http://localhost/svn" target="_blank">http://localhost/svn</a> 是否可以访问了</p>  <p>8 配置ssl访问subversion</p>  <p>&nbsp; 参考了<a href="http://ywbanm.javaeye.com/blog/341562" target="_blank">Linux SVN+Apache+SSL 安装配置</a>和<a href="http://koda.javaeye.com/blog/288187" target="_blank">Apache SSL配置</a></p>  <p>openssl genrsa -des3 -out ca.key 1024    <br />//按提示输入密码：****     <br />chmod 400 ca.key     <br />//生成证书     <br />openssl req -new -x509 -days 3650 -key ca.key -out ca.crt     <br />//按提示分别输入如下内容：     <br />//Country Name: CN     <br />//State or Province Name:     <br />//Locality Name:     <br />//Organization Name:     <br />//Organizational Unit Name:     <br />//Common Name:     <br />//Email Address: your email     <br />chmod 400 ca.crt     <br />openssl genrsa -des3 -out server.key 1024     <br />//按提示输入密码：     <br />chmod 400 server.key     <br />openssl req -new -key server.key -out server.csr     <br />//按提示分别输入如下内容：     <br />//Country Name: CN     <br />//State or Province Name:     <br />//Locality Name:     <br />//Organization Name:     <br />//Organizational Unit Name:     <br />//Common Name:     <br />//Email Address: your email     <br />(ca.crt 和server.csr 的Common Name不能一样)     <br />openssl req -noout -text -in server.csr     <br />openssl x509 -req -days 365 -in server.csr -signkey server.key -out&nbsp; server.crt     <br />chmod 400 server.crt </p>  <p>vim /usr/local/apache2/conf/httpd.conf    <br />取消Include conf/extra/httpd-ssl.conf前的注释#</p>  <p>打开httpd-ssl.conf文件查看如下两行，确保前面生成的证书放置在该路径下</p>  <p>SSLCertificateFile "/usr/local/apache2/conf/server.crt"</p>  <p>SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"</p>  <p>重启httpd服务</p>  <p>查看<a title="https://202.194.254.21/svn/" href="https://localhost/svn/" target="_blank">https://localhost/svn/</a>&nbsp; 浏览器显示此网站的安全证书有问题，忽略它，输入用户名和密码，至此基于SSL的Subversion配置完成。</p>  <p>9 总结</p>  <p>&nbsp; 配置过程中应该了解ubuntu和其他linux发行版安装软件的不同，掌握make和make install。如碰到apache2不能启动，阅读错误提示以及查看日志(/usr/local/apache2/logs/error.log).</p><img src="http://www.cnblogs.com/basilwang/aggbug/1666785.html?type=1" width="1" height="1" alt=""/><p>评论: 0　<a href="http://www.cnblogs.com/basilwang/archive/2010/02/09/1666785.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/basilwang/archive/2010/02/09/1666785.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>.NET/CLI元数据中使用的压缩整数</title><link>http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Tue, 09 Feb 2010 11:20:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html</guid><description><![CDATA[<p>阅读: 336 评论: 8 作者: <a href="http://www.cnblogs.com/AndersLiu/" target="_blank">Anders Liu</a> 发表于 2010-02-09 19:20 <a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html" target="_blank">原文链接</a></p><div class="al-body">
<h1>.NET/CLI元数据中使用的压缩整数</h1>
<div class="al-copy">
	<p>本文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html"
title=".NET/CLI元数据中使用的压缩整数">http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：.NET/CLI的PE文件中广泛采用了一种整数压缩算法，这种算法可以将一个32位整数根据其大小的不同放置在1、2或4个字节中。当整数的值比较小时，这种算法能够有效地减少PE文件的大小。本文介绍了这种压缩算法，并给出了压缩/解压缩的参考实现。</p>
</div>
<h2>参考文献</h2>
<ul class="dotul">
<li>《ECMA-335——Common Language Infrastructure (CLI) 4th Edition》，June 2006</li>
<li>《Expert .NET 2.0 IL Assembler》，Serge Lidin，Apress，2006</li>
<li>《.NET探秘：MSIL权威指南》（《Expert .NET 2.0 IL Assembler》中文版），Serge Lidin著，包建强 译，人民邮电出版社，2009</li>
</ul>
<h2>简介</h2>
<p>简单来说，整数压缩算法就是将一个32位整数（通常占用4个字节）放置到尽可能少的存储空间中（1、2或4个字节）的方法。</p>
<p>整数压缩算法广泛地应用在.NET/CLI PE文件中，如各种元数据签名、#Blob和#US流等。在这些地方，需要使用整数值来记录条目的数量或是数据块的大小等。如果单纯地采用32位整数，由于绝大多数数量值或大小值都不大，会造成大量字节都被置为无意义的0值。在这些场景中使用压缩算法，可以有效地节省PE文件占用的磁盘空间或网络带宽。</p>
<p>以下是PE文件中一些使用到压缩整数的场景：</p>
<ul class="dotul">
<li>Blob堆（#Blob流和#US流所采用的存储格式）中的每个条目开始处，使用压缩的无符号整数表示条目的大小；</li>
<li>方法的元数据签名中，使用压缩的无符号整数存储参数的数量；</li>
<li>元数据签名中的数组下标，采用压缩的带符号整数进行存储。</li>
</ul>
<p>注意，本文所介绍的压缩与解压算法，都是针对32位整数的。此外，在本文的介绍中，如果没有特殊提及，则所出现的整数都按照大尾数法表示（最高权重字节放在左侧或上方）。</p>
<h2>无符号整数的压缩与解压</h2>
<h3>无符号整数的压缩算法</h3>
<p>无符号整数的压缩是比较简单的，即将无符号整数的整个取值范围划分为几个区段，而整数值根据其所在的区段不同，放置在1、2或4个字节中。表1列出了无符号整数的区段划分和压缩方式。</p>
<table>
<caption>表1 - 无符号整数的区段划分</caption>
<tr><th>区段</th><th>字节数</th><th>掩码</th><th>二进制形式</th></tr>
<tr><td>[00000000h, 0000007Fh]</td><td>1</td><td>80h</td><td>0BBBBBBBB</td></tr>
<tr><td>[00000080h, 00003FFFh]</td><td>2</td><td>C0h</td><td>10BBBBBB BBBBBBBB</td></tr>
<tr><td>[00004000h, 1FFFFFFFh]</td><td>4</td><td>E0h</td><td>110BBBBB BBBBBBBB BBBBBBBB BBBBBBBB</td></tr>
</table>
<p>在表1中：</p>
<ul class="dotul">
<li>“区段”列出了每个区段的最小值（含）和最大值（含）。</li>
<li>“字节数”列出了压缩后的值占用的字节数。</li>
<li>“掩码”列出了在压缩后的值上施加的掩码，
    <ul class="dotul">
    <li>如果压缩后的整数值占用1字节，则与掩码80h进行&amp;（按位与）操作后的结果为0h，</li>
    <li>如果压缩后的整数值占用2字节，则其首字节与掩码C0h进行&amp;操作后的结果是80h，</li>
    <li>如果压缩后的整数值占用4字节，则其首字节与掩码E0h进行&amp;操作后的结果是C0h。</li>
    </ul>
</li>
<li>“二进制形式”列出了压缩结果的二进制形式，其中的“1”和“0”都是固定值，而“B”则表示实际整数值的有效位。</li>
</ul>
<p>从表1可以清晰地看出，无符号整数压缩算法的适用范围是[0h, 1FFFFFFFh]（[0, 536870911]）之内的无符号整数，大于1FFFFFFFh的无符号整数不能用这种方式进行压缩。</p>
<p>代码1给出了无符号整数压缩算法的参考实现。</p>
<div class="al-ins">
<p class="al-ins-title">代码1 - 无符号整数压缩算法的参考实现</p>
<pre class="al-code-para">
public static byte[] CompressUInt(uint data)
{
  if (data <= 0x7F)
  {
    var bytes = new byte[1];
    bytes[0] = (byte)data;
    return bytes;
  }
  else if (data <= 0x3FFF)
  {
    var bytes = new byte[2];
    bytes[0] = (byte)(((data & 0xFF00) >> 8) | 0x80);
    bytes[1] = (byte)(data & 0x00FF);
    return bytes;
  }
  else if (data <= 0x1FFFFFFF)
  {
    var bytes = new byte[4];
    bytes[0] = (byte)(((data & 0xFF000000) >> 24) | 0xC0);
    bytes[1] = (byte)((data & 0x00FF0000) >> 16);
    bytes[2] = (byte)((data & 0x0000FF00) >> 8);
    bytes[3] = (byte)(data & 0x000000FF);
    return bytes;
  }
  else
    throw new NotSupportedException();
}
</pre>
</div>
<h3>无符号整数的解压缩算法</h3>
<p>无符号整数的解压缩算法也非常简单，如下所示：</p>
<ul class="dotul">
<li>如果首字节的二进制形式型如0bbbbbbb（与80h进行按位与运算，结果为0h），则采用1个字节存放整数值（字节值为b0），原整数值=b0。</li>
<li>如果首字节的二进制形式型如10bbbbbb（与C0h进行按位与运算，结果为80h），则采用2个字节存放整数值（字节值依次为b0，b1），原整数值=(b0 &amp; 0x3F) &lt;&lt; 8 | b1。</li>
<li>	如果首字节的二进制形式型如110bbbbb（与E0h进行按位与运算，结果为C0h），则采用4个字节存放整数值（字节值依次为b0，b1，b2，b3），原整数值=(b0 &amp; 0x1F) &lt;&lt; 24 | b1 &lt;&lt; 16 | b2 &lt;&lt; 8 | b3。.</li>
</ul>
<p>代码2给出了无符号整数解压缩算法的参考实现。</p>
<div class="al-ins">
<p class="al-ins-title">代码2 – 无符号整数解压缩算法的参考实现</p>
<pre class="al-code-para">
public static uint DecompressUInt(byte[] data)
{
  if (data == null)
    throw new ArgumentNullException("data");

  if ((data[0] & 0x80) == 0
    && data.Length == 1)
  {
    return (uint)data[0];
  }
  else if ((data[0] & 0xC0) == 0x80
    && data.Length == 2)
  {
    return (uint)((data[0] & 0x3F) << 8 | data[1]);
  }
  else if ((data[0] & 0xE0) == 0xC0
    && data.Length == 4)
  {
    return (uint)((data[0] & 0x1F) << 24
      | data[1] << 16 | data[2] << 8 | data[3]);
  }
  else
    throw new NotSupportedException();
}
</pre>
</div>
<h2>带符号整数的压缩与解压</h2>
<h3>带符号整数的压缩算法</h3>
<p>带符号整数的压缩与解压略微复杂一些，因为需要处理符号位。简单来说，需要在确定好所需的存储字节数之后，将原整数整体向左移1位，然后将符号位放置在最低位上（0表示正数，1表示负数），最后按照同无符号整数一样的方式为首字节设置掩码。</p>
<p>在为带符号整数确定需要用多少个字节来存放压缩值时，需要首先取得原整数的“准绝对值”，即对负数进行按位取反（而不是数学求负），然后将这个“准绝对值”左移1位（为符号位空出最低位），再按照表1列出的区段取得最终占用的字节数。</p>
<p>或者，可以省略左移1位的操作，而是按照表2中列出的区段进行查找。</p>
<table>
<caption>表2 - 带符号整数“准绝对值”的区段划分</caption>
<tr><th>区段</th><th>字节数</th><th>有效位掩码</th></tr>
<tr><td>[00000000h, 0000003Fh]</td><td>1</td><td>0000003Fh</td></tr>
<tr><td>[00000040h, 00001FFFh]</td><td>2</td><td>00001FFFh</td></tr>
<tr><td>[00002000h, 0FFFFFFFh]</td><td>4</td><td>0FFFFFFFh</td></tr>
</table>
<p>在表2中：</p>
<ul class="dotul">
<li>“区段”列出的是根据原整数“准绝对值”划分出的每个区段的最小值（含）和最大值（含）。</li>
<li>“字节数”列出了压缩后的值占用的字节数。</li>
<li>“有效位掩码”列出的掩码在与原整数进行&amp;操作之后，可以取得原整数中真正有意义的位数。这建立在这样一个事实上——对于正整数来说，其最左侧的一些位都是0，是没有意义的，可以省略；而对于负整数来说，其最左侧的一些位都是1，也是没有意义的，可以省略。</li>
</ul>
<p>在与有效位掩码进行&amp;操作取得有效位之后，需要将这些有效位整体左移1位。接下来，如果原整数是负数，则需要将最低位（符号位）置1。</p>
<p>最后，为压缩值的首字节设置掩码，规则与无符号整数一样。</p>
<p>带符号整数压缩算法的适用范围为——对于正数为[0h, 0FFFFFFFh]（[0, 268435455]），对于负数为[F0000000h, FFFFFFFFh]（[-268435456, -1]），在此范围之外的整数不能用这种方式进行压缩。</p>
<p>代码3给出了带符号整数压缩算法的参考实现。</p>
<div class="al-ins">
<p class="al-ins-title">代码3 -带符号整数压缩算法的参考实现</p>
<pre class="al-code-para">
public static byte[] CompressInt(int data)
{
    var u = data >= 0 ? (uint)data : ~(uint)data;
    if (u <= 0x3F)
    {
        var uv = ((uint)data & 0x0000003F) << 1;
        if (data < 0)
            uv |= 0x01;

        var bytes = new byte[1];
        bytes[0] = (byte)uv;
        return bytes;
    }
    else if (u <= 0x1FFF)
    {
        var uv = ((uint)data & 0x00001FFF) << 1;
        if (data < 0)
            uv |= 0x01;

        var bytes = new byte[2];
        bytes[0] = (byte)(((uv & 0xFF00) >> 8) | 0x80);
        bytes[1] = (byte)(uv & 0x00FF);
        return bytes;
    }
    else if (u <= 0x0FFFFFFF)
    {
        var uv = ((uint)data & 0x0FFFFFFF) << 1;
        if (data < 0)
            uv |= 0x01;

        var bytes = new byte[4];
        bytes[0] = (byte)(((uv & 0xFF000000) >> 24) | 0xC0);
        bytes[1] = (byte)((uv & 0x00FF0000) >> 16);
        bytes[2] = (byte)((uv & 0x0000FF00) >> 8);
        bytes[3] = (byte)(uv & 0x000000FF);
        return bytes;
    }
    else
        throw new NotSupportedException();
}
</pre>
</div>
<p>注意，只有在确定压缩值占用的字节数时用到了原整数的“准绝对值”，一旦字节数确定之后，实际进行压缩时，使用的还是原整数，只不过将其当做无符号整数对待。</p>
<h3>带符号整数的解压缩算法</h3>
<p>由于带符号整数的压缩值与无符号整数的压缩值具有相同的结构，所以带符号整数的解压缩算法可以建立在无符号整数的解压缩算法基础之上。</p>
<p>首先，按照无符号整数的解压缩算法对压缩值进行解压缩，得到一个32位无符号整数，根据最低位（符号位）确定原整数的符号。</p>
<p>如果原整数为正数（最低位，即符号位为0），则将解压得到的无符号整数右移1位，再强制转换为带符号整数，即可得到原整数值。</p>
<p>如果原整数为负数（最低位，即符号位为1），则需要将解压得到的无符号整数右移1位，再将负数最左侧那些没有意义的“1”位恢复回来：</p>
<ul class="dotul">
<li>如果压缩值占用了1字节，则与FFFFFFC0h进行|（按位或）操作；</li>
<li>如果压缩值占用了2字节，则与FFFFE000h进行|操作；</li>
<li>如果压缩值占用了4字节，则与F0000000h进行|操作。</li>
</ul>
<p>最后，将这个无符号整数强制转换为带符号整数，即可得到原整数值。</p>
<p>代码4给出了带符号整数解压缩算法的参考实现。</p>
<div class="al-ins">
<p class="al-ins-title">代码4 - 带符号整数解压缩算法的参考实现</p>
<pre class="al-code-para">
public static int DecompressInt(byte[] data)
{
    var u = DecompressUInt(data);

    if ((u & 0x00000001) == 0)
        return (int)(u >> 1);

    var nb = GetCompressedIntSize(data[0]);
    uint sm;
    switch (nb)
    {
        case 1: sm = 0xFFFFFFC0; break;
        case 2: sm = 0xFFFFE000; break;
        case 4: sm = 0xF0000000; break;
        default: throw new NotSupportedException();
    }

    return (int)((u >> 1) | sm);
}
</pre>
</div>
<p>这里调用了一个工具方法GetCompressedIntSize，用于根据压缩值的第一个字节判断采用几个字节存放该压缩值。该方法非常简单，如代码5所示。</p>
<div class="al-ins">
<p class="al-ins-title">代码5 – 根据压缩值的第一个字节判断所需字节数</p>
<pre class="al-code-para">
public static uint GetCompressedIntSize(byte firstByte)
{
  if ((firstByte & 0x80) == 0)
    return 1;
  else if ((firstByte & 0xC0) == 0x80)
    return 2;
  else if ((firstByte & 0xE0) == 0xC0)
    return 4;
  else
      throw new NotSupportedException();
}
</pre>
</div>
<h2>各种实现中的问题</h2>
<p>压缩的带符号整数在.NET/CLI元数据中的使用场景非常少——据我所知，只有元数据签名中的数组下标值使用了压缩的带符号整数（这意味着原理上.NET/CLI的底层是支持下标为负数的数组的）。而在这方面，几乎所有现有的CLI实现都或多或少的出现了一些问题，同时，我所参考的文献中，关于带符号整数压缩算法的描述也都是含糊不清的。幸运的是，几乎所有高级语言都不允许开发者声明下标为负数的数组，CLS规范也要求数组的下标必须从0开始，所以这些问题并不会对实际项目造成重大影响。</p>
<p>下面列举几个我所研究过的实现中的问题，下一节将列出参考文献中的问题。</p>
<h3>ILASM/ILDASM</h3>
<p>很显然，微软自己对带符号整数的压缩算法也不是很清晰。ILASM是我所接触过的编译器中唯一能接受负数下标数组的，也是我在研究这个课题时使用最多的编译器。对于正数数组下标，ILASM完全没有问题；但对于负数下标，当下标值在-8192（含）到-8129（含）之间时，得到的压缩值是错误的。</p>
<p>另外，ILASM使用的带符号整数压缩算法实现，很明显与本文介绍的不同，因此并不能涵盖所有理论上支持的整数（[-268435456, 268435455]），当下标值小于或等于-268427265时，得到的压缩值也是错误的。</p>
<p>由于ILASM存在错误，所以对ILDASM无法进行完全准确的测验。不过，即便是对ILASM产生的错误值进行解压缩，ILDASM得到的结果和本文中介绍的带符号整数解压缩算法得到的结果都是一致的，所有有理由相信ILDASM在解压缩算法上应该是正确的。但是，错误的压缩值会随机造成ILDASM的崩溃。</p>
<p>以上问题存在于ILASM的2.0、3.0和3.5版本中，但在4.0 Beta版中已经得到改正，.NET Framework SDK 4.0 Beta携带的ILASM能够对所有理论上可接受的负数数组下标进行正确的压缩，而ILDASM也能对其进行正确的解压缩。</p>
<h3>Mono Cecil</h3>
<p>通过对Mono Cecil源代码的研究发现，Mono Cecil的实现非常忠诚于ECMA-335标准，而ECMA-335对数组下标的描述恰恰是错误的（参见后面“参考文献之修正”一节）——称数组下标值是压缩的无符号整数（而不是带符号整数）。</p>
<p>因此，Mono Cecil只提供了针对无符号整数的压缩和解压缩实现（参见Mono.Cecil.dll中的Mono.Cecil.Metadata.Utilities.WriteCompressedInteger(BinaryWriter, Int32) : Int32方法和Mono.Cecil.Metadata.Utilities.ReadCompressedInteger(Byte[], Int32, Int32&) : Int32方法）。而在写入和读取元数据签名时，也是将数组下标作为无符号整数处理的（参见Mono.Cecil.Signatures.SignatureWriter.Write(SigType) : Void方法和Mono.Cecil.Signatures.SignatureReader.ReadType(Byte[], Int32, Int32&) : SigType方法）。</p>
<p>在使用Mono Cecil库进行反射时，如果数组的下标为正数，则得到的结果是实际下标的2倍（因为缺少了解压缩带符号整数时的右移操作）；而如果数组的下表是负数，则得到的结果就是完全错误的了。</p>
<p>我只对Mono Cecil 0.6版本的源代码做了调查，其他版本不详，读者可自行检查、分析。</p>
<h3>CCI Metadata</h3>
<p>CCI Metadata则确实将数组下标当作带符号整数对待了，但是它使用的压缩算法非常简单——将原整数的绝对值左移1位，再将符号位放置在最低位（参见Microsoft.Cci.PeWriter.dll中的Microsoft.Cci.BinaryWriter.WriteCompressedInt(Int32) : Void方法），然后按照无符号整数进行压缩；而解压缩算法是对应的——先按照无符号整数的解压算法得到一个无符号整数，然后根据最低位确定结果的符号，最后将整个无符号数右移1位，再根据符号位设置正负号（参见Microsoft.Cci.PeReader.dll中的Microsoft.Cci.UtilityDataStructures.MemoryReader.ReadCompressedInt32() : Int32方法）。</p>
<p>CCI Metadata所采用的算法与《Expert .NET 2.0 IL Assembler》一书中提到的算法描述相符，但该书中的描述也是有误的（参见后面“参考文献之修正”一节）。</p>
<p>我所调研的CCI Metadata版本是2.0.49.23471。</p>
<h3>其他尚未研究的实现</h3>
<p>还有一些.NET/CLI的实现尚未研究，例如：</p>
<ul class="dotul">
<li>System.Reflection/System.Reflection.Emit</li>
<li>Shared Source CLI (Rotor)</li>
</ul>
<h2>参考文献之修正</h2>
<h3>《Expert .NET 2.0 IL Assembler》</h3>
<p>本书在第8章表8-4之后的一个自然段（P150第一段）描述了带符号整数的压缩算法，此处的描述有误，正确的描述请参见本文中“带符号整数的压缩算法”一节。</p>
<p>不幸的是，本书的中文版《.NET探秘：MSIL权威指南》并没有对这个问题进行修正（同样是第8章表8-4之后的一个自然段，P132）。当初包建强在翻译这本书的时候，我也向他提到过这里的问题，不过那时候我还没有完全准确地推断出正确的压缩算法，因此他只好直译。</p>
<h3>《ECMA-335——Common Language Infrastructure (CLI) 4th Edition》</h3>
<p>在ECMA-335标准中，完全没有区分“压缩的无符号整数”和“压缩的带符号整数”这两个术语，统称之为“compressed integer”。</p>
<p>ECMA-335 Partition II: Metadata Definition and Semantics中的23.2 Blobs and signatures一节中给出了“compressed integer”的压缩算法（P153），这实际上是无符号整数的压缩算法，该算法是正确的。</p>
<p>ECMA-335 Partition II: Metadata Definition and Semantics中的23.2.13 ArrayShape一节中给出了元数据签名中的数组表示方法（P161），其中称Size和LoBound都是“compressed integer”，这是不准确的。</p>
<p>修正方法是，引入术语“compressed unsigned integer”，用于描述其他地方的“compressed integer”；引入术语“compressed signed integer”，用于描述数组下标值（LoBound）。并按照本文“带符号整数的压缩算法”一节的描述，提供带符号整数的压缩算法。</p>
<p>（完）</p>
</div>
<img src="http://www.cnblogs.com/AndersLiu/aggbug/1666773.html?type=1" width="1" height="1" alt=""/><p>评论: 8　<a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>请教：Total Commander里Help菜单在菜单栏右对齐的效果是怎么做的？</title><link>http://www.cnblogs.com/rufi/archive/2010/02/09/1666767.html</link><dc:creator>Junfeng Liu</dc:creator><author>Junfeng Liu</author><pubDate>Tue, 09 Feb 2010 10:59:00 GMT</pubDate><guid>http://www.cnblogs.com/rufi/archive/2010/02/09/1666767.html</guid><description><![CDATA[<p>阅读: 334 评论: 3 作者: <a href="http://www.cnblogs.com/rufi/" target="_blank">Junfeng Liu</a> 发表于 2010-02-09 18:59 <a href="http://www.cnblogs.com/rufi/archive/2010/02/09/1666767.html" target="_blank">原文链接</a></p><p>Total Commander里Help菜单在菜单栏右对齐的效果是什么原理，用C#怎么做？</p>
<p>如图：</p>
<p><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/rufi/TotalCommander.png" width="783" height="92" /></p>
<p>&nbsp;</p><img src="http://www.cnblogs.com/rufi/aggbug/1666767.html?type=1" width="1" height="1" alt=""/><p>评论: 3　<a href="http://www.cnblogs.com/rufi/archive/2010/02/09/1666767.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/rufi/archive/2010/02/09/1666767.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>获取元素的文本</title><link>http://www.cnblogs.com/rubylouvre/archive/2010/02/09/1666752.html</link><dc:creator>司徒正美</dc:creator><author>司徒正美</author><pubDate>Tue, 09 Feb 2010 10:37:00 GMT</pubDate><guid>http://www.cnblogs.com/rubylouvre/archive/2010/02/09/1666752.html</guid><description><![CDATA[<p>阅读: 376 评论: 0 作者: <a href="http://www.cnblogs.com/rubylouvre/" target="_blank">司徒正美</a> 发表于 2010-02-09 18:37 <a href="http://www.cnblogs.com/rubylouvre/archive/2010/02/09/1666752.html" target="_blank">原文链接</a></p><p>记得John Resig说过，类库就是用来屏蔽浏览器的差异，但他的getText函数就明显做不到这一点，怎么没人去提醒他一下呢？我的英文特烂，阅读还可以，但要我正经八儿地写几句英文，估计句句都是病句……不说了，看下面测试(如果在IE下报错，请刷新当前页面)：</p>
<textarea id="runcode1" style="width:75%" rows="10"> 
&lt;!doctype html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
    &lt;script src="http://common.cnblogs.com/script/jquery.js" type="text/javascript"&gt;&lt;/script&gt;
    &lt;script type="text/javascript"&gt;


      $(function(){
        alert($("p").text());
      });

    &lt;/script&gt;
    &lt;title&gt;jQuery.text测试 by 司徒正美&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;

    &lt;p&gt;司徒正美&lt;br/&gt;
      &lt;!--这是注释节点//--&gt;
      &lt;a href="javascript:void(0)" href="http://www.cnblogs.com/rubylouvre/"&gt;Ruby's Louvre&lt;/a&gt;
    &lt;/p&gt;
    &lt;p&gt;司徒正美&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;
</textarea> 
<p><button type="button" class="runcode" title="runcode1">运行代码</button></p>
<p>IE下明显少了空行与空白，因为IE会自动删除空白节点。所以，要让所有浏览器保持一下，就只有去掉空白节点了。还有，我不明白jQuery会把所有匹配元素的文本串成一个，为何不返回一个字符串数组呢，省得到时我们还要一个个分解……</p>
<p>下面是我的方案：</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
      //@author:司徒正美(zhongqincheng)
      //获取某个节点的文本，如果此节点为元素节点，则取其childNodes的所有文本，
      //为了让结果在所有浏览器下一致，忽略所有空白节点，因此它非元素的innerText或textContent
      var getText = function(nodes){
        var result = "",node;
        for(var i=0,n=nodes.length;i&lt;n;i++){
          node = nodes[i];
          //如果是文本节点或者CDATA节点
          if ( node.nodeType === 3 || node.nodeType === 4 ) {
            if(!/^\s+$/.test(node.nodeValue))//忽略空白节点与换行符
              result += node.nodeValue;
            //如果是元素节点
          } else if ( node.nodeType === 1 ) {
            result += getText( node.childNodes );
          }
        }
        return result;
      }
</pre>
<p>看一下效果：</p>
<textarea id="runcode2" style="width:75%" rows="10"> 
&lt;!doctype html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
   
    &lt;script type="text/javascript"&gt;

      //@author:司徒正美(zhongqincheng)
      //获取某个节点的文本，如果此节点为元素节点，则取其childNodes的所有文本，
      //为了让结果在所有浏览器下一致，忽略所有空白节点，因此它非元素的innerText或textContent
      var getText = function(nodes){
        var result = "",node;
        for(var i=0,n=nodes.length;i&lt;n;i++){
          node = nodes[i];
          ////如果是文本节点或者CDATA节点
          if ( node.nodeType === 3 || node.nodeType === 4 ) {
            if(!/^\s+$/.test(node.nodeValue))
              result += node.nodeValue;
            //如果是元素节点
          } else if ( node.nodeType === 1 ) {
            result += getText( node.childNodes );
          }
        }
        return result;
      }
      window.onload = function(){
        var p = document.getElementsByTagName("p");
        alert(getText(p))
      }


    &lt;/script&gt;
    &lt;title&gt;jQuery.text测试 by 司徒正美&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;

    &lt;p&gt;司徒正美&lt;br/&gt;
      &lt;!--这是注释节点//--&gt;
      &lt;a href="javascript:void(0)" href="http://www.cnblogs.com/rubylouvre/"&gt;Ruby's Louvre&lt;/a&gt;
    &lt;/p&gt;
    &lt;p&gt;司徒正美&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;
</textarea> 
<p><button type="button" class="runcode" title="runcode2">运行代码</button></p><img src="http://www.cnblogs.com/rubylouvre/aggbug/1666752.html?type=1" width="1" height="1" alt=""/><p>评论: 0　<a href="http://www.cnblogs.com/rubylouvre/archive/2010/02/09/1666752.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/rubylouvre/archive/2010/02/09/1666752.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>轮廓的查找、表达、绘制、特性及匹配(How to Use Contour? Find, Component, Construct, Features &amp;amp; Match)</title><link>http://www.cnblogs.com/xrwang/archive/2010/02/09/HowToUseContour.html</link><dc:creator>Wuya</dc:creator><author>Wuya</author><pubDate>Tue, 09 Feb 2010 09:13:00 GMT</pubDate><guid>http://www.cnblogs.com/xrwang/archive/2010/02/09/HowToUseContour.html</guid><description><![CDATA[<p>阅读: 325 评论: 2 作者: <a href="http://www.cnblogs.com/xrwang/" target="_blank">Wuya</a> 发表于 2010-02-09 17:13 <a href="http://www.cnblogs.com/xrwang/archive/2010/02/09/HowToUseContour.html" target="_blank">原文链接</a></p><p>作者：王先荣</p>
<p><strong>前言</strong><br />&nbsp;&nbsp;&nbsp; 轮廓是构成任何一个形状的边界或外形线。前面讲了如何根据色彩及色彩的分布（直方图对比和模板匹配）来进行匹配，现在我们来看看如何利用物体的轮廓。包括以下内容：轮廓的查找、表达方式、组织方式、绘制、特性、匹配。</p>
<p>&nbsp;<strong>查找轮廓</strong><br />&nbsp;&nbsp;&nbsp; 首先我们面对的问题是如何在图像中找到轮廓，OpenCv(EmguCv)为我们做了很多工作，我们的任务只是调用现成的函数而已。Image&lt;TColor,TDepth&gt;类的FindContours方法可以很方便的查找轮廓，不过在查找之前，我们需要将彩色图像转换成灰度图像，然后再将灰度图像转换成二值图像。代码如下所示：</p>
<div onclick="cnblogs_code_show('bec6a7d1-34eb-4354-89f4-8b7e483f4ed6')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_bec6a7d1-34eb-4354-89f4-8b7e483f4ed6" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('bec6a7d1-34eb-4354-89f4-8b7e483f4ed6',event)" style="display: none;" id="code_img_opened_bec6a7d1-34eb-4354-89f4-8b7e483f4ed6" class="code_img_opened" /><span class="cnblogs_code_collapse">查找轮廓</span>
<div id="cnblogs_code_open_bec6a7d1-34eb-4354-89f4-8b7e483f4ed6" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageSource </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, </span><span style="color: #0000ff;">byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">(sourceImageFileName);       </span><span style="color: #008000;">//</span><span style="color: #008000;">获取源图像</span><span style="color: #008000;"><br /></span><span style="color: #000000;">Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageGray </span><span style="color: #000000;">=</span><span style="color: #000000;"> imageSource.Convert</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();                </span><span style="color: #008000;">//</span><span style="color: #008000;">将源图像转换成灰度图像</span><span style="color: #008000;"><br /></span><span style="color: #0000ff;">int</span><span style="color: #000000;"> thresholdValue </span><span style="color: #000000;">=</span><span style="color: #000000;"> tbThreshold.Value;                                         </span><span style="color: #008000;">//</span><span style="color: #008000;">用于二值化的阀值</span><span style="color: #008000;"><br /></span><span style="color: #000000;">Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageThreshold </span><span style="color: #000000;">=</span><span style="color: #000000;"> imageGray.ThresholdBinary(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Gray(thresholdValue), </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Gray(255d)); </span><span style="color: #008000;">//</span><span style="color: #008000;">对灰度图像二值化</span><span style="color: #008000;"><br /></span><span style="color: #000000;">Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour</span><span style="color: #000000;">=</span><span style="color: #000000;">imageThreshold.FindContours();</span></div></pre>
</div>
</div>
<p>&nbsp;</p>
<p><strong>轮廓的表达方式</strong><br />&nbsp;&nbsp;&nbsp; 使用上面的代码可以得到图像的默认轮廓，但是轮廓在电脑中是如何表达的呢？在OpenCv(EmguCv)中提供了两类表达轮廓的方式：顶点的序列、Freeman链码。</p>
<p><strong>1.顶点的序列<br /></strong>&nbsp;&nbsp;&nbsp; 用多个顶点（或各点间的线段）来表达轮廓。假设要表达一个从(0,0)到(2,2)的矩形，<br />（1）如果用点来表示，那么依次存储的可能是：(0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,1)；<br />（2）如果用点间的线段来表达轮廓，那么依次存储的可能是：(0,0),(2,0),(2,2),(0,2)。<br />以下代码可以用来获取轮廓上的点：</p>
<div class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> contour.Total; i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />     sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">{0},</span><span style="color: #800000;">"</span><span style="color: #000000;">, contour[i]);</span></div></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;<strong>2.Freeman链码<br /></strong>&nbsp;&nbsp;&nbsp; Freeman链码需要一个起点，以及从起点出发的一系列位移。每个位移有8个方向，从0～7分别指向从正北开始的8个方向。假设要用Freeman链码表达从(0,0)到(2,2)的矩形，可能的表示方法是：起点(0,0),方向链2,2,4,4,6,6,0,0。<br />&nbsp;&nbsp;&nbsp; EmguCv对Freeman链码的支持很少，我们需要做一系列的工作才能在.net中使用Freeman链码：<br /><strong>（1）获取Freeman链码</strong></p>
<div onclick="cnblogs_code_show('67eb26c3-0c61-4305-b623-e3230b98bcbe')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_67eb26c3-0c61-4305-b623-e3230b98bcbe" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('67eb26c3-0c61-4305-b623-e3230b98bcbe',event)" style="display: none;" id="code_img_opened_67eb26c3-0c61-4305-b623-e3230b98bcbe" class="code_img_opened" /><span class="cnblogs_code_collapse">查找用Freeman链码表示的轮廓</span>
<div id="cnblogs_code_open_67eb26c3-0c61-4305-b623-e3230b98bcbe" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000;">//</span><span style="color: #008000;">查找用Freeman链码表示的轮廓</span><span style="color: #008000;"><br /></span><span style="color: #000000;">Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray,Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageTemp</span><span style="color: #000000;">=</span><span style="color: #000000;">imageThreshold.Copy();<br />IntPtr storage </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvCreateMemStorage(</span><span style="color: #800080;">0</span><span style="color: #000000;">);<br />IntPtr ptrFirstChain </span><span style="color: #000000;">=</span><span style="color: #000000;"> IntPtr.Zero;<br /></span><span style="color: #0000ff;">int</span><span style="color: #000000;"> total </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvFindContours(imageTemp.Ptr, storage, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> ptrFirstChain, </span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(MCvChain), mode, CHAIN_APPROX_METHOD.CV_CHAIN_CODE, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Point(</span><span style="color: #800080;">0</span><span style="color: #000000;">, </span><span style="color: #800080;">0</span><span style="color: #000000;">));<br /></span></div></pre>
</div>
</div>
<p><br /><strong>（2）遍历Freeman链码上的点</strong></p>
<div onclick="cnblogs_code_show('160c0a60-877d-4545-9cea-f440c97bc1cf')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_160c0a60-877d-4545-9cea-f440c97bc1cf" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('160c0a60-877d-4545-9cea-f440c97bc1cf',event)" style="display: none;" id="code_img_opened_160c0a60-877d-4545-9cea-f440c97bc1cf" class="code_img_opened" /><span class="cnblogs_code_collapse">读取Freeman链码上的点</span>
<div id="cnblogs_code_open_160c0a60-877d-4545-9cea-f440c97bc1cf" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000;">//</span><span style="color: #008000;">初始化Freeman链码读取</span><span style="color: #008000;"><br /></span><span style="color: #000000;">[DllImport(</span><span style="color: #800000;">"</span><span style="color: #800000;">cv200.dll</span><span style="color: #800000;">"</span><span style="color: #000000;">)]<br /></span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">extern</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> cvStartReadChainPoints(IntPtr ptrChain,IntPtr ptrReader);<br /></span><span style="color: #008000;">//</span><span style="color: #008000;">读取Freeman链码的点</span><span style="color: #008000;"><br /></span><span style="color: #000000;">[DllImport(</span><span style="color: #800000;">"</span><span style="color: #800000;">cv200.dll</span><span style="color: #800000;">"</span><span style="color: #000000;">)]<br /></span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">extern</span><span style="color: #000000;"> Point cvReadChainPoint(IntPtr ptrReader);<br />[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet </span><span style="color: #000000;">=</span><span style="color: #000000;"> System.Runtime.InteropServices.CharSet.Ansi)]<br /></span><span style="color: #008000;">//</span><span style="color: #008000;">定义链码读取结构</span><span style="color: #008000;"><br /></span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">struct</span><span style="color: #000000;"> MCvChainPtReader<br />{<br />    </span><span style="color: #008000;">//</span><span style="color: #008000;">seqReader</span><span style="color: #008000;"><br /></span><span style="color: #000000;">    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> MCvSeqReader seqReader;<br />    </span><span style="color: #808080;">///</span><span style="color: #008000;"> char</span><span style="color: #808080;"><br /></span><span style="color: #000000;">    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">byte</span><span style="color: #000000;"> code;<br />    </span><span style="color: #808080;">///</span><span style="color: #008000;"> POINT-&gt;tagPOINT</span><span style="color: #808080;"><br /></span><span style="color: #000000;">    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Point pt;<br />    </span><span style="color: #808080;">///</span><span style="color: #008000;"> char[16]</span><span style="color: #808080;"><br /></span><span style="color: #000000;">    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">16</span><span style="color: #000000;">)]<br />    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> deltas;<br />}<br /><br /></span><span style="color: #008000;">//</span><span style="color: #008000;">将链码指针转换成结构</span><span style="color: #008000;"><br /></span><span style="color: #000000;">MCvChain chain</span><span style="color: #000000;">=</span><span style="color: #000000;">(MCvChain)Marshal.PtrToStructure(ptrChain,</span><span style="color: #0000ff;">typeof</span><span style="color: #000000;">(MCvChain));<br /></span><span style="color: #008000;">//</span><span style="color: #008000;">定义存放链码上点的列表</span><span style="color: #008000;"><br /></span><span style="color: #000000;">List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> pointList </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">(chain.total);<br /></span><span style="color: #008000;">//</span><span style="color: #008000;">链码读取结构</span><span style="color: #008000;"><br /></span><span style="color: #000000;">MCvChainPtReader chainReader </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> MCvChainPtReader();<br />IntPtr ptrReader </span><span style="color: #000000;">=</span><span style="color: #000000;"> Marshal.AllocHGlobal(</span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(MCvSeqReader) </span><span style="color: #000000;">+</span><span style="color: #000000;"> </span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">) </span><span style="color: #000000;">+</span><span style="color: #000000;"> </span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(Point) </span><span style="color: #000000;">+</span><span style="color: #000000;"> </span><span style="color: #800080;">16</span><span style="color: #000000;"> </span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">));<br />Marshal.StructureToPtr(chainReader, ptrReader, </span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br /></span><span style="color: #008000;">//</span><span style="color: #008000;">开始读取链码</span><span style="color: #008000;"><br /></span><span style="color: #000000;">cvStartReadChainPoints(ptrChain, ptrReader);<br /></span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">;<br /></span><span style="color: #0000ff;">while</span><span style="color: #000000;"> (ptrReader </span><span style="color: #000000;">!=</span><span style="color: #000000;"> IntPtr.Zero </span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;"> i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> chain.total)<br />{<br />    </span><span style="color: #008000;">//</span><span style="color: #008000;">依次读取链码上的每个点</span><span style="color: #008000;"><br /></span><span style="color: #000000;">    Point p </span><span style="color: #000000;">=</span><span style="color: #000000;"> cvReadChainPoint(ptrReader);<br />    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (ptrReader </span><span style="color: #000000;">==</span><span style="color: #000000;"> IntPtr.Zero)<br />         </span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br />    </span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br />    {<br />         pointList.Add(p);<br />         sbChain.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">{0},</span><span style="color: #800000;">"</span><span style="color: #000000;">, p);<br />         i</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />    }<br />}<br />imageResult.DrawPolyline(pointList.ToArray(), </span><span style="color: #0000ff;">true</span><span style="color: #000000;">, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Bgr(lblExternalColor.BackColor), </span><span style="color: #800080;">2</span><span style="color: #000000;">);</span></div></pre>
</div>
</div>
<p>&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 需要注意的是：cvReadChainPoint函数似乎永远不会满足循环终止的条件，即ptrReader永远不会被置为null，这跟《学习OpenCv》和参考上不一致；我们需要用chain.total来辅助终止循环，读取了所有的点之后就可以罢手了。</p>
<p><strong>轮廓之间的组织方式</strong><br />&nbsp;&nbsp;&nbsp; 在查找到轮廓之后，不同轮廓是怎么组织的呢？根据不同的选择，它们可能是：（1）列表；（2）双层结构；（3）树型结构。<br />&nbsp;&nbsp;&nbsp; 从纵向上来看，列表只有一层，双层结构有一或者两层，树型结构可能有一层或者多层。<br />&nbsp;&nbsp;&nbsp; 如果要遍历所有的轮廓，可以使用递归的方式，代码如下：</p>
<div onclick="cnblogs_code_show('c38c6ef4-4c21-4f19-ad59-aa124abf9fa1')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_c38c6ef4-4c21-4f19-ad59-aa124abf9fa1" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('c38c6ef4-4c21-4f19-ad59-aa124abf9fa1',event)" style="display: none;" id="code_img_opened_c38c6ef4-4c21-4f19-ad59-aa124abf9fa1" class="code_img_opened" /><span class="cnblogs_code_collapse">遍历轮廓</span>
<div id="cnblogs_code_open_c38c6ef4-4c21-4f19-ad59-aa124abf9fa1" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">        </span><span style="color: #008000;">//</span><span style="color: #008000;">遍历轮廓，并生成遍历结果</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> TravelContour(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour,</span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> total,</span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> StringBuilder sbContour)<br />        {<br />            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (contour </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br />            {<br />                sbContour.Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">------------------------\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br />                sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">轮廓{0}，右节点：{1}，下级节点：{2}，外接矩形：（{3}）\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, total, contour.HNext </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">, contour.VNext </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">, contour.BoundingRectangle);<br />                sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">包含{0}个点（面积：{1}，周长：{2}）：\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, contour.Total, contour.Area, contour.Perimeter);<br />                </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> contour.Total; i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />                    sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">{0},</span><span style="color: #800000;">"</span><span style="color: #000000;">, contour[i]);<br />                sbContour.Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br />                total</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (contour.HNext </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br />                    TravelContour(contour.HNext, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> total, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> sbContour);<br />                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (contour.VNext </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br />                    TravelContour(contour.VNext, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> total, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> sbContour);<br />            }<br />        }<br /></span></div></pre>
</div>
</div>
<p>&nbsp;</p>
<p><strong>轮廓的绘制</strong><br />&nbsp;&nbsp;&nbsp; 轮廓的绘制比较简单，用上面提到的方法取得轮廓的所有点，然后把这些点连接成一个多边形即可。<br />&nbsp;&nbsp;&nbsp; 当然，对于用顶点序列表示的轮廓，用Image&lt;TColor,TDepth&gt;.Draw方法或者cvDrawContours函数可以很方便的绘制出轮廓。我发现，如果将参数max_level设置成2，可以绘制出所有的轮廓。<br />&nbsp;&nbsp;&nbsp; 绘制轮廓的代码如下：</p>
<div onclick="cnblogs_code_show('2618001f-35f4-4d1b-89fe-349dff2d8b75')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_2618001f-35f4-4d1b-89fe-349dff2d8b75" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('2618001f-35f4-4d1b-89fe-349dff2d8b75',event)" style="display: none;" id="code_img_opened_2618001f-35f4-4d1b-89fe-349dff2d8b75" class="code_img_opened" /><span class="cnblogs_code_collapse">绘制轮廓</span>
<div id="cnblogs_code_open_2618001f-35f4-4d1b-89fe-349dff2d8b75" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageResult </span><span style="color: #000000;">=</span><span style="color: #000000;"> imageThreshold.Convert</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();         </span><span style="color: #008000;">//</span><span style="color: #008000;">结果图像</span><span style="color: #008000;"><br /></span><span style="color: #0000ff;">int</span><span style="color: #000000;"> maxLevel </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">;                                                           </span><span style="color: #008000;">//</span><span style="color: #008000;">绘制的轮廓深度</span><span style="color: #008000;"><br /></span><span style="color: #0000ff;">int</span><span style="color: #000000;">.TryParse(txtMaxLevel.Text, </span><span style="color: #0000ff;">out</span><span style="color: #000000;"> maxLevel);<br />imageResult.Draw(contour, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Bgr(lblExternalColor.BackColor), </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Bgr(lblHoleColor.BackColor), maxLevel, </span><span style="color: #800080;">2</span><span style="color: #000000;">);<br /></span></div></pre>
</div>
</div>
<p><img src="http://pic002.cnblogs.com/img/xrwang/201002/2010020917085414.jpg" /><br /><strong>轮廓的特性<br /></strong>&nbsp;&nbsp;&nbsp; 轮廓的特性有很多，下面一一介绍。</p>
<p><strong>1.轮廓的多边形逼近<br /></strong>&nbsp;&nbsp;&nbsp; 轮廓的多边形逼近指的是：使用多边形来近似表示一个轮廓。<br />&nbsp;&nbsp;&nbsp; 多边形逼近的目的是为了减少轮廓的顶点数目。<br />&nbsp;&nbsp;&nbsp; 多边形逼近的结果依然是一个轮廓，只是这个轮廓相对要粗旷一些。<br />&nbsp;&nbsp;&nbsp; 可以使用Contour&lt;Point&gt;.ApproxPoly方法或者cvApproxyPoly函数来对轮廓进行多边形逼近，示例代码如下：</p>
<div class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">contour </span><span style="color: #000000;">=</span><span style="color: #000000;"> firstContour.ApproxPoly(</span><span style="color: #0000ff;">double</span><span style="color: #000000;">.Parse(txtApproxParameter.Text), </span><span style="color: #800080;">2</span><span style="color: #000000;">, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> MemStorage());</span></div></pre>
</div>
<p>&nbsp;&nbsp;</p>
<p><strong>2.轮廓的关键点</strong><br />&nbsp;&nbsp;&nbsp; 轮廓的关键点是：轮廓上包含曲线信息比较多的点。关键点是轮廓顶点的子集。<br />&nbsp;&nbsp;&nbsp; 可以使用cvFindDominantPoints函数来获取轮廓上的关键点，该函数返回的结果一个包含 关键点在轮廓顶点中索引 的序列。再次强调：是索引，不是具体的点。如果要得到关键点的具体坐标，可以用索引到轮廓上去找。<br />&nbsp;&nbsp;&nbsp; 以下代码演示了如何获取轮廓上的关键点：</p>
<div onclick="cnblogs_code_show('e2dbf6f8-e380-4410-964b-2365d3caa15b')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_e2dbf6f8-e380-4410-964b-2365d3caa15b" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('e2dbf6f8-e380-4410-964b-2365d3caa15b',event)" style="display: none;" id="code_img_opened_e2dbf6f8-e380-4410-964b-2365d3caa15b" class="code_img_opened" /><span class="cnblogs_code_collapse">轮廓的关键点</span>
<div id="cnblogs_code_open_e2dbf6f8-e380-4410-964b-2365d3caa15b" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">        </span><span style="color: #008000;">//</span><span style="color: #008000;">得到关键点信息</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> GetDominantPointsInfo(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> StringBuilder sbContour, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageResult, </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> parameter1, </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> parameter2, </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> parameter3, </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> parameter4, Bgr dominantPointColor)<br />        {<br />            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (contour.Total </span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> </span><span style="color: #800080;">2</span><span style="color: #000000;">)<br />            {<br />                MemStorage storage </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> MemStorage();<br />                </span><span style="color: #0000ff;">try</span><span style="color: #000000;"><br />                {<br />                    IntPtr ptrSeq </span><span style="color: #000000;">=</span><span style="color: #000000;"> cvFindDominantPoints(contour.Ptr, storage.Ptr, (</span><span style="color: #0000ff;">int</span><span style="color: #000000;">)CV_DOMINANT.CV_DOMINANT_IPAN, parameter1, parameter2, parameter3, parameter4);<br />                    Seq</span><span style="color: #000000;">&lt;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> seq </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Seq</span><span style="color: #000000;">&lt;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">(ptrSeq, storage);<br />                    sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">{0}个关键点：\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, seq.Total);<br />                    </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> seq.Total; i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />                    {<br />                        </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> idx </span><span style="color: #000000;">=</span><span style="color: #000000;"> seq[i];           </span><span style="color: #008000;">//</span><span style="color: #008000;">关键点序列中存储的数据 是 关键点在轮廓中所处位置的索引</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                        Point p </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour[idx];     </span><span style="color: #008000;">//</span><span style="color: #008000;">得到关键点的坐标</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                        sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">{0}({1},{2}),</span><span style="color: #800000;">"</span><span style="color: #000000;">, idx, p.X, p.Y);<br />                        imageResult.Draw(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> CircleF(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> PointF(p.X, p.Y), </span><span style="color: #800080;">3</span><span style="color: #000000;">), dominantPointColor, </span><span style="color: #000000;">-</span><span style="color: #800080;">1</span><span style="color: #000000;">);<br />                    }<br />                    sbContour.Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br />                }<br />                </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (CvException ex)<br />                {<br />                    sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">在获取关键点时发生异常，错误描述：{0}，错误源：{1}，错误堆栈：{2}\r\n错误文件：{3}，函数名：{4}，行：{5}，错误内部描述：{6}\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, ex.Message, ex.Source, ex.StackTrace, ex.FileName, ex.FunctionName, ex.Line, ex.ErrorStr);<br />                }<br />                </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e)<br />                {<br />                    sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">在获取关键点时发生异常，错误描述：{0}，错误源：{1}，错误堆栈：{2}\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, e.Message, e.Source, e.StackTrace);<br />                }<br />                </span><span style="color: #0000ff;">finally</span><span style="color: #000000;"><br />                {<br />                    storage.Dispose();<br />                }<br />            }<br />        }<br /></span></div></pre>
</div>
</div>
<p><br /><strong>3.轮廓的周长和面积</strong><br />&nbsp;&nbsp;&nbsp; 轮廓的周长可以用Contour&lt;Point&gt;.Perimeter属性或者cvArcLength函数来获取。<br />&nbsp;&nbsp;&nbsp; 轮廓的面积可以用Contour&lt;Point&gt;.Area属性或者cvContourArea函数来获取。</p>
<p><strong>4.轮廓的边界框<br /></strong>&nbsp;&nbsp;&nbsp; 有三种常见的边界框：矩形、圆形、椭圆。<br />&nbsp;&nbsp;&nbsp; （1）矩形：在图像处理系统中提供了一种叫Rectangle的矩形，不过它只能表达边垂直或水平的特例；OpenCv中还有一种叫Box的矩形，它跟数学上的矩形一致，只要4个角是直角即可。<br />&nbsp;&nbsp;&nbsp; 如果要获取轮廓的Rectangle，可以使用Contour&lt;Point&gt;.BoundingRectangle属性或者cvBoundingRect函数。<br />&nbsp;&nbsp;&nbsp; 如果要获取轮廓的Box，可以使用Contour&lt;Point&gt;.GetMinAreaRect方法或者cvMinAreaRect2函数。<br />&nbsp;&nbsp;&nbsp; （2）圆形<br />&nbsp;&nbsp;&nbsp; 如果要获取轮廓的圆形边界框，可以使用cvMinEnclosingCircle函数。<br />&nbsp;&nbsp;&nbsp; （3）椭圆<br />&nbsp;&nbsp;&nbsp; 如果要获取轮廓的椭圆边界框，可以使用cvFitEllipse2函数。<br />&nbsp;&nbsp;&nbsp; 下列代码演示了如何获取轮廓的各种边界框：</p>
<div onclick="cnblogs_code_show('e97ab52b-7ed1-46ce-9c7c-9162340189a6')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_e97ab52b-7ed1-46ce-9c7c-9162340189a6" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('e97ab52b-7ed1-46ce-9c7c-9162340189a6',event)" style="display: none;" id="code_img_opened_e97ab52b-7ed1-46ce-9c7c-9162340189a6" class="code_img_opened" /><span class="cnblogs_code_collapse">轮廓的边界框</span>
<div id="cnblogs_code_open_e97ab52b-7ed1-46ce-9c7c-9162340189a6" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">        </span><span style="color: #008000;">//</span><span style="color: #008000;">得到边界框信息</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> GetEdgeInfo(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour, </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> edge, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> StringBuilder sbContour, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageResult, Bgr edgeColor)<br />        {<br />            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (edge </span><span style="color: #000000;">==</span><span style="color: #000000;"> </span><span style="color: #800000;">"</span><span style="color: #800000;">Rect</span><span style="color: #800000;">"</span><span style="color: #000000;">)<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">矩形</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                imageResult.Draw(contour.BoundingRectangle, edgeColor, </span><span style="color: #800080;">2</span><span style="color: #000000;">);<br />            </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (edge </span><span style="color: #000000;">==</span><span style="color: #000000;"> </span><span style="color: #800000;">"</span><span style="color: #800000;">MinAreaRect</span><span style="color: #800000;">"</span><span style="color: #000000;">)<br />            {<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">最小矩形</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                MCvBox2D box </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvMinAreaRect2(contour.Ptr, IntPtr.Zero);<br />                PointF[] points </span><span style="color: #000000;">=</span><span style="color: #000000;"> box.GetVertices();<br />                Point[] ps </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Point[points.Length];<br />                </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> points.Length; i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />                    ps[i] </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Point((</span><span style="color: #0000ff;">int</span><span style="color: #000000;">)points[i].X, (</span><span style="color: #0000ff;">int</span><span style="color: #000000;">)points[i].Y);<br />                imageResult.DrawPolyline(ps, </span><span style="color: #0000ff;">true</span><span style="color: #000000;">, edgeColor, </span><span style="color: #800080;">2</span><span style="color: #000000;">);<br />            }<br />            </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (edge </span><span style="color: #000000;">==</span><span style="color: #000000;"> </span><span style="color: #800000;">"</span><span style="color: #800000;">Circle</span><span style="color: #800000;">"</span><span style="color: #000000;">)<br />            {<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">圆形</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                PointF center;<br />                </span><span style="color: #0000ff;">float</span><span style="color: #000000;"> radius;<br />                CvInvoke.cvMinEnclosingCircle(contour.Ptr, </span><span style="color: #0000ff;">out</span><span style="color: #000000;"> center, </span><span style="color: #0000ff;">out</span><span style="color: #000000;"> radius);<br />                imageResult.Draw(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> CircleF(center, radius), edgeColor, </span><span style="color: #800080;">2</span><span style="color: #000000;">);<br />            }<br />            </span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br />            {<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">椭圆</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (contour.Total </span><span style="color: #000000;">&gt;=</span><span style="color: #000000;"> </span><span style="color: #800080;">6</span><span style="color: #000000;">)<br />                {<br />                    MCvBox2D box </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvFitEllipse2(contour.Ptr);<br />                    imageResult.Draw(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Ellipse(box), edgeColor, </span><span style="color: #800080;">2</span><span style="color: #000000;">);<br />                }<br />                </span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br />                    sbContour.Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">轮廓点数小于6，不能创建外围椭圆。\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br />            }<br />        }<br /></span></div></pre>
</div>
</div>
<p>&nbsp;&nbsp;</p>
<p><strong>5.轮廓的矩<br /></strong>&nbsp;&nbsp;&nbsp; 我们可以使用Contour&lt;Point&gt;.GetMoments方法或者cvMoments函数方便的得到轮廓的矩集，然后再相应的方法或函数获取各种矩。<br />&nbsp;&nbsp;&nbsp; 特定的矩：MCvMoments.GetSpatialMoment方法、cvGetSpatialMoment函数<br />&nbsp;&nbsp;&nbsp; 中心矩：MCvMoments.GetCentralMoment方法、cvGetCentralMoment函数<br />&nbsp;&nbsp;&nbsp; 归一化中心矩：MCvMoments.GetNormalizedCentralMoment方法、cvGetNormalizedCentralMoment函数<br />&nbsp;&nbsp;&nbsp; Hu矩：MCvMoments.GetHuMoment方法、McvHuMoments.hu1～hu7字段、cvGetHuMoments函数<br />&nbsp;&nbsp;&nbsp; 以下代码演示了如何获取轮廓的矩：</p>
<div onclick="cnblogs_code_show('e031db19-ef0a-4b53-a48e-768c4904c4b1')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_e031db19-ef0a-4b53-a48e-768c4904c4b1" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('e031db19-ef0a-4b53-a48e-768c4904c4b1',event)" style="display: none;" id="code_img_opened_e031db19-ef0a-4b53-a48e-768c4904c4b1" class="code_img_opened" /><span class="cnblogs_code_collapse">轮廓的矩</span>
<div id="cnblogs_code_open_e031db19-ef0a-4b53-a48e-768c4904c4b1" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">        </span><span style="color: #008000;">//</span><span style="color: #008000;">得到各种矩的信息</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> GetMomentsInfo(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour, </span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> StringBuilder sbContour)<br />        {<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">矩</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            MCvMoments moments </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour.GetMoments();<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">遍历各种情况下的矩、中心矩及归一化矩，必须满足条件：xOrder&gt;=0; yOrder&gt;=0; xOrder+yOrder&lt;=3;</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> xOrder </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; xOrder </span><span style="color: #000000;">&lt;=</span><span style="color: #000000;"> </span><span style="color: #800080;">3</span><span style="color: #000000;">; xOrder</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />            {<br />                </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> yOrder </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; yOrder </span><span style="color: #000000;">&lt;=</span><span style="color: #000000;"> </span><span style="color: #800080;">3</span><span style="color: #000000;">; yOrder</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />                {<br />                    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (xOrder </span><span style="color: #000000;">+</span><span style="color: #000000;"> yOrder </span><span style="color: #000000;">&lt;=</span><span style="color: #000000;"> </span><span style="color: #800080;">3</span><span style="color: #000000;">)<br />                    {<br />                        </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> spatialMoment </span><span style="color: #000000;">=</span><span style="color: #000000;"> moments.GetSpatialMoment(xOrder, yOrder);<br />                        </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> centralMoment </span><span style="color: #000000;">=</span><span style="color: #000000;"> moments.GetCentralMoment(xOrder, yOrder);<br />                        </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> normalizedCentralMoment </span><span style="color: #000000;">=</span><span style="color: #000000;"> moments.GetNormalizedCentralMoment(xOrder, yOrder);<br />                        sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">矩（xOrder：{0}，yOrder：{1}），矩：{2:F09}，中心矩：{3:F09}，归一化矩：{4:F09}\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, xOrder, yOrder, spatialMoment, centralMoment, normalizedCentralMoment);<br />                    }<br />                }<br />            }<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">Hu矩</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            MCvHuMoments huMonents </span><span style="color: #000000;">=</span><span style="color: #000000;"> moments.GetHuMoment();<br />            sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">Hu矩 h1：{0:F09}，h2：{1:F09}，h3：{2:F09}，h4：{3:F09}，h5：{4:F09}，h6：{5:F09}，h7：{6:F09}\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, huMonents.hu1, huMonents.hu2, huMonents.hu3, huMonents.hu4, huMonents.hu5, huMonents.hu6, huMonents.hu7);<br />        }<br /></span></div></pre>
</div>
</div>
<p><br /><strong>6.轮廓的轮廓树</strong><br />&nbsp;&nbsp;&nbsp; 轮廓树用来描述某个特定轮廓的内部特征。注意：轮廓树跟轮廓是一一对应的关系；轮廓树不用于描述多个轮廓之间的层次关系。<br />&nbsp;&nbsp;&nbsp; 可以用函数cvCreateContourTree来构造轮廓树。</p>
<div class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">IntPtr ptrTree1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvCreateContourTree(contour1.Ptr, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> MemStorage().Ptr, thresholdOfCreate);</span></div></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;<strong>7.轮廓的凸包和凸缺陷<br /></strong>&nbsp;&nbsp;&nbsp; 轮廓的凸包和凸缺陷用于描述物体的外形。凸包和凸缺陷很容易获得，不过我目前不知道它们到底怎么使用。<br />&nbsp;&nbsp;&nbsp; 如果要判断轮廓是否是凸的，可以用Contour&lt;Point&gt;.Convex属性和cvCheckContourConvexity函数。<br />&nbsp;&nbsp;&nbsp; 如果要获取轮廓的凸包，可以用Contour&lt;Point&gt;.GetConvexHull方法或者cvConvexHull2函数，返回的是包含顶点的序列。<br />&nbsp;&nbsp;&nbsp; 如果要获取轮廓的凸缺陷，可以用Contour&lt;Point&gt;.GetConvexityDefacts方法或者cvConvexityDefects函数。<br />&nbsp;&nbsp;&nbsp; 注意：EmguCv将缺陷的单词拼写错了，defect才是缺陷。<br />&nbsp;&nbsp;&nbsp; 以下代码演示了如何获取轮廓的凸包及凸缺陷：</p>
<div onclick="cnblogs_code_show('22fe6c73-c167-49b9-85db-8e0432136541')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_22fe6c73-c167-49b9-85db-8e0432136541" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('22fe6c73-c167-49b9-85db-8e0432136541',event)" style="display: none;" id="code_img_opened_22fe6c73-c167-49b9-85db-8e0432136541" class="code_img_opened" /><span class="cnblogs_code_collapse">轮廓的凸包和凸缺陷</span>
<div id="cnblogs_code_open_22fe6c73-c167-49b9-85db-8e0432136541" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">        </span><span style="color: #008000;">//</span><span style="color: #008000;">得到凸包及缺陷信息</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> GetConvexInfo(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour,</span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> StringBuilder sbContour,</span><span style="color: #0000ff;">ref</span><span style="color: #000000;"> Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr,Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageResult)<br />        {<br />            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (</span><span style="color: #000000;">!</span><span style="color: #000000;">contour.Convex)    </span><span style="color: #008000;">//</span><span style="color: #008000;">判断轮廓是否为凸</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            {<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">凸包</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                Seq</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> convexHull </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour.GetConvexHull(ORIENTATION.CV_CLOCKWISE);<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">缺陷</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                Seq</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">MCvConvexityDefect</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> defects </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour.GetConvexityDefacts(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> MemStorage(), ORIENTATION.CV_CLOCKWISE);<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">显示信息</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">轮廓的凸包有{0}个点，依次为：</span><span style="color: #800000;">"</span><span style="color: #000000;">, convexHull.Total);<br />                Point[] points </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Point[convexHull.Total];<br />                </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> convexHull.Total; i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />                {<br />                    Point p </span><span style="color: #000000;">=</span><span style="color: #000000;"> convexHull[i];<br />                    points[i] </span><span style="color: #000000;">=</span><span style="color: #000000;"> p;<br />                    sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">{0},</span><span style="color: #800000;">"</span><span style="color: #000000;">, p);<br />                }<br />                sbContour.Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br />                imageResult.DrawPolyline(points, </span><span style="color: #0000ff;">true</span><span style="color: #000000;">, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Bgr(lblConvexColor.BackColor), </span><span style="color: #800080;">2</span><span style="color: #000000;">);<br />                MCvConvexityDefect defect;<br />                sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">轮廓有{0}个缺陷，依次为：\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, defects.Total);<br />                </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">; i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> defects.Total; i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />                {<br />                    defect </span><span style="color: #000000;">=</span><span style="color: #000000;"> defects[i];<br />                    sbContour.AppendFormat(</span><span style="color: #800000;">"</span><span style="color: #800000;">缺陷：{0}，起点：{1}，终点：{2}，最深的点：{3}，深度：{4}\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, i, defect.StartPoint, defect.EndPoint, defect.DepthPoint, defect.Depth);<br />                }<br />            }<br />            </span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br />                sbContour.Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">轮廓是凸的，凸包和轮廓一样。\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br />        }<br /></span></div></pre>
</div>
</div>
<p>&nbsp;</p>
<p>&nbsp;<strong>8.轮廓的成对几何直方图<br /></strong>&nbsp;&nbsp;&nbsp; 成对几何直方图的资料比较少，我是这么理解的。<br />&nbsp;&nbsp;&nbsp; （1）轮廓保存的是一系列的顶点，轮廓是由一系列线段组成的多边形。对于看起来光滑的轮廓（例如圆），只是线段条数比较多，线段长度比较短而已。实际上，电脑中显示的任何曲线都由线段组成。<br />&nbsp;&nbsp;&nbsp; （2）每两条线段之间都有一定的关系，包括它们（或者它们的延长线）之间的夹角，两条线段的夹角范围是：(0,180)。<br />&nbsp;&nbsp;&nbsp; （3）每两条线段上的点之间还有距离关系，包括最短（小）距离、最远（大）距离，以及平均距离。最大距离我用了一个偷懒的计算方法，我把轮廓外界矩形的对角线长度看作了最大距离。<br />&nbsp;&nbsp;&nbsp; （4）成对几何直方图所用的统计数据包括了夹角和距离。<br />&nbsp;&nbsp;&nbsp; 可以用函数cvCalcPGH来计算轮廓的成对几何直方图，示例代码如下：</p>
<div onclick="cnblogs_code_show('87ea5dc9-e97d-431d-a48e-bd47629e7235')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_87ea5dc9-e97d-431d-a48e-bd47629e7235" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('87ea5dc9-e97d-431d-a48e-bd47629e7235',event)" style="display: none;" id="code_img_opened_87ea5dc9-e97d-431d-a48e-bd47629e7235" class="code_img_opened" /><span class="cnblogs_code_collapse">轮廓的成对几何直方图</span>
<div id="cnblogs_code_open_87ea5dc9-e97d-431d-a48e-bd47629e7235" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">            </span><span style="color: #008000;">//</span><span style="color: #008000;">生成成对几何直方图</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            Rectangle rect1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour1.BoundingRectangle;<br />            </span><span style="color: #0000ff;">float</span><span style="color: #000000;"> maxDist1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">float</span><span style="color: #000000;">)Math.Sqrt(rect1.Width </span><span style="color: #000000;">*</span><span style="color: #000000;"> rect1.Width </span><span style="color: #000000;">+</span><span style="color: #000000;"> rect1.Height </span><span style="color: #000000;">*</span><span style="color: #000000;"> rect1.Height); </span><span style="color: #008000;">//</span><span style="color: #008000;">轮廓的最大距离：这里使用轮廓矩形边界框的对角线长度</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">int</span><span style="color: #000000;">[] bins1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;">[] { </span><span style="color: #800080;">60</span><span style="color: #000000;">, </span><span style="color: #800080;">20</span><span style="color: #000000;"> };<br />            RangeF[] ranges1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF[] { </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF(0f, 180f), </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF(0f, maxDist1) };     </span><span style="color: #008000;">//</span><span style="color: #008000;">直方图第0维为角度，范围在(0,180)，第2维为轮廓两条边缘线段的距离</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            DenseHistogram hist1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DenseHistogram(bins1, ranges1);<br />            CvInvoke.cvCalcPGH(contour1.Ptr, hist1.Ptr);<br /></span></div></pre>
</div>
</div>
<p>&nbsp;<img src="http://pic002.cnblogs.com/img/xrwang/201002/2010020917093724.jpg" /></p>
<p>&nbsp;</p>
<p><strong>轮廓的匹配</strong><br />&nbsp;&nbsp;&nbsp; 如果要比较两个物体，可供选择的特征很多。如果要判断某个人的性别，可以根据他（她）头发的长短来判断，这很直观，在长发男稀有的年代准确率也很高。也可以根据这个人尿尿的射程来判断，如果射程大于0.50米，则是男性。总之，方法很多，不一而足。<br />&nbsp;&nbsp;&nbsp; 我们在上文中得到了轮廓的这么多特征，它们也可以用于进行匹配。典型的轮廓匹配方法有：Hu矩匹配、轮廓树匹配、成对几何直方图匹配。<br /><strong>1.Hu矩匹配</strong><br />&nbsp;&nbsp;&nbsp; 轮廓的Hu矩对包括缩放、旋转和镜像映射在内的变化具有不变性。Contour&lt;Point&gt;.MatchShapes方法和cvMatchShapes函数可以很方便的实现对2个轮廓间的匹配。<br /><strong>2.轮廓树匹配</strong><br />&nbsp;&nbsp;&nbsp; 用树的形式比较两个轮廓。cvMatchContourTrees函数实现了轮廓树的对比。<br /><strong>3.成对几何直方图匹配<br /></strong>&nbsp;&nbsp;&nbsp; 在得到轮廓的成对几何直方图之后，可以使用直方图对比的方法来进行匹配。如果您和我一样忘记了直方图的对比方式，可以看看我写的另一篇文章《颜色直方图的计算、显示、处理、对比及反向投影(How to Use Histogram? Calculate, Show, Process, Compare and BackProject)》。</p>
<p>&nbsp;&nbsp;&nbsp; 各种轮廓匹配的示例代码如下：</p>
<div onclick="cnblogs_code_show('630a3280-607b-46b4-bc0e-62010a58be19')" class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_630a3280-607b-46b4-bc0e-62010a58be19" class="code_img_closed" /><img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" onclick="cnblogs_code_hide('630a3280-607b-46b4-bc0e-62010a58be19',event)" style="display: none;" id="code_img_opened_630a3280-607b-46b4-bc0e-62010a58be19" class="code_img_opened" /><span class="cnblogs_code_collapse">轮廓的匹配</span>
<div id="cnblogs_code_open_630a3280-607b-46b4-bc0e-62010a58be19" class="cnblogs_code_hide">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">        </span><span style="color: #008000;">//</span><span style="color: #008000;">开始匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> btnStartMatch_Click(</span><span style="color: #0000ff;">object</span><span style="color: #000000;"> sender, EventArgs e)<br />        {<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">准备轮廓（这里只比较最外围的轮廓）</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> image1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, </span><span style="color: #0000ff;">byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">((Bitmap)pbImage1.Image);<br />            Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> image2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Bgr, </span><span style="color: #0000ff;">byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">((Bitmap)pbImage2.Image);<br />            Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageGray1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> image1.Convert</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />            Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageGray2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> image2.Convert</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />            Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageThreshold1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> imageGray1.ThresholdBinaryInv(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Gray(128d), </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Gray(255d));<br />            Image</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Gray, Byte</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> imageThreshold2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> imageGray2.ThresholdBinaryInv(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Gray(128d), </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Gray(255d));<br />            Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> imageThreshold1.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_EXTERNAL);<br />            Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> imageThreshold2.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_EXTERNAL);<br />            </span><span style="color: #008000;">/*</span><span style="color: #008000;">if (contour1.Perimeter / 50 &gt; 2 &amp;&amp; contour2.Perimeter / 50 &gt; 2)<br />            {<br />                contour1 = contour1.ApproxPoly(contour1.Perimeter / 50, 2, new MemStorage());    //对轮廓进行多边形逼近（参数设为轮廓周长的1/50）<br />                contour2 = contour2.ApproxPoly(contour2.Perimeter / 50, 2, new MemStorage());<br />            }</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">进行匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> result </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800000;">""</span><span style="color: #000000;">;<br />            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (rbHuMoments.Checked)<br />                result </span><span style="color: #000000;">=</span><span style="color: #000000;"> MatchShapes(contour1, contour2);       </span><span style="color: #008000;">//</span><span style="color: #008000;">Hu矩匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (rbContourTree.Checked)<br />                result </span><span style="color: #000000;">=</span><span style="color: #000000;"> MatchContourTrees(contour1, contour2); </span><span style="color: #008000;">//</span><span style="color: #008000;">轮廓树匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (rbPGH.Checked)<br />                result </span><span style="color: #000000;">=</span><span style="color: #000000;"> MatchPghHist(contour1, contour2);      </span><span style="color: #008000;">//</span><span style="color: #008000;">成对几何直方图匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            txtResult.Text </span><span style="color: #000000;">+=</span><span style="color: #000000;"> result;<br />        }<br /><br />        </span><span style="color: #008000;">//</span><span style="color: #008000;">Hu矩匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> MatchShapes(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour1, Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour2)<br />        {<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">匹配方法</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            CONTOURS_MATCH_TYPE matchType </span><span style="color: #000000;">=</span><span style="color: #000000;"> rbHuI1.Checked </span><span style="color: #000000;">?</span><span style="color: #000000;"> CONTOURS_MATCH_TYPE.CV_CONTOUR_MATCH_I1 : (rbHuI2.Checked </span><span style="color: #000000;">?</span><span style="color: #000000;"> CONTOURS_MATCH_TYPE.CV_CONTOURS_MATCH_I2 : CONTOURS_MATCH_TYPE.CV_CONTOURS_MATCH_I3);<br />            Stopwatch sw </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Stopwatch();<br />            sw.Start();<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> matchValue </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour1.MatchShapes(contour2, matchType);<br />            sw.Stop();<br />            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> time </span><span style="color: #000000;">=</span><span style="color: #000000;"> sw.Elapsed.TotalMilliseconds;<br />            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;">.Format(</span><span style="color: #800000;">"</span><span style="color: #800000;">Hu矩匹配（{0:G}），结果：{1:F05}，用时：{2:F05}毫秒\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, matchType, matchValue, time);<br />        }<br /><br />        </span><span style="color: #008000;">//</span><span style="color: #008000;">轮廓树匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> MatchContourTrees(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour1, Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour2)<br />        {<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">生成轮廓树</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> thresholdOfCreate </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">double</span><span style="color: #000000;">.Parse(txtThresholdOfCreateContourTrees.Text); </span><span style="color: #008000;">//</span><span style="color: #008000;">生成轮廓树的阀值</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            IntPtr ptrTree1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvCreateContourTree(contour1.Ptr, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> MemStorage().Ptr, thresholdOfCreate);<br />            IntPtr ptrTree2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvCreateContourTree(contour2.Ptr, </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> MemStorage().Ptr, thresholdOfCreate);<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> thresholdOfMatch </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">double</span><span style="color: #000000;">.Parse(txtThresholdOfMatchContourTrees.Text);   </span><span style="color: #008000;">//</span><span style="color: #008000;">比较轮廓树的阀值</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            Stopwatch sw </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Stopwatch();<br />            sw.Start();<br />            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> matchValue </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvMatchContourTrees(ptrTree1, ptrTree2, MATCH_CONTOUR_TREE_METHOD.CONTOUR_TREES_MATCH_I1, thresholdOfMatch);<br />            sw.Stop();<br />            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> time </span><span style="color: #000000;">=</span><span style="color: #000000;"> sw.Elapsed.TotalMilliseconds;<br />            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;">.Format(</span><span style="color: #800000;">"</span><span style="color: #800000;">轮廓树匹配（生成轮廓树的阀值：{0}，比较轮廓树的阀值：{1}），结果：{2:F05}，用时：{3:F05}毫秒\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, thresholdOfCreate, thresholdOfMatch, matchValue, time);<br />        }<br /><br />        </span><span style="color: #008000;">//</span><span style="color: #008000;">成对几何直方图匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;"> MatchPghHist(Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour1, Contour</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Point</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> contour2)<br />        {<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">生成成对几何直方图</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            Rectangle rect1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour1.BoundingRectangle;<br />            </span><span style="color: #0000ff;">float</span><span style="color: #000000;"> maxDist1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">float</span><span style="color: #000000;">)Math.Sqrt(rect1.Width </span><span style="color: #000000;">*</span><span style="color: #000000;"> rect1.Width </span><span style="color: #000000;">+</span><span style="color: #000000;"> rect1.Height </span><span style="color: #000000;">*</span><span style="color: #000000;"> rect1.Height); </span><span style="color: #008000;">//</span><span style="color: #008000;">轮廓的最大距离：这里使用轮廓矩形边界框的对角线长度</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            </span><span style="color: #0000ff;">int</span><span style="color: #000000;">[] bins1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;">[] { </span><span style="color: #800080;">60</span><span style="color: #000000;">, </span><span style="color: #800080;">20</span><span style="color: #000000;"> };<br />            RangeF[] ranges1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF[] { </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF(0f, 180f), </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF(0f, maxDist1) };     </span><span style="color: #008000;">//</span><span style="color: #008000;">直方图第0维为角度，范围在(0,180)，第2维为轮廓两条边缘线段的距离</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            DenseHistogram hist1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DenseHistogram(bins1, ranges1);<br />            CvInvoke.cvCalcPGH(contour1.Ptr, hist1.Ptr);<br />            Rectangle rect2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> contour2.BoundingRectangle;<br />            </span><span style="color: #0000ff;">float</span><span style="color: #000000;"> maxDist2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">float</span><span style="color: #000000;">)Math.Sqrt(rect2.Width </span><span style="color: #000000;">*</span><span style="color: #000000;"> rect2.Width </span><span style="color: #000000;">+</span><span style="color: #000000;"> rect2.Height </span><span style="color: #000000;">*</span><span style="color: #000000;"> rect2.Height);<br />            </span><span style="color: #0000ff;">int</span><span style="color: #000000;">[] bins2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;">[] { </span><span style="color: #800080;">60</span><span style="color: #000000;">, </span><span style="color: #800080;">20</span><span style="color: #000000;"> };<br />            RangeF[] ranges2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF[] { </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF(0f, 180f), </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> RangeF(0f, maxDist2) };<br />            DenseHistogram hist2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DenseHistogram(bins2, ranges2);<br />            CvInvoke.cvCalcPGH(contour2.Ptr, hist2.Ptr);<br />            </span><span style="color: #008000;">//</span><span style="color: #008000;">匹配</span><span style="color: #008000;"><br /></span><span style="color: #000000;">            Stopwatch sw </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Stopwatch();<br />            sw.Start();<br />            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> compareResult;<br />            HISTOGRAM_COMP_METHOD compareMethod </span><span style="color: #000000;">=</span><span style="color: #000000;"> rbHistCorrel.Checked </span><span style="color: #000000;">?</span><span style="color: #000000;"> HISTOGRAM_COMP_METHOD.CV_COMP_CORREL : (rbHistChisqr.Checked </span><span style="color: #000000;">?</span><span style="color: #000000;"> HISTOGRAM_COMP_METHOD.CV_COMP_CHISQR : (rbHistIntersect.Checked </span><span style="color: #000000;">?</span><span style="color: #000000;"> HISTOGRAM_COMP_METHOD.CV_COMP_INTERSECT : HISTOGRAM_COMP_METHOD.CV_COMP_BHATTACHARYYA));<br />            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (rbHistEmd.Checked)<br />            {<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">EMD<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">将直方图转换成矩阵</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                Matrix</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Single</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> matrix1 </span><span style="color: #000000;">=</span><span style="color: #000000;"> FormProcessHist.ConvertDenseHistogramToMatrix(hist1);<br />                Matrix</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Single</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> matrix2 </span><span style="color: #000000;">=</span><span style="color: #000000;"> FormProcessHist.ConvertDenseHistogramToMatrix(hist2);<br />                compareResult </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvCalcEMD2(matrix1.Ptr, matrix2.Ptr, DIST_TYPE.CV_DIST_L2, </span><span style="color: #0000ff;">null</span><span style="color: #000000;">, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);<br />                matrix1.Dispose();<br />                matrix2.Dispose();<br />            }<br />            </span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br />            {<br />                </span><span style="color: #008000;">//</span><span style="color: #008000;">直方图对比方式</span><span style="color: #008000;"><br /></span><span style="color: #000000;">                hist1.Normalize(1d);<br />                hist2.Normalize(1d);<br />                compareResult </span><span style="color: #000000;">=</span><span style="color: #000000;"> CvInvoke.cvCompareHist(hist1.Ptr, hist2.Ptr, compareMethod);<br />            }<br />            sw.Stop();<br />            </span><span style="color: #0000ff;">double</span><span style="color: #000000;"> time </span><span style="color: #000000;">=</span><span style="color: #000000;"> sw.Elapsed.TotalMilliseconds;<br />            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">string</span><span style="color: #000000;">.Format(</span><span style="color: #800000;">"</span><span style="color: #800000;">成对几何直方图匹配（匹配方式：{0}），结果：{1:F05}，用时：{2:F05}毫秒\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, rbHistEmd.Checked </span><span style="color: #000000;">?</span><span style="color: #000000;"> </span><span style="color: #800000;">"</span><span style="color: #800000;">EMD</span><span style="color: #800000;">"</span><span style="color: #000000;"> : compareMethod.ToString(</span><span style="color: #800000;">"</span><span style="color: #800000;">G</span><span style="color: #800000;">"</span><span style="color: #000000;">), compareResult, time);<br />        }<br /><br /></span></div></pre>
</div>
</div>
<p>&nbsp;<img src="http://pic002.cnblogs.com/img/xrwang/201002/2010020917101646.jpg" /></p>
<p>&nbsp;<strong>&nbsp;</strong></p>
<p>&nbsp;&nbsp;&nbsp; 通过以上代码，可以计算出两个轮廓对比的值，但是这些值具体代表什么意义呢？实际上，我目前还不清楚，需要进行大量的试验才行。</p>
<p>&nbsp;</p>
<p><strong>感谢您耐心看完本文，希望对您有所帮助。</strong></p><img src="http://www.cnblogs.com/xrwang/aggbug/1666684.html?type=1" width="1" height="1" alt=""/><p>评论: 2　<a href="http://www.cnblogs.com/xrwang/archive/2010/02/09/HowToUseContour.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/xrwang/archive/2010/02/09/HowToUseContour.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Compressed Integer In .NET/CLI Metadata</title><link>http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Tue, 09 Feb 2010 09:00:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html</guid><description><![CDATA[<p>阅读: 408 评论: 4 作者: <a href="http://www.cnblogs.com/AndersLiu/" target="_blank">Anders Liu</a> 发表于 2010-02-09 17:00 <a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html" target="_blank">原文链接</a></p><div class="al-body">
<h1>Compressed Integer In .NET/CLI Metadata</h1>
<div class="al-copy">
	<p>URL: <a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html"
title="Compressed Integer In .NET/CLI Metadata">http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html</a></p>
	<p>Author: <a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>Abstract: Compressed Integer is widely used in .NET/CLI PE files; this algorithm can place a 32-bit integer into 1, 2, or 4 bytes base on its value. Compressed integer can save the size of a PE file effectively, especially when the integer value is small. This document introduces the compression algorithm for integer, and gives the reference implement of the algorithm.</p>
</div>
<h2>Bibliographies</h2>
<ul class="dotul">
<li><cite>ECMA-335: Common Language Infrastructure (CLI) 4th Edition</cite>, June 2006.</li>
<li><cite>Expert .NET 2.0 IL Assembler</cite>, Serge Lidin, Apress, 2006.</li>
</ul>
<h2>Introduction</h2>
<p>In short, the compression algorithm is used to place a 32-bit integer (takes 4 bytes) into as little as possible number of storage (1, 2, or 4 bytes).</p>
<p>This compression algorithm is widely used in .NET/CLI PE files, such as metadata signatures, #blob stream and #US stream. In such cases, integers are used to save the number of records, or size of data blocks. Since such numbers and sizes are all very small, use 32-bit integers will cause many bytes set to 0, which makes no sense. In such cases, compressed integer can effectively reduce the disk space a PE file takes, and saves network bandwidth.</p>
<p>Some scenarios of using compressed integer within a PE file are listed below:</p>
<ul class="dotul">
<li>In the beginning of each record in Blob heap (storage format of #Blob stream and #US stream), compressed unsigned integer is used to store the size of the record data.</li>
<li>In the method metadata signature, compressed unsigned integer is used to store the number of parameters.</li>
<li>In metadata signatures, lower bounds of each array are saved in compressed signed integer.</li>
</ul>
<p>Note, all compression and decompression algorithm referred here are applied for 32-bit integer. Also, if not special mentioned, all integers are present as big-endian (most significant byte presents in left or on top).</p>
<h2>Compression and Decompression for Unsigned Integer</h2>
<h3>Compression Algorithm for Unsigned Integer</h3>
<p>Compression for unsigned integer is simple, split the range of unsigned integer into 3 ranges, and then place the unsigned integer value into 1, 2, or 4 bytes based on which range the value fall off. Table 1 lists all ranges and the format of compressed value.</p>
<table>
<caption>Table 1 – Ranges for unsigned integer</caption>
<tr><th>Range</th><th>Bytes Used</th><th>Mask</th><th>Binary Format</th></tr>
<tr><td>[00000000h, 0000007Fh]</td><td>1</td><td>80h</td><td>0BBBBBBBB</td></tr>
<tr><td>[00000080h, 00003FFFh]</td><td>2</td><td>C0h</td><td>10BBBBBB BBBBBBBB</td></tr>
<tr><td>[00004000h, 1FFFFFFFh]</td><td>4</td><td>E0h</td><td>110BBBBB BBBBBBBB BBBBBBBB BBBBBBBB</td></tr>
</table>
<p>In Table 1,</p>
<ul class="dotul">
<li><cite>Range</cite> lists the min value (inclusive) and max value (inclusive) of the range.</li>
<li><cite>Bytes Used</cite> lists how many bytes the compressed value will take.</li>
<li><cite>Mask</cite> lists mask value applied on the first byte of the compressed value,
    <ul class="dotul">
    <li>If the compressed value takes 1 byte, perform &amp; (bitwise and) with 80h, the result will be 0h;</li>
    <li>If the compressed value takes 2 bytes, perform &amp; with C0h, the result will be 80h;</li>
    <li>If the compressed value takes 4 bytes, perform &amp; with E0h, the result will be C0h.</li>
    </ul>
</li>
<li><cite>Binary Format</cite> lists the binary format of the compressed value, where 1 and 0 are fixed bit, while B means significant bit.</li>
</ul>
<p>From Table 1, we know that unsigned integers between [0h, 1FFFFFFFh] are suitable for this algorithm, values large than 1FFFFFFFh are not supported.</p>
<p>Code 1 shows a reference implement of unsigned integer compressing.</p>
<div class="al-ins">
<p class="al-ins-title">Code 1 – Reference implement of unsigned integer compressing</p>
<pre class="al-code-para">
public static byte[] CompressUInt(uint data)
{
  if (data <= 0x7F)
  {
    var bytes = new byte[1];
    bytes[0] = (byte)data;
    return bytes;
  }
  else if (data <= 0x3FFF)
  {
    var bytes = new byte[2];
    bytes[0] = (byte)(((data & 0xFF00) >> 8) | 0x80);
    bytes[1] = (byte)(data & 0x00FF);
    return bytes;
  }
  else if (data <= 0x1FFFFFFF)
  {
    var bytes = new byte[4];
    bytes[0] = (byte)(((data & 0xFF000000) >> 24) | 0xC0);
    bytes[1] = (byte)((data & 0x00FF0000) >> 16);
    bytes[2] = (byte)((data & 0x0000FF00) >> 8);
    bytes[3] = (byte)(data & 0x000000FF);
    return bytes;
  }
  else
    throw new NotSupportedException();
}
</pre>
</div>
<h3>Decompression Algorithm for Unsigned Integer</h3>
<p>Decompression algorithm for unsigned integer is the same simple as compression, see below:</p>
<ul class="dotul">
<li>If the first byte is in form of 0bbbbbbb (perform bitwise and with 80h, the result is 0h), the compressed value is stored in 1 byte (byte value is <span class="al-inline-code">b0</span>), then the original integer value is <span class="al-inline-code">b0</span>.</li>
<li>If the first byte is in form of 10bbbbbb (perform bitwise and with C0h, the result is 80h), the compressed value is stored in 2 bytes (bytes values are <span class="al-inline-code">b0</span>, <span class="al-inline-code">b1</span> in order), then the original integer value is <span class="al-inline-code">(b0 &amp; 0x3F) &lt;&lt; 8 | b1</span>.</li>
<li>If the first byte is in form of 110bbbbb (perform bitwise and with E0h, the result is C0h), the compressed value is stored in 4 bytes (bytes values are <span class="al-inline-code">b0</span>, <span class="al-inline-code">b1</span>, <span class="al-inline-code">b2</span>, <span class="al-inline-code">b3</span> in order), then the original integer value is <span class="al-inline-code">(b0 &amp; 0x1F) &lt;&lt; 24 | b1 &lt;&lt; 16 | b2 &lt;&lt; 8 | b3</span>.</li>
</ul>
<p>The Code 2 gives reference implement of unsigned integer decompressing.</p>
<div class="al-ins">
<p class="al-ins-title">Code 2 – Reference implement of unsigned integer decompressing</p>
<pre class="al-code-para">
public static uint DecompressUInt(byte[] data)
{
  if (data == null)
    throw new ArgumentNullException("data");

  if ((data[0] & 0x80) == 0
    && data.Length == 1)
  {
    return (uint)data[0];
  }
  else if ((data[0] & 0xC0) == 0x80
    && data.Length == 2)
  {
    return (uint)((data[0] & 0x3F) << 8 | data[1]);
  }
  else if ((data[0] & 0xE0) == 0xC0
    && data.Length == 4)
  {
    return (uint)((data[0] & 0x1F) << 24
      | data[1] << 16 | data[2] << 8 | data[3]);
  }
  else
    throw new NotSupportedException();
}
</pre>
</div>
<h2>Compression and Decompression for Signed Integer</h2>
<h3>Compression Algorithm for Signed Integer</h3>
<p>The compressing of signed integer is slightly more complex than the unsigned integer, because we have to deal with the sign bit. In short, after determine how many bytes the compressed value will take, we should left shift the whole integer by 1 bit, and place the sign bit on the least significant bit (0 for positive, 1 for negative), and then set mask value for the first byte as the compressed unsigned integer value.</p>
<p>When determining how many bytes should use to store the compressed signed integer value, we should get the 'semi-absolute value' of the original integer, that is, for the negative value, we should take its bitwise reversed value (not the opposite number in mathematics). And then, left shift the 'semi-absolute value' by 1 bit, and search from Table 1 for getting the number bytes should use.</p>
<p>Or, you can omit the left ship operation, but use the Table 2 to search the range of the 'semi-absolute value'.</p>
<table>
<caption>Table 2 – Ranges of 'semi-absolute value' of signed integer</caption>
<tr><th>Range</th><th>Bytes Used</th><th>Significant Bit Mask</th></tr>
<tr><td>[00000000h, 0000003Fh]</td><td>1</td><td>0000003Fh</td></tr>
<tr><td>[00000040h, 00001FFFh]</td><td>2</td><td>00001FFFh</td></tr>
<tr><td>[00002000h, 0FFFFFFFh]</td><td>4</td><td>0FFFFFFFh</td></tr>
</table>
<p>In Table 2,</p>
<ul class="dotul">
<li><cite>Range</cite> lists the min 'semi-absolute value' (inclusive) and the max 'semi-absolute value' (inclusive) of each range.</li>
<li><cite>Bytes Used</cite> lists the number of bytes that the compressed value will take.</li>
<li><cite>Significant Bit Mask</cite> lists a series of mask, on which perform &amp; with the original integer value, you can get all the significant bits. In fact, for a positive value, all left side bits are 0, and make no sense so that can be omitted; also, for a negative value, all left side bits are 1, make no sense so that can be omitted too.</li>
</ul>
<p>After you got the significant bits through the bitwise and operation with the corresponding mask value, left shift all the significant bits. Next, if the original integer is negative, set the least significant bit (the sign bit) to 1.</p>
<p>Finally, apply mask value to the first byte of the compressed value, use the same rule as compressed unsigned integer.</p>
<p>The range of signed integers which are suitable for the compression algorithm contains, for positive integer, [0h, 0FFFFFFFh] ([0, 268435455]), while for negative integer, [F0000000h, FFFFFFFFh] ([-268435456, -1]). Integers fall out of these ranges are not suitable.</p>
<p>Code 3 gives the reference implement of signed integer compressing.</p>
<div class="al-ins">
<p class="al-ins-title">Code 3 – Reference implement of signed integer compressing</p>
<pre class="al-code-para">
public static byte[] CompressInt(int data)
{
    var u = data >= 0 ? (uint)data : ~(uint)data;
    if (u <= 0x3F)
    {
        var uv = ((uint)data & 0x0000003F) << 1;
        if (data < 0)
            uv |= 0x01;

        var bytes = new byte[1];
        bytes[0] = (byte)uv;
        return bytes;
    }
    else if (u <= 0x1FFF)
    {
        var uv = ((uint)data & 0x00001FFF) << 1;
        if (data < 0)
            uv |= 0x01;

        var bytes = new byte[2];
        bytes[0] = (byte)(((uv & 0xFF00) >> 8) | 0x80);
        bytes[1] = (byte)(uv & 0x00FF);
        return bytes;
    }
    else if (u <= 0x0FFFFFFF)
    {
        var uv = ((uint)data & 0x0FFFFFFF) << 1;
        if (data < 0)
            uv |= 0x01;

        var bytes = new byte[4];
        bytes[0] = (byte)(((uv & 0xFF000000) >> 24) | 0xC0);
        bytes[1] = (byte)((uv & 0x00FF0000) >> 16);
        bytes[2] = (byte)((uv & 0x0000FF00) >> 8);
        bytes[3] = (byte)(uv & 0x000000FF);
        return bytes;
    }
    else
        throw new NotSupportedException();
}
</pre>
</div>
<p>Note, the 'semi-absolute value' is used only when determining the number bytes the compressed value takes, once the number is calculated, use the original integer value for compressing, treat it as unsigned.</p>
<h3>Decompression Algorithm for Signed Integer</h3>
<p>Since the compressed signed integer and the compressed unsigned integer use the same binary format, the decompression of signed integer can be based on the decompression of unsigned integer.</p>
<p>First, decompress the compressed value as unsigned, and got a 32-bit unsigned integer. Then, get the sign of the original integer according to the least significant bit (sign bit).</p>
<p>If the original integer is positive (the least significant bit, i.e. the sign bit is 0), right shift the decompressed value by 1 bit, and convert to signed integer, then you get the original signed integer.</p>
<p>If the original integer is negative (the least significant bit, i.e. the sign bit is 1), right shift the decompressed value by 1 bit, and bring back the non-sense 1 bits in the left side of the integer:</p>
<ul class="dotul">
<li>If the compressed value takes 1 byte, perform | (bitwise or) operation with FFFFFFC0h;</li>
<li>If the compressed value takes 2 bytes, perform | operation with FFFFE000h;</li>
<li>If the compressed value takes 4 bytes, perform | operation with F0000000h.</li>
</ul>
<p>Finally, convert the result to signed integer; you will get the original negative signed integer.</p>
<p>Code 4 give the reference implement of signed integer decompressing.</p>
<div class="al-ins">
<p class="al-ins-title">Code 4 – Reference implement of signed integer decompressing</p>
<pre class="al-code-para">
public static int DecompressInt(byte[] data)
{
    var u = DecompressUInt(data);

    if ((u & 0x00000001) == 0)
        return (int)(u >> 1);

    var nb = GetCompressedIntSize(data[0]);
    uint sm;
    switch (nb)
    {
        case 1: sm = 0xFFFFFFC0; break;
        case 2: sm = 0xFFFFE000; break;
        case 4: sm = 0xF0000000; break;
        default: throw new NotSupportedException();
    }

    return (int)((u >> 1) | sm);
}
</pre>
</div>
<p>Here a utility method <span class="al-inline-code">GetCompressedIntSize</span> is called, which is used to determine how many bytes the compressed value takes, through the first byte of the compressed value. This method is really simple, see Code 5.</p>
<div class="al-ins">
<p class="al-ins-title">Code 5 – Get bytes number of the compressed value through the first byte</p>
<pre class="al-code-para">
public static uint GetCompressedIntSize(byte firstByte)
{
  if ((firstByte & 0x80) == 0)
    return 1;
  else if ((firstByte & 0xC0) == 0x80)
    return 2;
  else if ((firstByte & 0xE0) == 0xC0)
    return 4;
  else
      throw new NotSupportedException();
}
</pre>
</div>
<h2>Implement Issues</h2>
<p>The compressed signed integer is used less in .NET/CLI metadata, as I know, only in array lower bound value with in metadata signatures (which means, the negative array lower bound is supported by the .NET/CLI naturally). In such a scenario, almost all CLI implements have problems when dealing with compressed signed integer, more or less. And in all bibliographies, the description of compression for signed integer is not clear enough. Fortunately, most high level programming language don't support array with negative lower bound, and in CLS, all lower bounds of an array should be 0, so these problems don't have serious implications for actual projects.</p>
<p>In the following sections, I'll list problems occurred in some CLI implements that I've researched, followed by the issues appear in bibliographies.</p>
<h3>ILASM/ILDASM</h3>
<p>Obviously, Microsoft doesn't clarify the compression algorithm for signed integer itself. ILASM is the only compiler can accept negative array lower bound that I've used, it is also the most used compiler when I researching on this question. For the positive lower bound within in array, no problem in ILASM; while for the negative lower bound, you'll get an incorrect compressed value when the lower bound value is between -8192 (inclusive) and -8129 (inclusive).</p>
<p>In addition, ILASM uses different decompression algorithm for signed integer other than the one described in this article, which cannot cover all theoretically supported integers ([-268435456, 268435455]), when the lower bound is less than or equal to -268427265, you'll also get an incorrect value.</p>
<p>We can't test the ILDASM precisely, because of the problem occurred in the ILASM. However, though try to decompress the incorrect value generated by the ILASM, the ILDASM and the reference implement referred in this article both get the same value, so I prefer to consider that the decompression algorithm used in ILDASM is correct. But the incorrect value will make ILDASM crashed randomly.</p>
<p>The problems introduced above appear in version 2.0, 3.0, and 3.5 of ILASM, in version 4.0 beta, all the problems are resolved. The ILASM shipped with .NET Framework SDK 4.0 Beta can accept all suitable signed value as the lower bound of an array, and generate correct compressed value; and the ILDASM can also decompress the compressed value correctly.</p>
<h3>Mono Cecil</h3>
<p>After read the source code of Mono Cecil, I find that Mono Cecil is loyal to the ECMA-335 standard, but ECMA-335 makes mistake on the description of array lower bound (see <cite>Revision of Bibliographies</cite> section later), where the array lower bound is treat as unsigned (not signed) integer.</p>
<p>So, Mono Cecil provides only compression and decompression for unsigned integer (see 'Mono.Cecil.Metadata.Utilities.WriteCompressedInteger(BinaryWriter, Int32) : Int32' method and 'Mono.Cecil.Metadata.Utilities.ReadCompressedInteger(Byte[], Int32, Int32&) : Int32' method in Mono.Cecil.dll). When writing and reading array lower bounds, it also treat the lower bounds as unsigned integers (see 'Mono.Cecil.Signatures.SignatureWriter.Write(SigType) : Void' method and 'Mono.Cecil.Signatures.SignatureReader.ReadType(Byte[], Int32, Int32&) : SigType' method in the same library).</p>
<p>When you reflecting an assembly by using Mono Cecil, if the array lower bound is positive, you will get a lower bound twice as the real value (because the right shift operation is missed); or if the array lower bound is negative, the result is totally wrong.</p>
<p>I only researched version 0.6 of Mono Cecil, no sure in other versions, you can research them yourself.</p>
<h3>CCI Metadata</h3>
<p>CCI Metadata treats the array lower bound as signed integer indeed, but uses an oversimplification algorithm: left shift the absolute value of the original integer, then place the sign bit in the least significant bit (see 'Microsoft.Cci.BinaryWriter.WriteCompressedInt(Int32) : Void' method in Microsoft.Cci.PeWriter.dll), and compress the value as an unsigned integer. The decompression algorithm is opposite, decompress the compressed value as unsigned integer, determine the sign according to the least significant bit, right shift the decompressed unsigned value by 1 bit, then convert it to signed integer and set the sign according the sign bit (see 'Microsoft.Cci.UtilityDataStructures.MemoryReader.ReadCompressedInt32() : Int32' method in Microsoft.Cci.PeReader.dll).</p>
<p>CCI Metadata uses the same algorithm with <cite>Expert .NET 2.0 IL Assembler</cite>, which has problem also (see <cite>Revision of Bibliographies</cite> section later).</p>
<p>Version 2.0.49.23471 of CCI Metadata has been researched.</p>
<h3>Implements Not Researched</h3>
<p>Some other implements are not covered in this article, such as:</p>
<ul class="dotul">
<li>System.Reflection/System.Reflection.Emit</li>
<li>Shared Source CLI (Rotor)</li>
</ul>
<h2>Revision of Bibliographies</h2>
<h3>Expert .NET 2.0 IL Assembler</h3>
<p>This book describes the compression algorithm in Chapter 8, in the paragraph after Table 8-4 (first paragraph in P150). The description is incorrect, for the correct description, see <cite>Compression Algorithm for Signed Integer</cite> section in this article.</p>
<h3>ECMA-335——Common Language Infrastructure (CLI) 4th Edition</h3>
<p>The ECMA-335 standard doesn't discriminate the terms <span class="term-en">compressed unsigned integer</span> and <span class="term-en">compressed signed integer</span>, they are collectively called <span class="term-en">compressed integer</span>.</p>
<p><cite>23.2 Blobs and signatures</cite> section in <cite>ECMA-335 Partition II: Metadata Definition and Semantics</cite> defines compression algorithm for <span class="term-en">compressed integer</span> (P153), which is in fact <span class="term-en">compressed unsigned integer</span> and is correct when applied on unsigned integer.</p>
<p><cite>23.2.13 ArrayShape</cite> section in <cite>ECMA-335 Partition II: Metadata Definition and Semantics</cite> defines the array shape used in metadata signatures (P161), where the Size element and LoBound element are all called <span class="term-en">compressed integer</span>, which is incorrect.</p>
<p>The revision is that, involve term <span class="term-en">compressed unsigned integer</span> to describe the original <span class="term-en">compressed integer</span> other than LoBound in ArrayShape; and involve term <span class="term-en">compressed signed integer</span> for the LoBound in ArrayShape. And provide description for signed integer compression algorithm according to the description in the <cite>Compression Algorithm for Signed Integer</cite> section.</p>
<p>(End)</p>
</div>
<img src="http://www.cnblogs.com/AndersLiu/aggbug/1666708.html?type=1" width="1" height="1" alt=""/><p>评论: 4　<a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>【翻译作品】JavaScript Event学习第十章：一些可替换的事件对</title><link>http://www.cnblogs.com/beiyu/archive/2010/02/09/events_pairs.html</link><dc:creator>北玉</dc:creator><author>北玉</author><pubDate>Tue, 09 Feb 2010 08:02:00 GMT</pubDate><guid>http://www.cnblogs.com/beiyu/archive/2010/02/09/events_pairs.html</guid><description><![CDATA[<p>阅读: 530 评论: 4 作者: <a href="http://www.cnblogs.com/beiyu/" target="_blank">北玉</a> 发表于 2010-02-09 16:02 <a href="http://www.cnblogs.com/beiyu/archive/2010/02/09/events_pairs.html" target="_blank">原文链接</a></p><p>&nbsp;</p>
<p>为了让我们的JavaScript驱动的页面对那些不能或者不想使用鼠标的用户也能很好的使用，我们对于像mouseover和click这样的事件做一些处理，同样的，对于非鼠标事件也同样的要我们的脚本执行。    <br />转载请注明以下信息：     <br />    <br /></p>
<h2>测试的局限性</h2>
<p>   <br />这一章我们打算寻找哪些事件可以用来仿真鼠标事件。注意这一系列测试不包括屏幕阅读器。因为我不能满足所有的条件，因此测试也有局限性。这个测试目标仅仅只是那些在图形界面下的浏览器中不使用鼠标的用户。 </p>
<p>我假设这些测试一样可以用来在一些移动设备上。因为条件不足，所以也不能测试。很多时候在移动设备上表现总是不尽如人意。 </p>
<h2>总结</h2>
<p>   <br />不幸的是，我们不能制定一个严格的鼠标事件和非鼠标的一对一的方案，因为非鼠标事件和鼠标事件有很多不同。所以下面的建议在大多数场合适用，但不是所有。 </p>
<p>  下面就是我的测试结果：</p>
<p>1、mouseover:focus <br />2、mouseout:blur<br />3、click:不需要匹配<br />4、dblclick:不知道<br />5、mousedown:keydown(最不坏的选择）<br />6、mouseup:keyup(最不坏的选择)<br />7、mousemove:没有鼠标不可能</p>
<p>&nbsp;</p>
<p>如果页面一定需要完美的支持非鼠标用户，那么我们可选择的能应用事件处理程序的元素就非常少。实际上就回到了Netscape 3时代，那时候事件处理程序只在链接和表单里才有用。 </p>
<p>更多的研究还是有必要的。 </p>
<h2>准备</h2>
<p>   <br />在大多数的浏览器中用户可以通过Tab键来飘过整个页面。当他们这样做的时候，焦点就会跳到下一个链接或者表单上。这在IE和Mozilla都可以运作。在Safari里面你需要按F1然后才能激活键盘快捷键。 </p>
<p>Opera用户是另外一个系统的。链接上的跳转需要按Ctrl+方向键。虽然是不同的组合，但是我还是会称其为&ldquo;Tabbing&rdquo;。 </p>
<p>例子：传统的mouseover    <br />如何做到呢？首先也是最重要的为mouseover和mouseout的添加两个事件：     </p>
<p>&nbsp;</p>
<pre class="brush:javascript">imgs[i].onmouseover = imgs[i].onfocus = mouseGoesOver;<br />imgs[i].onmouseout = imgs[i].onblur = mouseGoesOut;<br /><br /></pre>
<p>&nbsp;</p>
<p>现在当用户鼠标经过或者tab经过时，函数都会被执行。 </p>
<p>然而，添加一些事件还是不够的。我原始的工作室直接在图像上设置onmouseover和mouseout。不幸的是tab一个图像基本上不可能：tab只对链接和表单有用。所以我需要对把事件添加的图像的父节点上：链接。 </p>
<p>这个简单的例子不会因为我们的重新注册改变，但更复杂的脚本可能会无法界定访问活动，例如，一个div。 </p>
<p>为了保证完美的可用性，我们可以只定义链接和表单的事件，就像我们再1998年时候那样干的。虽然网上的大部分事件还是定义在链接上，不过一个复杂的比如文本编辑的脚本对于非鼠标用户就不可用，因为我们要在上面click。 </p>
<p>翻译地址：http://www.quirksmode.org/js/events_pairs.html</p><img src="http://www.cnblogs.com/beiyu/aggbug/1666669.html?type=1" width="1" height="1" alt=""/><p>评论: 4　<a href="http://www.cnblogs.com/beiyu/archive/2010/02/09/events_pairs.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/beiyu/archive/2010/02/09/events_pairs.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>SQL Azure 学习初探 - “More than two parts of column name” 错误原因及其解决方案</title><link>http://www.cnblogs.com/pchen/archive/2010/02/09/1666423.html</link><dc:creator>Chen@Ethos</dc:creator><author>Chen@Ethos</author><pubDate>Tue, 09 Feb 2010 06:24:00 GMT</pubDate><guid>http://www.cnblogs.com/pchen/archive/2010/02/09/1666423.html</guid><description><![CDATA[<p>阅读: 450 评论: 2 作者: <a href="http://www.cnblogs.com/pchen/" target="_blank">Chen@Ethos</a> 发表于 2010-02-09 14:24 <a href="http://www.cnblogs.com/pchen/archive/2010/02/09/1666423.html" target="_blank">原文链接</a></p><p><strong>摘要：</strong>本文主要介绍我在将一个本地数据迁移到SQL Azure数据库时，遇到的一个错误信息&#8220;Deprecated feature 'More than two-part column name' is not supported in this version of SQL Server.&#8221; （&#8220;列名多于两个组成部分&#8221;），及其解决办法。</p><p><strong>关键词：</strong> SQL Azure， TSQL, SQL Server 2008<br /></p><p>&nbsp;</p><p>我在把本地数据迁移到SQL Azure时，执行如下脚本时：</p><p><span style="background-color: #c0c0c0;">CREATE VIEW [dbo].[V_UserName]</span><br /><span style="background-color: #c0c0c0;">AS</span><br /><span style="background-color: #c0c0c0;">SELECT dbo.[User].Email FROM dbo.[User]</span><br /><span style="background-color: #c0c0c0;">GO</span>&nbsp;</p><p>出现错误信息：</p><p><span style="color: red;">Msg 40512, Level 16, State 1, Procedure V_UserName, Line 3</span><br /><span style="color: red;">Deprecated feature 'More than two-part column name' is not supported in this version of SQL Server.</span>&nbsp;</p><p>&nbsp;</p><p>这个错误信息是由于在SELECT语句中列名dbo.[User].Email 由多于两个部分组成。在MSDN的相关文档（中文版：http://msdn.microsoft.com/zh-cn/library/ms143729.aspx， 英文版：http://msdn.microsoft.com/en-us/library/ms143729.aspx）中，有这个说明：在SQL Server 2008里支持 ，但是在SQL Server 2008之后的版本（具体哪一版本尚未确定）不再支持的功能列表里，包含这个功能，SELECT语句中的列名标准格式应该是由两部分组成，不支持三个部分或者四个部分组成的列名。通过这个例子，我们可以知道这个功能在SQL Azure数据库已经不再被支持。<br /></p><p><img alt="" src="http://images.cnblogs.com/cnblogs_com/pchen/SQLAzureMigrate1.jpg" width="1260" border="0" height="643" />&nbsp;</p><p>解决办法：我们可以去掉服务器名dbo, 执行如下语句：<br /></p><p><span style="background-color: #c0c0c0;">CREATE VIEW [dbo].[V_UserName]</span><br /> <span style="background-color: #c0c0c0;">AS</span><br /> <span style="background-color: #c0c0c0;">SELECT [User].Email FROM dbo.[User]</span><br /> <span style="background-color: #c0c0c0;">GO</span> </p><p>&nbsp;</p><p>【<strong>参考资料</strong>】<br /></p><p>1. SQL Azure Notes,&nbsp;http://www.tewari.info/2009/09/10/sql-azure-notes/, 10 Sep, 2009</p><p>2.&nbsp;Deprecated Database Engine Features in SQL Server 2008, http://msdn.microsoft.com/en-us/library/ms143729.aspx, MSDN</p><p>3.&nbsp;SQL Server 2008 中不推荐使用的数据库引擎功能, http://msdn.microsoft.com/zh-cn/library/ms143729.aspx, MSDN <br /></p><img src="http://www.cnblogs.com/pchen/aggbug/1666423.html?type=1" width="1" height="1" alt=""/><p>评论: 2　<a href="http://www.cnblogs.com/pchen/archive/2010/02/09/1666423.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/pchen/archive/2010/02/09/1666423.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Digg被黑了？No，呵呵</title><link>http://www.cnblogs.com/blodfox777/archive/2010/02/09/1666388.html</link><dc:creator>LanceZhang</dc:creator><author>LanceZhang</author><pubDate>Tue, 09 Feb 2010 05:38:00 GMT</pubDate><guid>http://www.cnblogs.com/blodfox777/archive/2010/02/09/1666388.html</guid><description><![CDATA[<p>阅读: 4849 评论: 21 作者: <a href="http://www.cnblogs.com/blodfox777/" target="_blank">LanceZhang</a> 发表于 2010-02-09 13:38 <a href="http://www.cnblogs.com/blodfox777/archive/2010/02/09/1666388.html" target="_blank">原文链接</a></p><p>今天老大让我打开Digg.com的首页，看它的源码</p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="assdfddd1" border="0" alt="assdfddd1" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/assdfddd1_1.png" width="442" height="103" /> </p>  <p>这一看不要紧，偶当场就被雷到了：</p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="asdfasdfdsdfsdf" border="0" alt="asdfasdfdsdfsdf" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/asdfasdfdsdfsdf_1.png" width="749" height="819" /> </p>  <p>&nbsp;</p>  <p>随即百思不得其解，被黑了？在老大的提示下，才发现玄机原来在这里：</p>  <p>&nbsp;</p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="aaaaadffdf21" border="0" alt="aaaaadffdf21" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/aaaaadffdf21_1.png" width="749" height="718" /> </p>  <p>&nbsp;</p>  <p>&nbsp;</p>  <p>&nbsp;</p>  <p>看得出，这是一个网站留下的网址和密码，是黑客留下的吗？进入<a href="http://www.hellisnight.com/" target="_blank">www.hellisnight.com</a>, 来一看究竟：</p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="asdfas232323" border="0" alt="asdfas232323" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/asdfas232323_1.png" width="599" height="484" /> </p>  <p>进来之后，选择输入我的生日，并继续，便出现了这个界面，让我输入6个密码，当然，我们在Digg.com的首页源码中看到的<strong>password1:excommunicate</strong>，当然就是指第一个密码了，其它的六个密码呢？想必在其它几个大网站中藏着吧。</p>  <p>呵呵，看到这里，我们差不多已经明白了，这是个别出心裁的广告！通过在各大网站中留下线索让大家到这个网站，再指引大家去找别的密码O(&#8745;_&#8745;)O~</p>  <p>再看这个网页的footer，呵呵，是游戏大鳄EA啊，原来这是游戏《但丁的地域》的网站，当你找到别的密码并在这个网站提交后，便可以下载&#8220;<strong>奖品</strong>&#8221;啦！</p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Hellisnighrewards" border="0" alt="Hellisnighrewards" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/Hellisnighrewards_3.jpg" width="586" height="313" /> </p>  <p>&nbsp;</p>  <p>我google了一下，发现了其它几个游戏大站上藏着的密码，如下：</p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="asdfasdfdsdfsdf3333" border="0" alt="asdfasdfdsdfsdf3333" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/asdfasdfdsdfsdf3333_1.png" width="726" height="796" /> </p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="asdfasdfdsdfsdf4444" border="0" alt="asdfasdfdsdfsdf4444" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/asdfasdfdsdfsdf4444_1.png" width="788" height="819" /> </p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="asdfasdfdsdfsdf5555" border="0" alt="asdfasdfdsdfsdf5555" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/asdfasdfdsdfsdf5555_1.png" width="727" height="764" /> </p>  <p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="asdfasdfdsdfsdf6666" border="0" alt="asdfasdfdsdfsdf6666" src="http://images.cnblogs.com/cnblogs_com/blodfox777/WindowsLiveWriter/Digg_AC12/asdfasdfdsdfsdf6666_1.png" width="737" height="842" /> </p>  <p>&nbsp;</p>  <p>呵呵，EA Games Challenge Everything！这样的广告果然非同凡响啊。</p><img src="http://www.cnblogs.com/blodfox777/aggbug/1666388.html?type=1" width="1" height="1" alt=""/><p>评论: 21　<a href="http://www.cnblogs.com/blodfox777/archive/2010/02/09/1666388.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/blodfox777/archive/2010/02/09/1666388.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>开源那些事儿(三)-iToday的总体设计</title><link>http://www.cnblogs.com/procoder/archive/2010/02/09/About-Open-Source-3.html</link><dc:creator>Jake Lin</dc:creator><author>Jake Lin</author><pubDate>Tue, 09 Feb 2010 05:30:00 GMT</pubDate><guid>http://www.cnblogs.com/procoder/archive/2010/02/09/About-Open-Source-3.html</guid><description><![CDATA[<p>阅读: 797 评论: 5 作者: <a href="http://www.cnblogs.com/procoder/" target="_blank">Jake Lin</a> 发表于 2010-02-09 13:30 <a href="http://www.cnblogs.com/procoder/archive/2010/02/09/About-Open-Source-3.html" target="_blank">原文链接</a></p><p>本文讲述iToday总体设计的第一版本。</p>  <h1>UML 图</h1>  <p><a target="_blank" href="http://images.cnblogs.com/cnblogs_com/procoder/WindowsLiveWriter/CodePlex_CDF0/iToday-design-1_4.png"><img style="border-width: 0px; display: inline;" title="iToday-design-1" alt="iToday-design-1" src="http://images.cnblogs.com/cnblogs_com/procoder/WindowsLiveWriter/CodePlex_CDF0/iToday-design-1_thumb_1.png" border="0" width="862" height="519" /></a> </p>  <h1>PanelManager</h1>  <p>iToday的主程序只是包含PanelManager，主窗口线程的消息循环接收到的所有消息都传递给PanelManager来处理，iToday主程序不做任何业务逻辑处理。</p>  <p>&nbsp;</p>  <p>PanelManager有三个主要的成员变量，其中Panels表示所有Panel的指针的容器，保存是指向PanelBase的指针，对象为各个具体的Panel对象。而ActivePanel为指向当前活动Panel的指针。Swither是特殊的Panel，单独一个对象，不在map容器里面。</p>  <p>&nbsp;</p>  <h1>消息事件处理</h1>  <p>主窗口线程接收到任何事件都给PanelManager处理，而PanelManager又分发给ActivePanel和Swither来处理。所有继承于抽象类PanelBase的具体Panel类需要重载虚函数ProcessMouseDownEvent(),ProcessMouseUpEvent(),ProcessOtherEvents()和ProcessGesture()来处理事件和消息。如果是鼠标事件，ActivePanel和Swither会根据内部判断是否自己区域，不是就不用处理了。而具体Panel类根据需要可以调用PanelManager的Draw()函数，直接要求重画，为了做动画过程。</p>  <p>&nbsp;</p>  <h1>绘画</h1>  <p>iToday的绘画由PanelManager的Draw()函数负责，由两部分组成，ActivePanel和Swither的绘画组成了整个iToday的绘画过程。PanelManager的Draw()的过程是这样的，分别调用 ActivePanel和Swither的PrepareDraw()，ActivePanel和Swither只是把需要画的画到内存内。然后由PanelManager一次画到真实设备，这里使用了双缓冲。以此，所有继承于抽象类PanelBase的具体Panel类需要根据当前的自己的需求去重载PrepareDraw()函数，把信息画到内存去。</p>  <p>&nbsp;</p>  <h1>如何开发新Panel</h1>  <p>这个框架的原则是新增Panel的时候，PanelManager只是在初始化加载Panel的地方进行更改，不用更改任何消息处理和绘画过程。PanelManager和具体的Panel类解耦。开发具体的Panel类，需要重载事件函数ProcessMouseDownEvent(),ProcessMouseUpEvent(),ProcessOtherEvents()和ProcessGesture()，具体的Panel类可以自定义自己的事件，例如HomePanel可以自己定义监控注册表消息事件，PanelManager完全不需要进行更改来支持这自定义事件。同时需要重载PrepareDraw()来画自己的逻辑。</p>  <p>&nbsp;</p>  <p>请根据具体Panel的需求来考虑这个框架是否完善，有任何意见请及时反馈，及时改进。</p>  <p>&nbsp;</p>  <p>其他iToday的文章请看：</p>  <p><a href="http://www.cnblogs.com/procoder/archive/2010/02/02/About-Open-Source.html" target="_blank">开源(Open Source)那些事儿 (一)</a></p>  <p><a target="_blank" href="http://www.cnblogs.com/procoder/archive/2010/02/05/About-Open-Source-2.html">开源那些事儿 (二) - iToday开源项目计划</a></p><img src="http://www.cnblogs.com/procoder/aggbug/1666382.html?type=1" width="1" height="1" alt=""/><p>评论: 5　<a href="http://www.cnblogs.com/procoder/archive/2010/02/09/About-Open-Source-3.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/procoder/archive/2010/02/09/About-Open-Source-3.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>我自己的ColorSpy</title><link>http://www.cnblogs.com/zhjp11/archive/2010/02/09/1666343.html</link><dc:creator>萧萧空间</dc:creator><author>萧萧空间</author><pubDate>Tue, 09 Feb 2010 04:12:00 GMT</pubDate><guid>http://www.cnblogs.com/zhjp11/archive/2010/02/09/1666343.html</guid><description><![CDATA[<p>阅读: 961 评论: 3 作者: <a href="http://www.cnblogs.com/zhjp11/" target="_blank">萧萧空间</a> 发表于 2010-02-09 12:12 <a href="http://www.cnblogs.com/zhjp11/archive/2010/02/09/1666343.html" target="_blank">原文链接</a></p><p><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好久没有写博客了，今天闲下来无聊，就简单写了一个小程序，名称叫做ColorSpy。以前经常使用别人的ColorSpy进行取色，决定非常有意思，今天就自己简简单单的实现一个，也为自己练练手。</font></p>
<p><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ColorSpy简单来说就是对屏幕的取色，其中有几个难点：</font></p>
<ol><li><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1、获取鼠标在屏幕上的位置：</font></li><li><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、获取鼠标在屏幕上位置的区域图片。</font></li><li><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3、获取鼠标指向的颜色。</font> 
<ol><li><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4、C#中使用Hook，来显示和拷贝颜色。</font> </li></ol></li><li><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然，为了方便，我还在放大了鼠标位置的区域，并将其显示在自己的ColorSpy窗体上。</font></li><li><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其他的就不说了，看看效果图就可以了：</font></li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://images.cnblogs.com/cnblogs_com/zhjp11/WindowsLiveWriter/ColorSpy_AA1C/image_2.png" target="_blank"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/zhjp11/WindowsLiveWriter/ColorSpy_AA1C/image_thumb.png" width="335" height="192" /></a></li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 代码如下：<a title="屏幕取色" href="http://files.cnblogs.com/zhjp11/ColorSpy.rar" target="_blank">屏幕取色</a></li></ol><img src="http://www.cnblogs.com/zhjp11/aggbug/1666343.html?type=1" width="1" height="1" alt=""/><p>评论: 3　<a href="http://www.cnblogs.com/zhjp11/archive/2010/02/09/1666343.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/zhjp11/archive/2010/02/09/1666343.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>silverlight 3D引擎 Balder 0.8.8.6发布了！</title><link>http://www.cnblogs.com/nasa/archive/2010/02/09/Balder-0886-is-out.html</link><dc:creator>王喆(nasa)</dc:creator><author>王喆(nasa)</author><pubDate>Tue, 09 Feb 2010 03:45:00 GMT</pubDate><guid>http://www.cnblogs.com/nasa/archive/2010/02/09/Balder-0886-is-out.html</guid><description><![CDATA[<p>阅读: 1391 评论: 11 作者: <a href="http://www.cnblogs.com/nasa/" target="_blank">王喆(nasa)</a> 发表于 2010-02-09 11:45 <a href="http://www.cnblogs.com/nasa/archive/2010/02/09/Balder-0886-is-out.html" target="_blank">原文链接</a></p><p>silverlight 3D引擎 Balder 0.8.8.6发布了！</p>
<div><br />
<div>原文：<a href="http://www.ingebrigtsen.info/post/2010/02/08/Balder-0886-is-out.aspx" id="di24">http://www.ingebrigtsen.info/post/2010/02/08/Balder-0886-is-out.aspx</a></div>
<div>翻译：<a href="http://www.cnblogs.com/nasa">nasa</a></div>
<div><br />
<div>经过几个月的艰苦努力，重构代码、API以及提高性能，<a href="http://balder.codeplex.com/" title="Balder 0.8.8.6" id="wjzl">Balder 0.8.8.6</a>终于发布了。这里有一个<a href="http://www.ingebrigtsen.info/silverlight/Balder/20100208/TestPage.html" title="SampleBrowser Demo" id="zln9">SampleBrowser Demo</a>演示了<a href="http://balder.codeplex.com/" title="Balder">Balder</a>绝大多数的功能。</div>
<div></div>
<div>
<div id="qlcx" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_985fg7hcwcd_b" style="width: 420px; height: 305px;" /></div>
</div>
<div></div>
<div>新特性如下：</div>
<div>
<ul>
<li>核心引入silverlight，但是仍然保持独立平台-直接在XAML中可以完成全部的工作</li>
<li>引入View命名空间IView接口以及 Camera摄像机概念</li>
<li>Viewport有了View属性来替代了Camera</li>
<li>从单线程改变到多线程执行，已得到更高的效率。（现在线程间的同步还有问题，有待解决）</li>
<li>全新优化过的绘图程序</li>
<li>原生<a href="http://en.wikipedia.org/wiki/Heightmap" title="高度图" id="dwyr">高度图</a></li>
<li>原生立方型</li>
<li>原生圆柱型</li>
<li>旋转、根据节点缩放</li>
<li>DebugLevel改名为DebugInfo</li>
<li>材质系统</li>
<li>支持对材质的高光反射</li>
<li>支持双面材质</li>
<li>渲染时支持Alpha通道</li>
<li>移除在Balder.Core.Silverlight.Controls所有的控件，他们不在需要了。</li>
<li>在Balder.Core.Silverlight.Controls中新增NodesControl控件-datadriven nodes control with templating</li>
<li>在Balder.Core.Silverlight.Controls中新增NodesStack控件-datadriven stacking of nodes with templating</li>
</ul>
</div>
<div></div>
<div>
<div></div>
</div>
<br />
<div>截图：</div>
<div></div>
<div id="t9fx" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_986hh2gwfgg_b" style="width: 321px; height: 239px;" /></div>
<div id="hkm_" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_987sgw3xsgq_b" style="width: 320px; height: 241px;" /></div>
<div id="jsmn" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_988d7c4z5cf_b" style="width: 320px; height: 238px;" /></div>
<div id="z3ca" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_989fnhjf5jn_b" style="width: 321px; height: 239px;" /></div>
<div id="p3_o" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_990ct2fjrf4_b" style="width: 321px; height: 239px;" /></div>
<div id="v-1s" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_991c5r6h7cj_b" style="width: 421px; height: 306px;" /></div>
<div id="t1gb" style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_9924v66hnt6_b" style="width: 421px; height: 289px;" /></div>
<div style="text-align: left;"><img src="http://docs.google.com/File?id=df5kjk97_993f57gvccb_b" style="width: 421px; height: 289px;" /></div>
</div>
</div><img src="http://www.cnblogs.com/nasa/aggbug/1666322.html?type=1" width="1" height="1" alt=""/><p>评论: 11　<a href="http://www.cnblogs.com/nasa/archive/2010/02/09/Balder-0886-is-out.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/nasa/archive/2010/02/09/Balder-0886-is-out.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>代码控制PrivateBinPath和ConfigurationFile的位置</title><link>http://www.cnblogs.com/NullReference/archive/2010/02/09/Set_AppDomain_PrivateBinPath_and_ConfigurationFile_Location.html</link><dc:creator>NullReference</dc:creator><author>NullReference</author><pubDate>Tue, 09 Feb 2010 03:45:00 GMT</pubDate><guid>http://www.cnblogs.com/NullReference/archive/2010/02/09/Set_AppDomain_PrivateBinPath_and_ConfigurationFile_Location.html</guid><description><![CDATA[<p>阅读: 638 评论: 5 作者: <a href="http://www.cnblogs.com/NullReference/" target="_blank">NullReference</a> 发表于 2010-02-09 11:45 <a href="http://www.cnblogs.com/NullReference/archive/2010/02/09/Set_AppDomain_PrivateBinPath_and_ConfigurationFile_Location.html" target="_blank">原文链接</a></p><p>.Net的WinForm程序有的时候让人很烦的是，在执行目录下总是一大堆的DLL，配置文件，最少则是个以下，多的时候怕有四五十个吧&hellip;&hellip;，自己程序中的类库，第三方的类库&hellip;&hellip;加载一起让人感觉乱糟糟的，非常不爽。在下虽然在个人卫生上没有什么洁癖，可是对于应用程序的这个样子确实没有一点容忍力的，是可忍孰不可忍啊！</p>
<p>&nbsp;</p>
<p>处理这些DLL还是比较简单的，Configuration文件里就可以配置了。先将DLL分门别类，Core, Module, Misc等等，然后将这几个目录名称加入App.config中。如：</p>
<p>
<pre class="brush:html;gutter:false;">  &lt;runtime&gt;
    &lt;assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"&gt;
      &lt;probing privatePath="Castle;Core;Module;UI;Misc;"/&gt;
    &lt;/assemblyBinding&gt;
  &lt;/runtime&gt;
</pre>
</p>
<p>嗯，现在看上去就舒服多了，各就各位的，清爽！慢&hellip;&hellip;在主目录下，出了我们的应用程序Exe可执行文件外，还有一个例外，app.config&hellip;&hellip; 多少还是有点儿不爽，怎么办呢？我们建个Data目录，将config文件移动进去吧~！说做就做，我们就把app.config移动到下级目录了，开心啊，看看程序能运行不？ 杯具发生了，程序运行毫无反应&hellip;&hellip; 用VS调试之，发现是找不到其他类库中的类型导致的。也是，我们刚刚在config文件中加了privatePath，现在有把这个文件移动了，怎么能加载到其他目录下的DLL呢？</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>知道原因了，当然就好解决。据说configuration文件的位置是可以指定的。AppDomainSetup.ConfiguraitonFile属性就是指的这个。让我们来试试&hellip;&hellip;</p>
<p>&nbsp;</p>
<p>分析一下：</p>
<p>要改config的路径，当然是要在使用config之前咯，而且越早越好&hellip;&hellip; 哪里比较早呢？嗯，Main函数，程序的入口是个不错的选择&hellip;&hellip;</p>
<p>
<pre class="brush:csharp;gutter:false;">    static class Program
    {
         /// &lt;summary&gt;
        /// 应用程序的主入口点。
        /// &lt;/summary&gt;
        [STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.SetupInformation.ConfigurationFile = "Data\MyApp.Config";

            Application.Run(new MyAppForm());
        }
    }
</pre>
</p>
<p>&nbsp;</p>
<p>好，编译，信心满满的再次运行程序，可是杯具再一次啊发生了，依然没有反应&hellip;&hellip;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&hellip;&hellip;在此调试，惊奇的发现这个属性设置居然完全没有效果，语句执行完后，该属性值依然是默认值。于是查文档啊，百度啊，谷歌啊~ 最后终于知道设置config的正确方法。</p>
<p>
<pre class="brush:csharp;gutter:false;">AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", path);
</pre>
</p>
<p>&nbsp;</p>
<p>嗯，这个这个&hellip;&hellip;不是微软在害人吗&hellip;&hellip; 好了，停下抱怨继续工作先&hellip;&hellip; 嗯，看到了，config文件的位置生效了，指向了我们预定的位置，可是&hellip;&hellip;&hellip;&hellip; 杯具毫无意外的再次发生&hellip;&hellip;DLL依然不能加载！</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>虽然我已经在AppDomainSetup里看到了PrivateBinPath属性，可是我却一点兴奋感觉都没有，应该跟前面的结果类似把！</p>
<p>想想程序运行的过程，PrivateBinPath是AppDomain.Current.SetupInformation里的属性，而这个属性只有在AppDomain.Create的时候才会生效，在AppDomain已经建立后更改config的路径再去设置这个值，应该是不行的。看来只好代码里来设置了。可是如何下手呢？</p>
<p>&nbsp;</p>
<p>关门， 放Reflector！呵呵，让我们看看AppDomain内部的情况。果其不然啊！SetupInforamtion只是其内部配置的一个Copy，这也就解释了为什么更改属性却没有生效的原因！</p>
<p>
<pre class="brush:csharp;gutter:false;">public AppDomainSetup SetupInformation
{
    get
    {
        return new AppDomainSetup(this.FusionStore, true);
    }
}
</pre>
</p>
<p>&nbsp;</p>
<p>
看起来不动用终极手段是不行的了！反射！更改内部字段来达到我们的目的！仔细检查了以下代码，发现AppDomain内部的Setup信息是保存在一个FunsionStore的Internal属性里的。好，我们就从这里动手把！</p>
<p>&nbsp;</p>
<p>
<pre class="brush:csharp;gutter:false;">            AppDomain.CurrentDomain.SetData("PRIVATE_BINPATH", "Castle;Core;Module;UI;Misc;");
            AppDomain.CurrentDomain.SetData("BINPATH_PROBE_ONLY", "Castle;Core;Module;UI;Misc;");
</pre>
</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>
这段代码确实就更改了AppDomain.CurrentDomain.SetupInformation.PrivateBinPath的值了，可是运行发现类型还是不能加载！可能内部还有个什么RefrefshConfiguraiton或UpdateCache之类的方法来刷新！继续找啊找的~ 终于发现了。是一个static extern的方法。呵呵&hellip;&hellip;这下简单了，看代码！</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>在调试过程中，我们还发现Main方法不是最好的地方，所以我们将代码放在了Program的静态构造方法中，这里是除了静态字段外的最早的起始地了。</p>
<p>
<pre class="brush:csharp;gutter:false;">        static Program()
        {
            AppDomain.CurrentDomain.SetData("PRIVATE_BINPATH", "Castle;Core;Module;UI;Misc;");
            AppDomain.CurrentDomain.SetData("BINPATH_PROBE_ONLY", "Castle;Core;Module;UI;Misc;");
            var m = typeof(AppDomainSetup).GetMethod("UpdateContextProperty", BindingFlags.NonPublic | BindingFlags.Static);
            var funsion = typeof(AppDomain).GetMethod("GetFusionContext", BindingFlags.NonPublic | BindingFlags.Instance);
            m.Invoke(null, new object[] { funsion.Invoke(AppDomain.CurrentDomain, null), "PRIVATE_BINPATH", "Castle;Core;Module;UI;Misc;" });
        }
</pre>
</p>
<p>
嗯，迫不及待的编译，运行！哇！毫不意外的，程序正常了，再看看应用程序目录，只有一个干干净净的exe文件的存在，真是爽到极点~</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>告诉你，exe文件就一个，我可以！哈哈！</p>
<p>&nbsp;</p>
<p>最后提醒，如果Program里有静态字段，不要定义为用其他类库的类型，因为这个时候我们上述的方法还没有执行到，会因为找不到类型而出错的哦~</p><img src="http://www.cnblogs.com/NullReference/aggbug/1666323.html?type=1" width="1" height="1" alt=""/><p>评论: 5　<a href="http://www.cnblogs.com/NullReference/archive/2010/02/09/Set_AppDomain_PrivateBinPath_and_ConfigurationFile_Location.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/NullReference/archive/2010/02/09/Set_AppDomain_PrivateBinPath_and_ConfigurationFile_Location.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>WPF Binding实现自推——强烈要求拍砖</title><link>http://www.cnblogs.com/Curry/archive/2010/02/09/WPFBinding.html</link><dc:creator>Curry</dc:creator><author>Curry</author><pubDate>Tue, 09 Feb 2010 03:20:00 GMT</pubDate><guid>http://www.cnblogs.com/Curry/archive/2010/02/09/WPFBinding.html</guid><description><![CDATA[<p>阅读: 654 评论: 9 作者: <a href="http://www.cnblogs.com/Curry/" target="_blank">Curry</a> 发表于 2010-02-09 11:20 <a href="http://www.cnblogs.com/Curry/archive/2010/02/09/WPFBinding.html" target="_blank">原文链接</a></p><h2>&#160;</h2>  <p>&#160;&#160;&#160;&#160;&#160; 因为项目经常碰到Binding的问题，常常不能联动，所以才进行了分析，我解决方法的思路比较死板：定义问题 -&gt;分解问题 -&gt;初步解决问题 -&gt;优化解法。在解决问题过程中是有收获的，所以分享下心得，也希望各位大大能够帮助小弟查验下思路是否正确，帮忙解解惑，在这里先谢过了。<strong>当然推理这段是和WPF无关的，先有WPF的做法才有WPF的，还请各位不要因为WPF而却步</strong>。</p>  <p>&#160;</p>  <h2>一.定义问题</h2>  <p>&#160;</p>  <p>我们先从最简单的推断，建立2个类，见代码：</p>  <pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">Class1
</span>{
    <span style="color: blue">public int </span>Count1{<span style="color: blue">get</span>;<span style="color: blue">set</span>;}
}

<span style="color: blue">public class </span><span style="color: #2b91af">Class2
</span>{
    <span style="color: blue">public int </span>Count2{<span style="color: blue">get</span>;<span style="color: blue">set</span>;}
}

<span style="color: blue">class </span><span style="color: #2b91af">Program
</span>{
    <span style="color: blue">static void </span>Main(<span style="color: blue">string</span>[] args)
    {
        <span style="color: #2b91af">Class1 </span>class1 = <span style="color: blue">new </span><span style="color: #2b91af">Class1</span>();

        <span style="color: #2b91af">Class2 </span>class2 = <span style="color: blue">new </span><span style="color: #2b91af">Class2</span>();
        
        <span style="color: green">//TODO:
        //**********************************************
        //填写内容补充代码，可修改Class1,Class2
        //要求：Class1和Class2不能互相知道
         //**********************************************

        </span>class1.Count1 = 10;
        <span style="color: #2b91af">Console</span>.WriteLine(class2.Count2);<span style="color: green">//希望输出 10

        </span>class2.Count2 = 20;
        <span style="color: #2b91af">Console</span>.WriteLine(class1.Count1);<span style="color: green">//希望输出 20
    </span>}
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>&#160;</p>

<p>&#160;</p>

<h2>二.简单实现</h2>

<p>&#160;</p>

<p>我们知道了需求修改class1.Count1的值，class2.Count2的值同时改变。修改class2.Count2的值class1.Count1也改变。</p>

<p>代码中要求不能互相知道的意思是：Class1类的实现中不能有Class2类，Class2类中不能包含Class1类，也就是<strong>两者不能耦合</strong>。</p>

<p>需要Count1或Count2属性更改后通知，就要给Class1和Class2各加个事件：</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">PropertyEventArgs </span>: <span style="color: #2b91af">EventArgs
</span>{
    <span style="color: blue">private string </span>_propertyName;

    <span style="color: blue">public </span>PropertyEventArgs(<span style="color: blue">string </span>propertyName)
    {
        _propertyName = propertyName;
    }
    <span style="color: blue">public string </span>PropertyName
    {
        <span style="color: blue">get </span>{ <span style="color: blue">return </span>_propertyName; }
        <span style="color: blue">set </span>{ _propertyName = <span style="color: blue">value</span>; }
    }
}


<span style="color: blue">public class </span><span style="color: #2b91af">Class1
</span>{
    <span style="color: blue">public event </span><span style="color: #2b91af">EventHandler</span>&lt;<span style="color: #2b91af">PropertyEventArgs</span>&gt; PropertyChanged;

    <span style="color: blue">private int </span>_count1;
    <span style="color: blue">public int </span>Count1
    {
        <span style="color: blue">get </span>{ <span style="color: blue">return </span>_count1; }
        <span style="color: blue">set
        </span>{
            <span style="color: blue">if </span>(_count1 != <span style="color: blue">value</span>)
            {
                _count1 = <span style="color: blue">value</span>;
                PropertyChanged(<span style="color: blue">this</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyEventArgs</span>(<span style="color: #a31515">&quot;Count1&quot;</span>));
            }
        }
    }
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>class2同class1</p>

<p>题目要求class1,class2不能耦合，所以必然有个中间类来“包装”。</p>

<pre class="code"><span style="color: blue">public static class </span><span style="color: #2b91af">InteropWarp
</span>{
    <span style="color: blue">public static void </span>ListenChanged(<span style="color: #2b91af">Class1 </span>class1,<span style="color: blue">string </span>properyName1,<span style="color: #2b91af">Class2 </span>class2,<span style="color: blue">string </span>properyName2)
    {
        class1.PropertyChanged += (sender, e) =&gt;
                      {
                          <span style="color: blue">var </span>newValue = sender.GetType().GetProperty(e.PropertyName).GetValue(sender, <span style="color: blue">null</span>);
                          class2.GetType().GetProperty(properyName2).SetValue(class2,newValue,<span style="color: blue">null</span>);
                      };

        class2.PropertyChanged+=(sender, e) =&gt;
                      {
                          <span style="color: blue">var </span>newValue = sender.GetType().GetProperty(e.PropertyName).GetValue(sender, <span style="color: blue">null</span>);
                          class1.GetType().GetProperty(properyName1).SetValue(class1, newValue, <span style="color: blue">null</span>);
                      };

    }
}</pre>

<p>在主函数中写下如下调用便可以完成题目要求了</p>

<pre class="code"><span style="color: #2b91af">InteropWarp</span>.ListenChanged(class1, <span style="color: #a31515">&quot;Count1&quot;</span>, class2, <span style="color: #a31515">&quot;Count2&quot;</span>);</pre>

<p><a href="http://11011.net/software/vspaste"></a></p>

<p>不过我们也可以看出这个包装类有许多的问题：</p>

<p>1.参数类型为具体类，规定死了class1或class2.</p>

<p>2.其中的事件注册，如果class1销毁了，class2的事件应该予以销毁，要不然还是依赖不能销毁，产生内存泄露。如下:</p>

<pre class="code">class1.Count1 = 10;
<span style="color: #2b91af">Console</span>.WriteLine(class2.Count2);<span style="color: green">//希望输出 10

</span>class1 = <span style="color: blue">null</span>;
<span style="color: #2b91af">GC</span>.Collect();

class2.Count2 = 20;<span style="color: green">//运行这句class2注册的事件去通知class1,这时候我们希望报错，因为class1应该销毁，可实际并没有，说明class1并未销毁</span></pre>
<a href="http://11011.net/software/vspaste"></a>

<p>3.使用反射，如果多个属性更改必然会有性能影响。</p>

<p>&#160;</p>

<h2>三.初步优化</h2>

<p>&#160;</p>

<p>问题是一个个解决的，首先来看第一个问题，为什么会需要把参数定为具体类型？那是因为从具体的类上才能得到事件信息(class1,class2上才定义了事件)，所以把事件抽出到<strong>接口或抽象类</strong>中便可以了。</p>

<pre class="code"><span style="color: blue">public interface </span><span style="color: #2b91af">IPropertyChanged
</span>{
    <span style="color: blue">event </span><span style="color: #2b91af">EventHandler</span>&lt;<span style="color: #2b91af">PropertyEventArgs</span>&gt; PropertyChanged;
}</pre>

<p><a href="http://11011.net/software/vspaste"></a>这样class1,class2继承这个接口实现具体就可以了。 </p>

<p><strong>不足：但这个也需要加个接口，需要在类中具体实现，每个类都加似乎有些烦。</strong></p>

<p>&#160;</p>

<p>第二问题，那就是需要在<span style="color: #2b91af">InteropWarp</span>内部传递方法需要使用弱引用,结合第一点我们可能会改写如下：</p>

<pre class="code"><span style="color: blue">public static void </span>ListenChanged(<span style="color: #2b91af">IPropertyChanged </span>class1, <span style="color: blue">string </span>properyName1, <span style="color: #2b91af">IPropertyChanged </span>class2, <span style="color: blue">string </span>properyName2)
{
    <span style="color: blue">var </span>class1WeakReference = <span style="color: blue">new </span><span style="color: #2b91af">WeakReference</span>(class1);
    <span style="color: blue">var </span>class2WeakReference = <span style="color: blue">new </span><span style="color: #2b91af">WeakReference</span>(class2);

    <span style="color: #2b91af">Action</span>&lt;<span style="color: #2b91af">WeakReference</span>,<span style="color: blue">string</span>, <span style="color: #2b91af">WeakReference</span>,<span style="color: blue">string</span>&gt; registerChangedEvent =
        (getValueClass, listenedProperty, setValueClass, needChangedProperty) =&gt;
              {
                  ((<span style="color: #2b91af">IPropertyChanged</span>)getValueClass.Target).PropertyChanged += (sender, e) =&gt;
                  {
                      <span style="color: blue">if </span>(e.PropertyName == listenedProperty)<span style="color: green">//判断是是否是需要更新的属性名
                      </span>{
                          <span style="color: green">//得到新值
                          </span><span style="color: blue">var </span>newValue = sender.GetType().GetProperty(e.PropertyName).GetValue(sender, <span style="color: blue">null</span>);
                          <span style="color: green">//把值赋入
                          </span><span style="color: blue">if </span>(setValueClass.Target != <span style="color: blue">null</span>)
                              setValueClass.Target.GetType().GetProperty(needChangedProperty).
                                  SetValue(setValueClass.Target, newValue, <span style="color: blue">null</span>);
                      }
                  };
              };

    registerChangedEvent(class1WeakReference,properyName1, class2WeakReference,properyName2);
    registerChangedEvent(class2WeakReference,properyName2, class1WeakReference,properyName1);

}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>&#160;</p>

<p>这样当</p>

<pre class="code">class1 = <span style="color: blue">null</span>;
<span style="color: #2b91af">GC</span>.Collect();</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>之后class1是这真的被销毁了，class1没了，class2为class1注册的事件自然也没有意义，也应该销毁。所以当执行事件的时候，我们还要判断如果class1WeakReference .Target为空的话，就把事件给注销掉：</p>

<pre class="code"><span style="color: blue">var </span>propertyChangedEvent = <span style="color: blue">default</span>(<span style="color: #2b91af">EventHandler</span>&lt;<span style="color: #2b91af">PropertyEventArgs</span>&gt;);
  propertyChangedEvent = (sender, e) =&gt;
 {
    <span style="color: blue">if</span>(setValueClass.Target == <span style="color: blue">null</span>)
    {
        <span style="color: green">//注销事件，注意propertyChangedEvent实际上是被闭包的,但在此处无影响
        </span>((<span style="color: #2b91af">IPropertyChanged</span>) getValueClass.Target).PropertyChanged -= propertyChangedEvent;
    }
    <span style="color: blue">else if </span>(e.PropertyName == listenedProperty)<span style="color: green">//判断需要更新的属性名
    </span>{
        <span style="color: green">//得到新值
        </span><span style="color: blue">var </span>newValue = sender.GetType().GetProperty(e.PropertyName).GetValue(sender, <span style="color: blue">null</span>);
        <span style="color: green">//把值赋入
            </span>setValueClass.Target.GetType().GetProperty(needChangedProperty).
                SetValue(setValueClass.Target, newValue, <span style="color: blue">null</span>);
    }
};

((<span style="color: #2b91af">IPropertyChanged</span>) getValueClass.Target).PropertyChanged += propertyChangedEvent;</pre>

<p>这样便可以在执行事件时对class1进行判断决定是否要把事件给去掉。</p>

<p><strong>不足：这个是在执行对应事件后进行处理，假如没有执行事件内存依旧浪费。</strong></p>

<p>&#160;</p>

<p>第三个问题，反射在大数据量，属性更换频繁的前提下自然是能不用则不用，那如若不用反射？我们用什么？Emit?确实有了Expression ,使得我们书写Emit已经很方便，在我们自己的使用过程中或许真的就这么做了，可WPF还有个XAML，XAML是解释性的，动态调用，解析字符串在这种条件下用Emit还不是“最省”的。那怎么才是最省的？</p>

<p>&#160;&#160;&#160;&#160; 每个属性名对一个类来说都是唯一的，那样便可以把属性名当做一个字符串来处理，把字符串当做Key存放到一个键值对应的散列表中(如:HashTable),Value域放属性的值，属性的类型是多样的可以是int,long,DateTime等系统自带的，也可以是Employee、Order等自定义的类所以用object类型存放。并在类中写两个函数Get,Set来获取处理值。</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">Class1 </span>: <span style="color: #2b91af">IPropertyChanged
</span>{
    <span style="color: blue">protected </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: blue">object</span>&gt; _dic = <span style="color: blue">new </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: blue">object</span>&gt;();
    <span style="color: blue">public event </span><span style="color: #2b91af">EventHandler</span>&lt;<span style="color: #2b91af">PropertyEventArgs</span>&gt; PropertyChanged;

    <span style="color: blue">public object </span>GetValue(<span style="color: blue">string </span>propertyName)
    {
        <span style="color: blue">if </span>(_dic[propertyName] == <span style="color: blue">null</span>)
            <span style="color: blue">return </span>10;
        <span style="color: blue">return </span>_dic[propertyName];
    }

    <span style="color: blue">public void </span>SetValue(<span style="color: blue">string </span>propertyName,<span style="color: blue">object </span>value)
    {
        <span style="color: blue">int </span>count = (<span style="color: blue">int</span>) value;

        <span style="color: blue">if </span>(propertyName == <span style="color: #a31515">&quot;Count1&quot;</span>)
        {
            <span style="color: blue">if </span>(count &gt; 100)
                count = 100;
        }
        <span style="color: blue">else if </span>(propertyName == <span style="color: #a31515">&quot;Count2&quot;</span>)
        {
            <span style="color: blue">if </span>(count &gt; 50)
                count = 50;
        }

        _dic[propertyName] = count;

        <span style="color: blue">if </span>(PropertyChanged != <span style="color: blue">null</span>)
            PropertyChanged(<span style="color: blue">this</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyEventArgs</span>(propertyName));
    }
}</pre>
<a href="http://11011.net/software/vspaste"></a>这里的弊端我们已经看出来，在SetValue中if语句是如此的丑陋，假使GetValue也要一一校验返回的话……。 

<p>我们自然希望每个属性都有属于他们自己的处理方法，类中的SetValue，GetValue只要调用下就可以。</p>

<p>可一般属性都有Get,Set两个方法， Value域只是一块内存，要放两个函数就需要一对多的映射，反映到代码中就是需要添加一个类。</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">PropertyDefinition
</span>{
    <span style="color: blue">protected object </span>_value;
    <span style="color: blue">public virtual object </span>GetValue()
    {
        <span style="color: blue">return </span>_value;
    }

    <span style="color: blue">public virtual void </span>SetValue(<span style="color: blue">object </span>value)
    {
        _value = value;
    }
}

<span style="color: blue">public class </span><span style="color: #2b91af">NamePropertyDefinition </span>: <span style="color: #2b91af">PropertyDefinition
</span>{
    <span style="color: blue">public override object </span>GetValue()
    {
        <span style="color: blue">if </span>(Equals(_value,<span style="color: #a31515">&quot;Curry&quot;</span>))
            <span style="color: blue">return </span><span style="color: #a31515">&quot;http://wwww.cnblogs.com/Curry&quot;</span>;
        <span style="color: blue">return </span>_value;
    }

    <span style="color: blue">public override void </span>SetValue(<span style="color: blue">object </span>value)
    {
        _value = value;
    }
}

<span style="color: blue">public class </span><span style="color: #2b91af">Class1 </span>: <span style="color: #2b91af">IPropertyChanged
</span>{
    <span style="color: blue">private </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">PropertyDefinition</span>&gt; _dic = <span style="color: blue">new </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">PropertyDefinition</span>&gt;();
    <span style="color: blue">public event </span><span style="color: #2b91af">EventHandler</span>&lt;<span style="color: #2b91af">PropertyEventArgs</span>&gt; PropertyChanged;

    <span style="color: blue">public </span>Class1()
    {
        RegisterPropery(<span style="color: #a31515">&quot;Count1&quot;</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyDefinition</span>());
        RegisterPropery(<span style="color: #a31515">&quot;Name&quot;</span>, <span style="color: blue">new </span><span style="color: #2b91af">NamePropertyDefinition</span>());<font color="#008000">//需要为每个属性定义类，很废品
</font>    }

    <span style="color: blue">protected  void </span>RegisterPropery(<span style="color: blue">string </span>propertyName,<span style="color: #2b91af">PropertyDefinition </span>definition)
    {
        _dic.Add(propertyName, definition);
    }

    <span style="color: blue">public object </span>GetValue(<span style="color: blue">string </span>propertyName)
    {
        <span style="color: blue">return </span>_dic[propertyName].GetValue();
    }

    <span style="color: blue">public void </span>SetValue(<span style="color: blue">string </span>propertyName,<span style="color: blue">object </span>value)
    {
        _dic[propertyName].SetValue(value);

        <span style="color: blue">if </span>(PropertyChanged != <span style="color: blue">null</span>)
            PropertyChanged(<span style="color: blue">this</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyEventArgs</span>(propertyName));
    }
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>这样的话确实类中的SetValue和GetValue方法是方便了，可每次都要为属性弄个新类，这点人性化太差。</p>

<p>假如我们把Get,Set的方法，通过委托传入就好了。让SetValue有个返回值，以便传回修改完的值。</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">PropertyDefinition
</span>{
    <span style="color: blue">internal object </span>_value;

    <span style="color: blue">public </span>PropertyDefinition(<span style="color: blue">object </span>defaultValue)
    {
        _value = defaultValue;
    }

    <span style="color: blue">public </span>PropertyDefinition(<span style="color: blue">object </span>defaultValue,<span style="color: #2b91af">Func</span>&lt;<span style="color: blue">object</span>, <span style="color: blue">object</span>, <span style="color: blue">object</span>&gt; setFunc,<span style="color: #2b91af">Func</span>&lt;<span style="color: blue">object</span>&gt; getFunc)
    {
        _value = defaultValue;
        SetValue = setFunc;
        GetValue = getFunc;
    }

    <span style="color: green">//三个object的顺序是 修改后的值，新值，旧值
    </span><span style="color: blue">public </span><span style="color: #2b91af">Func</span>&lt;<span style="color: blue">object</span>, <span style="color: blue">object</span>, <span style="color: blue">object</span>&gt; SetValue { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }

    <span style="color: blue">public </span><span style="color: #2b91af">Func</span>&lt;<span style="color: blue">object</span>&gt; GetValue { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
}

<span style="color: blue">public class </span><span style="color: #2b91af">Class1 </span>: <span style="color: #2b91af">IPropertyChanged
</span>{
    <span style="color: blue">private </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">PropertyDefinition</span>&gt; _dic = <span style="color: blue">new </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">PropertyDefinition</span>&gt;();
    <span style="color: blue">public event </span><span style="color: #2b91af">EventHandler</span>&lt;<span style="color: #2b91af">PropertyEventArgs</span>&gt; PropertyChanged;

    <span style="color: blue">public </span>Class1()
    {
        <span style="color: green">//不传委托，只传默认值
        </span>RegisterPropery(<span style="color: #a31515">&quot;Name&quot;</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyDefinition</span>(<span style="color: #a31515">&quot;Curry&quot;</span>));
        <span style="color: green">//传入委托
        </span>RegisterPropery(<span style="color: #a31515">&quot;Count1&quot;</span>, 
            <span style="color: blue">new </span><span style="color: #2b91af">PropertyDefinition</span>(1,(oldValue,newValue)=&gt;
                                   {
                                       <span style="color: blue">int </span>count = (<span style="color: blue">int</span>) newValue;
                                       <span style="color: blue">if </span>(count &gt; 100 || count &lt; 0)
                                           <span style="color: blue">return </span>oldValue;
                                       <span style="color: blue">return </span>count/10;
                                   },<span style="color: blue">null</span>));
    }

    <span style="color: blue">protected  void </span>RegisterPropery(<span style="color: blue">string </span>propertyName,<span style="color: #2b91af">PropertyDefinition </span>definition)
    {
        _dic.Add(propertyName, definition);
    }

    <span style="color: blue">public object </span>GetValue(<span style="color: blue">string </span>propertyName)
    {
        <span style="color: blue">if </span>(_dic[propertyName].GetValue != <span style="color: blue">null</span>)
            <span style="color: blue">return </span>_dic[propertyName].GetValue();
        <span style="color: blue">return </span>_dic[propertyName]._value;
    }

    <span style="color: blue">public void </span>SetValue(<span style="color: blue">string </span>propertyName,<span style="color: blue">object </span>value)
    {
        <span style="color: green">//可以用三元表达式
        //        _dic[propertyName]._value = _dic[propertyName].SetValue != null ?
        //              _dic[propertyName].SetValue(_dic[propertyName]._value, value) : value;

        </span><span style="color: blue">if</span>(_dic[propertyName].SetValue!=<span style="color: blue">null</span>)
        {
            _dic[propertyName]._value = _dic[propertyName].SetValue(_dic[propertyName]._value, value);
        }
        <span style="color: blue">else
        </span>{
            _dic[propertyName]._value = value;
        }

        <span style="color: blue">if </span>(PropertyChanged != <span style="color: blue">null</span>)
            PropertyChanged(<span style="color: blue">this</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyEventArgs</span>(propertyName));
    }
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>这似乎已经符合我们的需要了，可仔细考虑下<strong>每次返回都调用GetValue</strong>是否真的有必要呢？</p>

<p>当值传入的时候往往就已经决定了最后的结果，我们每次取值的时候都要GetValue进行处理实际上有些浪费效能。</p>

<p>所以我们把GetValue委托处理完的值，保存起来，用的时候直接拿那个值就可以了。以这种保存值的做法，我们也可以把第一次传入的值作为默认值，加一个变量保存下就可以了。把这些值都定义为<span style="color: blue">internal </span>是为了更好封装性。</p>

<pre class="code"><span style="color: blue">internal object </span>_value;
<span style="color: blue">internal object </span>_coerceValue;
<span style="color: blue">internal object </span>_default;</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>当然你可以把这些<strong>会变化的值</strong>在放到一个类中， 比如ModifiedValue，使操作和数据分离的更彻底些，不过看上去有点像贫血模式囧。value可以理解为变化的基准值所以名为BaseValue。CoerceValue 是GetValue处理后的值。<font color="#ff0000">动静值的分离在这里提出有点仓促，实际很可能是为了在继承时准备的。</font></p>

<pre class="code"><span style="color: blue">internal class </span><span style="color: #2b91af">ModifiedValue
</span>{
    <span style="color: blue">internal object </span>BaseValue { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
    <span style="color: blue">internal object </span>CoerceValue { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>如果有defaultValue和ModifiedValue的话，也就是说一个固值，一个是动值，那么操作也可以分为固值操作和动值操作。</p>

<p>自然的我们会把眼光放在动值操作上，因为动值有两个BaseValue 和CoerceValue ，在有GetValue的前提下用CoerceValue ，没有的话用BaseValue。这个判断操作可以进行封装，让代码层次更清晰，毕竟分层的目的是为了让每个类，每个方法做自己对应的事。</p>

<pre class="code"><span style="color: blue">internal struct </span><span style="color: #2b91af">EffectiveValueEntry
</span>{
    <span style="color: green">//为了获值统一
    </span><span style="color: blue">internal </span><span style="color: #2b91af">EffectiveValueEntry </span>GetFlattenedEntry()
    {
        <span style="color: #2b91af">EffectiveValueEntry </span>entry = <span style="color: blue">new </span><span style="color: #2b91af">EffectiveValueEntry</span>();
            
        <span style="color: blue">if</span>(IsCoerced)
        {
            entry.ModifiedValue.BaseValue = <span style="color: blue">this</span>.ModifiedValue.CoerceValue;
        }
        <span style="color: blue">else
        </span>{
            entry.ModifiedValue.BaseValue = <span style="color: blue">this</span>.ModifiedValue.BaseValue;
        }

        <span style="color: blue">return </span>entry;
    }

    <span style="color: blue">internal void </span>SetCoercedValue(<span style="color: blue">object </span>value)
    {
        ModifiedValue.CoerceValue = value;
        <span style="color: blue">this</span>.IsCoerced = <span style="color: blue">true</span>;
    }

    <span style="color: blue">internal void </span>ResetCoercedValue()
    {
        <span style="color: blue">if </span>(<span style="color: blue">this</span>.IsCoerced)
        {
            <span style="color: #2b91af">ModifiedValue </span>modifiedValue = <span style="color: blue">this</span>.ModifiedValue;
            modifiedValue.CoerceValue = <span style="color: blue">null</span>;
            <span style="color: blue">this</span>.IsCoerced = <span style="color: blue">false</span>;
        }
    }

    <span style="color: blue">internal bool </span>IsCoerced
    { 
        <span style="color: blue">get</span>;
        <span style="color: blue">private set</span>;
    }

    <span style="color: blue">private </span><span style="color: #2b91af">ModifiedValue </span>_modifiedValue;
    <span style="color: blue">internal </span><span style="color: #2b91af">ModifiedValue </span>ModifiedValue
    {
        <span style="color: blue">get
        </span>{
            <span style="color: blue">if </span>(_modifiedValue == <span style="color: blue">null</span>)
            {
                _modifiedValue = <span style="color: blue">new </span><span style="color: #2b91af">ModifiedValue</span>();
            }
            <span style="color: blue">return </span>_modifiedValue;
        }
    }         
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>其他几个函数都能明白什么意思，GetFlattenedEntry函数是为了外面调用得到值的时候统一获得，这样就不需要在外面进行什么IsCoerced的判断了，拿值都只需要取BaseValue即可。这样整个类就变为下面这个样子：</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">Class1 </span>: <span style="color: #2b91af">IPropertyChanged
</span>{
    <span style="color: blue">private </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">PropertyDefinition</span>&gt; _dic = <span style="color: blue">new </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">PropertyDefinition</span>&gt;();
    <span style="color: blue">private </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">EffectiveValueEntry</span>&gt; _entryDic = <span style="color: blue">new </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">string</span>, <span style="color: #2b91af">EffectiveValueEntry</span>&gt;();

    <span style="color: blue">public event </span><span style="color: #2b91af">EventHandler</span>&lt;<span style="color: #2b91af">PropertyEventArgs</span>&gt; PropertyChanged;

    <span style="color: blue">public </span>Class1()
    {
        <span style="color: green">//不传委托，只传默认值
        </span>RegisterProperty(<span style="color: #a31515">&quot;Name&quot;</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyDefinition</span>(<span style="color: #a31515">&quot;Curry&quot;</span>));
        <span style="color: green">//传入委托
        </span>RegisterProperty(<span style="color: #a31515">&quot;Count1&quot;</span>, 
            <span style="color: blue">new </span><span style="color: #2b91af">PropertyDefinition</span>(1,(oldValue,newValue)=&gt;
                                   {
                                       <span style="color: blue">int </span>count = (<span style="color: blue">int</span>) newValue;
                                       <span style="color: blue">if </span>(count &gt; 100 || count &lt; 0)
                                           <span style="color: blue">return </span>oldValue;
                                       <span style="color: blue">return </span>count/10;
                                   },<span style="color: blue">null</span>));
    }

    <span style="color: blue">protected  void </span>RegisterProperty(<span style="color: blue">string </span>propertyName,<span style="color: #2b91af">PropertyDefinition </span>definition)
    {
        _dic.Add(propertyName, definition);
        _entryDic.Add(propertyName, <span style="color: blue">new </span><span style="color: #2b91af">EffectiveValueEntry</span>());
    }

    <span style="color: blue">public object </span>GetValue(<span style="color: blue">string </span>propertyName)
    {
        <span style="color: green">//统一获取
        </span><span style="color: blue">return </span>_entryDic[propertyName].GetFlattenedEntry().ModifiedValue.BaseValue;
    }

    <span style="color: blue">public void </span>SetValue(<span style="color: blue">string </span>propertyName,<span style="color: blue">object </span>value)
    {
        <span style="color: blue">if</span>(_dic[propertyName].SetValue!=<span style="color: blue">null</span>)
        {
            <span style="color: blue">var </span>oldValue = _entryDic[propertyName].GetFlattenedEntry().ModifiedValue.BaseValue;
            <span style="color: blue">var </span>coercedValue = _dic[propertyName].SetValue(oldValue, value);

            <span style="color: green">//在经过了SetValue委托的调用后又进行了GetValue的调用
            </span><span style="color: blue">if </span>(_dic[propertyName].GetValue != <span style="color: blue">null</span>)
                _entryDic[propertyName].SetCoercedValue(coercedValue);
            <span style="color: blue">else </span><span style="color: green">//可以看出来 XXX.ModifiedValue.BaseValue 的赋值太繁琐
                </span>_entryDic[propertyName].ModifiedValue.BaseValue = coercedValue;
        }
        <span style="color: blue">else
        </span>{
            _entryDic[propertyName].ModifiedValue.BaseValue = value;
        

        <span style="color: blue">if </span>(PropertyChanged != <span style="color: blue">null</span>)
            PropertyChanged(<span style="color: blue">this</span>, <span style="color: blue">new </span><span style="color: #2b91af">PropertyEventArgs</span>(propertyName));
    }
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>这里我们又看出了还有几个可以改进的地方：</p>

<ul>
  <li>1.SetValue和GetValue 委托都是在函数SetValue(<span style="color: blue">string </span>propertyName,<span style="color: blue">object </span>value) 中调用，既然是对值进行处理，那么其实两者是可以统一的，用CoerceValueCallback来代替就可以了。 </li>

  <li>2._entryDic[propertyName].ModifiedValue.BaseValue 的作用只是为了取BaseValue，而在这过程中却把ModifiedValue曝露，且ModifiedValue除了得到BaseValue没有其它的意义，可以用_entryDic[propertyName].SetBaseValue(object value) 和_entryDic[propertyName].GetBaseValue()来代替，但Get,Set这样的函数又还不如加个属性来的痛快，_entryDic[propertyName].Value这样来操作。 </li>
</ul>

<p>&#160;</p>

<p>以上修改应该较容易理解所以就不用代码列出了。</p>

<p>&#160;&#160;&#160;&#160;&#160; 接下来就是类的继承，继承的类如何沿用原来的属性，如何Override父类的属性。这个问题其实也不是问题，面向对象本身就有继承，用来保存<span style="color: #2b91af">PropertyDefinition</span>的<span style="color: #2b91af">Dictionary</span>CLR就会自动继承，我们只要构造函数调用基类的构造函数，对RegisterProperty函数进行调用就可以了。说要Override基类的属性其实就是要替换<span style="color: #2b91af">PropertyDefinition</span>，因为我们已经把<span style="color: #2b91af">EffectiveValueEntry</span>提出来了，更改<span style="color: #2b91af">EffectiveValueEntry</span>的BaseValue就可以，当然现在已经改造为Value了。</p>

<p>这个过程也很简单，只要写个OverrideProperty在构造函数里调用下就好了：</p>

<pre class="code"><span style="color: blue">protected void </span>OverrideProperty(<span style="color: blue">string </span>propertyName, <span style="color: #2b91af">PropertyDefinition </span>definition)
{
    <span style="color: green">//覆盖基类的PropertyDefinition Dictionary
    </span>_dic[propertyName] = definition;
    <span style="color: green">//更改默认值
    </span><span style="color: blue">var </span>newEntry = _entryDic[propertyName];
    newEntry.Value = definition._default;<font color="#008000">//这里用_default是不应该的，我偷懒了，一般字段应该是对类内部而言的，对外要公开为属性
</font>    _entryDic[propertyName] = newEntry;
}</pre>

<p>这样确实就OK了，可有没发现基类的<span style="color: #2b91af">PropertyDefinition</span>是不变的，下面不管派生多少个子类这都是不变的，除非子类调用了OverrideProperty方法来进行重载，因为<span style="color: #2b91af">PropertyDefinition</span>中只有默认值、CoerceValueCallback这两个东西，理论上定义一次就可以了，可现在的做法却是每个派生都相当要拷贝一份，这明显是浪费内存的。</p>

<p><strong>不足：基类的<span style="color: #2b91af">PropertyDefinition</span>需要子类都拷贝一份，造成内存浪费。</strong></p>

<p>&#160;</p>

<p>&#160;</p>

<h2>四.深入优化</h2>

<p>&#160;</p>

<p><strong>优化点：基类的<span style="color: #2b91af">PropertyDefinition</span>需要子类都拷贝一份，造成内存浪费。</strong></p>

<p>&#160;&#160;&#160;&#160; 我们乘热打铁先来看如何继承父类的属性而不浪费资源，所谓的浪费就在于每个基类都拷贝了一份<span style="color: #2b91af">PropertyDefinition</span>，造成不会变动的<span style="color: #2b91af">PropertyDefinition</span>有多份，<span style="color: #2b91af">PropertyDefinition</span>为属性而生，要是每个属性对应的Type都是全局唯一的不就可以解决了。做个全局表把 <strong>属性名 + 类的Type</strong>作为Key(propertyName.GetHashCode() ^ type.GetHashCode()), 还是为了唯一，当把子项加入全局表时也需要是静态的方法。这个静态的方法应该放在谁身上呢？那个所谓的全局表又该放在何处？一个做法把他们都放在<span style="color: #2b91af">PropertyDefinition</span>类中，理由是本身就是定义这个类型的。另一个做法是再定义一个类DependencyProperty存放全局表和注册方外存放属性名称等，并把<span style="color: #2b91af">PropertyDefinition</span>改名为PropertyMetadata只存放数据，理由是数据和具体归属相分离。哪种做法好？这个就看项目的大小和<span style="color: #2b91af">PropertyDefinition</span>本身的复杂度(因业务需求增加)，当然后一种做法还可以避免因为属性名查找拼装而造成太多字符串垃圾(字符串缓存只在显示定义时有效)。</p>

<p>&#160;&#160;&#160; 当我们把Class1类上的Count变为0时，所希望的只是那个操作过的Class1的属性发生变化，而不是每个Class1的Count都发生变化，所以对每个类的每个属性来说都有一个<span style="color: #2b91af">EffectiveValueEntry</span>实例，但每个<span style="color: #2b91af">EffectiveValueEntry</span>的默认值都需要从<span style="color: #2b91af">PropertyDefinition</span>中拷贝下来， 常用方式一个是<strong>即时加载</strong>就是说在初始化类的时候就把父类的属性全都拷贝过来，另一个是<strong>延迟加载</strong>，在用的时候才去拷贝。一般的CLR属性都是一次完工的属于即时，但现在我们可以使用延迟加载，即在GetValue的时候先查看该属性是否需要继承是否已获得，如果没有拷贝过父类属性，才拷贝一份。在基类有大量属性，可使用并不多的情况下有很大优势。这里再费下口水，拷贝只拷贝<span style="color: #2b91af">PropertyDefinition</span>中的默认值，CoerceValueCallback还是每次都去取，毕竟拷贝过来的值会变，委托不会变嘛。</p>

<p>&#160;&#160;&#160;&#160; 代码就不罗列了，有兴趣的可以看看 <a href="http://www.cnblogs.com/Zhouyongh/archive/2009/09/10/1564099.html">一站式WPF--依赖属性（DependencyProperty）一</a></p>

<p><font color="#ff0000">&#160;&#160;&#160;&#160; 我有几次想把<strong><font color="#000000">PropertyDefinition</font></strong>写成泛型，因为WPF传入默认值的时候在编译时是不会检查的，运行时报的错又很奇怪，比如定义了一个double类型的属性，可你默认值写100是错误的，因为是100是int类型，你需要写成100.0这样；可都由于<strong><font color="#000000">EffectiveValueEntry</font></strong>的原因被打回来。</font></p>

<p><font color="#ff0000">&#160;&#160;&#160;&#160;&#160;&#160; </font></p>

<p><strong>优化点：在执行对应事件后才把没有类依赖的事件去掉，假如没有执行事件内存依旧浪费。</strong></p>

<p>&#160;&#160;&#160;&#160;&#160; 这个怕的就是没有执行，被忽略。如果把这个事件加入一个全局表中，不时对这个全局表中的项目进行检查，发现没有用的就从全局表中剔除，当然这个做法有点耗费效率，而且也花费了一个全局表的资源，到底值得不值得就看使用场景了。</p>

<p>如声明： static List&lt;WeakReference&gt; _globleWeakReferenceList = new List&lt;WeakReference&gt;();</p>

<p>增加</p>

<p>globleWeakReferenceList.Add(WeakReference);</p>

<p>循环列表逐一判断</p>

<p>if(WeakReference.Target==null)</p>

<p>&#160;&#160; globleWeakReferenceList.Remove(WeakReference);</p>

<p>&#160;&#160;&#160; 在具体运用的时候就不需要放入一个WeakReference这么死板，WeakReference可以写成XX类把WeakReference作为其中的属性，在那个类中写些委托，在委托中可以写用注册事件，发现所处对象的WeakReference为null时，可以使用另外一个委托来把注册事件销毁掉。</p>

<p>&#160;&#160;&#160; 检查全局表的时机？这个可以在每次增加之后检查， 每次销毁之后检查，在运行完特定程序后检查。</p>

<p>&#160;</p>

<p><strong>优化点：属性变化通知的类需要加个接口，可这个接口却是和具体业务没有关系，总之耦合太强。</strong></p>

<p>&#160;&#160;&#160; 这个问题其实是仁者见仁，智者见智，有些人会说加个接口就接口，这样已经OK了，如果你要监听全部的属性变化，那肯定是这种方式了。如果你感觉这种方式类似对消息队列的判断，用起来并不那么舒服，你只需要对某个具体的属性进行监听。那么你可以为你的类做个容器，放到容器里用，用容器里的SetValue来操作数据的赋值工作，这样就可以在容器里做手脚，在SetValue方法中对象具体赋值前引发个事件或委托。<strong>注意以下只是演示写法。</strong></p>

<pre class="code"><span style="color: blue">public  class </span><span style="color: #2b91af">Wrapper
</span>{
   <span style="color: green">//注意这里是临时写法，当对象销毁时，会有内存泄露
    </span><span style="color: blue">private static readonly </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">object</span>, <span style="color: #2b91af">Action</span>&lt;<span style="color: blue">object</span>&gt;&gt; _delegateList = <span style="color: blue">new </span><span style="color: #2b91af">Dictionary</span>&lt;<span style="color: blue">object</span>, <span style="color: #2b91af">Action</span>&lt;<span style="color: blue">object</span>&gt;&gt;();

    <span style="color: blue">public void </span>SetValue(<span style="color: blue">object </span>obj,<span style="color: blue">string </span>propertyName,<span style="color: blue">object </span>value)
    {
        <span style="color: green">//查看是否有 发生变化的委托,而且实际上还要判断属性名称
        </span><span style="color: blue">if </span>(_delegateList.ContainsKey(obj))
            _delegateList[obj](value);

        obj.GetType().GetProperty(propertyName).SetValue(obj, value,<span style="color: blue">null</span>);
    }

    <span style="color: green">//增加对属性改变的委托
    </span><span style="color: blue">public void </span>AddValueChanged(<span style="color: blue">object </span>obj,<span style="color: blue">string </span>propertyName,<span style="color: #2b91af">Action</span>&lt;<span style="color: blue">object</span>&gt; action)
    {
        <span style="color: blue">if </span>(_delegateList.ContainsKey(obj))
            _delegateList[obj] = action;
        <span style="color: blue">else
            </span>_delegateList.Add(obj, action);
    }
}</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>对于其中的内存泄露需要用优化二中的解法。</p>

<p>大家可能发现其中的属性赋值依然用的是反射，这个在上面我们改进之后应该是从SetValue的入口点进行赋值的。所以还需要再一层，通过某个容器把属性拿出来。</p>

<p>其实CLR中有个TypeDescriptor来做这方面的工作。不知您是否注意到了<span style="color: #2b91af">DependencyObject </span>上的标签：</p>

<pre class="code">[<span style="color: #2b91af">TypeDescriptionProvider</span>(<span style="color: blue">typeof</span>(DependencyObjectProvider))]
<span style="color: blue">public class </span><span style="color: #2b91af">DependencyObject </span>: <span style="color: #2b91af">DispatcherObject
</span>{</pre>
<a href="http://11011.net/software/vspaste"></a>

<p>关于TypeDescriptor的用法，意义请看<a href="http://blog.csdn.net/bluedog">BlueDog</a>的Dotnet专业组件开发揭密系列，我认为他讲的已经非常好，特别是他写的sample建议下载阅读。</p>

<p>这方面也可以看 Stephen Toub在MSDN杂志2005年4月和五月的 ICustomTypeDescriptor, Part 1和ICustomTypeDescriptor, Part 2，其中Part 2关于串行非一致性(sequential consistency),园子里Jeffrey Sun的<a href="http://www.cnblogs.com/sun/archive/2010/02/03/1663064.html">Memory Reordering/Memory Model 及其对.NET的影响</a> 及其文中链接上的文章给出了很好解释。</p>

<p>WPF中的具体应用你可以看熊力的<a href="http://eparg.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&amp;_c=BlogPart&amp;partqs=amonth%3d12%26ayear%3d2007">WPF 谁玩谁呢(1)</a></p>

<p>在自己的项目中你还可以通过动态代理技术把属性中的方法封装下，达到一定的业务和框架分离。</p>

<p>&#160; </p>

<h2>五.WPF出彩处</h2>

<p>&#160;</p>

<p>当然WPF出彩的地方很多，每个人对出彩的概念也会有些不同。</p>

<p>在优化方法二中，我们说到一个清理全局表的时机，这里简单说下WeakEventManager，他在我们AddListener的时候便下了个</p>

<p>base.Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new DispatcherOperationCallback(this.<strong>CleanupOperation</strong>), null);</p>

<p><strong>CleanupOperation</strong>中有ScheduleCleanup方法来检查清理全局表(每个线程各有一个)。</p>

<p>刚得知原由的时候我真的有种拍案叫绝的冲动。</p>

<p>这个结论是怎么得出来的呢？</p>

<p>我们自己自定义一个继承于WeakEventManager类的时候，会重载他的StartListening和StopListening方法，当执行完点击Button的事件后，会发现他自动调用我们的StopListening方法，那他什么时候调用了我自己的方法来销毁事件？</p>

<p><a href="http://images.cnblogs.com/cnblogs_com/Curry/WindowsLiveWriter/WPFBinding_D9C3/image_4.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Curry/WindowsLiveWriter/WPFBinding_D9C3/image_thumb_1.png" width="786" height="273" /></a> </p>

<p>看堆栈可以看到是用WeakEventTable的CleanupOperation方法，可是具体什么时候调用堆栈还是没有给出具体信息，用Reflector查看了下发现CleanupOperation的调用存在于WeakEventTable的<strong>ScheduleCleanup</strong>中，端出Windbg给对应函数下断点：</p>

<p>0:010&gt; !name2ee WindowsBase.dll!MS.Internal.WeakEventTable.ScheduleCleanup 
  <br />Module: 6f901000 (WindowsBase.dll) 

  <br />Token: 0x06000a58 

  <br />MethodDesc: 6f91b8d4 

  <br />Name: MS.Internal.WeakEventTable.ScheduleCleanup() 

  <br />JITTED Code Address: <font color="#ff0000">6f9a2000</font> 

  <br />0:010&gt; bp <font color="#ff0000">6f9a2000</font> 

  <br />0:010&gt; g</p>

<p>断到后看clr栈，就可知在AddListener时已做了“准备”。</p>

<p>0:000&gt; !clrstack 
  <br />OS Thread Id: 0x11c0 (0) 

  <br />ESP&#160;&#160;&#160;&#160;&#160;&#160; EIP&#160;&#160;&#160;&#160; <br /><font color="#ff0000">0028e84c 6f9a2000 MS.Internal.WeakEventTable.ScheduleCleanup()</font> 

  <br />0028e850 6f9a1d23 System.Windows.WeakEventManager.ProtectedAddListener(System.Object, System.Windows.IWeakEventListener)</p>

<p>……</p>

<p>这个检查是在ContextIdle后做的也就是说是在数据加载完UI呈现完之后执行的，数据该销毁的都销毁了，这个时候检查可以说时机点是最好的。所以大家如果继承于WeakEventManager做类自定义AddListener时最好中调用下base.ScheduleCleanup();</p>

<p><strong>这里注意下：Dispatcher.BeginInvoke是在有创建message-only window的前提下(在Dispatcher初始化时)，所以你在普通的Console Application程序中使用WeakEventManager意义就没有原来的那么大。</strong></p>

<p>&#160;</p>

<h2>六.未完工的遗憾</h2>

<p>&#160;</p>

<p>&#160;&#160;&#160;&#160; 最后说声对不起标题党了，本来我是希望能把WPF Bindng还原，但是后来发现以自己现在的实力还不行，还需要继续修炼。Bindng的联动我们想大家应该能了解个大概了，我没有拼装，也是希望不要束缚住大家的思想，有时候一个技术是多种技术的融合，WPF也一直对这部分内容进行着修改完善，所以发挥出我们自己的能动性才是关键。</p>

<p>&#160;&#160;&#160;&#160; 对于BindingExpresstion 想向各位高手请教有什么深意，在框架中的Expression指的有哪些？具体应用场景是什么？因为我实在无法理解源码中DependencyObjectType的意图：</p>

<blockquote>
  <p>DTypes have 2 purposes: 
    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; More performant type operations (especially for Expressions that 

    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; rely heavily on type inspection) </p>

  <p>
    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Forces static constructors of base types to always run first. This 

    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; consistancy is necessary for components (such as Expressions) that 

    <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; rely on static construction order for correctness.</p></blockquote><img src="http://www.cnblogs.com/Curry/aggbug/1666297.html?type=1" width="1" height="1" alt=""/><p>评论: 9　<a href="http://www.cnblogs.com/Curry/archive/2010/02/09/WPFBinding.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/Curry/archive/2010/02/09/WPFBinding.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56793/" target="_blank">知名扩展 Firebug 的简化版登陆 Chrome</a><span style="color:gray">(2010-02-09 22:59)</span><br/>· <a href="http://news.cnblogs.com/n/56792/" target="_blank">Google 悄悄地启用 1e100.net，打枪地不要</a><span style="color:gray">(2010-02-09 22:57)</span><br/>· <a href="http://news.cnblogs.com/n/56791/" target="_blank">从 Google 代码库找到的好东西</a><span style="color:gray">(2010-02-09 22:46)</span><br/>· <a href="http://news.cnblogs.com/n/56789/" target="_blank">苹果在线商店临时关闭 或将推新Macbook</a><span style="color:gray">(2010-02-09 22:39)</span><br/>· <a href="http://news.cnblogs.com/n/56788/" target="_blank">豆瓣网推出豆瓣电台iPhone客户端</a><span style="color:gray">(2010-02-09 21:51)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56782/" target="_blank">2010年2月编程语言排行榜</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item></channel></rss>