2008年12月21日星期日

[转载] VC/C++的中文字符处理方式

全文下载链接(可打印,已排版,并修改若干错误)
下文为原文转载,未经任何修改:

以下为原文转载,未经任何修改:
一 引入问题
代码 wchar_t a[3]=L”中国”,编译时出错,出错信息为:数组越界。但wchar_t 是一个宽字节类型,数组a的大小应为6个字节,而两个汉字的的unicode码占4个字节,再加上一个结束符,最多6个字节,所以应该不会越界。难道是编译器出问题了?
二 解决引入问题所需的知识
主要需两方面的知识,第一个为字符尤其是汉字的编码,以及语言和工具的支持情况,第二个是vc/c++中MutiByte Charater Set 和 Wide Character Set有关内存分配的情况.
三 汉字的编码方式及在vc/c++中的处理
1.汉字编码方式的介绍
对英文字符的处理,7位ASCII码字符集中的字符即可满足使用需求,且英文字符在计算机上的输入及输出也非常简单,因此,英文字符的输入、存储、内部处理和输出都可以只用同一个编码(如ASCII码)。
而汉字是一种象形文字,字数极多(现代汉字中仅常用字就有六、七千个,总字数高达5万个以上),且字形复杂,每一个汉字都有"音、形、义"三要素,同音字、异体字也很多,这些都给汉字的的计算机处理带来了很大的困难。要在计算机中处理汉字,必须解决以下几个问题:首先是汉字的输入,即如何把结构复杂的方块汉字输入到计算机中去,这是汉字处理的关键;其次,汉字在计算机内如何表示和存储?如何与西文兼容?最后,如何将汉字的处理结果从计算机内输出?
为此,必须将汉字代码化,即对汉字进行编码。对应于上述汉字处理过程中的输入、内部处理及输出这三个主要环节,每一个汉字的编码都包括输入码、交换码、内部码和字形码。在计算机的汉字信息处理系统中,处理汉字时要进行如下的代码转换:输入码→交换码→内部码→字形码。
(1)输入码: 作用是,利用它和现有的标准西文键盘结合来输入汉字。输入码也称为外码。主要归为四类:
a) 数字编码:数字编码是用等长的数字串为汉字逐一编号,以这个编号作为汉字的输入码。例如,区位码、电报码等都属于数字编码。
b) 拼音码:拼音码是以汉字的读音为基础的输入办法。
c) 字形码:字形码是以汉字的字形结构为基础的输入编码。例如,五笔字型码(王码)。
d) 音形码:音形码是兼顾汉字的读音和字形的输入编码。
(2)交换码:用于汉字外码和内部码的交换。交换码的国家标准代号为GB2312-80。
(3)内部码:内部码是汉字在计算机内的基本表示形式,是计算机对汉字进行识别、存储、处理和传输所用的编码。内部码也是双字节编码,将国标码两个字节的最高位都置为"1",即转换成汉字的内部码。
(4)字形码:字形码是表示汉字字形信息(汉字的结构、形状、笔划等)的编码,用来实现计算机对汉字的输出(显示、打印)。
2.VC中汉字的编码方式
vc/c++正是采用了GB2312内部码作为汉字的编码方式,因此vc/c++中的各种输入输出方法,如cin/wcin,cout/wcout,scanf/wsanf,printf/wprintf...都是基于GB2312的,如果汉字的内码不是这种编码方式,那么利用上述各种方法就不会正确的解析汉字。
仔细观察ASCII字符表,从第161个字符开始,后面的字符并不经常为用户所使用,负值也未使用。GB2312编码方式充分利用这一特性,将161-255(-95~-1)之间的数值空间作为汉字的标识码。既然255-161 = 94不能满足汉字容量的要求,就将每两个字符并在一块(即一个汉字占两个字节),显然,94* 94 =8836基本上已经满足了常用汉字个数的要求。计算机处理字符时,当连续处理到两个大与160(或-95~-1)的字节时,就认为这两个字节存放了一个汉字字符。可以用下面的Demo程序来模拟vc/c++中输出汉字字符的过程。
unsigned char input[50];
cin>>input;
int flag=0;
for(int i =0 ;i < 50 ;i++)
{
if(input[i] > 0xa0 && input[i] != 0)
{
if(flag == 1)
{
cout<<"chinese character"<<endl;
flag = 0;
}
else
{
flag++;
}
}
else if(input[i] == 0)
{
break;
}
else
{
cout<<"english character"<<endl;
}
}
输入:Hello中国 (“中国”对应的GB2312内码为:214 208,185 250)
输出:english character
english character
english character
english character
english character
chinese character
chinese character
vc/c++中的英文字符仍然采用ASCII编码方式。可以设想,其他国家程序员利用vc/c++编写程序输入本国字符时,vc/c++则会采用该国的字符编码方式来处理这些字符。
问题又产生了,韩国的vc/c++程序在中国的vc/c++上运行时,如果没有相应的内码库,则对韩语字符的显示有可能出现乱码。我个人猜测,vc安装程序中应该带有不同国家的内码库,这样一来肯定会占用很大的空间。如果所有的国家使用统一的编码方式,且所有的程序设计语言和开发工具都支持这种编码方式该多好!而现实中,确实已经有这种编码方式了,且许多新的语言也都支持这种编码方式,如Java、C#等,它就是下面的Unicode编码
3.新的内码标准---Unicode
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。随着计算机工作能力的增强,Unicode也在面世以来的十多年里得到普及。最新版本的 Unicode 是 2005年3月31日推出的Unicode 4.1.0 。另外,5.0 Beta已于2005年12月12日推出,以供各会员评价。
Unicode 编码系统可分为编码方式和实现方式两个层次。
编码方式:Unicode 的编码方式与 ISO 10646 的通用字符集(Universal Character Set,UCS)概念相对应,目前的用于实用的 Unicode 版本对应于 UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示 216 个字符。基本满足各种语言的使用。实际上目前版本的 Unicode 尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。
实现方式:Unicode 的实现方式不同于编码方式。一个字符的 Unicode 编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对 Unicode 编码的实现方式有所不同。Unicode 的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 UTF)。如,UTF-8 编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他 Unicode 字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。
Java与C#语言都是采用Unicode编码方式,在这两种语言中定义一个字符,在内存中存放的就是这个字符的两字节Unicode码。如下所示:
char a='我'; => 内存中存放的Unicode码为:25105
4.内码的相互转换
(1)vc中的实现方法
利用Windows系统提供的API:::MultiByteToWideChar::WideCharToMultiByte
::MultiByteToWideChar实现当前码到Unicode码的转换;
::WideCharToMultiByte实现Unicode码到当前码的转换;
(2)Java中的实现方法
String vcString=new String(javaString.getBytes("UTF-8"),"gb2312");
java的编码应该是UTF-8
(3)C#中的实现方法
??
四 vc中的MutiByte Charater Set 和 Wide Character Set
1.MultiByte Charater Set方式
这种方式以按字节为单位存放字符,即如果一个字符码为两字节,则在内存中占两字节,字符码为一字节,就占一字节。例如,字符串“中国abc”的编码为:中(0xd6、0xd0)、国(0xb9、0xfa)、a(0x61)、b(0x62)、c(0x63)、\0(0x00),就存为如下方式:

原始图片链接:http://p.blog.csdn.net/images/p_blog_csdn_net/fishinthewind/win_1.bmp




对应的类型,方法有:
char、scanf、printf、cin、cout …
2.Wide Character Set
这种方式是以两字节为单位存放字符,即如果一个字符码为两字节,则在内存中占四字节,字符码为一字节,就占两字节。例如,字符串“中国abc”就存为如下方式:

原始图片链接:http://p.blog.csdn.net/images/p_blog_csdn_net/fishinthewind/win_2.bmp



对应的类型,方法有:
wchar_t、wscanf、wprintf、wcin、wcout …
造成上面存储方式的根本原因在于,wchar_t类型其实是一个unsigned short 类型。如,存储上面字符串的数组的定义为:wchar_t buffer[8] 等价于unsigned short buffer[8].而所有以字母w开头的方法也都是以unsigned short类型,即两字节为单位来处理字符,因此,存储在wchar_t类型数组中的字符串无法用cout显示,只能用wcout方法来显示。
由于Unicode码也是采用两个字节,因此Wide Character Set方式能够很好的支持Unicode码的存储,但是在vc的环境下要将一个Unicode码存入两字节而不是四字节内存中,必须通过上面的API函数::MultiByteToWideChar首先,将当前的编码转换为Unicode码,然后,将每个字符的Unicode码放入每一个wchar_t类型的变量中。以下是一个实例代码:
char input[50];
cin>>input;
int size;
size=::MultiByteToWideChar(CP_ACP,0,input,strlen(input)+1,NULL,0);
if(size==0)
return -1;
wchar_t *widebuff=new wchar_t[size];
::MultiByteToWideChar(CP_ACP,0,input,strlen(input)+1,widebuff,size);
输入:中国abc
Debug断点调试:
size==6
数组widebuff[0-size]占12字节,存放了6个字符的Unicode码,码值为:
中(0x4e2d) 国(0x56fd) a(0x0061) b(0x0062) c(0x0063) d(0x0000)
这时,数组的大小size等于输入的字符个数加上一个结束符,符合我们的想象。
五 引入问题的错误分析
(1) 没有理解编译器中的编码方式
虽然vc/c++中汉字的编码占两个字节,但并不是Unicode码,是GB2312码。
(2) 没有理解MutiByte Charater Set 和 Wide Character Set的存储原则;
在vc/c++中,“中国”按char[5]来对待,而wchar_t a[3]实际上是三个unsigned short类型的变量,因此赋值时会越界。

2008年10月16日星期四

Received the MSDN Subscription compast disks

I have received the MSDN Subscriptions from the Beijing filiale of Microsoft in Oct 17, 2008. It's a large bag!It contains a lots of Microsoft products. Oh, I have nothing to say......! :) -_-"
Posted by Picasa

2008年9月17日星期三

微软关系搜索——人立方,垂直个性搜索

什么是人立方?
微软人立方关系搜索是由微软亚洲研究院网络搜索与挖掘组研发的对象级别(object-level)互联网搜索引擎
人立方关系搜索从超过十亿的中文网页中自动的抽取出人名、地名、机构名以及中文短语,并且通过算法自动的计算出它们之间存在关系的可能性;同时,人立方关系搜索还索引了支持它们之间关系的网页文字。此外,人立方关系搜索还自动的找出人名之间最可能的关系描述词、与人名最可能相关的称呼、作品等词条等。人立方关系搜索从这些中文网页中自动的辨别出人名所对应的人物简介文字,并且按照这些文字是人物简介的可能性进行排序。
当用户给定任意搜索关键词,它能够找出与关键词最可能相关的人名、地名和机构名,并且根据它们与关键词之间的相关度排序。除此之外,人立方关系搜索还提供基于人名的新闻浏览功能,可视化关系搜索功能等。

人立方是网络名人的夺金魔方
根据人立方的“设计建造”原理,它最可能成为网络名人的搜索夺金魔方。如果一个词(比如人名)被囊括入网页的次数越多,那么他将被搜索的机会就会越多。
依此推算:
名歌影星 夺冠指数:5颗星
具备绝对的夺冠实力,无数的粉丝善于利用搜索引擎搜寻他们的记录,善于利用网络媒体八卦明星们的消息,因此通过网络搜索的结果量将是非常巨大的。
网界名人 夺冠指数:4颗星
互联网媒体成就了一批又一批的互联网精英,他们是这个载体的中坚力量,出镜率不亚于传统媒体上的明星出镜率。
历史巨人 夺冠指数:3颗星
经过千年历史的沉淀,仍然能被我们记住的历史巨人所剩无几,这些人也早已被搬上了互联网这个开放的知识海洋。

人立方的缺陷
机器的思维永远也赶不上人类的思维,搜索引擎始终也无法解决所有的问题。人立方也因此暴露出了很多严重的缺陷。
相对于名人搜索来说,许多人更希望搜索自己的社会关系,这也是关系搜索所有待解决的最主要问题。如果您所提供的搜索关键词与热门关键字相同,那么搜索结果将指向热门搜索的结果,并非你要的。


同名同姓


假设一个同志就是名叫“张三”,即便他确实在某一领域声名大噪,但要搜索到跟他自己关系最亲密的估计还是“李四”,但事实上,张三并不认识李四。



亮点技术


人性的搜索结果


同样是通过一个输入框一个按钮开始搜索的“人立方”搜索引擎提供了更符合要求的搜索结果。在“所有结果”页面,我们还可以看到“关系”、“网页”、“资讯”、“简介”等更符合个人简历特征的信息。事实上将根据不同人所具有的不同信息,在网页上会显示出更多的个性化信息,如“头衔”、“机构职务”等



关系


关系一览位于页面左侧,关系的搜索结果将返回两种类型的搜索结果,一种是文字列表方式,另一种则是最具特色的关系图方式。


文字列表方式依照关联度顺序,从上至下列出关系表。相比之下您或许会更喜欢关系图方式。



在网页右上方有“关系图”,在搜索页面的文字列表方式下方也有当前搜索关系的关系图。下图对“秦始皇”进行了搜索。想知道谁跟“她/他”关系最密切,关注人立方。



网页


网页搜索也是该搜索引擎的数据根本,因为您可能搜不到您期待的结果,网页搜索提供了传统方式的搜索结果供您自己塞选。该部分内容被放置在页面的最下方。


资讯


资讯搜索像是整合了类似于谷歌的趋势搜索的一种搜索体验。这些结果将直接呈现在搜索界面,上方的数据更显示了按时间顺序最新的一些网页搜索结果。也可以通过设定时间精确您的搜索结果。



简介、头衔、机构职务


这些根据关键字如人物、伟人、董事长、科学家等头衔或职务标记过的名字将列出这些相关网页的搜索结果,省去了您在千万网页中自己辨别的麻烦。垂直一下,更加个性,更加精准。


人立方网址


更多搜索体验,请您自己访问:http://renlifang.msra.cn/

2008年7月8日星期二

去掉“打开此类文件前总是询问”之后该如何恢复

很不幸之前意外点着了下载文件时的“打开此类文件前总是询问”的勾勾,导致rar下载的时候总是自动下载并打开,这要么不符合大家习惯,要么不符合大家需求,反正我在rar文件的时候犯下了这个错误,只好自行解决了。

image 

打开“我的电脑”/“工具”/“文件夹选项”

image

选择“文件类型”选项卡,点击“高级”选项卡,选择“已注册的文件类型”中的相应文件类型,这里为RAR

image

点击“高级”按钮,确保“下载后确认打开”按钮处于勾选状态,点击“确定”保存。
image

2008年5月16日星期五

Windows Word 2007 如何解决关闭产生的问题

昨天没有关机子就离开了办公室,可是自作聪明的Windows Vista系统因为Windows Update,就自动重启了,Winword2007秉承Office家族一贯的传统就来了个自动保存。
今天打开Windows Word 2007发现居然无法打开文件。莫名的死机让我陷入了一次又一次地崩溃状态,性能不来就差的机器更加不堪重负了。
有同事建议重装系统,我没接受,这简直就是杀鸡用牛刀,同时也不是解决问题的方法。我始终认为,问题是人解决的。再说,咱也是写程序的,不能老把责任推给操作系统吧。我还是得把问题解决了。起码重装不是王道!
因为很难确定是否是Windows Update与Office冲突呢,还是因为异常关闭而导致的程序问题。因此只能先任由Windows Office的诊断程序大行其道。因为诊断程序设计复杂,经历了半个多钟头的诊断最后告诉我没有问题。这说明什么呢?难道真的说明没有问题吗?我自圆其说的解释就是程序本身的结构和组件没有出现问题。因为Windows Word是COM组件程序,因此很可能的问题是因为COM组件的加载所引起的。因为多次的错误导致了Windows提示我是否采用“安全模式”进行启动(注意这里的“安全模式”是Word自己的安全模式,而不是系统的安全模式)。进入安全模式后Windows Word没有初始化任何的加载项和模板因此程序很正常地启动了。这更加让我怀疑加载项的问题。可是加载项也没有做过多余的变化,那问题就可能是模板了。尝试了网络上说的在运行中输入Winword /a的方式(恢复默认的设置状态来启动Word)来启动,一切正常。因此只要修改注册表的相关选项即可。
关于如何重建Word注册表键值
将以下注册表键值改名:  
HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word  
然后启动Word,该键值将会重建。
通过该方法即可解决本次所产生的问题。

关于如何强制使用安全模式打开Word 2007
1. 将以下文本复制到记事本, 然后再保存文件作为 EnableSafeMode.reg。 Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word\Resiliency]
"ForceSafeMode"=dword:00000001
2. 双击 EnableSafeMode.reg 。 文件内容合并到注册表。
3. 重新启动计算机。

2008年5月14日星期三

(转)如何彻底删除微软拼音输入法

GoCool:真的被微软拼音3.0给气死了,简直就是难用到爆,还经常冒出来,用优化大师解决或许一两天有效,可是过后又冒出来了,讨厌至极。刚才在网上搜了这么一个方法,已经试了方法2,希望以后都不会再见到它了。
见过讨厌的,真的没见过这么讨厌的。
2008年5月16日,离使用该方法一天了(关机又开机了),还是不可以,刚刚才发现那个该死的拼音又出来了。真是心寒。

方法一:

注册表中找到:
“HKEY_USERS\.DEFAULT\Software\Microsft\Windows\currentVersion\Run”,看到一个键 值名为ctfmon.exe,值为ctfmon.exe。把这个键值名改为“internat.exe”,值为“internat.exe”,重启。
2.退出所有Office程序。

3.在控制面板里,双击添加/删除程序。

4.在当前安装程序列表里,选择MicrosoftOffice产品——更改。

5.在维护模式对话框里,选择添加或删除组件,然后点击下一步。这时显示选择安装选项的对话框。

6.点击Office共享功能前的加号展开。

7.点击中文可选用户输入方法的图标,选择不安装。

8.点击更新。

9.分别运行以下命令:
Regsvr32 /U Msimtf.dll
Regsvr32 /U Msctf.dll

方法二:

把HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\MSSCIPY这一主键删除,完成

微软拼音输入法3.0为何阴魂不散
如果以前安装Office XP的时候,附带安装有微软拼音3.0的版本,在安装了微软拼音
2003以后,似乎并没有将原来的微软拼音3.0自动升级,再按下了Ctrl+空格键以后,显示
出来的还是微软拼音3.0的版本,只有在少量的程序中才能享用微软拼音输入法2003.
这其中的原因可能在于微软拼音输入法2003不能自动对以往的版本进行升级的原因造
成的,一个简单的办法就是打开任务管理器,结束其中的ctfmon.exe进程,然后再按ctrl+
空格键,这时候就可以打开微软拼音输入法2003了.但这种方法只是一种临时解决的方案,
如果这时候重新启动Office,微软拼音3.0有重新回来了.
一个彻底的解决方法就是打开注册表,然后删除所有根mspy3有关的键值,再试一下行
不行,如果不行,用Office 2003安装盘,卸载掉其中的微软拼音2003(注意不是通过控制
面板的输入法设置来进行删除),然后到微软公司的网站上下载微软拼音2003独立发行版,
在进行安装,这时候微软拼音2003就可以重新使用了.

2008年4月21日星期一

Lenovo/IBM ThinkPad Seveice Centre应该算是最垃圾的计算机服务中心吧?

别以为冠名有IBM就是有着先进服务意识的团队了,对于它们的小黑,不提钱,硬件上我还是很支持的,毕竟贵了那么多,要做的太差也早就退出历史舞台了,但是软件呢?我也是依照品牌机的使用惯例尽量保留原厂“效果”的,IBM/Lenovo推荐的那些服务我都也开着,不要说系统占用是否大,我都不考虑这些因素了,但是它们是否真的有用,我就很是怀疑了。算是我的无知,对这一点我也不提。

但是能把支持中心的网站做成那样,真让人心寒。先是整合有点问题,一会跳到联想,一会儿跳到IBM,连界面风格都不太一致,关键是两边的账户还不能共享。我已经注册了IBM产品了,在我填写各种信息的时候还是要求我一次又一次地输入,真是心寒,输入也就算了。机器背后的服务编号和机器型号都是有带“-”符号的,但是最后要求的主机编号(这两个编号的组合)却不需要这些连字符,我算是试验成功了,但是要是换成别人,不知道运气有没有这么好。这也是简单的问题了,我就开始想办法“联系Lenovo”了。我先是看到一个智能什么的,我辛辛苦苦回答完问题,发现要在右侧输入编号,输入就输入吧,结果告诉我只支持联想产品线,对不起哦,我买的ThinkPad的IBM都被他们改成Lenovo了,现在跟我说不是联想系列吗?真是好处都让他一个人占尽了。好吧,我忍,我换“网上报修”,跟我说“正在建设中……”,建设中你就不要弄出来误导消费者了嘛。搞个Flash在那里那么漂亮,东西都是虚的……邮件支持的Link也是没办法重定向的。只能跑到“联系Lenovo”的链接里面去找“在线保修”,不知道和那个“网上报修”是什么关系,总之这个是可以的。又要填写主机序列号,惨了我了……

好吧,进去洋洋洒洒写下了一篇长篇问题描述,估计联想自己的报修记录都没我描述地详细吧,结果点击提交,跟我说“保存失败”,但是没有任何提示啊,我怎么知道什么问题啊,我按习惯觉得应该是文字太长了,可是我的问题就是如此长啊,只好截断一半,然后补充一句另外提供联系方式联系吧。我真的没辙了。这下文字短了,该可以了吧?点击提交,天哪,进度条是好像在提交数据了,但是结果是我的网页连个“Succeed”都没告诉我,反正进度条是走了一遍,除了没有之前的保存失败之外,就跟没有变化一样。至今还纳闷是不是申报成功了……

联想啊,你就这样走向全世界吗?真让我伤心……不知道整这么个慢悠悠的IBM是干嘛使的,浪费心情,还不如DELL,又便宜售后又还不错……关键是人家不会把网站做成那个水平啊……

2008年4月20日星期日

如何设计高性能的ASP.NET应用程序

一、问题
    1、资源,增加内存、CPU、服务器来解决性能问题。
    2、额外的配置,这样的额外配置通常是临时的,例如在长循环中对string做+=操作时候分配更多的内存等。   
    3、错误地分配稀缺资源,如数据库connection资源未调用Close()方法释放资源导致connection耗尽。
    4、阻塞操作,比如下载的时候单一线程被占用而无法做其他事,导致了长时间线程占用,或执行一个耗时长的存储过程的时候远程对象将被阻塞。
    5、滥用线程,错误地使用单线程模型STA COM对象将导致多个请求被放入一个队列中顺序执行从而导致性能问题。
    6、延迟绑定(late-bound)调用,(在运行时载入指定的代码进行运行)当目标代码为托管/非托管的时候最好避免这样的额外操作。
    7、滥用COM互操作,虽然COM互操作很有用但是它仍然有很多影响性能的因素。包括当你跨越托管/非托管边界传递它的参数,参数的尺寸和类型都是瓶颈,由它们所导致的线程切换的代价是巨大的。
    8、大页面,页面的尺寸通常由控件的数量和类型来决定,同时也可能由于数据和图片的数量决定。大量的数据穿越网络将带来巨大的花销,高额的带宽占用也可能是一个性能瓶颈。
    9、错误地使用数据缓存,缓存静态数据或者很少使用的数据都是没意义的,缓存近乎所有的数据将导致缓存频繁更新,这方面的开销且不说,甚至命中率都会严重下降。缓存应该用来缓存高频请求的数据。
    10、错误地使用输出缓存,错误地或者不使用输出缓存都将导致Web服务器的性能瓶颈。
    11、低效率的渲染(rendering),散布不必要的postback数据或者延迟绑定数据将导致页面的真实性能下降。

二、考虑以下的设计原则:
    1、考虑安全与性能之间的权衡,比如a.若使用SQLServer的话则可以利用其连接池的可扩展性为应用程序带来丰富的可扩展性;b.若只是不想让Hacker通过Network篡改数据,可以用Hash算法计算一个表示当前数据的唯一Hash值,在信息达到后进行核对,而不需要将所有的数据进行加密,加密要能损耗是应该引起注意的。
    2、逻辑上分割Application,对应用程序合理地分层也可以带来更好的扩展性,同时也可能提高性能。层与层之间的距离也是值得关注的问题,比如最快的调用是进程内调用,接下来是同主机跨进程调用,接下来是远程网络调用。尽量将层与层之间的距离缩至最短。微软的建议是将数据库数据访问逻辑都放在WebServer的bin目录下。
    3、评估Affinity(关联度?),包括线程内会话状态和指定计算机加密锁。
    4、减少循环,
        a.HttpResponse.IsClientConnected,获取一个值,通过该值指示客户端是否仍连接在服务器上。(比如用户点击一个操作可能要消耗服务端3秒的时间,但是用户等不了这么久,就停止了网页或者直接跳到别的网页去了,这时这个属性的值将是false但是若不进行任何操作的话,长达3秒的操作仍将继续。)但是调用这个属性可能会消耗更多的资源,你必须自己权衡是否使用它。
        b.缓存,如果你的应用程序近乎静态或者你的数据也都是静态的,则使用缓存来避免频繁向数据库读取重复的数据,以提高程序性能。
        c.输出缓冲,可以利用类似HttpResponse.Flush()的方法来将缓存强制输出给用户,这样可以避免用户等到全部处理完才看到内容。注意,如果关闭缓冲,并且客户端连接网络缓慢时,会影响服务器的响应时间。(因为你的服务器必须等待客户端确认,在客户端接到服务器端发送的所有内容后会发出确认)。
        d.Server.Transfer,如果可能的话使用Server.Transfer取代Response.Redirect。Response.Redirect发送一个response头给客户端,让客户端发送一个新的请求给服务端进行重定向。Server.Transfer直接在服务端转向。因为Server.Transfer在请求处理阶段时用了新的Handler。如果你必须在重定向时验证和授权检查,那么你就应该用Response.Redirect。Server.Transfer要先把当前页面POST过来的数据保存住,再去执行新的页面,有时候速度会很慢。另外Server.Transfer可以用于控制在同一个应用程序中的页面,若是跨应用程序重定向,就应该用Response.Redirect。
    5、避免一个需要较长时间运行的任务阻塞,如果执行长时间操作或者阻塞操作的时候,可以考虑以下几种异步方式让Web Server处理其他请求。
        a.异步调用WebServices或者远程对象,当调用的时候可以并行地处理一些其他的事务。要避免同步调用WebService,处理请求的线程来自ASP.NET线程池,如果线程都去处理WebService的调用,将势必减少用来处理新请求的线程数量。
        b.如果你不需要WebService或者远程对象回复,最好将它们的调用接口设置为OneWay(就是有去无回的那种调用,发出命令,却不关心命令的执行结果)。
        c.排队工作,然后由客户端投票决定完成。允许WebServer调用代码,然后让WebClient投票让服务器确认工作完成情况。
    6、使用缓存,缓存策略可能是提高性能的至关重要的一个设计点。ASP.NET缓存机制包括输出缓存,部分页缓存,以及缓存API,利用这些特征来设计你的应用程序。缓存主要是用来减少数据访问和渲染的次数,因为很多这样的操作是完全重复的。
        a.标识数据和输出的创建和找回是代价高昂的。
        b.评估数据的缓存价值。缓存是有限而宝贵的,应该用来缓存不易改变且又经常访问的数据,对那些经常变化的数据,就没有缓存的必要了。
        c.评估数据的使用频度。对于那些经常使用而不经常更新的数据有缓存的价值。
        d.将稳定的数据和不稳定的数据区别开来。设计用户控件来封装静态内容例如导航栏或者帮助系统,并将它们与易变数据分开。
        e.选择正确的缓存机制。用户信息通常放在Session对象中,静态页或者一些类型的动态页比如那些供很多人查阅的公共资料可以用输出缓存和响应缓存。一个页面内的静态内容也可以用用户控件缓存。ASP.NET缓存特征提供一种内建的机制来更新缓存。应用程序状态,会话状态和其他缓存都没有提供内建的更新缓存机制。
    7、避免不必要的异常,如果可以的话不要使用异常来控制判断(比如取代if判断)。设计异常的时候考虑以下一些建议。
        a.设计避免异常的代码。
        b.避免在逻辑判断流中使用异常,如用它来取代if判断等。
        c.避免依靠全局的处理器(handler)处理所有的异常。处理异常是一种高昂的花销,它会导致运行时操作和进入堆栈。然后在堆中找到异常处理者。
        d.在最接近异常发生的地方缓存和处理异常。它避免了更多的堆栈查找和操作。
        e.不要捕获你无法处理的异常。如果你无法即时处理你的异常,可以使用try/finally块来关闭那些资源,比如数据库连接什么的。让异常传播到那些能够处理它的地方去。
        f.尽早发现错误,避免避免在长时间操作后才报错。
        g.将异常记入日志文件中。有利于管理者或者开发者能够亡羊补牢一下爱避免向用户展示过多的异常信息。首先是用户可能不喜欢看到这些错误的信息,避免用户的信任度下降。其次也是最重要的是避免那些钻空子的人利用这些不为人知的异常信息找到系统漏洞进行恶意破坏。

三、考虑以下的实现原则
    ASP.NET应用程序的性能考察点通常是响应时间、吞吐量以及资源管理。
    1、通过减少页面尺寸、减少服务器控件的使用、利用缓冲来减少与客户端的纠缠不清的交互可以避免被这些不可见的资源浪费你的工作。
    2、通过有效地使用线程可以增加吞吐量。用线程池来减少连接,避免因为线程池阻塞而导致的可用工作线程减少,而带来吞吐能力的下降。
    3、落后的资源管理常在服务器的CPU和内存中发生额外的载入。通过改进资源的利用有效地将资源集合起来,显式地关闭或释放打开的资源,并有效地管理字符串。

建议一:如果下面的几条成立的话,则按接下来的表的设置(可以将其设置到machine.config中):
    a.有一个可用的CPU,通常指一个多核的CPU或者有多颗CPU的Server;
    b.正在读取I/O操作,如调用Web方法或访问磁盘文件。
    c.队列中ASP.NET应用程序/请求性能计数器指示你已经有队列的请求了。
表:
Best Practice For Setting
    a.maxIoThreads和maxWorkerThreads都的实际数量都会是推荐值×CPU的个数。
    b.minFreeThreads:当当前请求的线程数小于这个值的时候,它将有效地限制请求的数量并且只能有maxWorkerThreads*number of CPUs-minFreeThreads个请求同时进行。它限制了同时请求的数量为(maxWorkerThreads*number of CPUs-minFreeThreads),在推荐表中,这个值则为12。也就是说,请求数量大于12的时候将会有请求被拒绝(RejectRequestNow),而请求小于12,并且队列中没有请求的时候将正常处理。
    c.minLocalRequestFreeThreads:这个选项与minFreeThreads具备类似的特性,它处理来自本地的队列的请求。值得强调的是它只处理本地的(localhost)请求。

相关参考:
1、《Contention, poor performance, and deadlocks when you make Web service requests from ASP.NET applications》( http://support.microsoft.com/default.aspx?scid=kb;en-us;821268 )
2、《Information about the new GetMinThreads method and the new SetMinThreads method of the ThreadPool class in the .NET Framework 1.1》( http://support.microsoft.com/default.aspx?scid=kb;en-us;827419 )
3、《FIX: SetMinThreads and GetMinThreads API Added to Common Language Runtime ThreadPool Class》( http://support.microsoft.com/default.aspx?scid=kb;en-us;810259 )

建议二:不要为每一次请求创建线程
    创建线程是一种非常奢侈的操作,它要求要初始化托管和非托管的资源。应该坚决避免手动创建线程。
    可以考虑为那些可以并行运行的调用执行异步操作。比如那些I/O操作或者那些Web服务中的远程方法等。你可以使用.NET Framework提供的基于Begin+同步方法名与End+同步方法名这样的配对方法的异步调用模型来实施你的异步方案。如果这种模式没法满足您的需求,还可以使用CLR线程池来调用。下面的代码是CLR线程池调用:
    WaitCallback methodTarget = new WaitCallback(myClass.UpdateCache);
    bool isQueued = ThreadPool.QueueUserWorkItem(methodTarget);
建议三:避免线程阻塞
    很明显这点意味着用于响应的工作线程越来越少了。
建议四:如果你有并行工作,最好使用异步调用(Avoid Asynchronous Calls Unless You Have Additional Parallel Work)
    这一点要考虑到异步编程模型,看上去你好像利用了一个工作线程在进行异步操作,实际上是另外一个线程在替你完成这件事,而工作线程在你去完成并行操作的同时将空闲下来去处理其他的请求或者其他的异步调用。当异步操作返回结果之后才会返回给工作线程进行结果的返回操作。通过获得平均时间内更多的响应工作线程数量,从而提高了应用程序的吞吐量。
相关参考:
1、《Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code》( http://msdn2.microsoft.com/zh-cn/magazine/cc164128(en-us).aspx )

建议五:合理管理资源
    资源的匮乏将是影响性能的一大因素,匮乏资源的程序必将性能低下。考虑以下的几点,可以利于管理资源。
    a.资源池:ADO.NET提供了内建的数据库连接池,你的任何一次调用都将自动地连接到池里采用已经初始化过的连接资源,当你使用完后它又会回到池中待命。因此你不应该长时间地霸占它也不应该将它缓存起来,这样的结果只会导致别的请求可能因为池中没有连接对象而无法访问数据库,而事实上这些资源都是被你无端地浪费了。
    b.明确地调用关闭方法释放你有限的资源:这样的资源通常包括向数据库连接对象或者文件操作这样的共享稀缺资源的操作,明确地将其关闭将减轻内存的压力,也使资源更早地等待重用。通常可以使用try/catch/finally块将关闭的操作放在finally中,这样将保证不管是否发生异常都能关闭该稀缺对象。在C#中还可将IDisposiable接口的对象用在using(××××)块中,它将在using块执行结束后调用××××的Dispose方法。
    c.不要缓存池资源,这一点在a中已经有所提及。它们通常包括:数据库连接,网络连接,企业服务池对象等。
    d.了解我们的编程方式将有助于理解资源管理:.NET的程序通常通过GC(垃圾回收)来释放资源,GC并不是及时地释放资源,而是有一定的时间拖延的,而且就其机制本身而言也比手动释放要来得慢。通常这不会带来性能问题,但起码这是一个可以改善的点。比如用string的+=方法来将很多的字符串累加,这将无形中创建了很多的临时对象,释放这些临时对象就将增加GC的压力从而导致性能问题,.NET推荐的做法是在执行大量的字符串叠加的时候使用stringBuilder的方式进行,这样不仅仅是在字符串叠加的操作上效率更高,而且更为重要的是减轻了GC的压力。于此类似的还有String.Split方法。当出现此类问题的时候我们可以通过CLR Profiler和系统监视器来查看具体的性能问题。
    e.尽量缩短占用资源的时间:比较推荐尽量迟的地获得资源的使用,而不要在程序刚开始就获得它,而到真正使用它的这段时间内导致了总的可用资源的数量的减少。尽早地将其释放也是一种良好的习惯。没用就将其释放,霸占着它只会带来不良的后果。比如数据库连接、网络连接以及事务模块等。
    f.避免每次请求都使用最小的安全性以及最大的资源共享:应该验证一下用户的身份,不能总是让它们使用Web应用程序进程或者使用默认的系统帐户来进行操作那些像事件日志或者数据库等资源。

建议六、页面成为性能的重点
    ASP.NET页面与*.ASPX.CS文件逻辑成为设计高性能网站的一个重中之重。下面一些建议值得遵循。
    a.精简页面尺寸,使用include的方式包含脚本,这将有利于客户端缓存它们。去除页面中的空格和Tabs等没必要的空格,这将减少页面的尺寸,这在多行表格的时候尤为有效,因为表格的标签很多,因此标签间的空隙也就比较多了。手动去除空格是不太明智的,虽然少去了CPU判断空格的性能损耗,但是却给编码工作带来了极大的不便利,而因此所获得的性能提升就显得没有什么份量了。可以使用ISAPI来过滤这些不必要的字符或者使用HttpModule来进行处理。相比之下ISAPI的性能更优,但是却对编写ISAPI的程序员提出了更高的要求。可以考虑使用IIS压缩。启用IIS压缩的方式在后面这篇微软的官方帮助中有详细的步骤,并简要阐释了部分基本原理(《HOW TO: Enable ASPX Compression in IIS》http://support.microsoft.com/default.aspx?scid=kb;en-us;322603 )。另外还有几点值得注意的是减少ViewState的使用,这一点也已经被大多数的程序员所接受了。在没有用到ViewState的情况下最好将它们关闭。减少图形的使用或者用压缩图形,这一点也很容易理解。用css文件而不是将样式代码散布在网页中,这一点和脚本一样,可以起到客户端缓存的作用。但有时候很多地方类似淘宝这样的站点限制用户使用这种include的方式是出于安全等方面的考虑,但同时牺牲的就是一点的性能了。另外在模板控件的子项如Repeater中的控件要特别注意控件名的长度,它们都将用于动态生成UniqueID和ClientID,比如每个名字有10个字符,每行3个控件的话,那么100行记录,但若每个名字只有2个字符,则这些名字所带来的数据量在(10-2)×3×100=2400个字符,而这些字符对页面性能的提高就是显而易见的了。
    b.启用缓冲。缓冲所带来的问题就是对于一个较大的页面,可能用户需要等待才能看到数据。当运行ASP.NET应用程序的时候,ASP.NET的工作线程首先会用响应缓冲的形式发送响应给IIS。在ISAPI过滤器运行之后,IIS接收到缓冲响应。这些缓冲响应时31KB大小,在此之后IIS才发送实际的响应给客户端。如果缓冲不可用的话,31KB缓冲将被代替,取而代之的则是应用程序只能发送少量的字符给缓冲,这将导致在ASP.NET和IIS两方面都增加了CPU的消耗,更戏剧性地带来内存的大量消耗。

2008年4月19日星期六

看看属性被变异(编译)后的样子……

准备工作

其实没有什么准备工作,不过可以看看《关于打开ILDASM的方法(2种)》,或许对您有用。

 

前言

本来没有这个前言的,刚才自己看了标题,感觉有属性大全的味道,顺便改了标题,很可惜这里不讲那些你需要基础知识,那些知识您可以从MSDN获取。本文究竟讲些什么呢?本文其实没讲什么,就想看看属性被“编译”(变异)后的样子……

 

关于属性

get、set访问器

在使用了get、set访问器后,同类中不能够定义名称为get_[PropertyName]()与set_[PropertyName](TypeOfProperty value)这样的方法,否则将遭遇编译错误,如下图所示:

image

通过ILDASM工具查看后方可知道,get和set访问器会被编译器编译成依照上述规则而转换的公有方法供外部调用,这时我们若定义了相同签名的方法体将导致编译器的编译结果违背最基本的定义规则。

image 

自动实现的属性

众所周知在.NET 3.0的新版本中增加了自动实现的属性(automatically implemented property)语言特性。这将带来形如下面这样的属性声明方式:

        public int DotNet3Field1
{
get;
set;
}


那么它将如何编译呢?同样用ILDASM查看:



image



在图中我们可以很容易看到它通过了'<PropertyName>k__BackingField'的方式进行了声明。关于自动实现的属性的限制,请参看《自动实现的属性》而这样的方式避免了之前编译错误的尴尬,因为由编译器生成的字段名不符合我们的语言规范。



索引器



为了说明问题以下仅依照索引器的定义规范实现索引器,但不保证索引器能够正常工作:



        public int this[int index]
{
get {
return 0;
}
set {
}
}


image



仔细对比就可以看见多了



.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = (01 00 04 49 74 65 6D 00 00)



get_Item : int32(int32)



set_Item : void(int32,int32)



Item : instance int32(int32)



以下分别是get_Item和set_Item的IL代码



.method public hidebysig specialname instance int32 
get_Item(int32 index) cil managed
{
// 代码大小 7 (0x7)
.maxstack 1
.locals init ([0] int32 CS$1$0000)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
} // end of method BaseClass::get_Item

.method public hidebysig specialname instance void
set_Item(int32 index,
int32 'value') cil managed
{
// 代码大小 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method BaseClass::set_Item


因此其实索引器与属性之间的唯一差别仅在于参数的个数上,当然外部表现还有那个中括号。

2008年3月28日星期五

看看这个“指飘器”,可没少写代码……

可爱的“指飘器”光荣测试,很庆幸在Google这个页面中还是很不错滴……

2008年3月27日星期四

[翻译]注册协议(Register Protocol)

原文链接http://kb.mozillazine.org/Register_protocol

协议是一个用来通过连接来发送、接收和处理信息的方法。普通常见的浏览器协议包括http、https、ftp和mailto。为了你能够发送一个指定的协议来查看信息,你必须注册它们。如果你输入一个未知协议(如foo)的URL在你的定位符工具栏中,你将接收到一个这样的消息:“FireFox不知道如何打开这个地址,因为协议(foo)并不关联于任何程序或者在Mozilla Suite/SeaMonkey中,foo不是一个已经注册的协议”。(译注1)

一旦注册,协议就可以用你指定的程序进行处理,就像你的浏览器或者第三方的阅读器。这意味着一个超级链接(例如:foo://fred)可以使用协议foo来打开文件fred。(译注2)

内容:(以下链接指向原文)

[hide]

 

Windows

在Windows中,协议注册时写入操作系统的。例如,当你设置一个网页浏览器为默认浏览器,操作系统将注册http、https和ftp协议并且将它们关联到这个网页浏览器;当你在邮件消息或者一个互联网快捷方式中打开http、https和ftp链接的时候,这个URL将通过这个网页浏览器来打开。相似的,当你设置默认的邮件客户端,操作系统将注册mailto协议,以至于在浏览器中打开mailto链接的时候会打开这个关联的邮件应用程序。

注意: Mozilla Suite/SeaMonkey integrates mail and browser functions以至于一个邮件消息的网页链接(例如:http://链接)将在SeaMonkey浏览器窗体中打开,“mailto:”链接将在SeaMonkey Mail中打开,而不管操作系统中协议处理器的相关设置。

其他协议可以通过配置一个安装的程序被注册为关联到一个外部应用程序来处理相关内容。例如:你可以通过RealPlayer的“偏好”->“内容”->“媒体类型”设置为RTSP URLs注册相关的处理器为RealPlayer(Real-Time Streaming Protocol)。接下来当你再次打开rtsp://链接的时候,Firefox或者Mozilla Suite/SeaMonkey将在应用程序启动前询问你是否确定这个外部的协议请求。(译注3)

同样也可以通过创建一个像下面这样的.reg文件来注册协议,将它们改成文件:

REGEDIT4

[HKEY_CLASSES_ROOT\foo]
@="URL:foo Protocol"
"URL Protocol"=""

[HKEY_CLASSES_ROOT\foo\shell]

[HKEY_CLASSES_ROOT\foo\shell\open]

[HKEY_CLASSES_ROOT\foo\shell\open\command]
@="\"C:\\Program Files\\Application\\program.exe\" \"%1\""


将foo替换为你指定的希望运行的程序C:\\Program Files\\Application\\program.exe。


注意:两个反斜干(\\)以及转义的引号(\")不应该被替换(必须的)。保存文件,然后双击它们以让他们插入Windows注册表。看看附加信息《Registering an Application to a URL Protocol


 


Linux和Mac


输入about:config到地址栏并敲入回车。



右键->新建->布尔->名字:网络.协议.处理器.外部.foo->值->true(用你指定的协议还原foo)



(英文版)Right-click -> New -> Boolean -> Name: network.protocol-handler.external.foo -> Value -> true (Replacing foo with the protocol you're specifying)



右键->新建->字符->名字:网络.协议.处理器.应用.foo->值->/path/to/app(用你指定的协议还原foo,并且path/to/app部分用你希望执行的应用程序路径来替换)



(英文版)Right-click -> New -> String -> Name: network.protocol-handler.app.foo -> Value -> /path/to/app (Replacing foo with the protocol you're specifying and /path/to/app with the path to the application you want to run.



确保network.protocol-handler.expose-all设置为true。(译注4)



 



解决纷争



(Linux和Mac)如果设置偏好的时候路径或者名字不正确,FireFox将显示错误为:“协议(foo)没有与任何程序关联。”(bug 312953)



如果你在Firefox使用mailto协议没有关联任何程序,请重新设置你的默认邮件客户端(default mail client.)。



默认情况下不安全的协议将被忽略(bug 173010).



 



关联





 



外部链接



The protocol is not associated with any program (Firefox Support)



 



译注



1.因为原文发布于mozillzaine,所以其以firefox为示例。



2.事实上,不是打开名为fred的文件。



3.默认安全设置下的Internet Explorer将不会询问,而Firefox将会询问。



4.如图所示



UrlProtocolFireFoxNetworkProtocol-HandlerExpose-All

[翻译]将应用程序注册为URL协议(Registering an Application to a URL Protocol)

原文链接:Registering an Application to a URL Protocol(http://msdn2.microsoft.com/en-us/library/aa767914.aspx

文章《About Asynchronous Pluggable Protocols》(中译[翻译]关于“异步可插协议”(About Asynchronous Pluggable Protocols(APPs)))描述了如何为一个新的协议开发处理程序(处理器handlers)。在一些案例中,可能会描述如何调用另外一个应用程序来处理自定义协议(custom protocol)。注册已经存在的应用程序为一个URL协议处理器即可。一旦应用程序被成功地启动,我们可以使用命令行参数来重新找回URL来启动它。

  • 注册应用程序来处理自定义协议
  • 启动处理程序
  • 示例
  • 相关主题

注册应用程序来处理自定义协议

你必须添加一个新的key以及相关的value到HKEY_CLASSES_ROOT中,来使应用程序可以处理特殊的URL协议。

新注册的key必须与协议scheme相匹配才可以被添加。例如,增加一个“alert:”协议,被增加到HKEY_CLASSES_ROOT的key必须是alert。在这个新的key之下,默认的字符串value将显示新协议的名字,并且URL协议字符串value将包含协议特有的信息或者空字符串。Keys将同样被添加到DefaultIconshell中。

默认的DefaultIcon key的字符串value必须是新URL协议图标文件名的路径。(译注1:DefaultIcon key

shell key之下,一个key使用一个动词(就像open)将被添加。一个command(命令) key和一个DDEEXEC(动态数据交换执行) key都是使用动词来添加的。这command和DDEEXEC keys之后的values都是用来调用(或者启动)处理新协议的应用程序。

 

启动处理程序

当一个用户点击一个注册了你的自定义URL协议的链接后,Windows Internet Explorer(IE)启动注册的URL协议的处理器。如果指定shellopen命令在注册表中包含一个%1参数的话,Internet Explorer传递这个URI给注册协议的处理器。这最后的统一资源标识符(URI)被编码(%1);即16进制换码符被转换为等价的UTF-16字符。例如,用%20字符串取代空格。

安全警示:应用程序处理URL协议必须全力面对恶意数据。因为处理程序接收来自不信任源的数据,URL和其它参数值传递给应用程序可能包含的恶意数据企图使用处理程序。因此,处理程序可以首先启动基于外部数据的空闲行为确认这些行为以及它们的用户。

注意:另外,处理程序将要有能力处理URLs有可能太长或者包含意想不到(或者多余的)字符串。更多信息,请参看《Writing Secure Code

 

示例

接下来的例子演示如何注册alert.exe应用程序,来处理alert协议。

HKEY_CLASSES_ROOT
alert

(Default) = "URL:Alert Protocol"
URL Protocol = ""
DefaultIcon
(Default) = "alert.exe"
shell
open
command

(Default) = "C:\Program Files\Alert\alert.exe" "%1"


增加这些设置信息到注册表,尝试导航到像“alert:Hello%20World”这样的URLs中,将会尝试启动alert.exe程序并且在命令行中传递“Hello World”


下面的代码包含了一个简单的C#控制台应用程序演示了一种实现alert协议处理程序的方式。



using System;
using System.Collections.Generic;
using System.Text;

namespace Alert1
{
class Program
{
static string ProcessInput(string s)
{
// TODO Verify and validate the input
// string as appropriate for your application.
// return s;
}

static void Main(string[] args)
{
Console.WriteLine("Alert.exe invoked with the following parameters.\r\n");
Console.WriteLine("Raw command-line: \n\t" + Environment.CommandLine);

Console.WriteLine("\n\nArguments:\n");
foreach (string s in args)
{
Console.WriteLine("\t" + ProcessInput(s));
}
Console.WriteLine("\nPress any key to continue...");
Console.ReadKey();
}
}
}


 


相关主题



  • About Asynchronous Pluggable Protocols


  • Debugging Tips

     



    译注



    2.详细步骤:




    1. 编写如上所示的控制台程序,并记录下exe所在的全路径;


    2. 开始->运行->输入“REGEDIT”确定,以启动“注册表编辑器”;


    3. 找到(通常是第一个)HKEY_CLASSES_ROOT节点,右键新建项,输入你期望的协议名,如GoCool;


    4. 同样方式建立如下所示层级结构:

      UrlProtocol


    5. 在浏览器执行:

      UrlProtocolResult



    值得注意的是参数会默认以“,”开始,不过没关系,能够得到所有的URL字符串,对我们来说,其他内容也就是一些老调重弹了。这个“,”就是在设置command中的那个"……","%1"中的这个逗号。也可以用“C:\GoCoolCenter\MyCSharpProject\ConsoleApplication\CA_RegisteringHandlingCustomProtocol\CA_RegisteringHandlingCustomProtocol\bin\Debug\CA_RegisteringHandlingCustomProtocol.exe "%1"”的语句来代替之前的语句。去掉“"%1"”两边的引号,之后的参数将以空格进行分隔输出。


  • [翻译]调试技巧(Debug Tips)

    原文链接:http://msdn2.microsoft.com/en-us/library/aa767915(VS.85).aspx

    文章提供了一些可插协议(pluggable protocols)处理异常和理解生成的HRESULT错误码的技巧。

     

    处理异常

    首先说说Windows Internet Explorer7,Urlmon.dll将安静地处理这些由可插协议引起的异常。开发者在这种配置下需要使用调试工具在异常通过Urlmon.dll的时候试着调试这些自定义处理器。

    IE7所带的Urlmon.dll版本将不再处理异常。开发者必须在可插协议中自行实现一个全局的异常处理程序。

     

    错误代码

    一些函数和方法返回非标准的HRESULT值,就像IInternetSecurityManager::SetZoneMapping。许多这些返回值是由宏生成的,它们设置低位为有效的Microsoft Win32错误码将高位设置为8007。

    [翻译]关于“异步可插协议”(About Asynchronous Pluggable Protocols(APPs))

    原文链接:http://msdn2.microsoft.com/en-us/library/aa767916(VS.85).aspx

    异步可插协议允许开发者创建可插协议处理器,MIME过滤器,以及命名空间处理器工作在微软IE4.0浏览器以及更高版本或者URL moniker中。这篇文章涵盖了Urlmon.dll动态链接库所公开(输出)的可插协议诸多功能。关于Urlmon.dll针对其他APIs所公开(输出)的信息,可以查看URL Monikers和URL Security Zones文章。

    (以下链接指向原文)

  • 益处Benefits
  • 概要Scenarios
  • 先决依赖条件Prerequisites and Dependencies
  • 关于URLs以及NamespacesAbout URLs and Namespaces
  • 关于可插协议About Pluggable Protocols
  • 创建一个异步可插协议处理器Creating an Asynchronous Pluggable Protocol Handler
  • 相关主题Related Topics

     

    益处

    应用程序可以使用可插协议处理器来处理自定义URL协议方案或者为指定的MIME类型过滤数据。

    允许开发者有能力通过使用URL monikers为IE4.0以上版本和应用程序实现新的或者自定义的协议方案处理自定义URL协议方案的可插协议处理方案。(The ability to handle a custom URL protocol scheme using a pluggable protocol handler allows developers to implement new or custom protocol schemes for Internet Explorer 4.0 (and later) and for applications that use URL monikers.)Windows Internet Explorer已存在的协议如HTTP和FTP都包括在默认的协议处理器中。

    可插协议过滤器可以被用来为指定的MIME类型过滤数据。不像标准的协议处理器和可插的命名空间处理器只提供数据,可插协议过滤器不仅提供数据,还要读取数据。可插MIME过滤器通过可插协议处理器通过实现IInternetProtocolSink接口读取下载的数据。在数据被处理完之后,MIME过滤器通过IInternetProtocol接口允许系统重新得到经过处理的数据。

     

    概要

    让我们说说已经介绍的新的协议方案和你的公司想要为IE4.0以上版本用户提供的支持。可插协议允许你在新的协议方案基础上,提供能够被任何请求调用的新的协议处理程序。

    或者说,你的公司想要设计一个能够帮助他们的用户(一些家长)监管他们的孩子在互联网上阅读黄色的信息的产品。你可以设计一个可插的MIME过滤器,将它们注册为监管所有的text/*的MIME类型,并且将所有这些亵渎的东西替换成<BEEP>标签或者一些类似的东西。任何内容都有一个匹配的MIME类型,就像网页(MIME通常为text/html),将调用可插拔的MIME过滤器并将过滤后的数据传给用户。

     

    先决依赖条件

    这个文档假设你明白Microsoft Win32编程。并且你需要明白OLE和组件对象编程(COM)以及URL中的格式化和符号。更多信息可以参看RFC-1738 on Uniform Resource Locators (URL)

    使用Urlmon.dll提供的功能编译程序,确保在你的include目录下包含了Urlmon.h头文件并且Urlmon.lib类库文件在你用的的C/C++编辑器所使用的类库文件夹中。

     

    关于URLs以及Namespaces

    URL跟随的符号描述在RFC1738中,指定了协议方案跟随的指定方案的一部分(<scheme>:<scheme-specific portion>)。例如,在URL http://www.microsoft.com中,“http”是scheme,“//www.microsoft.com”是指定方案的一部分。

    这个指定方案的一部分的起始节(section)包含服务器名。这部分URL通常被引用来作为URL的命名空间。

     

    关于可插协议

    IE使用两种注册新URL协议处理器的机制。第一种方式是注册URL协议并且将它和应用程序相关联并尝试让所有的这种URL都转向指定的URL协议处理器以起到用协议启动程序的作用。(例如,注册用于处理mailto:或者new:的URL协议的应用程序)。第二种方式是使用异步可插协议API,它允许你通过映射协议方案到一个类来定义新的协议。

    关于如何为一个应用程序注册一个指定的URL协议的更多信息,请参看Registering an Application to a URL Protocol。(中译:[翻译]将应用程序注册为URL协议(Registering an Application to a URL Protocol)

    异步可插协议提供三种将协议方案映射到类的方式:

    • 永久地将一个异步可插协议处理器添加到注册表中。这个处理程序处理来自有所有列入清单的方案的任意的URL。(如HTTP,FTP等)。(译注1)
    • 注册临时的可插命名空间处理器。这个处理程序处理来自有所有特殊指定方案的任意的URL。(译注1)
    • 注册一个永久的或者临时的MIME过滤器。处理器将处理它接收的数据流并为任何指定MIME类型的资源返回数据流。

    注意:所有异步可插协议必须支持BINDF_NO_UIBINDF_SILENTOPERATION 标记。

    异步可插协议相关接口

    异步可插协议相关函数

     

    关于异步可插协议

    异步可插协议处理器是一个用于处理任何注册为协议方案的调用的线程单元COM对象。当客户端程序作出请求,Urlmon在注册表中查看协议方案并创建一个已经为这个协议方案注册的协议处理程序的实例。如果协议注册方案被成功地映射到协议处理器的类标识(CLSID),将调用IClassFactory接口的CoCreateInstance方法。协议处理器通过IClassFactory::CreateInstance函数来获得实例。

    注册自定义协议,在注册表中为协议方案添加一个key到HKEY_CLASSES_ROOT\PROTOCOLS\Handler\protocol_scheme下。在这个key名下,添加一个字符串为CLSID,必须设置这个协议处理器的CLSID。

    一个注册自定义协议方案的例子,增加一个名为example的key到注册表HKEY_CLASSES_ROOT\PROTOCOLS\Handler下。在这个新key,HKEY_CLASSES_ROOT\PROTOCOLS\Handler\example,添加字符串,CLSID,必须为这个处理程序指派CLSID。任何URLs使用形如example:的协议方案将被这个CLSID标识关联的协议处理器所处理。

    协议处理器不能使用任何微软Windows消息机制转回到线程中,协议处理器必须工作在非GUI线程中。

    注意:所有的异步可插协议必须支持the BINDF_NO_UI 和BINDF_SILENTOPERATION标记。

     

    关于异步命名空间处理器

    临时可插命名空间处理器用来处理所有用(指定)协议方案的URLs,临时可插命名空间处理器被注册在一个特殊的进程中。你可以通过IInternetSession接口注册或者取消临时可插命名空间处理器。

     

    关于可插MIME过滤器

    可插MIME过滤器是一个接收流的异步可插协议,执行一些数据操作并且返回数据流。输出流可能不同于原始的流。

    更多关于如何在IE4.0中定义MIME类型的信息,请参看MIME Type Detection in Internet Explorer

    注意:所有的异步可插MIME过滤器必须支持BINDF_NO_UI和BINDF_SILENTOPERATION标记。

    另外,之可以通过指定于URL的特定资源通过IE来调用可插的MIME过滤器。MIME将不被其他通过指定URL关联的资源调用(就像HTML页里面的图像)。

     

    永久的可插MIME过滤器

    你必须使用key HKEY_CLASSES_ROOT\PROTOCOLS\Filter\<mime_filter>以及设置了CLSID的value,在注册表中注册一个永久的可插MIME过滤器。

     

    临时可插MIME过滤器

    你可以通过IInternetSession接口注册或者取消一个临时可插MIME过滤器。

     

    创建一个异步可插协议处理器

    按以下步骤来创建一个可插协议处理器:

    1. 实现一个IInternetProtocol接口
    2. 实现一个IInternetProtocolRoot接口
    3. 实现一个IClassFactory接口
    4. 可选。实现一个IInternetProtocolInfo接口。通过事务处理器提供对HTTP协议的支持。
    5. 如果IInternetProtocolInfo已经实现了,为了可以完全地处理安全,为URL安全区域管理提供PARSE_SECURITY_URLPARSE_SECURITY_DOMAIN的支持。
    6. 为你的协议处理器写一些代码。
    7. 提供对BINDF_NO_UI和BINDF_SILENTOPERATION的支持。
    8. 在注册表HKEY_CLASSES_ROOT\PROTOCOLS\Handler下为你的协议处理器增加一个子键。
    9. 在你子键下创建一个字符串value,CLSID,将它设置为你的协议处理器。

    在创建玩协议处理器并将其添加到注册表后,任何程序都可以通过使用由Urlmon.dll提供的功能使用协议处理器。下面的步骤提供当一个拥有你注册的处理器协议方案的URL被调用的时候,Urlmon.dll和你的协议处理器之间的调用的大致的步骤:

    1.Urlmon.dll调用QueryInterface查看你的协议处理器是否实现了IInternetProtocolInfo接口。

    1_Calling the asynchronous pluggable protocol 

    2.如果IInternetProtocolInfo接口已经实现了,Urlmon.dll调用你的可插处理协议的IInternetProtocolInfo::ParseUrl 接口

    2_Calling the asynchronous pluggable protocol 

    当更新了订阅后,IE调用可插协议处理器的IInternetProtocolInfo::QueryInfo方法将QUERYOPTION 值设置为QUERY_USES_CACHE。IE订阅机制将仅支持采用IE缓存机制的可插协议并且让QUERY_USES_CACHE标志为TRUE。

    3.Urlmon.dll调用QueryInterface查看你的可插协议处理器是否支持IInternetProtocol接口

    3_Calling the asynchronous pluggable protocol

    4.Urlmon.dll通过URL调用你的可插协议处理器的IInternetProtocolRoot::Start 方法,并且传递Urlmon.dll的IInternetProtocolSinkIInternetBindInfo接口的地址。

    4_Calling the asynchronous pluggable protocol

    5.你的可插协议处理器将需要接受请求数据。首先将从Internet请求数据。

    5_Requesting the data from Internet

    6.当你的可插协议处理器开始下载数据之后,推荐处理器调用Urlmon.dll的IInternetProtocolSink::ReportData方法。

    6_Notifying the transaction handler that data is available

    7.Urlmon.dll调用你的协议的IInternetProtocol::Read方法。

    7_Reading the data

    8.你的可插协议处理器可以调用Urlmon.dll的IInternetProtocolSink::ReportProgress方法。可插协议处理器必须支持MIME类型,通过使用BINDSTATUS_MIMETYPEAVAILABLE状态码,允许一个可插MIME过滤器被调用(如果有一个为这个MIME类型注册的MIME过滤器的话)。

    8_Reporting process

    9.步骤6到步骤8将一直重复到你的协议处理程序完成请求数据的下载任务之后。

    10.你的可插协议必须调用Urlmon.dll的IInternetProtocolSink::ReportResult方法。

    10_Reporting results

    11.Urlmon.dll调用你的可插协议处理的IInternetProtocol::LockRequest方法。

    11_Locking results

    12.Urlmon.dll调用你可插协议处理程序的IInternetProtocolRoot::Terminate方法。

    12_Teminating the pluggable protocol

    13.Urlmon.dll调用你的可插协议的IInternetProtocol::Read方法知道所有的数据都被重新取回。

    13_Reading the remaining data

    14.Urlmon.dll调用你的可插协议的IInternetProtocol::UnlockRequest方法。

    14_Unlocking the request

    注意:你的可插协议的IInternetProtocol::Read方法将在调用Urlmon.dll之后持续运行,甚至所有的数据已经读取完毕还将继续。所有的一部可插协议处理程序必须做好处理这些可能的准备。

     

    创建一个可插的MIME过滤器

    一个可插的MIME过滤器本质上只是一个实现了IInternetProtocolSink接口的异步可插处理器。Urlmon.dll使用实现了IInternetProtocolSink的可插的MIME过滤器来通知Urlmon.dll数据已经被过滤了。

    另外,过滤器处理多种MIME类型必须为每一种MIME类型注册一个特殊的CLSID单独处理它们。

    1.事务处理器调用可插协MIME过滤器的IInternetProtocolRoot::Start方法。

    MIME_1_Reading the data

    2.事务处理器调用可插协MIME过滤器的InternetProtocolSink::ReportProgressIInternetProtocolSink::ReportData方法。

    MIME_2_Reading the data

    3.可插MIME过滤器调用事务处理器的IInternetProtocol::Read方法。

    MIME_3_Reading the data

    4.可插MIME过滤器调用事务处理器的IInternetProtocolSink::ReportData方法。

    MIME_4_Reading the data

    5.事务处理器调用可插协MIME过滤器的IInternetProtocol::Read方法。

    MIME_5_Reading the data 

    相关主题

     

    译注

    1.异步可插协议处理器可以被指定为在所有进程内有效,而异步可插命名空间协议处理器仅被指定为在某个具体的进程内有效。

  • 2008年2月18日星期一

    如何批量下载VS2008中公开的微软源代码

    首先要感谢微软公司在VS2008中引入了部分开源的协议使大家不必再通过Reflector等工具进行反编译,着实让大家感受到了不少便利。

    不过微软的VS2008中的开源方式存在若干不足,最重要的不足之处在于它的查看方式。微软的做法是允许用户在调试项目的时候通过断点可以进入相关的源码进行查看。(具体配置等操作见:Shawn Burke's Blog《Configuring Visual Studio to Debug .NET Framework Source Code》或者ScottGu's Blog《.NET Framework Library Source Code now available》)。

    这样方式的好处是用户不必一次性下载太多的代码,甚至有些根本永远用不到,也可以保证每次断点进来之后所得到的代码是最新的。但是这样的坏处在短期内却显而易见,就我个人经验来说,因为网络速度,特别是非美国国家可能在源代码服务器的访问速度上就更加地滞后了,因此所带来的结果就是第一次断点的时候将会导致长时间的延迟(包括访问服务器,以及从服务器下载相关文件,就以C#来说,则有pdb文件和cs文件两种,光是cs文件也就几K的大小,但是pdb文件通常上兆,再搭配上本来就不宽裕的网络环境以及并没有类似常规的多线程加速下载,这样的延迟将带来的用户体验是极差的。)虽然官方的说法是仅在第一次才会如此,但是这的确给了大家诸多不便。社区内顿时就提出了相关的想法和意见(见MSDN论坛上的相关版块)。

    感谢Kerem KusmezerJohn Robbins在开源项目NetMassDownloader(http://www.codeplex.com/NetMassDownloader)中所做的贡献,通过一个控制台应用程序实现了批量下载(batch download)微软源代码的开源项目。以下源自John Robbins在项目文档中的部分文字:

    It turns out I wasn’t the only one who had the same wishes. Kerem Kusmezer contacted me and said that he’d been hacking around with Fiddler and figured out how to download the PDBs and code from Microsoft. He asked if I I’d be interested in helping him develop the utility and I don’t think I ever wrote a response faster in my life! Kerem had already started a discussion with Shawn Burke about the legality of producing a source downloader and once we got back a very encouraging “go for it” we got to work. All the credit for the tool belongs to Kerem. He did all the really hard work of figuring out the HTTP downloads ,the PE file processing, and PDB Stream Extraction. I did the console driver, a lot of testing, and sat around amazed at how much work Kerem could accomplish in very short periods of time. I really appreciate that Kerem let me work on the project with him and being his code monkey.

    许多朋友在使用该工具的时候出现了一些小问题,下面就以我最基本的使用方式列举一个比较简单的步骤,(在codeplex上有更多关于它的高级应用)。

    步骤:

    1、正确安装VS2008并确保我们已经按Shawn Burke's Blog《Configuring Visual Studio to Debug .NET Framework Source Code》进行了正确的配置。

    2、假设我们将NetMassDownloader Release包解压到文件夹

    C:\GoCoolCenter\MyCSharpProject\SampleCode\NetMassDownloader
    我们就需要进入相关目录:


    通过命令提示符(开始->运行->cmd),若解压包放在其他盘如F盘下,则先输入


    F:


    回车,然后cd F:\GoCoolCenter\MyCSharpProject\SampleCode\NetMassDownloader



    这里我们由于在C盘则直接



    cd C:\GoCoolCenter\MyCSharpProject\SampleCode\NetMassDownloader



    这时运行:



    NetMassDownloader -d "C:\Windows\Microsoft.NET\Framework\v2.0.50727" -v



    很明显,-d之后是我们的.NET Framework的目录,这个可能需要根据我们的具体情况做出相应调整。



    之后就可以看到类似以下的代码,这时不要关闭当前命令提示符框,并保持网络连接通畅即可。



    以上总结为两句命令:




    <>

    </>







    cd C:\GoCoolCenter\MyCSharpProject\SampleCode\NetMassDownloader




    NetMassDownloader -d "C:\Windows\Microsoft.NET\Framework\v2.0.50727" -v






     



    .NET Framework 3.0和.NET Framework 3.5版本的下载代码如下


    cd C:\GoCoolCenter\MyCSharpProject\SampleCode\NetMassDownloader
    netmassdownloader -d "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5" -d "c:\program files\Reference Assemblies\Microsoft\Framework\v3.0"







    下载后的文件将保存在VS2008中设置的相应目录:





    symbols_setting


    按照NetMassDownloader的说明,经过设置后该源代码还可用于VS2005的使用。